aboutsummaryrefslogtreecommitdiff
path: root/network
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-04-02 06:23:30 +0000
committerGravatar Runxi Yu2026-04-02 06:28:39 +0000
commita041d523de389b65b98a5373a8034041db2a8d83 (patch)
tree7b423dc735f463be616045f2c3c2095a7737aca7 /network
parentresearch: Add dynamic pack resources (diff)
signatureNo signature
*: Remove
Diffstat (limited to 'network')
-rw-r--r--network/doc.go5
-rw-r--r--network/protocol/doc.go2
-rw-r--r--network/protocol/pktline/append.go39
-rw-r--r--network/protocol/pktline/append_data_preserves_dst_on_error_test.go25
-rw-r--r--network/protocol/pktline/append_helpers_test.go24
-rw-r--r--network/protocol/pktline/chunk_writer.go74
-rw-r--r--network/protocol/pktline/chunk_writer_write_and_read_from_test.go60
-rw-r--r--network/protocol/pktline/constants.go12
-rw-r--r--network/protocol/pktline/decoder.go191
-rw-r--r--network/protocol/pktline/decoder_data_control_and_0004_test.go60
-rw-r--r--network/protocol/pktline/decoder_invalid_0003_test.go20
-rw-r--r--network/protocol/pktline/decoder_peek_test.go32
-rw-r--r--network/protocol/pktline/decoder_rejects_over_maximum_length_test.go22
-rw-r--r--network/protocol/pktline/decoder_resync_after_over_max_data_test.go51
-rw-r--r--network/protocol/pktline/decoder_resync_after_over_wire_max_test.go37
-rw-r--r--network/protocol/pktline/decoder_unexpected_eof_test.go21
-rw-r--r--network/protocol/pktline/doc.go2
-rw-r--r--network/protocol/pktline/encode_length_header_test.go28
-rw-r--r--network/protocol/pktline/encoder.go143
-rw-r--r--network/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go50
-rw-r--r--network/protocol/pktline/encoder_buffered_flush_behavior_test.go86
-rw-r--r--network/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go26
-rw-r--r--network/protocol/pktline/encoder_writes_frames_test.go51
-rw-r--r--network/protocol/pktline/errors.go31
-rw-r--r--network/protocol/pktline/frame.go10
-rw-r--r--network/protocol/pktline/header.go57
-rw-r--r--network/protocol/pktline/parse_length_header_test.go26
-rw-r--r--network/protocol/pktline/type.go15
-rw-r--r--network/protocol/sideband64k/append.go40
-rw-r--r--network/protocol/sideband64k/append_helpers_test.go30
-rw-r--r--network/protocol/sideband64k/append_preserves_dst_on_error_test.go34
-rw-r--r--network/protocol/sideband64k/band.go13
-rw-r--r--network/protocol/sideband64k/chunk_writer.go73
-rw-r--r--network/protocol/sideband64k/chunk_writer_write_and_read_from_test.go60
-rw-r--r--network/protocol/sideband64k/constants.go10
-rw-r--r--network/protocol/sideband64k/decoder.go162
-rw-r--r--network/protocol/sideband64k/decoder_data_control_and_keepalive_test.go78
-rw-r--r--network/protocol/sideband64k/decoder_invalid_band_test.go20
-rw-r--r--network/protocol/sideband64k/decoder_invalid_empty_payload_test.go20
-rw-r--r--network/protocol/sideband64k/decoder_malformed_pktline_test.go32
-rw-r--r--network/protocol/sideband64k/decoder_partial_read_test.go32
-rw-r--r--network/protocol/sideband64k/decoder_peek_test.go34
-rw-r--r--network/protocol/sideband64k/decoder_resync_after_over_max_data_test.go51
-rw-r--r--network/protocol/sideband64k/decoder_resync_after_over_wire_max_test.go37
-rw-r--r--network/protocol/sideband64k/decoder_unexpected_eof_test.go21
-rw-r--r--network/protocol/sideband64k/doc.go2
-rw-r--r--network/protocol/sideband64k/encoder.go103
-rw-r--r--network/protocol/sideband64k/encoder_buffered_flush_behavior_test.go59
-rw-r--r--network/protocol/sideband64k/encoder_partial_write_test.go46
-rw-r--r--network/protocol/sideband64k/encoder_set_max_data_cannot_exceed_wire_limit_test.go23
-rw-r--r--network/protocol/sideband64k/encoder_writes_frames_test.go58
-rw-r--r--network/protocol/sideband64k/errors.go27
-rw-r--r--network/protocol/sideband64k/frame.go12
-rw-r--r--network/protocol/sideband64k/frame_type.go19
-rw-r--r--network/protocol/sideband64k/helpers_test.go46
-rw-r--r--network/protocol/v0v1/doc.go2
-rw-r--r--network/protocol/v0v1/server/advertise.go53
-rw-r--r--network/protocol/v0v1/server/advertise_test.go101
-rw-r--r--network/protocol/v0v1/server/advertised_ref.go22
-rw-r--r--network/protocol/v0v1/server/doc.go2
-rw-r--r--network/protocol/v0v1/server/errors.go18
-rw-r--r--network/protocol/v0v1/server/frame.go20
-rw-r--r--network/protocol/v0v1/server/helpers.go29
-rw-r--r--network/protocol/v0v1/server/helpers_test.go28
-rw-r--r--network/protocol/v0v1/server/receivepack/capabilities.go192
-rw-r--r--network/protocol/v0v1/server/receivepack/doc.go2
-rw-r--r--network/protocol/v0v1/server/receivepack/errors.go11
-rw-r--r--network/protocol/v0v1/server/receivepack/helpers_test.go28
-rw-r--r--network/protocol/v0v1/server/receivepack/parse_test.go255
-rw-r--r--network/protocol/v0v1/server/receivepack/report_status.go185
-rw-r--r--network/protocol/v0v1/server/receivepack/report_status_test.go293
-rw-r--r--network/protocol/v0v1/server/receivepack/session.go303
-rw-r--r--network/protocol/v0v1/server/receivepack/types.go45
-rw-r--r--network/protocol/v0v1/server/session.go142
-rw-r--r--network/protocol/v0v1/server/version.go12
-rw-r--r--network/receivepack/advertise.go57
-rw-r--r--network/receivepack/capabilities_defaults.go17
-rw-r--r--network/receivepack/commands.go19
-rw-r--r--network/receivepack/doc.go3
-rw-r--r--network/receivepack/errors.go15
-rw-r--r--network/receivepack/hook.go97
-rw-r--r--network/receivepack/hooks/chain.go51
-rw-r--r--network/receivepack/hooks/doc.go2
-rw-r--r--network/receivepack/hooks/reject_force_push.go69
-rw-r--r--network/receivepack/int_test.go1095
-rw-r--r--network/receivepack/options.go71
-rw-r--r--network/receivepack/receivepack.go139
-rw-r--r--network/receivepack/results.go26
-rw-r--r--network/receivepack/service/apply.go137
-rw-r--r--network/receivepack/service/command.go26
-rw-r--r--network/receivepack/service/command_result.go13
-rw-r--r--network/receivepack/service/doc.go6
-rw-r--r--network/receivepack/service/execute.go120
-rw-r--r--network/receivepack/service/hook.go48
-rw-r--r--network/receivepack/service/hook_apply.go31
-rw-r--r--network/receivepack/service/ingest_quarantine.go81
-rw-r--r--network/receivepack/service/options.go31
-rw-r--r--network/receivepack/service/request.go15
-rw-r--r--network/receivepack/service/result.go9
-rw-r--r--network/receivepack/service/run_hook.go93
-rw-r--r--network/receivepack/service/service.go13
-rw-r--r--network/receivepack/service/service_test.go129
-rw-r--r--network/receivepack/service/update.go11
-rw-r--r--network/receivepack/version.go35
104 files changed, 0 insertions, 6544 deletions
diff --git a/network/doc.go b/network/doc.go
deleted file mode 100644
index d964997b..00000000
--- a/network/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Package network encapsulates network-facing Git packages.
-//
-// These packages implement wire formats, protocol framing, and application
-// services built on top of them.
-package network
diff --git a/network/protocol/doc.go b/network/protocol/doc.go
deleted file mode 100644
index d1e00447..00000000
--- a/network/protocol/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package protocol encapsulates network protocol implementations.
-package protocol
diff --git a/network/protocol/pktline/append.go b/network/protocol/pktline/append.go
deleted file mode 100644
index 9425e58e..00000000
--- a/network/protocol/pktline/append.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package pktline
-
-import "fmt"
-
-// AppendData appends one data frame to dst.
-//
-// Empty payload is encoded as 0004.
-func AppendData(dst, payload []byte) ([]byte, error) {
- if len(payload) > LargePacketDataMax {
- return dst, fmt.Errorf("%w: %d > %d", ErrTooLarge, len(payload), LargePacketDataMax)
- }
-
- var hdr [4]byte
-
- err := EncodeLengthHeader(&hdr, len(payload)+4)
- if err != nil {
- return dst, err
- }
-
- dst = append(dst, hdr[:]...)
- dst = append(dst, payload...)
-
- return dst, nil
-}
-
-// AppendFlushPkt appends control frame 0000 (flush-pkt).
-func AppendFlushPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '0')
-}
-
-// AppendDelimPkt appends control frame 0001 (delim-pkt).
-func AppendDelimPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '1')
-}
-
-// AppendResponseEndPkt appends control frame 0002 (response-end-pkt).
-func AppendResponseEndPkt(dst []byte) []byte {
- return append(dst, '0', '0', '0', '2')
-}
diff --git a/network/protocol/pktline/append_data_preserves_dst_on_error_test.go b/network/protocol/pktline/append_data_preserves_dst_on_error_test.go
deleted file mode 100644
index d127fb39..00000000
--- a/network/protocol/pktline/append_data_preserves_dst_on_error_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package pktline_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestAppendDataPreservesDstOnError(t *testing.T) {
- t.Parallel()
-
- orig := []byte("seed")
- dst := append([]byte(nil), orig...)
-
- out, err := pktline.AppendData(dst, bytes.Repeat([]byte{'x'}, pktline.LargePacketDataMax+1))
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- if !bytes.Equal(out, orig) {
- t.Fatalf("got %q, want %q", string(out), string(orig))
- }
-}
diff --git a/network/protocol/pktline/append_helpers_test.go b/network/protocol/pktline/append_helpers_test.go
deleted file mode 100644
index 259ada16..00000000
--- a/network/protocol/pktline/append_helpers_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package pktline_test
-
-import (
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestAppendHelpers(t *testing.T) {
- t.Parallel()
-
- out, err := pktline.AppendData(nil, []byte("ok"))
- if err != nil {
- t.Fatalf("AppendData: %v", err)
- }
-
- out = pktline.AppendFlushPkt(out)
- out = pktline.AppendDelimPkt(out)
- out = pktline.AppendResponseEndPkt(out)
-
- if got, want := string(out), "0006ok000000010002"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/pktline/chunk_writer.go b/network/protocol/pktline/chunk_writer.go
deleted file mode 100644
index f009978e..00000000
--- a/network/protocol/pktline/chunk_writer.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package pktline
-
-import "io"
-
-// ChunkWriter packetizes arbitrary stream bytes into data pkt-lines.
-// It never writes control packets automatically.
-//
-// Labels: MT-Unsafe.
-type ChunkWriter struct {
- enc *Encoder
-}
-
-// NewChunkWriter creates a chunking adapter over enc.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewChunkWriter(enc *Encoder) *ChunkWriter {
- return &ChunkWriter{enc: enc}
-}
-
-// Write splits p into data frames not larger than enc's maxData.
-//
-// It implements io.Writer.
-func (cw *ChunkWriter) Write(p []byte) (int, error) {
- total := 0
- maxData := cw.enc.effectiveMaxData()
-
- for len(p) > 0 {
- n := min(len(p), maxData)
-
- err := cw.enc.WriteData(p[:n])
- if err != nil {
- return total, err
- }
-
- total += n
- p = p[n:]
- }
-
- return total, nil
-}
-
-// ReadFrom reads from r and writes pkt-line data frames to the encoder.
-//
-// It implements io.ReaderFrom.
-func (cw *ChunkWriter) ReadFrom(r io.Reader) (int64, error) {
- buf := make([]byte, cw.enc.effectiveMaxData())
-
- var total int64
-
- for {
- n, err := r.Read(buf)
- if n > 0 {
- werr := cw.enc.WriteData(buf[:n])
- if werr != nil {
- return total, werr
- }
-
- total += int64(n)
- }
-
- if err != nil {
- if err == io.EOF {
- return total, nil
- }
-
- return total, err
- }
- }
-}
-
-// Flush flushes buffered output in the underlying transport.
-func (cw *ChunkWriter) Flush() error {
- return cw.enc.Flush()
-}
diff --git a/network/protocol/pktline/chunk_writer_write_and_read_from_test.go b/network/protocol/pktline/chunk_writer_write_and_read_from_test.go
deleted file mode 100644
index efe19e23..00000000
--- a/network/protocol/pktline/chunk_writer_write_and_read_from_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestChunkWriterWriteAndReadFrom(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
-
- enc := pktline.NewEncoder(bw)
- enc.SetMaxData(3)
- cw := pktline.NewChunkWriter(enc)
-
- n, err := cw.Write([]byte("abcdefg"))
- if err != nil {
- t.Fatalf("Write: %v", err)
- }
-
- if n != 7 {
- t.Fatalf("Write n=%d, want 7", n)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0007abc0007def0005g"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
-
- rn, err := cw.ReadFrom(strings.NewReader("wxyz"))
- if err != nil {
- t.Fatalf("ReadFrom: %v", err)
- }
-
- if rn != 4 {
- t.Fatalf("ReadFrom n=%d, want 4", rn)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0007wxy0005z"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/pktline/constants.go b/network/protocol/pktline/constants.go
deleted file mode 100644
index 811eb3c6..00000000
--- a/network/protocol/pktline/constants.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package pktline
-
-const (
- // DefaultPacketMax is a conservative packet size commonly used by
- // line-oriented protocol messages.
- DefaultPacketMax = 1000
- // LargePacketMax is the maximum on-wire packet size including the
- // 4-byte hexadecimal length header.
- LargePacketMax = 65520
- // LargePacketDataMax is the maximum payload size in one packet.
- LargePacketDataMax = LargePacketMax - 4
-)
diff --git a/network/protocol/pktline/decoder.go b/network/protocol/pktline/decoder.go
deleted file mode 100644
index 682dd164..00000000
--- a/network/protocol/pktline/decoder.go
+++ /dev/null
@@ -1,191 +0,0 @@
-package pktline
-
-import (
- "errors"
- "fmt"
- "io"
-)
-
-// ReadOptions controls decoding behavior.
-type ReadOptions struct {
- // ChompLF removes one trailing '\n' from PacketData payloads.
- ChompLF bool
-}
-
-// Decoder reads pkt-line frames from an io.Reader.
-//
-// It is advisable to supply a buffered reader.
-//
-// It preserves frame boundaries and supports one-frame lookahead via PeekFrame.
-//
-// Labels: MT-Unsafe.
-type Decoder struct {
- r io.Reader
- maxData int
- opts ReadOptions
-
- peeked bool
- peek Frame
- peekErr error
-}
-
-// NewDecoder creates a decoder over r.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewDecoder(r io.Reader, opts ReadOptions) *Decoder {
- return &Decoder{
- r: r,
- maxData: LargePacketDataMax,
- opts: opts,
- }
-}
-
-// SetMaxData sets maximum payload size accepted for one data packet.
-//
-// Non-positive n resets to LargePacketDataMax.
-func (d *Decoder) SetMaxData(n int) {
- if n <= 0 {
- d.maxData = LargePacketDataMax
-
- return
- }
-
- d.maxData = n
-}
-
-func cloneFrame(f Frame) Frame {
- if f.Type != PacketData {
- return Frame{Type: f.Type}
- }
-
- out := Frame{Type: f.Type}
- if f.Payload != nil {
- out.Payload = append([]byte(nil), f.Payload...)
- }
-
- return out
-}
-
-// ReadFrame reads one frame.
-//
-// 0000 is a PacketFlush
-// 0001 is a PacketDelim
-// 0002 is a PacketResponseEnd
-// 0004 is a PacketData with empty payload
-//
-// 0003 and malformed headers return *ProtocolError.
-func (d *Decoder) ReadFrame() (Frame, error) {
- if d.peeked {
- d.peeked = false
-
- return cloneFrame(d.peek), d.peekErr
- }
-
- return d.readFrame()
-}
-
-// PeekFrame returns the next frame without consuming it.
-//
-// A subsequent ReadFrame returns the same frame.
-func (d *Decoder) PeekFrame() (Frame, error) {
- if !d.peeked {
- d.peek, d.peekErr = d.readFrame()
- d.peeked = true
- }
-
- return cloneFrame(d.peek), d.peekErr
-}
-
-func (d *Decoder) readFrame() (Frame, error) {
- var hdr [4]byte
-
- _, err := io.ReadFull(d.r, hdr[:])
- if err != nil {
- if errors.Is(err, io.EOF) {
- return Frame{}, io.EOF
- }
-
- if errors.Is(err, io.ErrUnexpectedEOF) {
- return Frame{}, io.ErrUnexpectedEOF
- }
-
- return Frame{}, err
- }
-
- n, err := ParseLengthHeader(hdr)
- if err != nil {
- return Frame{}, &ProtocolError{Header: hdr, Reason: err.Error()}
- }
-
- switch n {
- case 0:
- return Frame{Type: PacketFlush}, nil
- case 1:
- return Frame{Type: PacketDelim}, nil
- case 2:
- return Frame{Type: PacketResponseEnd}, nil
- case 3:
- return Frame{}, &ProtocolError{Header: hdr, Reason: "invalid pkt-line length 3"}
- }
-
- if n < 4 {
- return Frame{}, &ProtocolError{Header: hdr, Reason: fmt.Sprintf("invalid pkt-line length %d", n)}
- }
-
- if n > LargePacketMax {
- perr := &ProtocolError{Header: hdr, Reason: fmt.Sprintf("pkt-line length %d exceeds max %d", n, LargePacketMax)}
-
- err := d.discardPayload(n - 4)
- if err != nil {
- return Frame{}, errors.Join(perr, err)
- }
-
- return Frame{}, perr
- }
-
- payloadLen := n - 4
- if payloadLen > d.maxData {
- serr := fmt.Errorf("%w: %d > %d", ErrTooLarge, payloadLen, d.maxData)
-
- err := d.discardPayload(payloadLen)
- if err != nil {
- return Frame{}, errors.Join(serr, err)
- }
-
- return Frame{}, serr
- }
-
- payload := make([]byte, payloadLen)
-
- _, err = io.ReadFull(d.r, payload)
- if err != nil {
- if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
- return Frame{}, io.ErrUnexpectedEOF
- }
-
- return Frame{}, err
- }
-
- if d.opts.ChompLF && len(payload) > 0 && payload[len(payload)-1] == '\n' {
- payload = payload[:len(payload)-1]
- }
-
- return Frame{Type: PacketData, Payload: payload}, nil
-}
-
-func (d *Decoder) discardPayload(n int) error {
- if n <= 0 {
- return nil
- }
-
- _, err := io.CopyN(io.Discard, d.r, int64(n))
- if err == nil {
- return nil
- }
-
- if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
- return io.ErrUnexpectedEOF
- }
-
- return err
-}
diff --git a/network/protocol/pktline/decoder_data_control_and_0004_test.go b/network/protocol/pktline/decoder_data_control_and_0004_test.go
deleted file mode 100644
index ab92b603..00000000
--- a/network/protocol/pktline/decoder_data_control_and_0004_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package pktline_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderDataControlAnd0004(t *testing.T) {
- t.Parallel()
-
- input := "0006a\n0004000100020000"
- dec := pktline.NewDecoder(strings.NewReader(input), pktline.ReadOptions{ChompLF: true})
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #1: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "a" {
- t.Fatalf("frame #1 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || len(f.Payload) != 0 {
- t.Fatalf("frame #2 = %#v, want empty data", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #3: %v", err)
- }
-
- if f.Type != pktline.PacketDelim {
- t.Fatalf("frame #3 type = %v, want PacketDelim", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #4: %v", err)
- }
-
- if f.Type != pktline.PacketResponseEnd {
- t.Fatalf("frame #4 type = %v, want PacketResponseEnd", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #5: %v", err)
- }
-
- if f.Type != pktline.PacketFlush {
- t.Fatalf("frame #5 type = %v, want PacketFlush", f.Type)
- }
-}
diff --git a/network/protocol/pktline/decoder_invalid_0003_test.go b/network/protocol/pktline/decoder_invalid_0003_test.go
deleted file mode 100644
index 716da3f2..00000000
--- a/network/protocol/pktline/decoder_invalid_0003_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderInvalid0003(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0003"), pktline.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/network/protocol/pktline/decoder_peek_test.go b/network/protocol/pktline/decoder_peek_test.go
deleted file mode 100644
index a67da881..00000000
--- a/network/protocol/pktline/decoder_peek_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package pktline_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderPeek(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0005x0000"), pktline.ReadOptions{})
-
- f, err := dec.PeekFrame()
- if err != nil {
- t.Fatalf("PeekFrame: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "x" {
- t.Fatalf("peek frame = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "x" {
- t.Fatalf("read frame = %#v", f)
- }
-}
diff --git a/network/protocol/pktline/decoder_rejects_over_maximum_length_test.go b/network/protocol/pktline/decoder_rejects_over_maximum_length_test.go
deleted file mode 100644
index 357bfc36..00000000
--- a/network/protocol/pktline/decoder_rejects_over_maximum_length_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderRejectsOverMaximumLength(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("fffe"), pktline.ReadOptions{})
- dec.SetMaxData(70000)
-
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/network/protocol/pktline/decoder_resync_after_over_max_data_test.go b/network/protocol/pktline/decoder_resync_after_over_max_data_test.go
deleted file mode 100644
index 42a7572e..00000000
--- a/network/protocol/pktline/decoder_resync_after_over_max_data_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderResyncAfterOverMaxData(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("abcd"))
- if err != nil {
- t.Fatalf("WriteData #1: %v", err)
- }
-
- err = enc.WriteData([]byte("z"))
- if err != nil {
- t.Fatalf("WriteData #2: %v", err)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- dec := pktline.NewDecoder(bytes.NewReader(b.Bytes()), pktline.ReadOptions{})
- dec.SetMaxData(1)
-
- _, err = dec.ReadFrame()
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/network/protocol/pktline/decoder_resync_after_over_wire_max_test.go b/network/protocol/pktline/decoder_resync_after_over_wire_max_test.go
deleted file mode 100644
index 9413823b..00000000
--- a/network/protocol/pktline/decoder_resync_after_over_wire_max_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package pktline_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderResyncAfterOverWireMax(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- _, _ = b.WriteString("ffff")
- _, _ = b.Write(bytes.Repeat([]byte{'a'}, 65531))
- _, _ = b.WriteString("0005z")
-
- dec := pktline.NewDecoder(bytes.NewReader(b.Bytes()), pktline.ReadOptions{})
- dec.SetMaxData(70000)
-
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != pktline.PacketData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/network/protocol/pktline/decoder_unexpected_eof_test.go b/network/protocol/pktline/decoder_unexpected_eof_test.go
deleted file mode 100644
index e1bf4457..00000000
--- a/network/protocol/pktline/decoder_unexpected_eof_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "io"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestDecoderUnexpectedEOF(t *testing.T) {
- t.Parallel()
-
- dec := pktline.NewDecoder(strings.NewReader("0006a"), pktline.ReadOptions{})
-
- _, err := dec.ReadFrame()
- if !errors.Is(err, io.ErrUnexpectedEOF) {
- t.Fatalf("got err %v, want io.ErrUnexpectedEOF", err)
- }
-}
diff --git a/network/protocol/pktline/doc.go b/network/protocol/pktline/doc.go
deleted file mode 100644
index 3f7cca89..00000000
--- a/network/protocol/pktline/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package pktline implements the pkt-line format specified in gitprotocol-common(5).
-package pktline
diff --git a/network/protocol/pktline/encode_length_header_test.go b/network/protocol/pktline/encode_length_header_test.go
deleted file mode 100644
index 38a980f0..00000000
--- a/network/protocol/pktline/encode_length_header_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestEncodeLengthHeader(t *testing.T) {
- t.Parallel()
-
- var hdr [4]byte
-
- err := pktline.EncodeLengthHeader(&hdr, 4)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if got := string(hdr[:]); got != "0004" {
- t.Fatalf("got %q, want %q", got, "0004")
- }
-
- err = pktline.EncodeLengthHeader(&hdr, pktline.LargePacketMax+1)
- if !errors.Is(err, pktline.ErrInvalidLength) {
- t.Fatalf("got err %v, want ErrInvalidLength", err)
- }
-}
diff --git a/network/protocol/pktline/encoder.go b/network/protocol/pktline/encoder.go
deleted file mode 100644
index a7b17108..00000000
--- a/network/protocol/pktline/encoder.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package pktline
-
-import (
- "fmt"
- "io"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
-)
-
-// Encoder writes pkt-line frames to a flush-capable output transport.
-//
-// It writes exactly one frame per method call and does not auto-chunk data.
-//
-// Labels: MT-Unsafe.
-type Encoder struct {
- w iowrap.WriteFlusher
- maxData int
-}
-
-// NewEncoder creates an encoder over w.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewEncoder(w iowrap.WriteFlusher) *Encoder {
- return &Encoder{
- w: w,
- maxData: LargePacketDataMax,
- }
-}
-
-// SetMaxData sets the maximum payload size accepted by WriteData.
-//
-// Non-positive n resets to LargePacketDataMax.
-func (e *Encoder) SetMaxData(n int) {
- if n <= 0 {
- e.maxData = LargePacketDataMax
-
- return
- }
-
- e.maxData = n
-}
-
-func writeAll(w io.Writer, b []byte) error {
- for len(b) > 0 {
- n, err := w.Write(b)
- if err != nil {
- return err
- }
-
- if n <= 0 {
- return io.ErrShortWrite
- }
-
- b = b[n:]
- }
-
- return nil
-}
-
-// WriteData writes one data frame.
-//
-// Empty payload is encoded as 0004.
-func (e *Encoder) WriteData(p []byte) error {
- maxData := e.effectiveMaxData()
- if len(p) > maxData {
- return fmt.Errorf("%w: %d > %d", ErrTooLarge, len(p), maxData)
- }
-
- var hdr [4]byte
-
- err := EncodeLengthHeader(&hdr, len(p)+4)
- if err != nil {
- return err
- }
-
- err = writeAll(e.w, hdr[:])
- if err != nil {
- return err
- }
-
- return writeAll(e.w, p)
-}
-
-// WriteString writes one data frame containing s and returns len(s) on success.
-func (e *Encoder) WriteString(s string) (int, error) {
- err := e.WriteData([]byte(s))
- if err != nil {
- return 0, err
- }
-
- return len(s), nil
-}
-
-// WriteFlushPacket writes control frame 0000 (flush-pkt).
-func (e *Encoder) WriteFlushPacket() error {
- return e.writeControl(0)
-}
-
-// WriteDelimPacket writes control frame 0001 (delim-pkt).
-func (e *Encoder) WriteDelimPacket() error {
- return e.writeControl(1)
-}
-
-// WriteResponseEndPacket writes control frame 0002 (response-end-pkt).
-func (e *Encoder) WriteResponseEndPacket() error {
- return e.writeControl(2)
-}
-
-// Flush flushes buffered output in the underlying transport.
-//
-// Flush does not emit any pkt-line control frame.
-func (e *Encoder) Flush() error {
- return e.w.Flush()
-}
-
-// WriteFlushPacketAndFlush writes a flush-pkt (0000) then flushes transport I/O.
-func (e *Encoder) WriteFlushPacketAndFlush() error {
- err := e.WriteFlushPacket()
- if err != nil {
- return err
- }
-
- return e.Flush()
-}
-
-func (e *Encoder) writeControl(n int) error {
- var hdr [4]byte
-
- err := EncodeLengthHeader(&hdr, n)
- if err != nil {
- return err
- }
-
- return writeAll(e.w, hdr[:])
-}
-
-func (e *Encoder) effectiveMaxData() int {
- if e.maxData <= 0 || e.maxData > LargePacketDataMax {
- return LargePacketDataMax
- }
-
- return e.maxData
-}
diff --git a/network/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go b/network/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go
deleted file mode 100644
index d0f26878..00000000
--- a/network/protocol/pktline/encoder_buffered_flush_and_f_flush_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestEncoderBufferedFlushAndFFlush(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("x"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("unexpected immediate output: %q", out.String())
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if out.String() != "0005x" {
- t.Fatalf("got %q, want %q", out.String(), "0005x")
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
-
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteFlushPacketAndFlush()
- if err != nil {
- t.Fatalf("WriteFlushPacketAndFlush: %v", err)
- }
-
- if out.String() != "0000" {
- t.Fatalf("got %q, want %q", out.String(), "0000")
- }
-}
diff --git a/network/protocol/pktline/encoder_buffered_flush_behavior_test.go b/network/protocol/pktline/encoder_buffered_flush_behavior_test.go
deleted file mode 100644
index b6d14b4b..00000000
--- a/network/protocol/pktline/encoder_buffered_flush_behavior_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestEncoderBufferedFlushBehavior(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hello"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("WriteFlushPacket should not flush I/O, got %q", out.String())
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0009hello0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteData([]byte("ok"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("WriteFlushPacket should not flush I/O, got %q", out.String())
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0006ok0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
- enc = pktline.NewEncoder(bw)
-
- err = enc.WriteData([]byte("yo"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacketAndFlush()
- if err != nil {
- t.Fatalf("WriteFlushPacketAndFlush: %v", err)
- }
-
- if got, want := out.String(), "0006yo0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go b/network/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go
deleted file mode 100644
index d73baa4f..00000000
--- a/network/protocol/pktline/encoder_set_max_data_cannot_exceed_wire_limit_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestEncoderSetMaxDataCannotExceedWireLimit(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
-
- enc := pktline.NewEncoder(bw)
- enc.SetMaxData(pktline.LargePacketDataMax + 100)
-
- err := enc.WriteData(bytes.Repeat([]byte{'x'}, pktline.LargePacketDataMax+1))
- if !errors.Is(err, pktline.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-}
diff --git a/network/protocol/pktline/encoder_writes_frames_test.go b/network/protocol/pktline/encoder_writes_frames_test.go
deleted file mode 100644
index 1922b277..00000000
--- a/network/protocol/pktline/encoder_writes_frames_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package pktline_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestEncoderWritesFrames(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
-
- enc := pktline.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hi"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- err = enc.WriteDelimPacket()
- if err != nil {
- t.Fatalf("WriteDelimPacket: %v", err)
- }
-
- err = enc.WriteResponseEndPacket()
- if err != nil {
- t.Fatalf("WriteResponseEndPacket: %v", err)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- got := b.String()
-
- want := "0006hi000000010002"
- if got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/pktline/errors.go b/network/protocol/pktline/errors.go
deleted file mode 100644
index 866ff467..00000000
--- a/network/protocol/pktline/errors.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package pktline
-
-import "errors"
-
-var (
- // ErrInvalidLength indicates a malformed 4-byte hexadecimal length header.
- ErrInvalidLength = errors.New("pktline: invalid length header")
- // ErrTooLarge indicates a payload exceeds configured packet data limits.
- ErrTooLarge = errors.New("pktline: payload too large")
-)
-
-// ProtocolError reports invalid pkt-line framing.
-//
-// It is returned for protocol violations such as invalid control values
-// (for example 0003) or non-hex length headers.
-type ProtocolError struct {
- Header [4]byte
- Reason string
-}
-
-func (e *ProtocolError) Error() string {
- if e == nil {
- return "<nil>"
- }
-
- if e.Reason == "" {
- return "pktline: protocol error"
- }
-
- return "pktline: protocol error: " + e.Reason
-}
diff --git a/network/protocol/pktline/frame.go b/network/protocol/pktline/frame.go
deleted file mode 100644
index a1cf708c..00000000
--- a/network/protocol/pktline/frame.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package pktline
-
-// Frame is one decoded pkt-line frame.
-//
-// For PacketData, Payload holds frame bytes (possibly empty for 0004).
-// For control frames, Payload is nil.
-type Frame struct {
- Type PacketType
- Payload []byte
-}
diff --git a/network/protocol/pktline/header.go b/network/protocol/pktline/header.go
deleted file mode 100644
index 41e50e04..00000000
--- a/network/protocol/pktline/header.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package pktline
-
-import "fmt"
-
-func hexval(b byte) int {
- switch {
- case b >= '0' && b <= '9':
- return int(b - '0')
- case b >= 'a' && b <= 'f':
- return int(b-'a') + 10
- case b >= 'A' && b <= 'F':
- return int(b-'A') + 10
- default:
- return -1
- }
-}
-
-// ParseLengthHeader parses a 4-byte hexadecimal pkt-line length header.
-//
-// The returned value is the full on-wire packet size, including the 4-byte
-// header. Semantic interpretation (data/control/error) is done by Decoder.
-//
-// The 4-byte header is only an actual length when above or equal to 4.
-// Otherwise, it indicates some control packet.
-func ParseLengthHeader(h [4]byte) (int, error) {
- a := hexval(h[0])
- b := hexval(h[1])
- c := hexval(h[2])
- d := hexval(h[3])
-
- if a < 0 || b < 0 || c < 0 || d < 0 {
- return 0, fmt.Errorf("%w: %q", ErrInvalidLength, string(h[:]))
- }
-
- return (a << 12) | (b << 8) | (c << 4) | d, nil
-}
-
-// EncodeLengthHeader encodes n as a 4-byte hexadecimal pkt-line header.
-//
-// n is the full on-wire packet size including the 4-byte header.
-//
-// The 4-byte header is only an actual length when above or equal to 4.
-// Otherwise, it indicates some control packet.
-func EncodeLengthHeader(dst *[4]byte, n int) error {
- if n < 0 || n > LargePacketMax {
- return fmt.Errorf("%w: %d", ErrInvalidLength, n)
- }
-
- const hex = "0123456789abcdef"
-
- dst[0] = hex[(n>>12)&0xf]
- dst[1] = hex[(n>>8)&0xf]
- dst[2] = hex[(n>>4)&0xf]
- dst[3] = hex[n&0xf]
-
- return nil
-}
diff --git a/network/protocol/pktline/parse_length_header_test.go b/network/protocol/pktline/parse_length_header_test.go
deleted file mode 100644
index b1a4c1e5..00000000
--- a/network/protocol/pktline/parse_length_header_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package pktline_test
-
-import (
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-func TestParseLengthHeader(t *testing.T) {
- t.Parallel()
-
- n, err := pktline.ParseLengthHeader([4]byte{'0', '0', '0', '4'})
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if n != 4 {
- t.Fatalf("got %d, want 4", n)
- }
-
- _, err = pktline.ParseLengthHeader([4]byte{'0', '0', '0', 'x'})
- if !errors.Is(err, pktline.ErrInvalidLength) {
- t.Fatalf("got err %v, want ErrInvalidLength", err)
- }
-}
diff --git a/network/protocol/pktline/type.go b/network/protocol/pktline/type.go
deleted file mode 100644
index 641d1c6c..00000000
--- a/network/protocol/pktline/type.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package pktline
-
-// PacketType identifies the kind of pkt-line frame.
-type PacketType uint8
-
-const (
- // PacketData is a regular data frame whose payload is application-defined.
- PacketData PacketType = iota
- // PacketFlush is control frame 0000 and marks end of a message.
- PacketFlush
- // PacketDelim is control frame 0001 and separates sections in protocol v2.
- PacketDelim
- // PacketResponseEnd is control frame 0002 and marks response end on stateless v2 transports.
- PacketResponseEnd
-)
diff --git a/network/protocol/sideband64k/append.go b/network/protocol/sideband64k/append.go
deleted file mode 100644
index db6527f8..00000000
--- a/network/protocol/sideband64k/append.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package sideband64k
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-// AppendBand appends one side-band-64k data frame to dst.
-func AppendBand(dst []byte, band Band, payload []byte) ([]byte, error) {
- if !validBand(band) {
- return dst, fmt.Errorf("%w: %d", ErrInvalidBand, band)
- }
-
- maxData := effectiveMaxData(DataMax)
- if len(payload) > maxData {
- return dst, fmt.Errorf("%w: %d > %d", ErrTooLarge, len(payload), maxData)
- }
-
- framed := make([]byte, len(payload)+1)
- framed[0] = byte(band)
- copy(framed[1:], payload)
-
- return pktline.AppendData(dst, framed)
-}
-
-// AppendData appends one band-1 data frame to dst.
-func AppendData(dst, payload []byte) ([]byte, error) {
- return AppendBand(dst, BandData, payload)
-}
-
-// AppendProgress appends one band-2 progress frame to dst.
-func AppendProgress(dst, payload []byte) ([]byte, error) {
- return AppendBand(dst, BandProgress, payload)
-}
-
-// AppendError appends one band-3 error frame to dst.
-func AppendError(dst, payload []byte) ([]byte, error) {
- return AppendBand(dst, BandError, payload)
-}
diff --git a/network/protocol/sideband64k/append_helpers_test.go b/network/protocol/sideband64k/append_helpers_test.go
deleted file mode 100644
index 03196c38..00000000
--- a/network/protocol/sideband64k/append_helpers_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package sideband64k_test
-
-import (
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestAppendHelpers(t *testing.T) {
- t.Parallel()
-
- out, err := sideband64k.AppendData(nil, []byte("a"))
- if err != nil {
- t.Fatalf("AppendData: %v", err)
- }
-
- out, err = sideband64k.AppendProgress(out, []byte("b"))
- if err != nil {
- t.Fatalf("AppendProgress: %v", err)
- }
-
- out, err = sideband64k.AppendError(out, []byte("c"))
- if err != nil {
- t.Fatalf("AppendError: %v", err)
- }
-
- if got, want := string(out), "0006\x01a0006\x02b0006\x03c"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/sideband64k/append_preserves_dst_on_error_test.go b/network/protocol/sideband64k/append_preserves_dst_on_error_test.go
deleted file mode 100644
index 6fed4e4a..00000000
--- a/network/protocol/sideband64k/append_preserves_dst_on_error_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package sideband64k_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestAppendBandPreservesDstOnError(t *testing.T) {
- t.Parallel()
-
- orig := []byte("seed")
- dst := append([]byte(nil), orig...)
-
- out, err := sideband64k.AppendBand(dst, 4, []byte("x"))
- if !errors.Is(err, sideband64k.ErrInvalidBand) {
- t.Fatalf("got err %v, want ErrInvalidBand", err)
- }
-
- if !bytes.Equal(out, orig) {
- t.Fatalf("got %q, want %q", string(out), string(orig))
- }
-
- out, err = sideband64k.AppendData(dst, bytes.Repeat([]byte{'x'}, sideband64k.DataMax+1))
- if !errors.Is(err, sideband64k.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- if !bytes.Equal(out, orig) {
- t.Fatalf("got %q, want %q", string(out), string(orig))
- }
-}
diff --git a/network/protocol/sideband64k/band.go b/network/protocol/sideband64k/band.go
deleted file mode 100644
index 73c61fd8..00000000
--- a/network/protocol/sideband64k/band.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package sideband64k
-
-// Band identifies the sideband stream within a pkt-line data frame.
-type Band uint8
-
-const (
- // BandData carries primary payload bytes.
- BandData Band = 1
- // BandProgress carries progress or informational messages.
- BandProgress Band = 2
- // BandError carries fatal error messages.
- BandError Band = 3
-)
diff --git a/network/protocol/sideband64k/chunk_writer.go b/network/protocol/sideband64k/chunk_writer.go
deleted file mode 100644
index 78c66edf..00000000
--- a/network/protocol/sideband64k/chunk_writer.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package sideband64k
-
-import "io"
-
-// ChunkWriter packetizes arbitrary stream bytes into side-band-64k data frames
-// for one fixed band.
-//
-// It never writes control packets automatically.
-//
-// Labels: MT-Unsafe.
-type ChunkWriter struct {
- enc *Encoder
- band Band
-}
-
-// NewChunkWriter creates a chunking adapter over enc for one band.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewChunkWriter(enc *Encoder, band Band) *ChunkWriter {
- return &ChunkWriter{enc: enc, band: band}
-}
-
-// Write splits p into sideband frames not larger than enc's maxData.
-func (cw *ChunkWriter) Write(p []byte) (int, error) {
- total := 0
- maxData := cw.enc.effectiveMaxData()
-
- for len(p) > 0 {
- n := min(len(p), maxData)
-
- err := cw.enc.WriteBand(cw.band, p[:n])
- if err != nil {
- return total, err
- }
-
- total += n
- p = p[n:]
- }
-
- return total, nil
-}
-
-// ReadFrom reads from r and writes sideband frames to the encoder.
-func (cw *ChunkWriter) ReadFrom(r io.Reader) (int64, error) {
- buf := make([]byte, cw.enc.effectiveMaxData())
-
- var total int64
-
- for {
- n, err := r.Read(buf)
- if n > 0 {
- werr := cw.enc.WriteBand(cw.band, buf[:n])
- if werr != nil {
- return total, werr
- }
-
- total += int64(n)
- }
-
- if err != nil {
- if err == io.EOF {
- return total, nil
- }
-
- return total, err
- }
- }
-}
-
-// Flush flushes buffered output in the underlying transport.
-func (cw *ChunkWriter) Flush() error {
- return cw.enc.Flush()
-}
diff --git a/network/protocol/sideband64k/chunk_writer_write_and_read_from_test.go b/network/protocol/sideband64k/chunk_writer_write_and_read_from_test.go
deleted file mode 100644
index ef2b0fff..00000000
--- a/network/protocol/sideband64k/chunk_writer_write_and_read_from_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package sideband64k_test
-
-import (
- "bufio"
- "bytes"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestChunkWriterWriteAndReadFrom(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := sideband64k.NewEncoder(bw)
- enc.SetMaxData(3)
-
- cw := sideband64k.NewChunkWriter(enc, sideband64k.BandProgress)
-
- n, err := cw.Write([]byte("abcdefg"))
- if err != nil {
- t.Fatalf("Write: %v", err)
- }
-
- if n != 7 {
- t.Fatalf("Write n=%d, want 7", n)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0008\x02abc0008\x02def0006\x02g"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
-
- rn, err := cw.ReadFrom(strings.NewReader("wxyz"))
- if err != nil {
- t.Fatalf("ReadFrom: %v", err)
- }
-
- if rn != 4 {
- t.Fatalf("ReadFrom n=%d, want 4", rn)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "0008\x02wxy0006\x02z"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/sideband64k/constants.go b/network/protocol/sideband64k/constants.go
deleted file mode 100644
index 2a6a2e47..00000000
--- a/network/protocol/sideband64k/constants.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package sideband64k
-
-import "codeberg.org/lindenii/furgit/network/protocol/pktline"
-
-const (
- // PacketMax is the maximum on-wire pkt-line size used by side-band-64k.
- PacketMax = pktline.LargePacketMax
- // DataMax is the maximum sideband payload size excluding the 1-byte band designator.
- DataMax = pktline.LargePacketDataMax - 1
-)
diff --git a/network/protocol/sideband64k/decoder.go b/network/protocol/sideband64k/decoder.go
deleted file mode 100644
index e34f5d12..00000000
--- a/network/protocol/sideband64k/decoder.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package sideband64k
-
-import (
- "fmt"
- "io"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-// ReadOptions controls sideband decoding behavior.
-type ReadOptions struct {
- // ChompLF removes one trailing '\n' from FrameData payloads only.
- ChompLF bool
-}
-
-// Decoder reads side-band-64k frames from an io.Reader.
-//
-// It preserves frame boundaries and supports one-frame lookahead via
-// PeekFrame.
-//
-// Labels: MT-Unsafe.
-type Decoder struct {
- dec *pktline.Decoder
- maxData int
- opts ReadOptions
-
- peeked bool
- peek Frame
- peekErr error
-}
-
-// NewDecoder creates a decoder over r.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewDecoder(r io.Reader, opts ReadOptions) *Decoder {
- d := &Decoder{
- dec: pktline.NewDecoder(r, pktline.ReadOptions{}),
- maxData: DataMax,
- opts: opts,
- }
- d.dec.SetMaxData(pktline.LargePacketDataMax)
-
- return d
-}
-
-// SetMaxData sets maximum payload size accepted for one sideband data packet.
-//
-// Non-positive n resets to DataMax.
-func (d *Decoder) SetMaxData(n int) {
- if n <= 0 {
- d.maxData = DataMax
-
- return
- }
-
- d.maxData = n
-}
-
-// ReadFrame reads one frame.
-func (d *Decoder) ReadFrame() (Frame, error) {
- if d.peeked {
- d.peeked = false
-
- return cloneFrame(d.peek), d.peekErr
- }
-
- return d.readFrame()
-}
-
-// PeekFrame returns the next frame without consuming it.
-func (d *Decoder) PeekFrame() (Frame, error) {
- if !d.peeked {
- d.peek, d.peekErr = d.readFrame()
- d.peeked = true
- }
-
- return cloneFrame(d.peek), d.peekErr
-}
-
-func (d *Decoder) readFrame() (Frame, error) {
- f, err := d.dec.ReadFrame()
- if err != nil {
- return Frame{}, err
- }
-
- switch f.Type {
- case pktline.PacketFlush:
- return Frame{Type: FrameFlush}, nil
- case pktline.PacketDelim:
- return Frame{Type: FrameDelim}, nil
- case pktline.PacketResponseEnd:
- return Frame{Type: FrameResponseEnd}, nil
- case pktline.PacketData:
- if len(f.Payload) == 0 {
- return Frame{}, &ProtocolError{Reason: "missing sideband designator"}
- }
-
- payload := f.Payload[1:]
- if len(payload) > d.effectiveMaxData() {
- return Frame{}, fmt.Errorf("%w: %d > %d", ErrTooLarge, len(payload), d.effectiveMaxData())
- }
-
- band := Band(f.Payload[0])
- if !validBand(band) {
- return Frame{}, &ProtocolError{Reason: fmt.Sprintf("%v: %d", ErrInvalidBand, band)}
- }
-
- payload = append([]byte(nil), payload...)
- if d.opts.ChompLF && band == BandData && len(payload) > 0 && payload[len(payload)-1] == '\n' {
- payload = payload[:len(payload)-1]
- }
-
- return Frame{
- Type: frameTypeForBand(band),
- Payload: payload,
- }, nil
- default:
- return Frame{}, &ProtocolError{Reason: "unknown pkt-line frame type"}
- }
-}
-
-func (d *Decoder) effectiveMaxData() int {
- return effectiveMaxData(d.maxData)
-}
-
-func cloneFrame(f Frame) Frame {
- if f.Type == FrameFlush || f.Type == FrameDelim || f.Type == FrameResponseEnd {
- return Frame{Type: f.Type}
- }
-
- out := Frame{Type: f.Type}
- if f.Payload != nil {
- out.Payload = append([]byte(nil), f.Payload...)
- }
-
- return out
-}
-
-func validBand(band Band) bool {
- return band == BandData || band == BandProgress || band == BandError
-}
-
-func frameTypeForBand(band Band) FrameType {
- switch band {
- case BandData:
- return FrameData
- case BandProgress:
- return FrameProgress
- case BandError:
- return FrameError
- default:
- panic("invalid sideband64k band")
- }
-}
-
-func effectiveMaxData(n int) int {
- if n <= 0 || n > DataMax {
- return DataMax
- }
-
- return n
-}
diff --git a/network/protocol/sideband64k/decoder_data_control_and_keepalive_test.go b/network/protocol/sideband64k/decoder_data_control_and_keepalive_test.go
deleted file mode 100644
index 9103c492..00000000
--- a/network/protocol/sideband64k/decoder_data_control_and_keepalive_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package sideband64k_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderDataControlAndKeepalive(t *testing.T) {
- t.Parallel()
-
- input := "0007\x01a\n0005\x010007\x02p\n0007\x03e\n000100020000"
- dec := sideband64k.NewDecoder(strings.NewReader(input), sideband64k.ReadOptions{ChompLF: true})
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #1: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || string(f.Payload) != "a" {
- t.Fatalf("frame #1 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || len(f.Payload) != 0 {
- t.Fatalf("frame #2 = %#v, want empty data", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #3: %v", err)
- }
-
- if f.Type != sideband64k.FrameProgress || string(f.Payload) != "p\n" {
- t.Fatalf("frame #3 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #4: %v", err)
- }
-
- if f.Type != sideband64k.FrameError || string(f.Payload) != "e\n" {
- t.Fatalf("frame #4 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #5: %v", err)
- }
-
- if f.Type != sideband64k.FrameDelim {
- t.Fatalf("frame #5 type = %v, want FrameDelim", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #6: %v", err)
- }
-
- if f.Type != sideband64k.FrameResponseEnd {
- t.Fatalf("frame #6 type = %v, want FrameResponseEnd", f.Type)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #7: %v", err)
- }
-
- if f.Type != sideband64k.FrameFlush {
- t.Fatalf("frame #7 type = %v, want FrameFlush", f.Type)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_invalid_band_test.go b/network/protocol/sideband64k/decoder_invalid_band_test.go
deleted file mode 100644
index a4bc11a9..00000000
--- a/network/protocol/sideband64k/decoder_invalid_band_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package sideband64k_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderInvalidBand(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("0005\x04"), sideband64k.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*sideband64k.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_invalid_empty_payload_test.go b/network/protocol/sideband64k/decoder_invalid_empty_payload_test.go
deleted file mode 100644
index df9faa71..00000000
--- a/network/protocol/sideband64k/decoder_invalid_empty_payload_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package sideband64k_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderInvalidEmptyPayload(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("0004"), sideband64k.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*sideband64k.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want ProtocolError", err)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_malformed_pktline_test.go b/network/protocol/sideband64k/decoder_malformed_pktline_test.go
deleted file mode 100644
index 5e4e4551..00000000
--- a/network/protocol/sideband64k/decoder_malformed_pktline_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package sideband64k_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderInvalid0003(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("0003"), sideband64k.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want pktline.ProtocolError", err)
- }
-}
-
-func TestDecoderRejectsOverMaximumLength(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("fffe"), sideband64k.ReadOptions{})
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want pktline.ProtocolError", err)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_partial_read_test.go b/network/protocol/sideband64k/decoder_partial_read_test.go
deleted file mode 100644
index 3f103787..00000000
--- a/network/protocol/sideband64k/decoder_partial_read_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package sideband64k_test
-
-import (
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderHandlesPartialReads(t *testing.T) {
- t.Parallel()
-
- r := &byteReader{data: []byte("0007\x02ok0000")}
- dec := sideband64k.NewDecoder(r, sideband64k.ReadOptions{})
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #1: %v", err)
- }
-
- if f.Type != sideband64k.FrameProgress || string(f.Payload) != "ok" {
- t.Fatalf("frame #1 = %#v", f)
- }
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != sideband64k.FrameFlush {
- t.Fatalf("frame #2 = %#v", f)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_peek_test.go b/network/protocol/sideband64k/decoder_peek_test.go
deleted file mode 100644
index 31397762..00000000
--- a/network/protocol/sideband64k/decoder_peek_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package sideband64k_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderPeek(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("0006\x01x0000"), sideband64k.ReadOptions{})
-
- f, err := dec.PeekFrame()
- if err != nil {
- t.Fatalf("PeekFrame: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || string(f.Payload) != "x" {
- t.Fatalf("peek frame = %#v", f)
- }
-
- f.Payload[0] = 'y'
-
- f, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || string(f.Payload) != "x" {
- t.Fatalf("read frame = %#v", f)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_resync_after_over_max_data_test.go b/network/protocol/sideband64k/decoder_resync_after_over_max_data_test.go
deleted file mode 100644
index b0ae600a..00000000
--- a/network/protocol/sideband64k/decoder_resync_after_over_max_data_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package sideband64k_test
-
-import (
- "bufio"
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderResyncAfterOverMaxData(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
- enc := sideband64k.NewEncoder(bw)
-
- err := enc.WriteData([]byte("abcd"))
- if err != nil {
- t.Fatalf("WriteData #1: %v", err)
- }
-
- err = enc.WriteData([]byte("z"))
- if err != nil {
- t.Fatalf("WriteData #2: %v", err)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- dec := sideband64k.NewDecoder(bytes.NewReader(b.Bytes()), sideband64k.ReadOptions{})
- dec.SetMaxData(1)
-
- _, err = dec.ReadFrame()
- if !errors.Is(err, sideband64k.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_resync_after_over_wire_max_test.go b/network/protocol/sideband64k/decoder_resync_after_over_wire_max_test.go
deleted file mode 100644
index 73966925..00000000
--- a/network/protocol/sideband64k/decoder_resync_after_over_wire_max_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package sideband64k_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderResyncAfterOverWireMax(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- _, _ = b.WriteString("ffff")
- _, _ = b.Write(bytes.Repeat([]byte{'a'}, 65531))
- _, _ = b.WriteString("0006\x01z")
-
- dec := sideband64k.NewDecoder(bytes.NewReader(b.Bytes()), sideband64k.ReadOptions{})
-
- _, err := dec.ReadFrame()
-
- if _, ok := errors.AsType[*pktline.ProtocolError](err); !ok {
- t.Fatalf("got err %v, want pktline.ProtocolError", err)
- }
-
- f, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame #2: %v", err)
- }
-
- if f.Type != sideband64k.FrameData || string(f.Payload) != "z" {
- t.Fatalf("got frame %#v, want data z", f)
- }
-}
diff --git a/network/protocol/sideband64k/decoder_unexpected_eof_test.go b/network/protocol/sideband64k/decoder_unexpected_eof_test.go
deleted file mode 100644
index d9d71fb9..00000000
--- a/network/protocol/sideband64k/decoder_unexpected_eof_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package sideband64k_test
-
-import (
- "errors"
- "io"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestDecoderUnexpectedEOF(t *testing.T) {
- t.Parallel()
-
- dec := sideband64k.NewDecoder(strings.NewReader("0006\x01"), sideband64k.ReadOptions{})
-
- _, err := dec.ReadFrame()
- if !errors.Is(err, io.ErrUnexpectedEOF) {
- t.Fatalf("got err %v, want io.ErrUnexpectedEOF", err)
- }
-}
diff --git a/network/protocol/sideband64k/doc.go b/network/protocol/sideband64k/doc.go
deleted file mode 100644
index 55c33650..00000000
--- a/network/protocol/sideband64k/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package sideband64k implements Git side-band-64k multiplexing over pkt-line.
-package sideband64k
diff --git a/network/protocol/sideband64k/encoder.go b/network/protocol/sideband64k/encoder.go
deleted file mode 100644
index 6cdef38a..00000000
--- a/network/protocol/sideband64k/encoder.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package sideband64k
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-// Encoder writes side-band-64k frames to a flush-capable output transport.
-//
-// It writes exactly one frame per method call and does not auto-chunk data.
-//
-// Labels: MT-Unsafe.
-type Encoder struct {
- enc *pktline.Encoder
- maxData int
-}
-
-// NewEncoder creates an encoder over w.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewEncoder(w iowrap.WriteFlusher) *Encoder {
- return &Encoder{
- enc: pktline.NewEncoder(w),
- maxData: DataMax,
- }
-}
-
-// SetMaxData sets the maximum payload size accepted by WriteBand.
-//
-// Non-positive n resets to DataMax.
-func (e *Encoder) SetMaxData(n int) {
- if n <= 0 {
- e.maxData = DataMax
-
- return
- }
-
- e.maxData = n
-}
-
-// WriteBand writes one side-band-64k data frame for the given band.
-func (e *Encoder) WriteBand(band Band, p []byte) error {
- if !validBand(band) {
- return fmt.Errorf("%w: %d", ErrInvalidBand, band)
- }
-
- maxData := e.effectiveMaxData()
- if len(p) > maxData {
- return fmt.Errorf("%w: %d > %d", ErrTooLarge, len(p), maxData)
- }
-
- framed := make([]byte, len(p)+1)
- framed[0] = byte(band)
- copy(framed[1:], p)
-
- return e.enc.WriteData(framed)
-}
-
-// WriteData writes one band-1 data frame.
-func (e *Encoder) WriteData(p []byte) error {
- return e.WriteBand(BandData, p)
-}
-
-// WriteProgress writes one band-2 progress frame.
-func (e *Encoder) WriteProgress(p []byte) error {
- return e.WriteBand(BandProgress, p)
-}
-
-// WriteError writes one band-3 error frame.
-func (e *Encoder) WriteError(p []byte) error {
- return e.WriteBand(BandError, p)
-}
-
-// WriteFlushPacket writes control frame 0000 (flush-pkt).
-func (e *Encoder) WriteFlushPacket() error {
- return e.enc.WriteFlushPacket()
-}
-
-// WriteDelimPacket writes control frame 0001 (delim-pkt).
-func (e *Encoder) WriteDelimPacket() error {
- return e.enc.WriteDelimPacket()
-}
-
-// WriteResponseEndPacket writes control frame 0002 (response-end-pkt).
-func (e *Encoder) WriteResponseEndPacket() error {
- return e.enc.WriteResponseEndPacket()
-}
-
-// Flush flushes buffered output in the underlying transport.
-func (e *Encoder) Flush() error {
- return e.enc.Flush()
-}
-
-// WriteFlushPacketAndFlush writes a flush-pkt (0000) then flushes transport I/O.
-func (e *Encoder) WriteFlushPacketAndFlush() error {
- return e.enc.WriteFlushPacketAndFlush()
-}
-
-func (e *Encoder) effectiveMaxData() int {
- return effectiveMaxData(e.maxData)
-}
diff --git a/network/protocol/sideband64k/encoder_buffered_flush_behavior_test.go b/network/protocol/sideband64k/encoder_buffered_flush_behavior_test.go
deleted file mode 100644
index 83103ea3..00000000
--- a/network/protocol/sideband64k/encoder_buffered_flush_behavior_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package sideband64k_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestEncoderBufferedFlushBehavior(t *testing.T) {
- t.Parallel()
-
- var out bytes.Buffer
-
- bw := bufio.NewWriter(&out)
- enc := sideband64k.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hello"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- if out.Len() != 0 {
- t.Fatalf("WriteFlushPacket should not flush I/O, got %q", out.String())
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- if got, want := out.String(), "000a\x01hello0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- out.Reset()
- bw = bufio.NewWriter(&out)
- enc = sideband64k.NewEncoder(bw)
-
- err = enc.WriteData([]byte("yo"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacketAndFlush()
- if err != nil {
- t.Fatalf("WriteFlushPacketAndFlush: %v", err)
- }
-
- if got, want := out.String(), "0007\x01yo0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/sideband64k/encoder_partial_write_test.go b/network/protocol/sideband64k/encoder_partial_write_test.go
deleted file mode 100644
index 97c8f762..00000000
--- a/network/protocol/sideband64k/encoder_partial_write_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package sideband64k_test
-
-import (
- "errors"
- "io"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestEncoderHandlesPartialWrites(t *testing.T) {
- t.Parallel()
-
- dst := &limitWriter{maxPerWrite: 2}
- enc := sideband64k.NewEncoder(dst)
-
- err := enc.WriteProgress([]byte("abc"))
- if err != nil {
- t.Fatalf("WriteProgress: %v", err)
- }
-
- err = enc.WriteFlushPacketAndFlush()
- if err != nil {
- t.Fatalf("WriteFlushPacketAndFlush: %v", err)
- }
-
- if got, want := dst.buf.String(), "0008\x02abc0000"; got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-
- if dst.flushes != 1 {
- t.Fatalf("flushes=%d, want 1", dst.flushes)
- }
-}
-
-func TestEncoderReturnsShortWrite(t *testing.T) {
- t.Parallel()
-
- dst := &limitWriter{shortWrite: true}
- enc := sideband64k.NewEncoder(dst)
-
- err := enc.WriteData([]byte("x"))
- if !errors.Is(err, io.ErrShortWrite) {
- t.Fatalf("got err %v, want io.ErrShortWrite", err)
- }
-}
diff --git a/network/protocol/sideband64k/encoder_set_max_data_cannot_exceed_wire_limit_test.go b/network/protocol/sideband64k/encoder_set_max_data_cannot_exceed_wire_limit_test.go
deleted file mode 100644
index 2bfcf073..00000000
--- a/network/protocol/sideband64k/encoder_set_max_data_cannot_exceed_wire_limit_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package sideband64k_test
-
-import (
- "bytes"
- "errors"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestEncoderSetMaxDataCannotExceedWireLimit(t *testing.T) {
- t.Parallel()
-
- var dst limitWriter
-
- enc := sideband64k.NewEncoder(&dst)
- enc.SetMaxData(sideband64k.DataMax + 100)
-
- err := enc.WriteData(bytes.Repeat([]byte{'x'}, sideband64k.DataMax+1))
- if !errors.Is(err, sideband64k.ErrTooLarge) {
- t.Fatalf("got err %v, want ErrTooLarge", err)
- }
-}
diff --git a/network/protocol/sideband64k/encoder_writes_frames_test.go b/network/protocol/sideband64k/encoder_writes_frames_test.go
deleted file mode 100644
index 85fe5845..00000000
--- a/network/protocol/sideband64k/encoder_writes_frames_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package sideband64k_test
-
-import (
- "bufio"
- "bytes"
- "testing"
-
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
-)
-
-func TestEncoderWritesFrames(t *testing.T) {
- t.Parallel()
-
- var b bytes.Buffer
-
- bw := bufio.NewWriter(&b)
- enc := sideband64k.NewEncoder(bw)
-
- err := enc.WriteData([]byte("hi"))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteProgress([]byte("ok"))
- if err != nil {
- t.Fatalf("WriteProgress: %v", err)
- }
-
- err = enc.WriteError([]byte("no"))
- if err != nil {
- t.Fatalf("WriteError: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- err = enc.WriteDelimPacket()
- if err != nil {
- t.Fatalf("WriteDelimPacket: %v", err)
- }
-
- err = enc.WriteResponseEndPacket()
- if err != nil {
- t.Fatalf("WriteResponseEndPacket: %v", err)
- }
-
- err = enc.Flush()
- if err != nil {
- t.Fatalf("Flush: %v", err)
- }
-
- want := "0007\x01hi0007\x02ok0007\x03no000000010002"
- if got := b.String(); got != want {
- t.Fatalf("got %q, want %q", got, want)
- }
-}
diff --git a/network/protocol/sideband64k/errors.go b/network/protocol/sideband64k/errors.go
deleted file mode 100644
index 44e7c165..00000000
--- a/network/protocol/sideband64k/errors.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package sideband64k
-
-import "errors"
-
-var (
- // ErrTooLarge indicates a payload exceeds configured sideband data limits.
- ErrTooLarge = errors.New("sideband64k: payload too large")
- // ErrInvalidBand indicates a data frame has an invalid sideband designator.
- ErrInvalidBand = errors.New("sideband64k: invalid band designator")
-)
-
-// ProtocolError reports invalid side-band-64k framing.
-type ProtocolError struct {
- Reason string
-}
-
-func (e *ProtocolError) Error() string {
- if e == nil {
- return "<nil>"
- }
-
- if e.Reason == "" {
- return "sideband64k: protocol error"
- }
-
- return "sideband64k: protocol error: " + e.Reason
-}
diff --git a/network/protocol/sideband64k/frame.go b/network/protocol/sideband64k/frame.go
deleted file mode 100644
index 1335a8e3..00000000
--- a/network/protocol/sideband64k/frame.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package sideband64k
-
-// Frame is one decoded side-band-64k frame.
-//
-// For FrameData, FrameProgress, and FrameError, Payload holds frame bytes and
-// may be empty.
-//
-// For control frames, Payload is nil.
-type Frame struct {
- Type FrameType
- Payload []byte
-}
diff --git a/network/protocol/sideband64k/frame_type.go b/network/protocol/sideband64k/frame_type.go
deleted file mode 100644
index 052d8b10..00000000
--- a/network/protocol/sideband64k/frame_type.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package sideband64k
-
-// FrameType identifies the kind of decoded sideband frame.
-type FrameType uint8
-
-const (
- // FrameData carries primary payload bytes from band 1.
- FrameData FrameType = iota
- // FrameProgress carries progress bytes from band 2.
- FrameProgress
- // FrameError carries fatal error bytes from band 3.
- FrameError
- // FrameFlush is pkt-line control frame 0000.
- FrameFlush
- // FrameDelim is pkt-line control frame 0001.
- FrameDelim
- // FrameResponseEnd is pkt-line control frame 0002.
- FrameResponseEnd
-)
diff --git a/network/protocol/sideband64k/helpers_test.go b/network/protocol/sideband64k/helpers_test.go
deleted file mode 100644
index f9b2608f..00000000
--- a/network/protocol/sideband64k/helpers_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package sideband64k_test
-
-import (
- "bytes"
- "io"
-)
-
-type limitWriter struct {
- buf bytes.Buffer
- maxPerWrite int
- flushes int
- shortWrite bool
-}
-
-func (w *limitWriter) Write(p []byte) (int, error) {
- if w.shortWrite {
- return 0, nil
- }
-
- if w.maxPerWrite > 0 && len(p) > w.maxPerWrite {
- p = p[:w.maxPerWrite]
- }
-
- return w.buf.Write(p)
-}
-
-func (w *limitWriter) Flush() error {
- w.flushes++
-
- return nil
-}
-
-type byteReader struct {
- data []byte
-}
-
-func (r *byteReader) Read(p []byte) (int, error) {
- if len(r.data) == 0 {
- return 0, io.EOF
- }
-
- p[0] = r.data[0]
- r.data = r.data[1:]
-
- return 1, nil
-}
diff --git a/network/protocol/v0v1/doc.go b/network/protocol/v0v1/doc.go
deleted file mode 100644
index 2c96ea23..00000000
--- a/network/protocol/v0v1/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package v0v1 provides common constants and routines for the V0 and V1 protocols.
-package v0v1
diff --git a/network/protocol/v0v1/server/advertise.go b/network/protocol/v0v1/server/advertise.go
deleted file mode 100644
index 8ec7dfd9..00000000
--- a/network/protocol/v0v1/server/advertise.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package server
-
-import (
- "fmt"
- "strings"
-)
-
-// AdvertiseRefs writes one server ref advertisement.
-func (session *Session) AdvertiseRefs(ad Advertisement, capabilityTokens []string) error {
- if session.opts.Version == Version1 {
- err := session.enc.WriteData([]byte("version 1\n"))
- if err != nil {
- return err
- }
- }
-
- capList := strings.Join(capabilityTokens, " ")
-
- refs := sortAdvertisedRefs(ad.Refs)
- if len(refs) == 0 {
- line := fmt.Sprintf("%s capabilities^{}\x00%s\n", session.opts.Algorithm.Zero(), capList)
-
- err := session.enc.WriteData([]byte(line))
- if err != nil {
- return err
- }
-
- return session.WriteFlushPacket()
- }
-
- for i, entry := range refs {
- line := fmt.Sprintf("%s %s", entry.ID, entry.Name)
- if i == 0 {
- line += "\x00" + capList
- }
-
- err := session.enc.WriteData([]byte(line + "\n"))
- if err != nil {
- return err
- }
-
- if entry.Peeled != nil {
- peeled := fmt.Sprintf("%s %s^{}\n", *entry.Peeled, entry.Name)
-
- err = session.enc.WriteData([]byte(peeled))
- if err != nil {
- return err
- }
- }
- }
-
- return session.WriteFlushPacket()
-}
diff --git a/network/protocol/v0v1/server/advertise_test.go b/network/protocol/v0v1/server/advertise_test.go
deleted file mode 100644
index 3ad7a725..00000000
--- a/network/protocol/v0v1/server/advertise_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package server_test
-
-import (
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- server "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-func TestAdvertiseRefsWritesVersionOneHeadCapsAndPeeledTag(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- headID := mustHexID(t, algo, "1")
- tagID := mustHexID(t, algo, "2")
- peeledID := mustHexID(t, algo, "3")
- mainID := mustHexID(t, algo, "4")
-
- var out bufferWriteFlusher
-
- session := server.NewSession(
- strings.NewReader(""),
- &out,
- server.Options{
- Version: server.Version1,
- Algorithm: algo,
- },
- )
-
- err := session.AdvertiseRefs(server.Advertisement{
- Refs: []server.AdvertisedRef{
- {Name: "refs/tags/v1", ID: tagID, Peeled: &peeledID},
- {Name: "HEAD", ID: headID},
- {Name: "refs/heads/main", ID: mainID},
- },
- }, []string{
- "report-status",
- "delete-refs",
- "object-format=" + algo.String(),
- "agent=furgit-test/1",
- })
- if err != nil {
- t.Fatalf("AdvertiseRefs: %v", err)
- }
-
- got := out.String()
- wantParts := []string{
- "000eversion 1\n",
- headID.String() + " HEAD\x00report-status delete-refs object-format=" + algo.String() + " agent=furgit-test/1\n",
- mainID.String() + " refs/heads/main\n",
- tagID.String() + " refs/tags/v1\n",
- peeledID.String() + " refs/tags/v1^{}\n",
- "0000",
- }
-
- for _, part := range wantParts {
- if !strings.Contains(got, part) {
- t.Fatalf("advertisement missing %q in %q", part, got)
- }
- }
- })
-}
-
-func TestAdvertiseRefsWritesNoRefsCapabilitiesLine(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- var out bufferWriteFlusher
-
- session := server.NewSession(
- strings.NewReader(""),
- &out,
- server.Options{
- Algorithm: algo,
- },
- )
-
- err := session.AdvertiseRefs(server.Advertisement{}, []string{
- "report-status",
- "object-format=" + algo.String(),
- })
- if err != nil {
- t.Fatalf("AdvertiseRefs: %v", err)
- }
-
- got := out.String()
-
- want := algo.Zero().String() + " capabilities^{}\x00report-status object-format=" + algo.String() + "\n"
- if !strings.Contains(got, want) {
- t.Fatalf("unexpected no-refs advertisement %q", got)
- }
- })
-}
diff --git a/network/protocol/v0v1/server/advertised_ref.go b/network/protocol/v0v1/server/advertised_ref.go
deleted file mode 100644
index cf6ddcc8..00000000
--- a/network/protocol/v0v1/server/advertised_ref.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package server
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// AdvertisedRef is one ref entry in one v0/v1 server advertisement.
-type AdvertisedRef struct {
- // Name is the advertised reference name. It may be HEAD or one full
- // reference name.
- Name string
- // ID is the object ID currently advertised for Name.
- ID objectid.ObjectID
- // Peeled is the peeled annotated-tag target when available.
- //
- // If set, advertisement writes one immediate "<name>^{}" line after the
- // main entry, matching Git's advertisement rules.
- Peeled *objectid.ObjectID
-}
-
-// Advertisement is one server-side ref advertisement.
-type Advertisement struct {
- Refs []AdvertisedRef
-}
diff --git a/network/protocol/v0v1/server/doc.go b/network/protocol/v0v1/server/doc.go
deleted file mode 100644
index ea0b3f18..00000000
--- a/network/protocol/v0v1/server/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package server implements shared server-side Git protocol v0/v1 framing.
-package server
diff --git a/network/protocol/v0v1/server/errors.go b/network/protocol/v0v1/server/errors.go
deleted file mode 100644
index 6a456234..00000000
--- a/network/protocol/v0v1/server/errors.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package server
-
-// ProtocolError reports one malformed or unsupported protocol input.
-type ProtocolError struct {
- Reason string
-}
-
-// Error returns the formatted error string.
-func (err *ProtocolError) Error() string {
- return "protocol/v0v1/server: protocol error: " + err.Reason
-}
-
-// ErrUnexpectedPacket reports one unexpected pkt-line control packet.
-var ErrUnexpectedPacket = &ProtocolError{Reason: "unexpected control packet"}
-
-// ErrSideBandNotEnabled reports one attempt to write sideband frames without a
-// negotiated side-band-64k session.
-var ErrSideBandNotEnabled = &ProtocolError{Reason: "side-band-64k not enabled"}
diff --git a/network/protocol/v0v1/server/frame.go b/network/protocol/v0v1/server/frame.go
deleted file mode 100644
index ad2a0801..00000000
--- a/network/protocol/v0v1/server/frame.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package server
-
-import "codeberg.org/lindenii/furgit/network/protocol/pktline"
-
-// FrameType identifies one low-level v0/v1 server pkt-line frame type.
-type FrameType = pktline.PacketType
-
-const (
- // FrameData is one data pkt-line.
- FrameData = pktline.PacketData
- // FrameFlush is one flush-pkt.
- FrameFlush = pktline.PacketFlush
- // FrameDelim is one delim-pkt.
- FrameDelim = pktline.PacketDelim
- // FrameResponseEnd is one response-end-pkt.
- FrameResponseEnd = pktline.PacketResponseEnd
-)
-
-// Frame is one decoded low-level pkt-line frame.
-type Frame = pktline.Frame
diff --git a/network/protocol/v0v1/server/helpers.go b/network/protocol/v0v1/server/helpers.go
deleted file mode 100644
index 9a62f714..00000000
--- a/network/protocol/v0v1/server/helpers.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package server
-
-import (
- "slices"
-)
-
-func sortAdvertisedRefs(refs []AdvertisedRef) []AdvertisedRef {
- out := append([]AdvertisedRef(nil), refs...)
- slices.SortFunc(out, func(left, right AdvertisedRef) int {
- if left.Name == "HEAD" && right.Name != "HEAD" {
- return -1
- }
-
- if left.Name != "HEAD" && right.Name == "HEAD" {
- return 1
- }
-
- switch {
- case left.Name < right.Name:
- return -1
- case left.Name > right.Name:
- return 1
- default:
- return 0
- }
- })
-
- return out
-}
diff --git a/network/protocol/v0v1/server/helpers_test.go b/network/protocol/v0v1/server/helpers_test.go
deleted file mode 100644
index 261bbdc5..00000000
--- a/network/protocol/v0v1/server/helpers_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package server_test
-
-import (
- "bytes"
- "strings"
- "testing"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-type bufferWriteFlusher struct {
- bytes.Buffer
-}
-
-func (bufferWriteFlusher) Flush() error {
- return nil
-}
-
-func mustHexID(tb testing.TB, algo objectid.Algorithm, digit string) objectid.ObjectID {
- tb.Helper()
-
- id, err := objectid.ParseHex(algo, strings.Repeat(digit, algo.HexLen()))
- if err != nil {
- tb.Fatalf("objectid.ParseHex(%q): %v", strings.Repeat(digit, algo.HexLen()), err)
- }
-
- return id
-}
diff --git a/network/protocol/v0v1/server/receivepack/capabilities.go b/network/protocol/v0v1/server/receivepack/capabilities.go
deleted file mode 100644
index e0ff51a3..00000000
--- a/network/protocol/v0v1/server/receivepack/capabilities.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package receivepack
-
-import (
- "fmt"
- "slices"
- "strings"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// Capabilities describes one receive-pack capability set.
-type Capabilities struct {
- ReportStatus bool
- ReportStatusV2 bool
- DeleteRefs bool
- SideBand64K bool
- Quiet bool
- Atomic bool
- OfsDelta bool
- PushOptions bool
- PushCertNonce string
- ObjectFormat objectid.Algorithm
- SessionID string
- Agent string
-}
-
-// Normalize returns one normalized copy of caps.
-func (caps Capabilities) Normalize(defaultAlgorithm objectid.Algorithm) Capabilities {
- if caps.ObjectFormat == objectid.AlgorithmUnknown {
- caps.ObjectFormat = defaultAlgorithm
- }
-
- return caps
-}
-
-// Tokens returns capabilities in Git advertisement order.
-func (caps Capabilities) Tokens(defaultAlgorithm objectid.Algorithm) []string {
- caps = caps.Normalize(defaultAlgorithm)
-
- tokens := make([]string, 0, 11)
- if caps.ReportStatus {
- tokens = append(tokens, "report-status")
- }
-
- if caps.ReportStatusV2 {
- tokens = append(tokens, "report-status-v2")
- }
-
- if caps.DeleteRefs {
- tokens = append(tokens, "delete-refs")
- }
-
- if caps.SideBand64K {
- tokens = append(tokens, "side-band-64k")
- }
-
- if caps.Quiet {
- tokens = append(tokens, "quiet")
- }
-
- if caps.Atomic {
- tokens = append(tokens, "atomic")
- }
-
- if caps.OfsDelta {
- tokens = append(tokens, "ofs-delta")
- }
-
- if caps.PushCertNonce != "" {
- tokens = append(tokens, "push-cert="+caps.PushCertNonce)
- }
-
- if caps.PushOptions {
- tokens = append(tokens, "push-options")
- }
-
- if caps.SessionID != "" {
- tokens = append(tokens, "session-id="+caps.SessionID)
- }
-
- if caps.ObjectFormat != objectid.AlgorithmUnknown {
- tokens = append(tokens, "object-format="+caps.ObjectFormat.String())
- }
-
- if caps.Agent != "" {
- tokens = append(tokens, "agent="+caps.Agent)
- }
-
- return tokens
-}
-
-func (caps Capabilities) supportsToken(token string, defaultAlgorithm objectid.Algorithm) bool {
- name, value, _ := strings.Cut(token, "=")
-
- switch name {
- case "report-status":
- return caps.ReportStatus && value == ""
- case "report-status-v2":
- return caps.ReportStatusV2 && value == ""
- case "delete-refs":
- return caps.DeleteRefs && value == ""
- case "side-band-64k":
- return caps.SideBand64K && value == ""
- case "quiet":
- return caps.Quiet && value == ""
- case "atomic":
- return caps.Atomic && value == ""
- case "ofs-delta":
- return caps.OfsDelta && value == ""
- case "push-options":
- return caps.PushOptions && value == ""
- case "push-cert":
- return caps.PushCertNonce != "" && value != ""
- case "object-format":
- if value == "" {
- return false
- }
-
- algo, ok := objectid.ParseAlgorithm(value)
-
- return ok && algo == caps.Normalize(defaultAlgorithm).ObjectFormat
- case "session-id":
- return caps.SessionID != "" && value != ""
- case "agent":
- return caps.Agent != "" && value != ""
- default:
- return false
- }
-}
-
-func parseCapabilityList(s string) ([]string, error) {
- s = strings.TrimSuffix(s, "\n")
- if s == "" {
- return nil, nil
- }
-
- tokens := strings.Fields(s)
- if slices.Contains(tokens, "") {
- return nil, &ProtocolError{Reason: "empty capability token"}
- }
-
- return tokens, nil
-}
-
-func parseRequestedCapabilities(
- tokens []string,
- supported Capabilities,
- defaultAlgorithm objectid.Algorithm,
-) (Capabilities, error) {
- var requested Capabilities
-
- requested.ObjectFormat = defaultAlgorithm
-
- for _, token := range tokens {
- if !supported.supportsToken(token, defaultAlgorithm) {
- return Capabilities{}, &ProtocolError{
- Reason: fmt.Sprintf("unsupported capability %q", token),
- }
- }
-
- name, value, _ := strings.Cut(token, "=")
- switch name {
- case "report-status":
- requested.ReportStatus = true
- case "report-status-v2":
- requested.ReportStatusV2 = true
- case "delete-refs":
- requested.DeleteRefs = true
- case "side-band-64k":
- requested.SideBand64K = true
- case "quiet":
- requested.Quiet = true
- case "atomic":
- requested.Atomic = true
- case "ofs-delta":
- requested.OfsDelta = true
- case "push-options":
- requested.PushOptions = true
- case "push-cert":
- requested.PushCertNonce = value
- case "object-format":
- algo, _ := objectid.ParseAlgorithm(value)
- requested.ObjectFormat = algo
- case "session-id":
- requested.SessionID = value
- case "agent":
- requested.Agent = value
- }
- }
-
- return requested, nil
-}
diff --git a/network/protocol/v0v1/server/receivepack/doc.go b/network/protocol/v0v1/server/receivepack/doc.go
deleted file mode 100644
index 65793831..00000000
--- a/network/protocol/v0v1/server/receivepack/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package receivepack implements the receive-pack-specific server side of Git protocol v0/v1.
-package receivepack
diff --git a/network/protocol/v0v1/server/receivepack/errors.go b/network/protocol/v0v1/server/receivepack/errors.go
deleted file mode 100644
index d89f8959..00000000
--- a/network/protocol/v0v1/server/receivepack/errors.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package receivepack
-
-// ProtocolError reports one malformed or unsupported receive-pack protocol input.
-type ProtocolError struct {
- Reason string
-}
-
-// Error returns the formatted error string.
-func (err *ProtocolError) Error() string {
- return "protocol/v0v1/server/receivepack: protocol error: " + err.Reason
-}
diff --git a/network/protocol/v0v1/server/receivepack/helpers_test.go b/network/protocol/v0v1/server/receivepack/helpers_test.go
deleted file mode 100644
index 5db8e6a6..00000000
--- a/network/protocol/v0v1/server/receivepack/helpers_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package receivepack_test
-
-import (
- "bytes"
- "strings"
- "testing"
-
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-type bufferWriteFlusher struct {
- bytes.Buffer
-}
-
-func (bufferWriteFlusher) Flush() error {
- return nil
-}
-
-func mustHexID(tb testing.TB, algo objectid.Algorithm, digit string) objectid.ObjectID {
- tb.Helper()
-
- id, err := objectid.ParseHex(algo, strings.Repeat(digit, algo.HexLen()))
- if err != nil {
- tb.Fatalf("objectid.ParseHex(%q): %v", strings.Repeat(digit, algo.HexLen()), err)
- }
-
- return id
-}
diff --git a/network/protocol/v0v1/server/receivepack/parse_test.go b/network/protocol/v0v1/server/receivepack/parse_test.go
deleted file mode 100644
index d54d8f8d..00000000
--- a/network/protocol/v0v1/server/receivepack/parse_test.go
+++ /dev/null
@@ -1,255 +0,0 @@
-package receivepack_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- receivepack "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack"
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-func TestReadRequestParsesCommandsAndPushOptions(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- oldZero := algo.Zero().String()
- oneID := mustHexID(t, algo, "1")
-
- var wire bufferWriteFlusher
-
- enc := pktline.NewEncoder(&wire)
-
- err := enc.WriteData([]byte(
- oldZero + " " + oneID.String() + " refs/heads/main\x00report-status push-options object-format=" + algo.String() + "\n",
- ))
- if err != nil {
- t.Fatalf("WriteData(first): %v", err)
- }
-
- err = enc.WriteData([]byte(
- oneID.String() + " " + oldZero + " refs/heads/old\n",
- ))
- if err != nil {
- t.Fatalf("WriteData(second): %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket(commands): %v", err)
- }
-
- err = enc.WriteData([]byte("ci.skip\n"))
- if err != nil {
- t.Fatalf("WriteData(push-option): %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket(push-options): %v", err)
- }
-
- base := common.NewSession(strings.NewReader(wire.String()), &bufferWriteFlusher{}, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{
- ReportStatus: true,
- PushOptions: true,
- ObjectFormat: algo,
- })
-
- req, err := session.ReadRequest()
- if err != nil {
- t.Fatalf("ReadRequest: %v", err)
- }
-
- if len(req.Commands) != 2 {
- t.Fatalf("len(req.Commands) = %d, want 2", len(req.Commands))
- }
-
- if !req.Capabilities.ReportStatus || !req.Capabilities.PushOptions {
- t.Fatalf("capabilities = %#v", req.Capabilities)
- }
-
- if len(req.PushOptions) != 1 || req.PushOptions[0] != "ci.skip" {
- t.Fatalf("push options = %#v", req.PushOptions)
- }
-
- if !req.PackExpected {
- t.Fatalf("PackExpected = false, want true")
- }
-
- if req.DeleteOnly {
- t.Fatalf("DeleteOnly = true, want false")
- }
- })
-}
-
-func TestReadRequestDeleteOnlyDoesNotExpectPack(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- oneID := mustHexID(t, algo, "1")
-
- var wire bufferWriteFlusher
-
- enc := pktline.NewEncoder(&wire)
-
- err := enc.WriteData([]byte(
- oneID.String() + " " + algo.Zero().String() + " refs/heads/old\x00delete-refs object-format=" + algo.String() + "\n",
- ))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- base := common.NewSession(strings.NewReader(wire.String()), &bufferWriteFlusher{}, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{
- DeleteRefs: true,
- ObjectFormat: algo,
- })
-
- req, err := session.ReadRequest()
- if err != nil {
- t.Fatalf("ReadRequest: %v", err)
- }
-
- if req.PackExpected {
- t.Fatalf("PackExpected = true, want false")
- }
-
- if !req.DeleteOnly {
- t.Fatalf("DeleteOnly = false, want true")
- }
- })
-}
-
-func TestReadRequestRejectsUnsupportedCapability(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- oneID := mustHexID(t, algo, "1")
-
- var wire bufferWriteFlusher
-
- enc := pktline.NewEncoder(&wire)
-
- err := enc.WriteData([]byte(
- algo.Zero().String() + " " + oneID.String() + " refs/heads/main\x00atomic object-format=" + algo.String() + "\n",
- ))
- if err != nil {
- t.Fatalf("WriteData: %v", err)
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- base := common.NewSession(strings.NewReader(wire.String()), &bufferWriteFlusher{}, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{ObjectFormat: algo})
-
- _, err = session.ReadRequest()
- if err == nil {
- t.Fatalf("ReadRequest error = nil, want error")
- }
-
- protocolErr, ok := errors.AsType[*receivepack.ProtocolError](err)
- if !ok {
- t.Fatalf("errors.AsType[*receivepack.ProtocolError](%T) = false", err)
- }
-
- if !strings.Contains(protocolErr.Reason, "unsupported capability") {
- t.Fatalf("ProtocolError.Reason = %q", protocolErr.Reason)
- }
- })
-}
-
-func TestReadRequestParsesPushCertificate(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- oneID := mustHexID(t, algo, "1")
-
- var wire bufferWriteFlusher
-
- enc := pktline.NewEncoder(&wire)
-
- err := enc.WriteData([]byte("push-cert\x00push-cert=nonce object-format=" + algo.String() + "\n"))
- if err != nil {
- t.Fatalf("WriteData(push-cert): %v", err)
- }
-
- lines := []string{
- "certificate version 0.1\n",
- "pusher Example <example@example.com>\n",
- "nonce nonce\n",
- "push-option ci.skip\n",
- "\n",
- algo.Zero().String() + " " + oneID.String() + " refs/heads/main\n",
- "-----BEGIN PGP SIGNATURE-----\n",
- "abcdef\n",
- "push-cert-end\n",
- }
-
- for _, line := range lines {
- err = enc.WriteData([]byte(line))
- if err != nil {
- t.Fatalf("WriteData(%q): %v", line, err)
- }
- }
-
- err = enc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket: %v", err)
- }
-
- base := common.NewSession(strings.NewReader(wire.String()), &bufferWriteFlusher{}, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{
- PushCertNonce: "server-nonce",
- ObjectFormat: algo,
- })
-
- req, err := session.ReadRequest()
- if err != nil {
- t.Fatalf("ReadRequest: %v", err)
- }
-
- if req.PushCert == nil {
- t.Fatalf("PushCert = nil, want parsed certificate")
- }
-
- if len(req.Commands) != 1 {
- t.Fatalf("len(req.Commands) = %d, want 1", len(req.Commands))
- }
-
- if len(req.PushCert.EmbeddedOption) != 1 || req.PushCert.EmbeddedOption[0] != "ci.skip" {
- t.Fatalf("embedded options = %#v", req.PushCert.EmbeddedOption)
- }
- })
-}
diff --git a/network/protocol/v0v1/server/receivepack/report_status.go b/network/protocol/v0v1/server/receivepack/report_status.go
deleted file mode 100644
index d852a161..00000000
--- a/network/protocol/v0v1/server/receivepack/report_status.go
+++ /dev/null
@@ -1,185 +0,0 @@
-package receivepack
-
-import (
- "fmt"
-
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
-)
-
-// WriteReportStatus writes one classic report-status response.
-func (session *Session) WriteReportStatus(result ReportStatusResult) error {
- unpackResult := "ok"
- if result.UnpackError != "" {
- unpackResult = result.UnpackError
- }
-
- if !session.negotiated.SideBand64K {
- err := session.base.WriteData(fmt.Appendf(nil, "unpack %s\n", unpackResult))
- if err != nil {
- return err
- }
-
- for _, command := range result.Commands {
- line := fmt.Sprintf("ok %s\n", command.Name)
- if command.Error != "" {
- line = fmt.Sprintf("ng %s %s\n", command.Name, command.Error)
- }
-
- err = session.base.WriteData([]byte(line))
- if err != nil {
- return err
- }
- }
-
- return session.base.WriteFlushPacket()
- }
-
- buf, err := pktline.AppendData(nil, fmt.Appendf(nil, "unpack %s\n", unpackResult))
- if err != nil {
- return err
- }
-
- for _, command := range result.Commands {
- line := fmt.Sprintf("ok %s\n", command.Name)
- if command.Error != "" {
- line = fmt.Sprintf("ng %s %s\n", command.Name, command.Error)
- }
-
- buf, err = pktline.AppendData(buf, []byte(line))
- if err != nil {
- return err
- }
- }
-
- buf = pktline.AppendFlushPkt(buf)
-
- w := session.base.PrimaryDataWriter()
-
- _, err = w.Write(buf)
- if err != nil {
- return err
- }
-
- return session.base.WriteFlushPacket()
-}
-
-// WriteReportStatusV2 writes one report-status-v2 response.
-func (session *Session) WriteReportStatusV2(result ReportStatusResult) error {
- unpackResult := "ok"
- if result.UnpackError != "" {
- unpackResult = result.UnpackError
- }
-
- if !session.negotiated.SideBand64K { //nolint:nestif
- err := session.base.WriteData(fmt.Appendf(nil, "unpack %s\n", unpackResult))
- if err != nil {
- return err
- }
-
- for _, command := range result.Commands {
- if command.Error != "" {
- err = session.base.WriteData(fmt.Appendf(nil, "ng %s %s\n", command.Name, command.Error))
- if err != nil {
- return err
- }
-
- continue
- }
-
- err = session.base.WriteData(fmt.Appendf(nil, "ok %s\n", command.Name))
- if err != nil {
- return err
- }
-
- if command.RefName != "" {
- err = session.base.WriteData(fmt.Appendf(nil, "option refname %s\n", command.RefName))
- if err != nil {
- return err
- }
- }
-
- if command.OldID != nil {
- err = session.base.WriteData(fmt.Appendf(nil, "option old-oid %s\n", *command.OldID))
- if err != nil {
- return err
- }
- }
-
- if command.NewID != nil {
- err = session.base.WriteData(fmt.Appendf(nil, "option new-oid %s\n", *command.NewID))
- if err != nil {
- return err
- }
- }
-
- if command.ForcedUpdate {
- err = session.base.WriteData([]byte("option forced-update\n"))
- if err != nil {
- return err
- }
- }
- }
-
- return session.base.WriteFlushPacket()
- }
-
- buf, err := pktline.AppendData(nil, fmt.Appendf(nil, "unpack %s\n", unpackResult))
- if err != nil {
- return err
- }
-
- for _, command := range result.Commands {
- if command.Error != "" {
- buf, err = pktline.AppendData(buf, fmt.Appendf(nil, "ng %s %s\n", command.Name, command.Error))
- if err != nil {
- return err
- }
-
- continue
- }
-
- buf, err = pktline.AppendData(buf, fmt.Appendf(nil, "ok %s\n", command.Name))
- if err != nil {
- return err
- }
-
- if command.RefName != "" {
- buf, err = pktline.AppendData(buf, fmt.Appendf(nil, "option refname %s\n", command.RefName))
- if err != nil {
- return err
- }
- }
-
- if command.OldID != nil {
- buf, err = pktline.AppendData(buf, fmt.Appendf(nil, "option old-oid %s\n", *command.OldID))
- if err != nil {
- return err
- }
- }
-
- if command.NewID != nil {
- buf, err = pktline.AppendData(buf, fmt.Appendf(nil, "option new-oid %s\n", *command.NewID))
- if err != nil {
- return err
- }
- }
-
- if command.ForcedUpdate {
- buf, err = pktline.AppendData(buf, []byte("option forced-update\n"))
- if err != nil {
- return err
- }
- }
- }
-
- buf = pktline.AppendFlushPkt(buf)
-
- w := session.base.PrimaryDataWriter()
-
- _, err = w.Write(buf)
- if err != nil {
- return err
- }
-
- return session.base.WriteFlushPacket()
-}
diff --git a/network/protocol/v0v1/server/receivepack/report_status_test.go b/network/protocol/v0v1/server/receivepack/report_status_test.go
deleted file mode 100644
index 3cde5103..00000000
--- a/network/protocol/v0v1/server/receivepack/report_status_test.go
+++ /dev/null
@@ -1,293 +0,0 @@
-package receivepack_test
-
-import (
- "errors"
- "io"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- receivepack "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack"
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-func TestWriteReportStatusWritesClassicStatus(t *testing.T) {
- t.Parallel()
-
- var out bufferWriteFlusher
-
- base := common.NewSession(strings.NewReader(""), &out, common.Options{})
- session := receivepack.NewSession(base, receivepack.Capabilities{})
-
- err := session.WriteReportStatus(receivepack.ReportStatusResult{
- Commands: []receivepack.CommandResult{
- {Name: "refs/heads/main"},
- {Name: "refs/heads/dev", Error: "non-fast-forward"},
- },
- })
- if err != nil {
- t.Fatalf("WriteReportStatus: %v", err)
- }
-
- got := out.String()
- wantParts := []string{
- "unpack ok\n",
- "ok refs/heads/main\n",
- "ng refs/heads/dev non-fast-forward\n",
- "0000",
- }
-
- for _, part := range wantParts {
- if !strings.Contains(got, part) {
- t.Fatalf("report-status missing %q in %q", part, got)
- }
- }
-}
-
-func TestWriteReportStatusUsesSideBand64KWhenNegotiated(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- var requestWire bufferWriteFlusher
-
- requestEnc := pktline.NewEncoder(&requestWire)
-
- err := requestEnc.WriteData([]byte(
- algo.Zero().String() + " " + mustHexID(t, algo, "1").String() + " refs/heads/main\x00report-status side-band-64k object-format=" + algo.String() + "\n",
- ))
- if err != nil {
- t.Fatalf("WriteData(request): %v", err)
- }
-
- err = requestEnc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket(request): %v", err)
- }
-
- var out bufferWriteFlusher
-
- base := common.NewSession(strings.NewReader(requestWire.String()), &out, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{
- ReportStatus: true,
- SideBand64K: true,
- ObjectFormat: algo,
- })
-
- _, err = session.ReadRequest()
- if err != nil {
- t.Fatalf("ReadRequest: %v", err)
- }
-
- err = session.WriteReportStatus(receivepack.ReportStatusResult{
- Commands: []receivepack.CommandResult{
- {Name: "refs/heads/main"},
- },
- })
- if err != nil {
- t.Fatalf("WriteReportStatus: %v", err)
- }
-
- dec := sideband64k.NewDecoder(strings.NewReader(out.String()), sideband64k.ReadOptions{})
-
- frame, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(unpack): %v", err)
- }
-
- if frame.Type != sideband64k.FrameData {
- t.Fatalf("first frame = %#v", frame)
- }
-
- statusDec := pktline.NewDecoder(strings.NewReader(string(frame.Payload)), pktline.ReadOptions{})
-
- statusFrame, err := statusDec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(unpack status): %v", err)
- }
-
- if statusFrame.Type != pktline.PacketData || string(statusFrame.Payload) != "unpack ok\n" {
- t.Fatalf("first status frame = %#v", statusFrame)
- }
-
- statusFrame, err = statusDec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(ok status): %v", err)
- }
-
- if statusFrame.Type != pktline.PacketData || string(statusFrame.Payload) != "ok refs/heads/main\n" {
- t.Fatalf("second status frame = %#v", statusFrame)
- }
-
- statusFrame, err = statusDec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(status flush): %v", err)
- }
-
- if statusFrame.Type != pktline.PacketFlush {
- t.Fatalf("status flush frame.Type = %v, want FrameFlush", statusFrame.Type)
- }
-
- frame, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(outer flush): %v", err)
- }
-
- if frame.Type != sideband64k.FrameFlush {
- t.Fatalf("outer flush frame.Type = %v, want FrameFlush", frame.Type)
- }
- })
-}
-
-func TestWriteReportStatusV2WritesOptionLines(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- oldID := mustHexID(t, algo, "1")
- newID := mustHexID(t, algo, "2")
-
- var out bufferWriteFlusher
-
- base := common.NewSession(strings.NewReader(""), &out, common.Options{})
- session := receivepack.NewSession(base, receivepack.Capabilities{})
-
- err := session.WriteReportStatusV2(receivepack.ReportStatusResult{
- Commands: []receivepack.CommandResult{
- {
- Name: "refs/pseudo/proc",
- RefName: "refs/heads/main",
- OldID: &oldID,
- NewID: &newID,
- ForcedUpdate: true,
- },
- {Name: "refs/heads/dev", Error: "rejected"},
- },
- })
- if err != nil {
- t.Fatalf("WriteReportStatusV2: %v", err)
- }
-
- got := out.String()
- wantParts := []string{
- "unpack ok\n",
- "ok refs/pseudo/proc\n",
- "option refname refs/heads/main\n",
- "option old-oid " + oldID.String() + "\n",
- "option new-oid " + newID.String() + "\n",
- "option forced-update\n",
- "ng refs/heads/dev rejected\n",
- "0000",
- }
-
- for _, part := range wantParts {
- if !strings.Contains(got, part) {
- t.Fatalf("report-status-v2 missing %q in %q", part, got)
- }
- }
- })
-}
-
-func TestWriteProgressRequiresSideBand64K(t *testing.T) {
- t.Parallel()
-
- base := common.NewSession(strings.NewReader(""), &bufferWriteFlusher{}, common.Options{})
- session := receivepack.NewSession(base, receivepack.Capabilities{})
-
- err := session.WriteProgress([]byte("progress\n"))
- if !errors.Is(err, common.ErrSideBandNotEnabled) {
- t.Fatalf("WriteProgress error = %v, want %v", err, common.ErrSideBandNotEnabled)
- }
-}
-
-func TestProgressWriterDiscardsWithoutSideBand64K(t *testing.T) {
- t.Parallel()
-
- var out bufferWriteFlusher
-
- base := common.NewSession(strings.NewReader(""), &out, common.Options{})
- session := receivepack.NewSession(base, receivepack.Capabilities{})
-
- n, err := io.WriteString(session.ProgressWriter(), "progress line\n")
- if err != nil {
- t.Fatalf("ProgressWriter.Write: %v", err)
- }
-
- if n != len("progress line\n") {
- t.Fatalf("ProgressWriter.Write n = %d, want %d", n, len("progress line\n"))
- }
-
- if out.String() != "" {
- t.Fatalf("unexpected wire output without side-band-64k: %q", out.String())
- }
-}
-
-func TestProgressWriterUsesSideBand64KWhenNegotiated(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- var requestWire bufferWriteFlusher
-
- requestEnc := pktline.NewEncoder(&requestWire)
-
- err := requestEnc.WriteData([]byte(
- algo.Zero().String() + " " + mustHexID(t, algo, "1").String() + " refs/heads/main\x00report-status side-band-64k object-format=" + algo.String() + "\n",
- ))
- if err != nil {
- t.Fatalf("WriteData(request): %v", err)
- }
-
- err = requestEnc.WriteFlushPacket()
- if err != nil {
- t.Fatalf("WriteFlushPacket(request): %v", err)
- }
-
- var out bufferWriteFlusher
-
- base := common.NewSession(strings.NewReader(requestWire.String()), &out, common.Options{
- Algorithm: algo,
- })
- session := receivepack.NewSession(base, receivepack.Capabilities{
- ReportStatus: true,
- SideBand64K: true,
- ObjectFormat: algo,
- })
-
- _, err = session.ReadRequest()
- if err != nil {
- t.Fatalf("ReadRequest: %v", err)
- }
-
- _, err = io.WriteString(session.ProgressWriter(), "remote: stage 1\r")
- if err != nil {
- t.Fatalf("ProgressWriter.Write: %v", err)
- }
-
- dec := sideband64k.NewDecoder(strings.NewReader(out.String()), sideband64k.ReadOptions{})
-
- frame, err := dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(progress): %v", err)
- }
-
- if frame.Type != sideband64k.FrameProgress {
- t.Fatalf("frame.Type = %v, want FrameProgress", frame.Type)
- }
-
- if string(frame.Payload) != "remote: stage 1\r" {
- t.Fatalf("frame.Payload = %q, want %q", frame.Payload, "remote: stage 1\r")
- }
- })
-}
diff --git a/network/protocol/v0v1/server/receivepack/session.go b/network/protocol/v0v1/server/receivepack/session.go
deleted file mode 100644
index 5299b42d..00000000
--- a/network/protocol/v0v1/server/receivepack/session.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package receivepack
-
-import (
- "fmt"
- "strings"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// Session is one stateful server-side receive-pack protocol session.
-//
-// Labels: MT-Unsafe.
-type Session struct {
- base *common.Session
- supported Capabilities
- negotiated Capabilities
-}
-
-// NewSession creates one receive-pack session over one common server session.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-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.
-//
-// Labels: Life-Parent.
-func (session *Session) ProgressWriter() iowrap.WriteFlusher {
- 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.
-//
-// Labels: Life-Parent.
-func (session *Session) ErrorWriter() iowrap.WriteFlusher {
- 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 == cmd.NewID.Algorithm().Zero()
-}
-
-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))
- }
-}
diff --git a/network/protocol/v0v1/server/receivepack/types.go b/network/protocol/v0v1/server/receivepack/types.go
deleted file mode 100644
index b281a86b..00000000
--- a/network/protocol/v0v1/server/receivepack/types.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package receivepack
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// Command is one requested reference update.
-type Command struct {
- OldID objectid.ObjectID
- NewID objectid.ObjectID
- Name string
-}
-
-// PushCertificate is one parsed push certificate block.
-type PushCertificate struct {
- HeaderLines []string
- EmbeddedOption []string
- Commands []Command
- SignatureLines []string
-}
-
-// Request is one parsed receive-pack request.
-type Request struct {
- Capabilities Capabilities
- Shallow []objectid.ObjectID
- Commands []Command
- PushCert *PushCertificate
- PushOptions []string
- PackExpected bool
- DeleteOnly bool
-}
-
-// CommandResult is one per-command report-status result.
-type CommandResult struct {
- Name string
- Error string
- RefName string
- OldID *objectid.ObjectID
- NewID *objectid.ObjectID
- ForcedUpdate bool
-}
-
-// ReportStatusResult is one report-status payload.
-type ReportStatusResult struct {
- UnpackError string
- Commands []CommandResult
-}
diff --git a/network/protocol/v0v1/server/session.go b/network/protocol/v0v1/server/session.go
deleted file mode 100644
index a66cc37a..00000000
--- a/network/protocol/v0v1/server/session.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package server
-
-import (
- "io"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
- objectid "codeberg.org/lindenii/furgit/object/id"
-)
-
-// Options configures one server-side v0/v1 session.
-type Options struct {
- // Version selects protocol v0 or v1 framing.
- Version Version
- // Algorithm is the repository object ID algorithm for this session.
- Algorithm objectid.Algorithm
-}
-
-// Session is one stateful server-side v0/v1 server protocol session.
-//
-// Labels: MT-Unsafe.
-type Session struct {
- dec *pktline.Decoder
- enc *pktline.Encoder
- sideband *sideband64k.Encoder
- opts Options
- useSideBand bool
-}
-
-// NewSession creates one v0/v1 server session over r and w.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func NewSession(r io.Reader, w iowrap.WriteFlusher, opts Options) *Session {
- return &Session{
- dec: pktline.NewDecoder(r, pktline.ReadOptions{}),
- enc: pktline.NewEncoder(w),
- sideband: sideband64k.NewEncoder(w),
- opts: opts,
- }
-}
-
-// Algorithm returns the session object ID algorithm.
-func (session *Session) Algorithm() objectid.Algorithm {
- return session.opts.Algorithm
-}
-
-// ReadFrame reads one low-level pkt-line frame from the session input.
-func (session *Session) ReadFrame() (Frame, error) {
- return session.dec.ReadFrame()
-}
-
-// EnableSideBand64K enables side-band-64k output framing for subsequent data,
-// progress, error, and flush writes.
-func (session *Session) EnableSideBand64K() {
- session.useSideBand = true
-}
-
-// WriteData writes one primary output packet.
-func (session *Session) WriteData(p []byte) error {
- if session.useSideBand {
- return session.sideband.WriteData(p)
- }
-
- return session.enc.WriteData(p)
-}
-
-// WriteProgress writes one progress packet.
-func (session *Session) WriteProgress(p []byte) error {
- if !session.useSideBand {
- return ErrSideBandNotEnabled
- }
-
- return session.sideband.WriteProgress(p)
-}
-
-// WriteError writes one fatal error packet.
-func (session *Session) WriteError(p []byte) error {
- if !session.useSideBand {
- return ErrSideBandNotEnabled
- }
-
- return session.sideband.WriteError(p)
-}
-
-// WriteFlushPacket writes one trailing flush packet.
-func (session *Session) WriteFlushPacket() error {
- if session.useSideBand {
- return session.sideband.WriteFlushPacket()
- }
-
- return session.enc.WriteFlushPacket()
-}
-
-// Flush flushes buffered transport output without emitting pkt-line frames.
-func (session *Session) Flush() error {
- if session.useSideBand {
- return session.sideband.Flush()
- }
-
- return session.enc.Flush()
-}
-
-// ProgressWriter returns one chunking writer for sideband progress output.
-//
-// When side-band-64k was not negotiated, writes are discarded.
-//
-// Labels: Life-Parent.
-func (session *Session) ProgressWriter() iowrap.WriteFlusher {
- if !session.useSideBand {
- return iowrap.NopFlush(io.Discard)
- }
-
- return sideband64k.NewChunkWriter(session.sideband, sideband64k.BandProgress)
-}
-
-// ErrorWriter returns one chunking writer for sideband error output.
-//
-// When side-band-64k was not negotiated, writes are discarded.
-//
-// Labels: Life-Parent.
-func (session *Session) ErrorWriter() iowrap.WriteFlusher {
- if !session.useSideBand {
- return iowrap.NopFlush(io.Discard)
- }
-
- return sideband64k.NewChunkWriter(session.sideband, sideband64k.BandError)
-}
-
-// PrimaryDataWriter returns one chunking writer for primary output bytes.
-//
-// When side-band-64k is enabled, writes are chunked into band-1 sideband
-// frames. Otherwise writes are chunked into direct pkt-line data frames.
-//
-// Labels: Life-Parent.
-func (session *Session) PrimaryDataWriter() iowrap.WriteFlusher {
- if session.useSideBand {
- return sideband64k.NewChunkWriter(session.sideband, sideband64k.BandData)
- }
-
- return pktline.NewChunkWriter(session.enc)
-}
diff --git a/network/protocol/v0v1/server/version.go b/network/protocol/v0v1/server/version.go
deleted file mode 100644
index 23ae9466..00000000
--- a/network/protocol/v0v1/server/version.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package server
-
-// Version identifies the protocol version used on one v0/v1 server session.
-type Version uint8
-
-const (
- // Version0 is the original protocol framing with no leading version line.
- Version0 Version = iota
- // Version1 is protocol v1, which is v0 plus one leading "version 1\n"
- // pkt-line before ref advertisement.
- Version1
-)
diff --git a/network/receivepack/advertise.go b/network/receivepack/advertise.go
deleted file mode 100644
index 0fa010bf..00000000
--- a/network/receivepack/advertise.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package receivepack
-
-import (
- "errors"
-
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- "codeberg.org/lindenii/furgit/ref"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-func advertisedRefs(opts Options) ([]common.AdvertisedRef, error) {
- listed, err := opts.Refs.List("")
- if err != nil {
- return nil, err
- }
-
- return buildAdvertisedRefs(opts, listed)
-}
-
-func buildAdvertisedRefs(opts Options, listed []ref.Ref) ([]common.AdvertisedRef, error) {
- refs := make([]common.AdvertisedRef, 0, len(listed))
- for _, entry := range listed {
- switch resolved := entry.(type) {
- case ref.Detached:
- advertised := common.AdvertisedRef{
- Name: resolved.Name(),
- ID: resolved.ID,
- }
-
- if resolved.Peeled != nil {
- advertised.Peeled = resolved.Peeled
- }
-
- refs = append(refs, advertised)
- case ref.Symbolic:
- if resolved.Name() != "HEAD" {
- continue
- }
-
- head, err := opts.Refs.ResolveToDetached("HEAD")
- if err != nil {
- if errors.Is(err, refstore.ErrReferenceNotFound) {
- continue
- }
-
- return nil, err
- }
-
- refs = append(refs, common.AdvertisedRef{
- Name: "HEAD",
- ID: head.ID,
- })
- }
- }
-
- return refs, nil
-}
diff --git a/network/receivepack/capabilities_defaults.go b/network/receivepack/capabilities_defaults.go
deleted file mode 100644
index 72c36c30..00000000
--- a/network/receivepack/capabilities_defaults.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package receivepack
-
-import (
- "crypto/rand"
-)
-
-func defaultAgent() string {
- return "furgit"
-}
-
-func defaultSessionID() string {
- return "furgit-" + rand.Text()
-}
-
-func defaultPushCertNonce() string {
- return "furgit-" + rand.Text()
-}
diff --git a/network/receivepack/commands.go b/network/receivepack/commands.go
deleted file mode 100644
index a9edec1a..00000000
--- a/network/receivepack/commands.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package receivepack
-
-import (
- protoreceive "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack"
- "codeberg.org/lindenii/furgit/network/receivepack/service"
-)
-
-func translateCommands(commands []protoreceive.Command) []service.Command {
- out := make([]service.Command, 0, len(commands))
- for _, command := range commands {
- out = append(out, service.Command{
- OldID: command.OldID,
- NewID: command.NewID,
- Name: command.Name,
- })
- }
-
- return out
-}
diff --git a/network/receivepack/doc.go b/network/receivepack/doc.go
deleted file mode 100644
index b63f49d5..00000000
--- a/network/receivepack/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package receivepack provides the application-facing server-side push entry
-// point.
-package receivepack
diff --git a/network/receivepack/errors.go b/network/receivepack/errors.go
deleted file mode 100644
index 18e7a135..00000000
--- a/network/receivepack/errors.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package receivepack
-
-import "errors"
-
-var (
- // ErrMissingAlgorithm reports one missing repository hash algorithm.
- ErrMissingAlgorithm = errors.New("receivepack: missing object id algorithm")
- // ErrMissingRefs reports one missing reference store dependency.
- ErrMissingRefs = errors.New("receivepack: missing refs store")
- // ErrMissingObjects reports one missing object store dependency.
- ErrMissingObjects = errors.New("receivepack: missing objects store")
- // ErrUnsupportedProtocol reports one unsupported requested Git protocol
- // version.
- ErrUnsupportedProtocol = errors.New("receivepack: unsupported protocol version")
-)
diff --git a/network/receivepack/hook.go b/network/receivepack/hook.go
deleted file mode 100644
index 9c323bcc..00000000
--- a/network/receivepack/hook.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package receivepack
-
-import (
- "context"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
- "codeberg.org/lindenii/furgit/network/receivepack/service"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-type HookIO struct {
- Progress iowrap.WriteFlusher
- Error iowrap.WriteFlusher
-}
-
-// RefUpdate is one requested reference update presented to a receive-pack hook.
-type RefUpdate struct {
- Name string
- OldID objectid.ObjectID
- NewID objectid.ObjectID
-}
-
-// UpdateDecision is one hook decision for a requested reference update.
-type UpdateDecision struct {
- Accept bool
- Message string
-}
-
-// HookRequest is the input presented to a receive-pack hook before quarantine
-// promotion and ref updates.
-//
-// Labels: Life-Call.
-type HookRequest struct {
- Refs refstore.Reader
- ExistingObjects objectstore.Reader
- // QuarantinedObjects exposes quarantined objects for this push.
- //
- // When the push did not create a quarantine, QuarantinedObjects is nil.
- QuarantinedObjects objectstore.Reader
- CommitGraph *commitgraphread.Reader
- Updates []RefUpdate
- PushOptions []string
- IO HookIO
-}
-
-// Hook decides whether each requested update should proceed.
-//
-// The hook runs after pack ingestion into quarantine and before quarantine
-// promotion or ref updates. The returned decisions must have the same length as
-// HookRequest.Updates.
-type Hook func(context.Context, HookRequest) ([]UpdateDecision, error)
-
-func translateHook(hook Hook) service.Hook {
- if hook == nil {
- return nil
- }
-
- return func(ctx context.Context, req service.HookRequest) ([]service.UpdateDecision, error) {
- translatedUpdates := make([]RefUpdate, 0, len(req.Updates))
- for _, update := range req.Updates {
- translatedUpdates = append(translatedUpdates, RefUpdate{
- Name: update.Name,
- OldID: update.OldID,
- NewID: update.NewID,
- })
- }
-
- decisions, err := hook(ctx, HookRequest{
- Refs: req.Refs,
- ExistingObjects: req.ExistingObjects,
- QuarantinedObjects: req.QuarantinedObjects,
- CommitGraph: req.CommitGraph,
- Updates: translatedUpdates,
- PushOptions: append([]string(nil), req.PushOptions...),
- IO: HookIO{
- Progress: req.IO.Progress,
- Error: req.IO.Error,
- },
- })
- if err != nil {
- return nil, err
- }
-
- out := make([]service.UpdateDecision, 0, len(decisions))
- for _, decision := range decisions {
- out = append(out, service.UpdateDecision{
- Accept: decision.Accept,
- Message: decision.Message,
- })
- }
-
- return out, nil
- }
-}
diff --git a/network/receivepack/hooks/chain.go b/network/receivepack/hooks/chain.go
deleted file mode 100644
index f98c06f8..00000000
--- a/network/receivepack/hooks/chain.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package hooks
-
-import (
- "context"
- "fmt"
-
- receivepack "codeberg.org/lindenii/furgit/network/receivepack"
-)
-
-// Chain combines hooks by running them in order and intersecting their
-// decisions. The first rejecting message for each update is preserved.
-func Chain(hooks ...receivepack.Hook) receivepack.Hook {
- return func(
- ctx context.Context,
- req receivepack.HookRequest,
- ) ([]receivepack.UpdateDecision, error) {
- decisions := make([]receivepack.UpdateDecision, len(req.Updates))
- for i := range decisions {
- decisions[i].Accept = true
- }
-
- for _, hook := range hooks {
- if hook == nil {
- continue
- }
-
- hookDecisions, err := hook(ctx, req)
- if err != nil {
- return nil, err
- }
-
- if len(hookDecisions) != len(req.Updates) {
- return nil, fmt.Errorf("hook returned %d decisions for %d updates", len(hookDecisions), len(req.Updates))
- }
-
- for i, decision := range hookDecisions {
- if decision.Accept {
- continue
- }
-
- if decisions[i].Accept {
- decisions[i].Message = decision.Message
- }
-
- decisions[i].Accept = false
- }
- }
-
- return decisions, nil
- }
-}
diff --git a/network/receivepack/hooks/doc.go b/network/receivepack/hooks/doc.go
deleted file mode 100644
index bef2baf9..00000000
--- a/network/receivepack/hooks/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package hooks provides a few pre-defined hooks that callers might find useful.
-package hooks
diff --git a/network/receivepack/hooks/reject_force_push.go b/network/receivepack/hooks/reject_force_push.go
deleted file mode 100644
index 5840a031..00000000
--- a/network/receivepack/hooks/reject_force_push.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package hooks
-
-import (
- "context"
- "errors"
- "fmt"
-
- "codeberg.org/lindenii/furgit/commitquery"
- receivepack "codeberg.org/lindenii/furgit/network/receivepack"
- "codeberg.org/lindenii/furgit/object/fetch"
- objectmix "codeberg.org/lindenii/furgit/object/store/mix"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-// RejectForcePush rejects updates whose new value is not a fast-forward of the
-// currently resolved reference.
-func RejectForcePush() receivepack.Hook {
- return func(
- ctx context.Context,
- req receivepack.HookRequest,
- ) ([]receivepack.UpdateDecision, error) {
- _ = ctx
-
- objects := req.ExistingObjects
- if req.QuarantinedObjects != nil {
- objects = objectmix.New(req.QuarantinedObjects, req.ExistingObjects)
- }
-
- queries := commitquery.New(fetch.New(objects), req.CommitGraph)
-
- decisions := make([]receivepack.UpdateDecision, len(req.Updates))
- for i := range decisions {
- decisions[i].Accept = true
- }
-
- for i, update := range req.Updates {
- if update.OldID == update.OldID.Algorithm().Zero() || update.NewID == update.NewID.Algorithm().Zero() {
- continue
- }
-
- current, err := req.Refs.ResolveToDetached(update.Name)
- switch {
- case err == nil:
- case errors.Is(err, refstore.ErrReferenceNotFound):
- continue
- default:
- return nil, fmt.Errorf("resolve %s: %w", update.Name, err)
- }
-
- if current.ID == update.NewID {
- continue
- }
-
- ok, err := queries.IsAncestor(current.ID, update.NewID)
- if err != nil {
- return nil, fmt.Errorf("check fast-forward %s: %w", update.Name, err)
- }
-
- if !ok {
- decisions[i] = receivepack.UpdateDecision{
- Accept: false,
- Message: "non-fast-forward",
- }
- }
- }
-
- return decisions, nil
- }
-}
diff --git a/network/receivepack/int_test.go b/network/receivepack/int_test.go
deleted file mode 100644
index 352bbe7b..00000000
--- a/network/receivepack/int_test.go
+++ /dev/null
@@ -1,1095 +0,0 @@
-package receivepack_test
-
-import (
- "context"
- "fmt"
- "io"
- "os"
- "strings"
- "testing"
- "time"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- "codeberg.org/lindenii/furgit/network/protocol/pktline"
- "codeberg.org/lindenii/furgit/network/protocol/sideband64k"
- receivepack "codeberg.org/lindenii/furgit/network/receivepack"
- receivepackhooks "codeberg.org/lindenii/furgit/network/receivepack/hooks"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- objectdual "codeberg.org/lindenii/furgit/object/store/dual"
- objectloose "codeberg.org/lindenii/furgit/object/store/loose"
- objectpacked "codeberg.org/lindenii/furgit/object/store/packed"
-)
-
-func TestReceivePackDeleteOnlyAtomicDeleteSucceeds(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- GitProtocol: "",
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "ok refs/heads/main\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/main")
- if err == nil {
- t.Fatal("refs/heads/main still exists after delete push")
- }
- })
-}
-
-func TestReceivePackDeleteOnlyNonAtomicAppliesIndependentDeletes(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- _, _, staleID := testRepo.MakeCommit(t, "stale")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
- testRepo.UpdateRef(t, "refs/heads/topic", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- staleID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/topic\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- GitProtocol: "",
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ok refs/heads/topic\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/main")
- if err != nil {
- t.Fatalf("Resolve(main): %v", err)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/topic")
- if err == nil {
- t.Fatal("refs/heads/topic still exists after successful delete")
- }
- })
-}
-
-func TestReceivePackDeleteOnlyAtomicFailureLeavesAllRefsUntouched(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- _, _, staleID := testRepo.MakeCommit(t, "stale")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
- testRepo.UpdateRef(t, "refs/heads/topic", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- staleID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/topic\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- GitProtocol: "",
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ng refs/heads/topic ") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/main")
- if err != nil {
- t.Fatalf("Resolve(main): %v", err)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/topic")
- if err != nil {
- t.Fatalf("Resolve(topic): %v", err)
- }
- })
-}
-
-func TestReceivePackAdvertisesResolvedHEAD(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
- testRepo.SymbolicRef(t, "HEAD", "refs/heads/main")
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
-
- want := commitID.String() + " HEAD"
- if !strings.Contains(got, want) {
- t.Fatalf("HEAD advertisement missing %q in %q", want, got)
- }
- })
-}
-
-func TestReceivePackVersion2FallsBackToV0(t *testing.T) {
- t.Parallel()
-
- testReceivePackProtocolFallback(t, "version=2")
-}
-
-func TestReceivePackHighestRequestedVersionFallsBackToV0ForV2(t *testing.T) {
- t.Parallel()
-
- testReceivePackProtocolFallback(t, "version=1:version=2")
-}
-
-func TestReceivePackWithoutReportStatusWritesNoStatusPayload(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00delete-refs atomic object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if strings.Contains(got, "unpack ") || strings.Contains(got, "ng refs/heads/main ") || strings.Contains(got, "ok refs/heads/main\n") {
- t.Fatalf("unexpected status payload %q", got)
- }
- })
-}
-
-func testReceivePackProtocolFallback(t *testing.T, gitProtocol string) {
- t.Helper()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- GitProtocol: gitProtocol,
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- if strings.HasPrefix(output.String(), pktlineData("version 1\n")) {
- t.Fatalf("receive-pack output started with protocol v1 preface for %q: %q", gitProtocol, output.String())
- }
- })
-}
-
-func TestReceivePackPackRequestWithoutObjectIngressReportsNotConfigured(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + commitID.String() + " refs/heads/main\x00report-status object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "unpack object ingress not configured\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
- })
-}
-
-func TestReceivePackPackCreatePromotesObjectsAndUpdatesRef(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := sender.MakeCommit(t, "pushed commit")
-
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- repo := receiver.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, receiver, algo)
-
- packStream := sender.PackObjectsReader(t, []string{commitID.String()}, false)
- t.Cleanup(func() {
- _ = packStream.Close()
- })
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- algo.Zero().String() + " " + commitID.String() + " refs/heads/main\x00report-status-v2 atomic object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(
- context.Background(),
- &output,
- io.MultiReader(strings.NewReader(input.String()), packStream),
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- },
- )
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "unpack ok\n") || !strings.Contains(got, "ok refs/heads/main\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- reopened := receiver.OpenRepository(t)
-
- resolved, err := reopened.Refs().ResolveToDetached("refs/heads/main")
- if err != nil {
- t.Fatalf("ResolveToDetached(main): %v", err)
- }
-
- if resolved.ID != commitID {
- t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, commitID)
- }
-
- if gotType := receiver.Run(t, "cat-file", "-t", commitID.String()); gotType != "commit" {
- t.Fatalf("cat-file -t = %q, want commit", gotType)
- }
-
- packs := receiver.Run(t, "count-objects", "-v")
- if !strings.Contains(packs, "packs: 1") {
- t.Fatalf("count-objects output missing promoted pack: %q", packs)
- }
- })
-}
-
-func TestReceivePackHookSeesQuarantinedObjectsAndCanRejectBeforePromotion(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := sender.MakeCommit(t, "pushed commit")
-
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- repo := receiver.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, receiver, algo)
-
- packStream := sender.PackObjectsReader(t, []string{commitID.String()}, false)
- t.Cleanup(func() {
- _ = packStream.Close()
- })
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- hookCalled bool
- )
-
- input.WriteString(pktlineData(
- algo.Zero().String() + " " + commitID.String() + " refs/heads/main\x00report-status-v2 atomic object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(
- context.Background(),
- &output,
- io.MultiReader(strings.NewReader(input.String()), packStream),
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- Hook: func(ctx context.Context, req receivepack.HookRequest) ([]receivepack.UpdateDecision, error) {
- hookCalled = true
-
- if len(req.Updates) != 1 || req.Updates[0].NewID != commitID {
- t.Fatalf("unexpected hook updates: %+v", req.Updates)
- }
-
- _, _, err := req.ExistingObjects.ReadHeader(commitID)
- if err == nil {
- t.Fatalf("existing objects unexpectedly contained quarantined commit %s", commitID)
- }
-
- _, _, err = req.QuarantinedObjects.ReadHeader(commitID)
- if err != nil {
- t.Fatalf("quarantined objects missing commit %s: %v", commitID, err)
- }
-
- return []receivepack.UpdateDecision{{
- Accept: false,
- Message: "blocked by hook",
- }}, nil
- },
- },
- )
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- if !hookCalled {
- t.Fatal("hook was not called")
- }
-
- got := output.String()
- if !strings.Contains(got, "unpack ok\n") || !strings.Contains(got, "ng refs/heads/main blocked by hook\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/main")
- if err == nil {
- t.Fatal("refs/heads/main exists after hook rejection")
- }
-
- packs := receiver.Run(t, "count-objects", "-v")
- if !strings.Contains(packs, "packs: 0") {
- t.Fatalf("count-objects output shows unexpected promoted pack: %q", packs)
- }
- })
-}
-
-func TestReceivePackHookCanRejectSubsetOfNonAtomicDeleteOnlyPush(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
- testRepo.UpdateRef(t, "refs/heads/topic", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/topic\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- Hook: func(ctx context.Context, req receivepack.HookRequest) ([]receivepack.UpdateDecision, error) {
- return []receivepack.UpdateDecision{
- {Accept: false, Message: "leave main alone"},
- {Accept: true},
- }, nil
- },
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "ng refs/heads/main leave main alone\n") || !strings.Contains(got, "ok refs/heads/topic\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/main")
- if err != nil {
- t.Fatalf("Resolve(main): %v", err)
- }
-
- _, err = repo.Refs().Resolve("refs/heads/topic")
- if err == nil {
- t.Fatal("refs/heads/topic still exists after successful delete")
- }
- })
-}
-
-func TestReceivePackHookProgressUsesSideBand64K(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status side-band-64k atomic delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- Hook: func(ctx context.Context, req receivepack.HookRequest) ([]receivepack.UpdateDecision, error) {
- _, err := io.WriteString(req.IO.Progress, "hook says hello\n")
- if err != nil {
- return nil, err
- }
-
- return []receivepack.UpdateDecision{{Accept: true}}, nil
- },
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- _, sidebandWire, ok := strings.Cut(output.String(), "0000")
- if !ok {
- t.Fatalf("output missing advertisement flush: %q", output.String())
- }
-
- dec := sideband64k.NewDecoder(strings.NewReader(sidebandWire), sideband64k.ReadOptions{})
-
- sawHookProgress := false
-
- var frame sideband64k.Frame
-
- for {
- var err error
-
- frame, err = dec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame: %v", err)
- }
-
- if frame.Type == sideband64k.FrameProgress && string(frame.Payload) == "hook says hello\n" {
- sawHookProgress = true
- }
-
- if frame.Type == sideband64k.FrameData {
- break
- }
- }
-
- if !sawHookProgress {
- t.Fatal("missing hook progress frame")
- }
-
- statusDec := pktline.NewDecoder(strings.NewReader(string(frame.Payload)), pktline.ReadOptions{})
-
- statusFrame, err := statusDec.ReadFrame()
- if err != nil {
- t.Fatalf("ReadFrame(status unpack): %v", err)
- }
-
- if statusFrame.Type != pktline.PacketData || string(statusFrame.Payload) != "unpack ok\n" {
- t.Fatalf("status frame = %#v", statusFrame)
- }
- })
-}
-
-func TestReceivePackPredefinedRejectForcePushHookRejectsNonFastForward(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- _, treeID := testRepo.MakeSingleFileTree(t, "base.txt", []byte("base\n"))
- baseID := testRepo.CommitTree(t, treeID, "base")
- currentID := testRepo.CommitTree(t, treeID, "current", baseID)
- forcedID := testRepo.CommitTree(t, treeID, "forced", baseID)
- testRepo.UpdateRef(t, "refs/heads/main", currentID)
-
- repo := testRepo.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, testRepo, algo)
- packStream := testRepo.PackObjectsReader(t, []string{forcedID.String(), "^" + currentID.String()}, false)
- t.Cleanup(func() {
- _ = packStream.Close()
- })
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- currentID.String() + " " + forcedID.String() + " refs/heads/main\x00report-status atomic object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(
- context.Background(),
- &output,
- io.MultiReader(strings.NewReader(input.String()), packStream),
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- Hook: receivepackhooks.RejectForcePush(),
- },
- )
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "ng refs/heads/main non-fast-forward\n") {
- t.Fatalf("unexpected receive-pack output %q", got)
- }
-
- resolved, err := repo.Refs().ResolveToDetached("refs/heads/main")
- if err != nil {
- t.Fatalf("ResolveToDetached(main): %v", err)
- }
-
- if resolved.ID != currentID {
- t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, currentID)
- }
- })
-}
-
-func TestReceivePackReportStatusV2IncludesRefDetails(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := testRepo.MakeCommit(t, "base")
- testRepo.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := testRepo.OpenRepository(t)
-
- var (
- input strings.Builder
- output bufferWriteFlusher
- )
-
- input.WriteString(pktlineData(
- commitID.String() + " " + algo.Zero().String() + " refs/heads/main\x00report-status-v2 atomic delete-refs object-format=" + algo.String() + "\n",
- ))
- input.WriteString("0000")
-
- err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- })
- if err != nil {
- t.Fatalf("ReceivePack: %v", err)
- }
-
- got := output.String()
- if !strings.Contains(got, "option refname refs/heads/main\n") {
- t.Fatalf("missing option refname in %q", got)
- }
-
- if !strings.Contains(got, "option old-oid "+commitID.String()+"\n") {
- t.Fatalf("missing option old-oid in %q", got)
- }
-
- if !strings.Contains(got, "option new-oid "+algo.Zero().String()+"\n") {
- t.Fatalf("missing option new-oid in %q", got)
- }
- })
-}
-
-func TestReceivePackGitPushCreatesBranch(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- _, _, commitID := sender.MakeCommit(t, "pushed commit")
- sender.UpdateRef(t, "refs/heads/main", commitID)
-
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- repo := receiver.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, receiver, algo)
-
- stdout, stderr, clientErr, serverErr := runGitPushFD(
- t,
- sender,
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- },
- "push", "--porcelain", "fd::3,4/test", "refs/heads/main:refs/heads/main",
- )
- if clientErr != nil {
- t.Fatalf("git push failed: %v\nstdout=%s\nstderr=%s", clientErr, stdout, stderr)
- }
-
- if serverErr != nil {
- t.Fatalf("ReceivePack: %v", serverErr)
- }
-
- resolved, err := receiver.OpenRepository(t).Refs().ResolveToDetached("refs/heads/main")
- if err != nil {
- t.Fatalf("ResolveToDetached(main): %v", err)
- }
-
- if resolved.ID != commitID {
- t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, commitID)
- }
- })
-}
-
-func TestReceivePackGitPushRefUpdateWithoutNewObjectsSucceeds(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- blobID, treeID := sender.MakeSingleFileTree(t, "base.txt", []byte("base\n"))
- commitID := sender.CommitTree(t, treeID, "base")
- sender.UpdateRef(t, "refs/heads/main", commitID)
-
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- receiver.HashObject(t, "blob", sender.RunBytes(t, "cat-file", "blob", blobID.String()))
- receiver.HashObject(t, "tree", sender.RunBytes(t, "cat-file", "tree", treeID.String()))
- receiver.HashObject(t, "commit", sender.RunBytes(t, "cat-file", "commit", commitID.String()))
- receiver.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := receiver.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, receiver, algo)
-
- stdout, stderr, clientErr, serverErr := runGitPushFD(
- t,
- sender,
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- },
- "push", "--porcelain", "fd::3,4/test", "refs/heads/main:refs/heads/topic",
- )
- if clientErr != nil {
- t.Fatalf("git push failed: %v\nstdout=%s\nstderr=%s", clientErr, stdout, stderr)
- }
-
- if serverErr != nil {
- t.Fatalf("ReceivePack: %v", serverErr)
- }
-
- resolved, err := receiver.OpenRepository(t).Refs().ResolveToDetached("refs/heads/topic")
- if err != nil {
- t.Fatalf("ResolveToDetached(topic): %v", err)
- }
-
- if resolved.ID != commitID {
- t.Fatalf("refs/heads/topic = %s, want %s", resolved.ID, commitID)
- }
-
- packs := receiver.Run(t, "count-objects", "-v")
- if !strings.Contains(packs, "packs: 0") {
- t.Fatalf("count-objects output shows unexpected promoted pack: %q", packs)
- }
- })
-}
-
-func TestReceivePackGitPushAtomicDelete(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- _, _, commitID := receiver.MakeCommit(t, "base")
- receiver.UpdateRef(t, "refs/heads/main", commitID)
-
- repo := receiver.OpenRepository(t)
-
- stdout, stderr, clientErr, serverErr := runGitPushFD(
- t,
- sender,
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- },
- "push", "--porcelain", "--atomic", "fd::3,4/test", ":refs/heads/main",
- )
- if clientErr != nil {
- t.Fatalf("git push failed: %v\nstdout=%s\nstderr=%s", clientErr, stdout, stderr)
- }
-
- if serverErr != nil {
- t.Fatalf("ReceivePack: %v", serverErr)
- }
-
- _, err := receiver.OpenRepository(t).Refs().Resolve("refs/heads/main")
- if err == nil {
- t.Fatal("refs/heads/main still exists after delete push")
- }
- })
-}
-
-func TestReceivePackGitPushRejectsForcedUpdateViaHook(t *testing.T) {
- t.Parallel()
-
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
- t.Parallel()
-
- sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
- blobID, treeID := sender.MakeSingleFileTree(t, "base.txt", []byte("base\n"))
- baseID := sender.CommitTree(t, treeID, "base")
- currentID := sender.CommitTree(t, treeID, "current", baseID)
- forcedID := sender.CommitTree(t, treeID, "forced", baseID)
- sender.UpdateRef(t, "refs/heads/main", forcedID)
-
- receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
- receiver.HashObject(t, "blob", sender.RunBytes(t, "cat-file", "blob", blobID.String()))
- receiver.HashObject(t, "tree", sender.RunBytes(t, "cat-file", "tree", treeID.String()))
- receiver.HashObject(t, "commit", sender.RunBytes(t, "cat-file", "commit", baseID.String()))
- receiver.HashObject(t, "commit", sender.RunBytes(t, "cat-file", "commit", currentID.String()))
- receiver.UpdateRef(t, "refs/heads/main", currentID)
-
- repo := receiver.OpenRepository(t)
- objectIngress := openReceivePackIngress(t, receiver, algo)
-
- stdout, stderr, clientErr, serverErr := runGitPushFD(
- t,
- sender,
- receivepack.Options{
- Algorithm: algo,
- Refs: repo.Refs(),
- ExistingObjects: repo.Objects(),
- ObjectIngress: objectIngress,
- Hook: receivepackhooks.RejectForcePush(),
- },
- "push", "--porcelain", "--force", "fd::3,4/test", "refs/heads/main:refs/heads/main",
- )
- if clientErr == nil {
- t.Fatalf("git push unexpectedly succeeded\nstdout=%s\nstderr=%s", stdout, stderr)
- }
-
- if serverErr != nil {
- t.Fatalf("ReceivePack: %v", serverErr)
- }
-
- if !strings.Contains(stdout, "non-fast-forward") && !strings.Contains(stderr, "non-fast-forward") {
- t.Fatalf("git push output missing non-fast-forward message\nstdout=%s\nstderr=%s", stdout, stderr)
- }
-
- resolved, err := receiver.OpenRepository(t).Refs().ResolveToDetached("refs/heads/main")
- if err != nil {
- t.Fatalf("ResolveToDetached(main): %v", err)
- }
-
- if resolved.ID != currentID {
- t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, currentID)
- }
- })
-}
-
-type bufferWriteFlusher struct {
- strings.Builder
-}
-
-func (bufferWriteFlusher) Flush() error {
- return nil
-}
-
-func pktlineData(payload string) string {
- return fmt.Sprintf("%04x%s", len(payload)+4, payload)
-}
-
-func openReceivePackIngress(
- tb testing.TB,
- testRepo *testgit.TestRepo,
- algo objectid.Algorithm,
-) objectstore.Quarantiner {
- tb.Helper()
-
- objectsRoot := testRepo.OpenObjectsRoot(tb)
-
- err := objectsRoot.Mkdir("pack", 0o755)
- if err != nil && !os.IsExist(err) {
- tb.Fatalf("Mkdir(pack): %v", err)
- }
-
- packRoot, err := objectsRoot.OpenRoot("pack")
- if err != nil {
- tb.Fatalf("OpenRoot(pack): %v", err)
- }
-
- tb.Cleanup(func() {
- _ = packRoot.Close()
- })
-
- looseStore, err := objectloose.New(objectsRoot, algo)
- if err != nil {
- tb.Fatalf("loose.New: %v", err)
- }
-
- tb.Cleanup(func() {
- _ = looseStore.Close()
- })
-
- packedStore, err := objectpacked.New(packRoot, algo, objectpacked.Options{WriteRev: true})
- if err != nil {
- tb.Fatalf("packed.New: %v", err)
- }
-
- tb.Cleanup(func() {
- _ = packedStore.Close()
- })
-
- return objectdual.New(looseStore, packedStore)
-}
-
-type fileWriteFlusher struct {
- *os.File
-}
-
-func (fileWriteFlusher) Flush() error {
- return nil
-}
-
-func runGitPushFD(
- tb testing.TB,
- sender *testgit.TestRepo,
- opts receivepack.Options,
- gitArgs ...string,
-) (stdout string, stderr string, clientErr error, serverErr error) {
- tb.Helper()
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- serverRead, clientWrite, err := os.Pipe()
- if err != nil {
- tb.Fatalf("os.Pipe(serverRead/clientWrite): %v", err)
- }
-
- clientRead, serverWrite, err := os.Pipe()
- if err != nil {
- tb.Fatalf("os.Pipe(clientRead/serverWrite): %v", err)
- }
-
- tb.Cleanup(func() {
- _ = serverRead.Close()
- _ = clientWrite.Close()
- _ = clientRead.Close()
- _ = serverWrite.Close()
- })
-
- go func() {
- <-ctx.Done()
-
- _ = serverRead.Close()
- _ = clientWrite.Close()
- _ = clientRead.Close()
- _ = serverWrite.Close()
- }()
-
- serverErrCh := make(chan error, 1)
-
- go func() {
- defer func() {
- _ = serverRead.Close()
- _ = serverWrite.Close()
- }()
-
- serverErrCh <- receivepack.ReceivePack(
- ctx,
- fileWriteFlusher{serverWrite},
- serverRead,
- opts,
- )
- }()
-
- stdoutBytes, stderrBytes, clientErr := sender.RunWithExtraFilesEnvContextE(
- tb,
- ctx,
- nil,
- []*os.File{clientRead, clientWrite},
- gitArgs...,
- )
- _ = clientRead.Close()
- _ = clientWrite.Close()
-
- serverErr = <-serverErrCh
-
- if ctx.Err() != nil {
- tb.Fatalf(
- "git push fd:: timed out\nstdout=%s\nstderr=%s\nclientErr=%v\nserverErr=%v",
- stdoutBytes,
- stderrBytes,
- clientErr,
- serverErr,
- )
- }
-
- return string(stdoutBytes), string(stderrBytes), clientErr, serverErr
-}
diff --git a/network/receivepack/options.go b/network/receivepack/options.go
deleted file mode 100644
index 30792765..00000000
--- a/network/receivepack/options.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package receivepack
-
-import (
- commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-// Options configures one receive-pack invocation.
-//
-// ReceivePack borrows all configured dependencies.
-//
-// Refs and ExistingObjects are required and must be non-nil.
-// ObjectIngress is required if the invocation may need to ingest or quarantine a
-// pack.
-type Options struct {
- // GitProtocol is the raw Git protocol version string from the transport,
- // such as "version=1".
- GitProtocol string
- // Algorithm is the repository object ID algorithm used by the push session.
- Algorithm objectid.Algorithm
- // Refs is the reference store visible to the push.
- Refs interface {
- refstore.Reader
- refstore.Transactioner
- refstore.Batcher
- }
- // ExistingObjects is the object store visible to the push before any newly
- // uploaded quarantined objects are promoted.
- ExistingObjects objectstore.Reader
- // ObjectIngress creates coordinated quarantines for quarantined object and
- // pack ingestion during the push.
- ObjectIngress objectstore.Quarantiner
- // CommitGraph is an optional commit-graph snapshot corresponding to
- // ExistingObjects.
- CommitGraph *commitgraphread.Reader
- // Hook, when non-nil, runs after pack ingestion into quarantine and before
- // quarantine promotion or ref updates. Hook is borrowed for the duration of
- // ReceivePack.
- Hook Hook
- // Agent is the receive-pack agent string advertised via capability.
- //
- // When empty, ReceivePack derives one from build info and falls back to
- // "furgit".
- Agent string
- // SessionID is the advertised receive-pack session-id capability value.
- //
- // When empty, ReceivePack generates one random value per invocation.
- SessionID string
- // PushCertNonce is the advertised push-cert nonce capability value.
- //
- // When empty, ReceivePack generates one random value per invocation.
- PushCertNonce string
-}
-
-func validateOptions(opts Options) error {
- if opts.Algorithm == 0 {
- return ErrMissingAlgorithm
- }
-
- if opts.Refs == nil {
- return ErrMissingRefs
- }
-
- if opts.ExistingObjects == nil {
- return ErrMissingObjects
- }
-
- return nil
-}
diff --git a/network/receivepack/receivepack.go b/network/receivepack/receivepack.go
deleted file mode 100644
index d58e9fa0..00000000
--- a/network/receivepack/receivepack.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package receivepack
-
-import (
- "context"
- "io"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
- protoreceive "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack"
- "codeberg.org/lindenii/furgit/network/receivepack/service"
-)
-
-// TODO: Some more designing to do. In particular, we'd like to have access to
-// commit graphs and stored object abstractions and such here, especially because
-// hooks might want to access full repos, but we risk creating
-// circular dependencies if we import repository/ here. Might need an interface-ish
-// design, but that risks being over-complicated.
-// Theoretically we could also just give the hooks an os.Root but that
-// feels a bit ugly.
-
-// ReceivePack serves one receive-pack session over r/w.
-//
-// Labels: Deps-Borrowed.
-func ReceivePack(
- ctx context.Context,
- w iowrap.WriteFlusher,
- r io.Reader,
- opts Options,
-) error {
- err := validateOptions(opts)
- if err != nil {
- return err
- }
-
- version := parseVersion(opts.GitProtocol)
-
- base := common.NewSession(r, w, common.Options{
- Version: version,
- Algorithm: opts.Algorithm,
- })
-
- agent := opts.Agent
- if agent == "" {
- agent = defaultAgent()
- }
-
- sessionID := opts.SessionID
- if sessionID == "" {
- sessionID = defaultSessionID()
- }
-
- pushCertNonce := opts.PushCertNonce
- if pushCertNonce == "" {
- pushCertNonce = defaultPushCertNonce()
- }
-
- protoSession := protoreceive.NewSession(base, protoreceive.Capabilities{
- ReportStatus: true,
- ReportStatusV2: true,
- DeleteRefs: true,
- SideBand64K: true,
- Quiet: true,
- Atomic: true,
- OfsDelta: true,
- PushOptions: true,
- PushCertNonce: pushCertNonce,
- SessionID: sessionID,
- ObjectFormat: opts.Algorithm,
- Agent: agent,
- })
-
- refs, err := advertisedRefs(opts)
- if err != nil {
- return err
- }
-
- err = protoSession.AdvertiseRefs(common.Advertisement{Refs: refs})
- if err != nil {
- return err
- }
-
- err = base.Flush()
- if err != nil {
- return err
- }
-
- req, err := protoSession.ReadRequest()
- if err != nil {
- return err
- }
-
- progress := protoSession.ProgressWriter()
-
- if req.Capabilities.Quiet {
- progress = iowrap.NopFlush(io.Discard)
- }
-
- serviceReq := &service.Request{
- Commands: translateCommands(req.Commands),
- PushOptions: append([]string(nil), req.PushOptions...),
- Atomic: req.Capabilities.Atomic,
- PackExpected: req.PackExpected,
- Pack: r,
- }
-
- svc := service.New(service.Options{
- Refs: opts.Refs,
- ExistingObjects: opts.ExistingObjects,
- ObjectIngress: opts.ObjectIngress,
- CommitGraph: opts.CommitGraph,
- Progress: progress,
- Hook: translateHook(opts.Hook),
- HookIO: service.HookIO{
- Progress: progress,
- Error: protoSession.ErrorWriter(),
- },
- })
-
- result, err := svc.Execute(ctx, serviceReq)
- if err != nil {
- return err
- }
-
- protoResult := translateResult(result)
-
- if req.Capabilities.ReportStatusV2 {
- err = protoSession.WriteReportStatusV2(protoResult)
- if err != nil {
- return err
- }
- } else if req.Capabilities.ReportStatus {
- err = protoSession.WriteReportStatus(protoResult)
- if err != nil {
- return err
- }
- }
-
- return base.Flush()
-}
diff --git a/network/receivepack/results.go b/network/receivepack/results.go
deleted file mode 100644
index d43bee73..00000000
--- a/network/receivepack/results.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package receivepack
-
-import (
- protoreceive "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack"
- "codeberg.org/lindenii/furgit/network/receivepack/service"
-)
-
-func translateResult(result *service.Result) protoreceive.ReportStatusResult {
- out := protoreceive.ReportStatusResult{
- UnpackError: result.UnpackError,
- Commands: make([]protoreceive.CommandResult, 0, len(result.Commands)),
- }
-
- for _, command := range result.Commands {
- out.Commands = append(out.Commands, protoreceive.CommandResult{
- Name: command.Name,
- Error: command.Error,
- RefName: command.RefName,
- OldID: command.OldID,
- NewID: command.NewID,
- ForcedUpdate: command.ForcedUpdate,
- })
- }
-
- return out
-}
diff --git a/network/receivepack/service/apply.go b/network/receivepack/service/apply.go
deleted file mode 100644
index fdf3eef6..00000000
--- a/network/receivepack/service/apply.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package service
-
-import (
- "codeberg.org/lindenii/furgit/internal/utils"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-func (service *Service) applyAtomic(result *Result, commands []Command) error {
- total := len(commands)
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: 0/%d\r", total)
-
- tx, err := service.opts.Refs.BeginTransaction()
- if err != nil {
- return err
- }
-
- for i, command := range commands {
- err = queueWriteTransaction(tx, command)
- if err != nil {
- _ = tx.Abort()
-
- fillCommandErrors(result, commands, err.Error())
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at %d/%d.\n", i+1, total)
-
- return nil
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
- }
-
- err = tx.Commit()
- if err != nil {
- fillCommandErrors(result, commands, err.Error())
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at commit.\n")
-
- return nil
- }
-
- result.Applied = true
- for _, command := range commands {
- result.Commands = append(result.Commands, successCommandResult(command))
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: done.\n")
-
- return nil
-}
-
-func (service *Service) applyBatch(result *Result, commands []Command) error {
- total := len(commands)
-
- utils.BestEffortFprintf(service.opts.Progress, "updating refs...\r")
-
- batch, err := service.opts.Refs.BeginBatch()
- if err != nil {
- return err
- }
-
- for _, command := range commands {
- err = queueWriteBatch(batch, command)
- if err != nil {
- _ = batch.Abort()
-
- fillCommandErrors(result, commands, err.Error())
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed while queueing batch.\n")
-
- return nil
- }
- }
-
- batchResults, err := batch.Apply()
- if err != nil && len(batchResults) == 0 {
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at apply.\n")
-
- return err
- }
-
- appliedAny := false
- failedCount := 0
-
- for i, command := range commands {
- item := successCommandResult(command)
- if i < len(batchResults) && batchResults[i].Error != nil {
- item.Error = batchResults[i].Error.Error()
- failedCount++
- } else {
- appliedAny = true
- }
-
- result.Commands = append(result.Commands, item)
-
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
- }
-
- result.Applied = appliedAny
-
- if failedCount == 0 {
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: done.\n")
- } else {
- utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed (%d/%d).\n", failedCount, total)
- }
-
- return nil
-}
-
-func queueWriteTransaction(tx refstore.Transaction, command Command) error {
- if isDelete(command) {
- return tx.Delete(command.Name, command.OldID)
- }
-
- if command.OldID == command.OldID.Algorithm().Zero() {
- return tx.Create(command.Name, command.NewID)
- }
-
- return tx.Update(command.Name, command.NewID, command.OldID)
-}
-
-func queueWriteBatch(batch refstore.Batch, command Command) error {
- if isDelete(command) {
- return batch.Delete(command.Name, command.OldID)
- }
-
- if command.OldID == command.OldID.Algorithm().Zero() {
- return batch.Create(command.Name, command.NewID)
- }
-
- return batch.Update(command.Name, command.NewID, command.OldID)
-}
-
-func successCommandResult(command Command) CommandResult {
- return CommandResult{
- Name: command.Name,
- RefName: command.Name,
- OldID: new(command.OldID),
- NewID: new(command.NewID),
- }
-}
diff --git a/network/receivepack/service/command.go b/network/receivepack/service/command.go
deleted file mode 100644
index 9ad50c4f..00000000
--- a/network/receivepack/service/command.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package service
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// Command is one protocol-independent requested ref update.
-type Command struct {
- OldID objectid.ObjectID
- NewID objectid.ObjectID
- Name string
-}
-
-func fillCommandErrors(result *Result, commands []Command, errText string) {
- for _, command := range commands {
- result.Commands = append(result.Commands, CommandResult{
- Name: command.Name,
- Error: errText,
- RefName: command.Name,
- OldID: new(command.OldID),
- NewID: new(command.NewID),
- })
- }
-}
-
-func isDelete(command Command) bool {
- return command.NewID == command.NewID.Algorithm().Zero()
-}
diff --git a/network/receivepack/service/command_result.go b/network/receivepack/service/command_result.go
deleted file mode 100644
index 37549f08..00000000
--- a/network/receivepack/service/command_result.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package service
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// CommandResult is one per-command execution result.
-type CommandResult struct {
- Name string
- Error string
- RefName string
- OldID *objectid.ObjectID
- NewID *objectid.ObjectID
- ForcedUpdate bool
-}
diff --git a/network/receivepack/service/doc.go b/network/receivepack/service/doc.go
deleted file mode 100644
index c3fa3041..00000000
--- a/network/receivepack/service/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Package service implements the protocol-independent receive-pack service.
-//
-// A Service borrows the stores, hooks, and I/O endpoints supplied in
-// Options. Callers retain ownership of those dependencies and must keep them
-// valid for each Execute call that uses them.
-package service
diff --git a/network/receivepack/service/execute.go b/network/receivepack/service/execute.go
deleted file mode 100644
index 92d34a63..00000000
--- a/network/receivepack/service/execute.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package service
-
-import (
- "context"
-
- "codeberg.org/lindenii/furgit/internal/utils"
- objectstore "codeberg.org/lindenii/furgit/object/store"
-)
-
-// Execute validates one receive-pack request, optionally ingests its pack into
-// quarantine, runs the optional hook, and applies allowed ref updates.
-//
-// Labels: Deps-Borrowed.
-func (service *Service) Execute(ctx context.Context, req *Request) (*Result, error) {
- result := &Result{
- Commands: make([]CommandResult, 0, len(req.Commands)),
- }
-
- var err error
-
- quarantine, ok := service.ingestQuarantine(result, req.Commands, req)
- if !ok {
- return result, nil
- }
-
- if quarantine != nil {
- defer func(q objectstore.Quarantine) {
- _ = q.Discard()
- }(quarantine)
- }
-
- for _, command := range req.Commands {
- result.Planned = append(result.Planned, PlannedUpdate{
- Name: command.Name,
- OldID: command.OldID,
- NewID: command.NewID,
- Delete: isDelete(command),
- })
- }
-
- if len(req.Commands) == 0 {
- return result, nil
- }
-
- allowedCommands, allowedIndices, rejected, ok, errText := service.runHook(
- ctx,
- req,
- req.Commands,
- quarantine,
- )
- if !ok {
- fillCommandErrors(result, req.Commands, errText)
-
- return result, nil
- }
-
- if req.Atomic && len(rejected) != 0 {
- result.Commands = make([]CommandResult, 0, len(req.Commands))
- for index, command := range req.Commands {
- message := rejected[index]
- if message == "" {
- message = "atomic push rejected by hook"
- }
-
- result.Commands = append(result.Commands, resultForHookRejection(command, message))
- }
-
- return result, nil
- }
-
- if len(allowedCommands) == 0 {
- result.Commands = mergeCommandResults(req.Commands, rejected, nil, nil)
-
- return result, nil
- }
-
- if req.PackExpected && quarantine != nil {
- // Git migrates quarantined objects into permanent storage immediately
- // before starting ref updates.
- utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine...\r")
-
- err := quarantine.Promote()
- if err != nil {
- utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: failed: %v.\n", err)
-
- result.UnpackError = err.Error()
- fillCommandErrors(result, req.Commands, err.Error())
-
- return result, nil
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: done.\n")
- }
-
- if req.Atomic {
- subresult := &Result{}
-
- err := service.applyAtomic(subresult, allowedCommands)
- if err != nil {
- return result, err
- }
-
- result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
- result.Applied = subresult.Applied
-
- return result, nil
- }
-
- subresult := &Result{}
-
- err = service.applyBatch(subresult, allowedCommands)
- if err != nil {
- return result, err
- }
-
- result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
- result.Applied = subresult.Applied
-
- return result, nil
-}
diff --git a/network/receivepack/service/hook.go b/network/receivepack/service/hook.go
deleted file mode 100644
index 3826e6fb..00000000
--- a/network/receivepack/service/hook.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package service
-
-import (
- "context"
-
- "codeberg.org/lindenii/furgit/common/iowrap"
- commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-type HookIO struct {
- Progress iowrap.WriteFlusher
- Error iowrap.WriteFlusher
-}
-
-type RefUpdate struct {
- Name string
- OldID objectid.ObjectID
- NewID objectid.ObjectID
-}
-
-type UpdateDecision struct {
- Accept bool
- Message string
-}
-
-// HookRequest is the view passed to one Hook invocation.
-//
-// Labels: Life-Call.
-type HookRequest struct {
- Refs refstore.Reader
- ExistingObjects objectstore.Reader
- // QuarantinedObjects exposes quarantined objects for this push.
- //
- // When the push did not create a quarantine, QuarantinedObjects is nil.
- QuarantinedObjects objectstore.Reader
- CommitGraph *commitgraphread.Reader
- Updates []RefUpdate
- PushOptions []string
- IO HookIO
-}
-
-// Hook is an optional per-request validation hook.
-//
-// The returned decisions must have the same length as HookRequest.Updates.
-type Hook func(context.Context, HookRequest) ([]UpdateDecision, error)
diff --git a/network/receivepack/service/hook_apply.go b/network/receivepack/service/hook_apply.go
deleted file mode 100644
index 97d25009..00000000
--- a/network/receivepack/service/hook_apply.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package service
-
-func resultForHookRejection(command Command, message string) CommandResult {
- result := successCommandResult(command)
- result.Error = message
-
- return result
-}
-
-func mergeCommandResults(
- commands []Command,
- rejected map[int]string,
- applied []CommandResult,
- appliedIndices []int,
-) []CommandResult {
- out := make([]CommandResult, len(commands))
-
- for index, message := range rejected {
- out[index] = resultForHookRejection(commands[index], message)
- }
-
- for i, appliedResult := range applied {
- if i >= len(appliedIndices) {
- break
- }
-
- out[appliedIndices[i]] = appliedResult
- }
-
- return out
-}
diff --git a/network/receivepack/service/ingest_quarantine.go b/network/receivepack/service/ingest_quarantine.go
deleted file mode 100644
index 75f3a790..00000000
--- a/network/receivepack/service/ingest_quarantine.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package service
-
-import (
- "codeberg.org/lindenii/furgit/internal/utils"
- objectstore "codeberg.org/lindenii/furgit/object/store"
-)
-
-func (service *Service) ingestQuarantine(
- result *Result,
- commands []Command,
- req *Request,
-) (objectstore.Quarantine, bool) {
- if !req.PackExpected {
- return nil, true
- }
-
- if req.Pack == nil {
- utils.BestEffortFprintf(service.opts.Progress, "unpack failed: missing pack stream.\n")
-
- result.UnpackError = "missing pack stream"
- fillCommandErrors(result, commands, "missing pack stream")
-
- return nil, false
- }
-
- if service.opts.ObjectIngress == nil {
- utils.BestEffortFprintf(service.opts.Progress, "unpack failed: object ingress not configured.\n")
-
- result.UnpackError = "object ingress not configured"
- fillCommandErrors(result, commands, "object ingress not configured")
-
- return nil, false
- }
-
- var err error
-
- err = service.opts.ExistingObjects.Refresh()
- if err != nil {
- utils.BestEffortFprintf(service.opts.Progress, "unpack failed: refresh existing objects: %v.\n", err)
-
- result.UnpackError = err.Error()
- fillCommandErrors(result, commands, err.Error())
-
- return nil, false
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "creating quarantine...\r")
-
- quarantine, err := service.opts.ObjectIngress.BeginQuarantine(objectstore.QuarantineOptions{})
- if err != nil {
- utils.BestEffortFprintf(service.opts.Progress, "unpack failed: %v.\n", err)
-
- result.UnpackError = err.Error()
- fillCommandErrors(result, commands, err.Error())
-
- return nil, false
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "creating quarantine: done.\n")
- utils.BestEffortFprintf(service.opts.Progress, "unpacking...\r")
-
- err = quarantine.WritePack(req.Pack, objectstore.PackWriteOptions{
- ThinBase: service.opts.ExistingObjects,
- Progress: service.opts.Progress,
- RequireTrailingEOF: false,
- })
- if err != nil {
- utils.BestEffortFprintf(service.opts.Progress, "unpack failed: %v.\n", err)
-
- result.UnpackError = err.Error()
- fillCommandErrors(result, commands, err.Error())
-
- _ = quarantine.Discard()
-
- return nil, false
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "unpacking: done.\n")
-
- return quarantine, true
-}
diff --git a/network/receivepack/service/options.go b/network/receivepack/service/options.go
deleted file mode 100644
index b6e71d64..00000000
--- a/network/receivepack/service/options.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package service
-
-import (
- "codeberg.org/lindenii/furgit/common/iowrap"
- commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- refstore "codeberg.org/lindenii/furgit/ref/store"
-)
-
-// Options configures one protocol-independent receive-pack service.
-//
-// Service borrows all configured dependencies.
-//
-// Refs and ExistingObjects are required and must be non-nil.
-// ObjectIngress is required if Execute may need to ingest or quarantine a
-// pack.
-// CommitGraph, Progress, Hook, and HookIO are optional; when provided they are also
-// borrowed for the duration of Execute.
-type Options struct {
- Refs interface {
- refstore.Reader
- refstore.Transactioner
- refstore.Batcher
- }
- ExistingObjects objectstore.Reader
- ObjectIngress objectstore.Quarantiner
- CommitGraph *commitgraphread.Reader
- Progress iowrap.WriteFlusher
- Hook Hook
- HookIO HookIO
-}
diff --git a/network/receivepack/service/request.go b/network/receivepack/service/request.go
deleted file mode 100644
index 33e3796f..00000000
--- a/network/receivepack/service/request.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package service
-
-import "io"
-
-// Request is one protocol-independent receive-pack execution request.
-//
-// If PackExpected is true, Pack must be non-nil and remain valid until
-// Execute finishes consuming it.
-type Request struct {
- Commands []Command
- PushOptions []string
- Atomic bool
- PackExpected bool
- Pack io.Reader
-}
diff --git a/network/receivepack/service/result.go b/network/receivepack/service/result.go
deleted file mode 100644
index 7a75be11..00000000
--- a/network/receivepack/service/result.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package service
-
-// Result is one receive-pack execution result.
-type Result struct {
- UnpackError string
- Commands []CommandResult
- Planned []PlannedUpdate
- Applied bool
-}
diff --git a/network/receivepack/service/run_hook.go b/network/receivepack/service/run_hook.go
deleted file mode 100644
index c8b1b76c..00000000
--- a/network/receivepack/service/run_hook.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package service
-
-import (
- "context"
-
- "codeberg.org/lindenii/furgit/internal/utils"
- objectstore "codeberg.org/lindenii/furgit/object/store"
-)
-
-func (service *Service) runHook(
- ctx context.Context,
- req *Request,
- commands []Command,
- quarantinedObjects objectstore.Reader,
-) (
- allowedCommands []Command,
- allowedIndices []int,
- rejected map[int]string,
- ok bool,
- errText string,
-) {
- allowedCommands = append([]Command(nil), commands...)
-
- allowedIndices = make([]int, 0, len(commands))
- for index := range commands {
- allowedIndices = append(allowedIndices, index)
- }
-
- rejected = make(map[int]string)
- if service.opts.Hook == nil {
- return allowedCommands, allowedIndices, rejected, true, ""
- }
-
- utils.BestEffortFprintf(service.opts.Progress, "running hooks...\r")
-
- updates := make([]RefUpdate, 0, len(commands))
- for _, command := range commands {
- updates = append(updates, RefUpdate{
- Name: command.Name,
- OldID: command.OldID,
- NewID: command.NewID,
- })
- }
-
- decisions, err := service.opts.Hook(ctx, HookRequest{
- Refs: service.opts.Refs,
- ExistingObjects: service.opts.ExistingObjects,
- QuarantinedObjects: quarantinedObjects,
- CommitGraph: service.opts.CommitGraph,
- Updates: updates,
- PushOptions: append([]string(nil), req.PushOptions...),
- IO: service.opts.HookIO,
- })
- if err != nil {
- utils.BestEffortFprintf(service.opts.Progress, "running hooks: failed: %v.\n", err)
-
- return nil, nil, nil, false, err.Error()
- }
-
- if len(decisions) != len(commands) {
- utils.BestEffortFprintf(service.opts.Progress, "running hooks: failed: wrong decision count.\n")
-
- return nil, nil, nil, false, "hook returned wrong number of update decisions"
- }
-
- allowedCommands = allowedCommands[:0]
- allowedIndices = allowedIndices[:0]
-
- for index, decision := range decisions {
- if decision.Accept {
- allowedCommands = append(allowedCommands, commands[index])
- allowedIndices = append(allowedIndices, index)
-
- continue
- }
-
- message := decision.Message
- if message == "" {
- message = "rejected by hook"
- }
-
- rejected[index] = message
- }
-
- utils.BestEffortFprintf(
- service.opts.Progress,
- "running hooks: done (%d/%d accepted).\n",
- len(allowedCommands),
- len(commands),
- )
-
- return allowedCommands, allowedIndices, rejected, true, ""
-}
diff --git a/network/receivepack/service/service.go b/network/receivepack/service/service.go
deleted file mode 100644
index 0d931b64..00000000
--- a/network/receivepack/service/service.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package service
-
-// Service executes protocol-independent receive-pack requests.
-type Service struct {
- opts Options
-}
-
-// New creates one receive-pack service.
-//
-// Labels: Deps-Borrowed, Life-Parent.
-func New(opts Options) *Service {
- return &Service{opts: opts}
-}
diff --git a/network/receivepack/service/service_test.go b/network/receivepack/service/service_test.go
deleted file mode 100644
index 94e105da..00000000
--- a/network/receivepack/service/service_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package service_test
-
-import (
- "context"
- "os"
- "strings"
- "testing"
-
- "codeberg.org/lindenii/furgit/internal/testgit"
- "codeberg.org/lindenii/furgit/network/receivepack/service"
- objectid "codeberg.org/lindenii/furgit/object/id"
- objectstore "codeberg.org/lindenii/furgit/object/store"
- objectdual "codeberg.org/lindenii/furgit/object/store/dual"
- objectloose "codeberg.org/lindenii/furgit/object/store/loose"
- "codeberg.org/lindenii/furgit/object/store/memory"
- objectpacked "codeberg.org/lindenii/furgit/object/store/packed"
-)
-
-func TestExecutePackExpectedWithoutObjectIngress(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- store := memory.New(algo)
- svc := service.New(service.Options{
- ExistingObjects: store,
- })
-
- result, err := svc.Execute(context.Background(), &service.Request{
- Commands: []service.Command{{
- Name: "refs/heads/main",
- OldID: algo.Zero(),
- NewID: algo.Zero(),
- }},
- PackExpected: true,
- Pack: strings.NewReader("not a pack"),
- })
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
-
- if result.UnpackError != "object ingress not configured" {
- t.Fatalf("unexpected unpack error %q", result.UnpackError)
- }
- })
-}
-
-func TestExecuteDiscardedQuarantineAfterIngestFailure(t *testing.T) {
- t.Parallel()
-
- //nolint:thelper
- testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
- t.Parallel()
-
- store := memory.New(algo)
- objectIngress := newDualIngress(t, algo)
-
- svc := service.New(service.Options{
- ExistingObjects: store,
- ObjectIngress: objectIngress,
- })
-
- result, err := svc.Execute(context.Background(), &service.Request{
- Commands: []service.Command{{
- Name: "refs/heads/main",
- OldID: algo.Zero(),
- NewID: algo.Zero(),
- }},
- PackExpected: true,
- Pack: strings.NewReader("not a pack"),
- })
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
-
- if result.UnpackError == "" {
- t.Fatal("Execute returned empty unpack error for invalid pack")
- }
- })
-}
-
-func newDualIngress(tb testing.TB, algo objectid.Algorithm) objectstore.Quarantiner {
- tb.Helper()
-
- objectsRoot, err := os.OpenRoot(tb.TempDir())
- if err != nil {
- tb.Fatalf("os.OpenRoot: %v", err)
- }
-
- tb.Cleanup(func() {
- _ = objectsRoot.Close()
- })
-
- err = objectsRoot.Mkdir("pack", 0o755)
- if err != nil {
- tb.Fatalf("Mkdir(pack): %v", err)
- }
-
- packRoot, err := objectsRoot.OpenRoot("pack")
- if err != nil {
- tb.Fatalf("OpenRoot(pack): %v", err)
- }
-
- tb.Cleanup(func() {
- _ = packRoot.Close()
- })
-
- looseStore, err := objectloose.New(objectsRoot, algo)
- if err != nil {
- tb.Fatalf("loose.New: %v", err)
- }
-
- tb.Cleanup(func() {
- _ = looseStore.Close()
- })
-
- packedStore, err := objectpacked.New(packRoot, algo, objectpacked.Options{WriteRev: true})
- if err != nil {
- tb.Fatalf("packed.New: %v", err)
- }
-
- tb.Cleanup(func() {
- _ = packedStore.Close()
- })
-
- return objectdual.New(looseStore, packedStore)
-}
diff --git a/network/receivepack/service/update.go b/network/receivepack/service/update.go
deleted file mode 100644
index 753e3b02..00000000
--- a/network/receivepack/service/update.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package service
-
-import objectid "codeberg.org/lindenii/furgit/object/id"
-
-// PlannedUpdate is one requested ref update planned for this execution.
-type PlannedUpdate struct {
- Name string
- OldID objectid.ObjectID
- NewID objectid.ObjectID
- Delete bool
-}
diff --git a/network/receivepack/version.go b/network/receivepack/version.go
deleted file mode 100644
index 9a4544dc..00000000
--- a/network/receivepack/version.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package receivepack
-
-import (
- "strings"
-
- common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server"
-)
-
-func parseVersion(gitProtocol string) common.Version {
- if gitProtocol == "" {
- return common.Version0
- }
-
- var highestRequested uint8
-
- for field := range strings.SplitSeq(gitProtocol, ":") {
- switch field {
- case "version=0":
- case "version=1":
- if highestRequested < 1 {
- highestRequested = 1
- }
- case "version=2":
- if highestRequested < 2 {
- highestRequested = 2
- }
- }
- }
-
- if highestRequested == 1 {
- return common.Version1
- }
-
- return common.Version0
-}