diff options
| author | 2026-03-25 16:22:03 +0000 | |
|---|---|---|
| committer | 2026-03-25 16:22:03 +0000 | |
| commit | 311edcd50f3a84f4b860bde3cb887451d74eaa11 (patch) | |
| tree | be7aa5e9a51e636358f33b1c90637b5024b70dc3 /protocol/v0v1/server/receivepack/session.go | |
| parent | README: Split off contrib, benchmarks, remove history for now I guess, etc. (diff) | |
| signature | No signature | |
network/protocol: Rename from protocol v0.1.110
Diffstat (limited to 'protocol/v0v1/server/receivepack/session.go')
| -rw-r--r-- | protocol/v0v1/server/receivepack/session.go | 295 |
1 files changed, 0 insertions, 295 deletions
diff --git a/protocol/v0v1/server/receivepack/session.go b/protocol/v0v1/server/receivepack/session.go deleted file mode 100644 index 1ea4ab35..00000000 --- a/protocol/v0v1/server/receivepack/session.go +++ /dev/null @@ -1,295 +0,0 @@ -package receivepack - -import ( - "fmt" - "io" - "strings" - - objectid "codeberg.org/lindenii/furgit/object/id" - common "codeberg.org/lindenii/furgit/protocol/v0v1/server" -) - -// Session is one stateful server-side receive-pack protocol session. -type Session struct { - base *common.Session - supported Capabilities - negotiated Capabilities -} - -// NewSession creates one receive-pack session over one common server session. -func NewSession(base *common.Session, supported Capabilities) *Session { - return &Session{ - base: base, - supported: supported, - } -} - -// AdvertiseRefs writes one receive-pack ref advertisement. -func (session *Session) AdvertiseRefs(ad common.Advertisement) error { - return session.base.AdvertiseRefs(ad, session.supported.Tokens(session.base.Algorithm())) -} - -// ReadRequest reads one receive-pack request through optional push-options. -func (session *Session) ReadRequest() (*Request, error) { - req := &Request{} - - var sawCommands bool - - for { - frame, err := session.base.ReadFrame() - if err != nil { - return nil, err - } - - switch frame.Type { - case common.FrameFlush: - goto afterCommands - case common.FrameData: - case common.FrameDelim, common.FrameResponseEnd: - return nil, &ProtocolError{Reason: fmt.Sprintf("unexpected packet type %v", frame.Type)} - } - - payload := string(frame.Payload) - if strings.HasPrefix(payload, "shallow ") { - line := trimOneLF(payload) - - shallowID, err := parseObjectID(session.base.Algorithm(), line[len("shallow "):]) - if err != nil { - return nil, err - } - - req.Shallow = append(req.Shallow, shallowID) - - continue - } - - if strings.HasPrefix(payload, "push-cert\x00") { - if sawCommands { - return nil, &ProtocolError{Reason: "got both push certificate and unsigned commands"} - } - - capabilityTokens, err := parseCapabilityList(payload[len("push-cert\x00"):]) - if err != nil { - return nil, err - } - - requested, err := parseRequestedCapabilities( - capabilityTokens, - session.supported, - session.base.Algorithm(), - ) - if err != nil { - return nil, err - } - - req.Capabilities = requested - - cert, err := session.readPushCertificate() - if err != nil { - return nil, err - } - - req.PushCert = cert - req.Commands = append(req.Commands, cert.Commands...) - sawCommands = true - - continue - } - - line := trimOneLF(payload) - if !sawCommands && strings.Contains(line, "\x00") { - commandPart, capPart, _ := strings.Cut(line, "\x00") - - capabilityTokens, err := parseCapabilityList(capPart) - if err != nil { - return nil, err - } - - requested, err := parseRequestedCapabilities( - capabilityTokens, - session.supported, - session.base.Algorithm(), - ) - if err != nil { - return nil, err - } - - req.Capabilities = requested - line = commandPart - } - - cmd, err := parseCommand(session.base.Algorithm(), line) - if err != nil { - return nil, err - } - - req.Commands = append(req.Commands, cmd) - sawCommands = true - } - -afterCommands: - if req.Capabilities.PushOptions { - for { - frame, err := session.base.ReadFrame() - if err != nil { - return nil, err - } - - switch frame.Type { - case common.FrameFlush: - goto afterPushOptions - case common.FrameData: - req.PushOptions = append(req.PushOptions, trimOneLF(string(frame.Payload))) - case common.FrameDelim, common.FrameResponseEnd: - return nil, &ProtocolError{Reason: fmt.Sprintf("unexpected packet type %v", frame.Type)} - } - } - } - -afterPushOptions: - req.DeleteOnly = deleteOnly(req.Commands) - - req.PackExpected = len(req.Commands) > 0 && !req.DeleteOnly - - session.negotiated = req.Capabilities - - if req.Capabilities.SideBand64K { - session.base.EnableSideBand64K() - } - - return req, nil -} - -// WriteProgress writes one progress packet. -func (session *Session) WriteProgress(p []byte) error { - return session.base.WriteProgress(p) -} - -// ProgressWriter returns one chunking writer for sideband progress output. -// -// When side-band-64k was not negotiated, writes are discarded. -func (session *Session) ProgressWriter() io.Writer { - return session.base.ProgressWriter() -} - -// WriteError writes one fatal error packet. -func (session *Session) WriteError(p []byte) error { - return session.base.WriteError(p) -} - -// ErrorWriter returns one chunking writer for sideband error output. -// -// When side-band-64k was not negotiated, writes are discarded. -func (session *Session) ErrorWriter() io.Writer { - return session.base.ErrorWriter() -} - -func trimOneLF(s string) string { - return strings.TrimSuffix(s, "\n") -} - -func parseObjectID(algo objectid.Algorithm, s string) (objectid.ObjectID, error) { - id, err := objectid.ParseHex(algo, s) - if err != nil { - return objectid.ObjectID{}, &ProtocolError{ - Reason: fmt.Sprintf("invalid object id %q", s), - } - } - - return id, nil -} - -func commandIsDelete(cmd Command) bool { - return cmd.NewID == objectid.Zero(cmd.NewID.Algorithm()) -} - -func deleteOnly(commands []Command) bool { - if len(commands) == 0 { - return false - } - - for _, cmd := range commands { - if !commandIsDelete(cmd) { - return false - } - } - - return true -} - -func parseCommand(algo objectid.Algorithm, line string) (Command, error) { - fields := strings.Fields(line) - if len(fields) != 3 { - return Command{}, &ProtocolError{Reason: fmt.Sprintf("malformed command %q", line)} - } - - oldID, err := parseObjectID(algo, fields[0]) - if err != nil { - return Command{}, err - } - - newID, err := parseObjectID(algo, fields[1]) - if err != nil { - return Command{}, err - } - - return Command{OldID: oldID, NewID: newID, Name: fields[2]}, nil -} - -func (session *Session) readPushCertificate() (*PushCertificate, error) { - cert := &PushCertificate{} - inCommands := false - inSignature := false - - for { - frame, err := session.base.ReadFrame() - if err != nil { - return nil, err - } - - switch frame.Type { - case common.FrameFlush: - return nil, &ProtocolError{Reason: "unexpected flush inside push certificate"} - case common.FrameData: - case common.FrameDelim, common.FrameResponseEnd: - return nil, &ProtocolError{Reason: fmt.Sprintf("unexpected packet type %v", frame.Type)} - } - - line := string(frame.Payload) - if line == "push-cert-end\n" { - return cert, nil - } - - if !inCommands { - if line == "\n" { - inCommands = true - - continue - } - - trimmed := trimOneLF(line) - cert.HeaderLines = append(cert.HeaderLines, trimmed) - - if strings.HasPrefix(trimmed, "push-option ") { - cert.EmbeddedOption = append(cert.EmbeddedOption, trimmed[len("push-option "):]) - } - - continue - } - - if !inSignature { - trimmed := trimOneLF(line) - - cmd, err := parseCommand(session.base.Algorithm(), trimmed) - if err == nil { - cert.Commands = append(cert.Commands, cmd) - - continue - } - - inSignature = true - } - - cert.SignatureLines = append(cert.SignatureLines, trimOneLF(line)) - } -} |
