From 68893edf9ddc3de181431f1552e3b773cb66f080 Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Mon, 28 Nov 2016 09:57:38 +0100 Subject: remove old types from transport and use packp (#142) * protocol: move UploadPackRequest to protocol. * UploadPackRequest is now defined as an embedding of UploadRequest and UploadHaves. * Move http encoding specific code from UploadPackRequest to transport/http. * rename UlReq to UploadRequest * packp: move AdvRefs Encoder/Decoder to Encode/Decode methods. * packp: move UploadRequest Encoder/Decoder to Encode/Decode methods. * packp: Remove transport.UploadPackInfo in favor of packp. AdvRefs. --- plumbing/protocol/packp/advrefs.go | 69 +++ plumbing/protocol/packp/advrefs_decode.go | 51 +- plumbing/protocol/packp/advrefs_decode_test.go | 515 ++++++++++++++++++++ plumbing/protocol/packp/advrefs_decoder_test.go | 515 -------------------- plumbing/protocol/packp/advrefs_encode.go | 157 ++++++ plumbing/protocol/packp/advrefs_encode_test.go | 237 +++++++++ plumbing/protocol/packp/advrefs_encoder.go | 154 ------ plumbing/protocol/packp/advrefs_encoder_test.go | 237 --------- plumbing/protocol/packp/advrefs_test.go | 16 +- plumbing/protocol/packp/ulreq.go | 16 +- plumbing/protocol/packp/ulreq_decode.go | 259 ++++++++++ plumbing/protocol/packp/ulreq_decode_test.go | 532 +++++++++++++++++++++ plumbing/protocol/packp/ulreq_decoder.go | 258 ---------- plumbing/protocol/packp/ulreq_decoder_test.go | 532 --------------------- plumbing/protocol/packp/ulreq_encode.go | 141 ++++++ plumbing/protocol/packp/ulreq_encode_test.go | 257 ++++++++++ plumbing/protocol/packp/ulreq_encoder.go | 138 ------ plumbing/protocol/packp/ulreq_encoder_test.go | 257 ---------- plumbing/protocol/packp/ulreq_test.go | 8 +- plumbing/protocol/packp/upload_pack_request.go | 75 +++ .../protocol/packp/upload_pack_request_test.go | 33 ++ 21 files changed, 2320 insertions(+), 2137 deletions(-) create mode 100644 plumbing/protocol/packp/advrefs_decode_test.go delete mode 100644 plumbing/protocol/packp/advrefs_decoder_test.go create mode 100644 plumbing/protocol/packp/advrefs_encode.go create mode 100644 plumbing/protocol/packp/advrefs_encode_test.go delete mode 100644 plumbing/protocol/packp/advrefs_encoder.go delete mode 100644 plumbing/protocol/packp/advrefs_encoder_test.go create mode 100644 plumbing/protocol/packp/ulreq_decode.go create mode 100644 plumbing/protocol/packp/ulreq_decode_test.go delete mode 100644 plumbing/protocol/packp/ulreq_decoder.go delete mode 100644 plumbing/protocol/packp/ulreq_decoder_test.go create mode 100644 plumbing/protocol/packp/ulreq_encode.go create mode 100644 plumbing/protocol/packp/ulreq_encode_test.go delete mode 100644 plumbing/protocol/packp/ulreq_encoder.go delete mode 100644 plumbing/protocol/packp/ulreq_encoder_test.go create mode 100644 plumbing/protocol/packp/upload_pack_request.go create mode 100644 plumbing/protocol/packp/upload_pack_request_test.go (limited to 'plumbing/protocol') diff --git a/plumbing/protocol/packp/advrefs.go b/plumbing/protocol/packp/advrefs.go index c54f9d8..b36b180 100644 --- a/plumbing/protocol/packp/advrefs.go +++ b/plumbing/protocol/packp/advrefs.go @@ -1,7 +1,16 @@ package packp import ( + "fmt" + "strings" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/storer" + "gopkg.in/src-d/go-git.v4/storage/memory" +) + +const ( + symref = "symref" ) // AdvRefs values represent the information transmitted on an @@ -38,3 +47,63 @@ func NewAdvRefs() *AdvRefs { Shallows: []plumbing.Hash{}, } } + +func (a *AdvRefs) AddReference(r *plumbing.Reference) error { + switch r.Type() { + case plumbing.SymbolicReference: + v := fmt.Sprintf("%s:%s", r.Name().String(), r.Target().String()) + a.Capabilities.Add(symref, v) + case plumbing.HashReference: + a.References[r.Name().String()] = r.Hash() + default: + return plumbing.ErrInvalidType + } + + return nil +} + +func (a *AdvRefs) AllReferences() (memory.ReferenceStorage, error) { + s := memory.ReferenceStorage{} + if err := addRefs(s, a); err != nil { + return s, plumbing.NewUnexpectedError(err) + } + + return s, nil +} + +func addRefs(s storer.ReferenceStorer, ar *AdvRefs) error { + for name, hash := range ar.References { + ref := plumbing.NewReferenceFromStrings(name, hash.String()) + if err := s.SetReference(ref); err != nil { + return err + } + } + + return addSymbolicRefs(s, ar) +} + +func addSymbolicRefs(s storer.ReferenceStorer, ar *AdvRefs) error { + if !hasSymrefs(ar) { + return nil + } + + for _, symref := range ar.Capabilities.Get(symref).Values { + chunks := strings.Split(symref, ":") + if len(chunks) != 2 { + err := fmt.Errorf("bad number of `:` in symref value (%q)", symref) + return plumbing.NewUnexpectedError(err) + } + name := plumbing.ReferenceName(chunks[0]) + target := plumbing.ReferenceName(chunks[1]) + ref := plumbing.NewSymbolicReference(name, target) + if err := s.SetReference(ref); err != nil { + return nil + } + } + + return nil +} + +func hasSymrefs(ar *AdvRefs) bool { + return ar.Capabilities.Supports(symref) +} diff --git a/plumbing/protocol/packp/advrefs_decode.go b/plumbing/protocol/packp/advrefs_decode.go index df824a9..8d37066 100644 --- a/plumbing/protocol/packp/advrefs_decode.go +++ b/plumbing/protocol/packp/advrefs_decode.go @@ -11,8 +11,14 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" ) -// A AdvRefsDecoder reads and decodes AdvRef values from an input stream. -type AdvRefsDecoder struct { +// Decode reads the next advertised-refs message form its input and +// stores it in the AdvRefs. +func (a *AdvRefs) Decode(r io.Reader) error { + d := newAdvRefsDecoder(r) + return d.Decode(a) +} + +type advRefsDecoder struct { s *pktline.Scanner // a pkt-line scanner from the input stream line []byte // current pkt-line contents, use parser.nextLine() to make it advance nLine int // current pkt-line number for debugging, begins at 1 @@ -21,21 +27,16 @@ type AdvRefsDecoder struct { data *AdvRefs // parsed data is stored here } -// ErrEmpty is returned by Decode when there was no advertised-message at all -var ErrEmpty = errors.New("empty advertised-ref message") +// ErrEmptyAdvRefs is returned by Decode when there was no advertised-message at all +var ErrEmptyAdvRefs = errors.New("empty advertised-ref message") -// NewAdvRefsDecoder returns a new decoder that reads from r. -// -// Will not read more data from r than necessary. -func NewAdvRefsDecoder(r io.Reader) *AdvRefsDecoder { - return &AdvRefsDecoder{ +func newAdvRefsDecoder(r io.Reader) *advRefsDecoder { + return &advRefsDecoder{ s: pktline.NewScanner(r), } } -// Decode reads the next advertised-refs message form its input and -// stores it in the value pointed to by v. -func (d *AdvRefsDecoder) Decode(v *AdvRefs) error { +func (d *advRefsDecoder) Decode(v *AdvRefs) error { d.data = v for state := decodePrefix; state != nil; { @@ -45,10 +46,10 @@ func (d *AdvRefsDecoder) Decode(v *AdvRefs) error { return d.err } -type decoderStateFn func(*AdvRefsDecoder) decoderStateFn +type decoderStateFn func(*advRefsDecoder) decoderStateFn // fills out the parser stiky error -func (d *AdvRefsDecoder) error(format string, a ...interface{}) { +func (d *advRefsDecoder) error(format string, a ...interface{}) { d.err = fmt.Errorf("pkt-line %d: %s", d.nLine, fmt.Sprintf(format, a...)) } @@ -57,7 +58,7 @@ func (d *AdvRefsDecoder) error(format string, a ...interface{}) { // p.line and increments p.nLine. A successful invocation returns true, // otherwise, false is returned and the sticky error is filled out // accordingly. Trims eols at the end of the payloads. -func (d *AdvRefsDecoder) nextLine() bool { +func (d *advRefsDecoder) nextLine() bool { d.nLine++ if !d.s.Scan() { @@ -66,7 +67,7 @@ func (d *AdvRefsDecoder) nextLine() bool { } if d.nLine == 1 { - d.err = ErrEmpty + d.err = ErrEmptyAdvRefs return false } @@ -81,14 +82,14 @@ func (d *AdvRefsDecoder) nextLine() bool { } // The HTTP smart prefix is often followed by a flush-pkt. -func decodePrefix(d *AdvRefsDecoder) decoderStateFn { +func decodePrefix(d *advRefsDecoder) decoderStateFn { if ok := d.nextLine(); !ok { return nil } // If the repository is empty, we receive a flush here (SSH). if isFlush(d.line) { - d.err = ErrEmpty + d.err = ErrEmptyAdvRefs return nil } @@ -122,10 +123,10 @@ func isFlush(payload []byte) bool { // If the first hash is zero, then a no-refs is comming. Otherwise, a // list-of-refs is comming, and the hash will be followed by the first // advertised ref. -func decodeFirstHash(p *AdvRefsDecoder) decoderStateFn { +func decodeFirstHash(p *advRefsDecoder) decoderStateFn { // If the repository is empty, we receive a flush here (HTTP). if isFlush(p.line) { - p.err = ErrEmpty + p.err = ErrEmptyAdvRefs return nil } @@ -149,7 +150,7 @@ func decodeFirstHash(p *AdvRefsDecoder) decoderStateFn { } // Skips SP "capabilities^{}" NUL -func decodeSkipNoRefs(p *AdvRefsDecoder) decoderStateFn { +func decodeSkipNoRefs(p *advRefsDecoder) decoderStateFn { if len(p.line) < len(noHeadMark) { p.error("too short zero-id ref") return nil @@ -166,7 +167,7 @@ func decodeSkipNoRefs(p *AdvRefsDecoder) decoderStateFn { } // decode the refname, expectes SP refname NULL -func decodeFirstRef(l *AdvRefsDecoder) decoderStateFn { +func decodeFirstRef(l *advRefsDecoder) decoderStateFn { if len(l.line) < 3 { l.error("line too short after hash") return nil @@ -195,7 +196,7 @@ func decodeFirstRef(l *AdvRefsDecoder) decoderStateFn { return decodeCaps } -func decodeCaps(p *AdvRefsDecoder) decoderStateFn { +func decodeCaps(p *advRefsDecoder) decoderStateFn { if len(p.line) == 0 { return decodeOtherRefs } @@ -210,7 +211,7 @@ func decodeCaps(p *AdvRefsDecoder) decoderStateFn { // The refs are either tips (obj-id SP refname) or a peeled (obj-id SP refname^{}). // If there are no refs, then there might be a shallow or flush-ptk. -func decodeOtherRefs(p *AdvRefsDecoder) decoderStateFn { +func decodeOtherRefs(p *advRefsDecoder) decoderStateFn { if ok := p.nextLine(); !ok { return nil } @@ -253,7 +254,7 @@ func readRef(data []byte) (string, plumbing.Hash, error) { } // Keeps reading shallows until a flush-pkt is found -func decodeShallow(p *AdvRefsDecoder) decoderStateFn { +func decodeShallow(p *advRefsDecoder) decoderStateFn { if !bytes.HasPrefix(p.line, shallow) { p.error("malformed shallow prefix, found %q... instead", p.line[:len(shallow)]) return nil diff --git a/plumbing/protocol/packp/advrefs_decode_test.go b/plumbing/protocol/packp/advrefs_decode_test.go new file mode 100644 index 0000000..2b3da72 --- /dev/null +++ b/plumbing/protocol/packp/advrefs_decode_test.go @@ -0,0 +1,515 @@ +package packp + +import ( + "bytes" + "io" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" + + . "gopkg.in/check.v1" +) + +type AdvRefsDecodeSuite struct{} + +var _ = Suite(&AdvRefsDecodeSuite{}) + +func (s *AdvRefsDecodeSuite) TestEmpty(c *C) { + ar := NewAdvRefs() + var buf bytes.Buffer + d := newAdvRefsDecoder(&buf) + + err := d.Decode(ar) + c.Assert(err, Equals, ErrEmptyAdvRefs) +} + +func (s *AdvRefsDecodeSuite) TestEmptyFlush(c *C) { + ar := NewAdvRefs() + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) + e.Flush() + + d := newAdvRefsDecoder(&buf) + + err := d.Decode(ar) + c.Assert(err, Equals, ErrEmptyAdvRefs) +} + +func (s *AdvRefsDecodeSuite) TestEmptyPrefixFlush(c *C) { + ar := NewAdvRefs() + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) + e.EncodeString("# service=git-upload-pack") + e.Flush() + e.Flush() + + d := newAdvRefsDecoder(&buf) + + err := d.Decode(ar) + c.Assert(err, Equals, ErrEmptyAdvRefs) +} + +func (s *AdvRefsDecodeSuite) TestShortForHash(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*too short") +} + +func (s *AdvRefsDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) { + ar := NewAdvRefs() + d := newAdvRefsDecoder(input) + + err := d.Decode(ar) + c.Assert(err, ErrorMatches, pattern) +} + +func (s *AdvRefsDecodeSuite) TestInvalidFirstHash(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796alberto2219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*invalid hash.*") +} + +func (s *AdvRefsDecodeSuite) TestZeroId(c *C) { + payloads := []string{ + "0000000000000000000000000000000000000000 capabilities^{}\x00multi_ack thin-pack\n", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(ar.Head, IsNil) +} + +func (s *AdvRefsDecodeSuite) testDecodeOK(c *C, payloads []string) *AdvRefs { + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) + err := e.EncodeString(payloads...) + c.Assert(err, IsNil) + + ar := NewAdvRefs() + d := newAdvRefsDecoder(&buf) + + err = d.Decode(ar) + c.Assert(err, IsNil) + + return ar +} + +func (s *AdvRefsDecodeSuite) TestMalformedZeroId(c *C) { + payloads := []string{ + "0000000000000000000000000000000000000000 wrong\x00multi_ack thin-pack\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed zero-id.*") +} + +func (s *AdvRefsDecodeSuite) TestShortZeroId(c *C) { + payloads := []string{ + "0000000000000000000000000000000000000000 capabi", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*too short zero-id.*") +} + +func (s *AdvRefsDecodeSuite) TestHead(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(*ar.Head, Equals, + plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) +} + +func (s *AdvRefsDecodeSuite) TestFirstIsNotHead(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\x00", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(ar.Head, IsNil) + c.Assert(ar.References["refs/heads/master"], Equals, + plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) +} + +func (s *AdvRefsDecodeSuite) TestShortRef(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 H", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*too short.*") +} + +func (s *AdvRefsDecodeSuite) TestNoNULL(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADofs-delta multi_ack", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*NULL not found.*") +} + +func (s *AdvRefsDecodeSuite) TestNoSpaceAfterHash(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5-HEAD\x00", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*no space after hash.*") +} + +func (s *AdvRefsDecodeSuite) TestNoCaps(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(ar.Capabilities.IsEmpty(), Equals, true) +} + +func (s *AdvRefsDecodeSuite) TestCaps(c *C) { + for _, test := range [...]struct { + input []string + capabilities []Capability + }{ + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", + pktline.FlushString, + }, + capabilities: []Capability{}, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00\n", + pktline.FlushString, + }, + capabilities: []Capability{}, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta", + pktline.FlushString, + }, + capabilities: []Capability{ + { + Name: "ofs-delta", + Values: []string(nil), + }, + }, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack", + pktline.FlushString, + }, + capabilities: []Capability{ + {Name: "ofs-delta", Values: []string(nil)}, + {Name: "multi_ack", Values: []string(nil)}, + }, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack\n", + pktline.FlushString, + }, + capabilities: []Capability{ + {Name: "ofs-delta", Values: []string(nil)}, + {Name: "multi_ack", Values: []string(nil)}, + }, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master agent=foo=bar\n", + pktline.FlushString, + }, + capabilities: []Capability{ + {Name: "symref", Values: []string{"HEAD:refs/heads/master"}}, + {Name: "agent", Values: []string{"foo=bar"}}, + }, + }, + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master agent=foo=bar agent=new-agent\n", + pktline.FlushString, + }, + capabilities: []Capability{ + {Name: "symref", Values: []string{"HEAD:refs/heads/master"}}, + {Name: "agent", Values: []string{"foo=bar", "new-agent"}}, + }, + }, + } { + ar := s.testDecodeOK(c, test.input) + for _, fixCap := range test.capabilities { + c.Assert(ar.Capabilities.Supports(fixCap.Name), Equals, true, + Commentf("input = %q, capability = %q", test.input, fixCap.Name)) + c.Assert(ar.Capabilities.Get(fixCap.Name).Values, DeepEquals, fixCap.Values, + Commentf("input = %q, capability = %q", test.input, fixCap.Name)) + } + } +} + +func (s *AdvRefsDecodeSuite) TestWithPrefix(c *C) { + payloads := []string{ + "# this is a prefix\n", + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00foo\n", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(len(ar.Prefix), Equals, 1) + c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix")) +} + +func (s *AdvRefsDecodeSuite) TestWithPrefixAndFlush(c *C) { + payloads := []string{ + "# this is a prefix\n", + pktline.FlushString, + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00foo\n", + pktline.FlushString, + } + ar := s.testDecodeOK(c, payloads) + c.Assert(len(ar.Prefix), Equals, 2) + c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix")) + c.Assert(ar.Prefix[1], DeepEquals, []byte(pktline.FlushString)) +} + +func (s *AdvRefsDecodeSuite) TestOtherRefs(c *C) { + for _, test := range [...]struct { + input []string + references map[string]plumbing.Hash + peeled map[string]plumbing.Hash + }{ + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + pktline.FlushString, + }, + references: make(map[string]plumbing.Hash), + peeled: make(map[string]plumbing.Hash), + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "1111111111111111111111111111111111111111 ref/foo", + pktline.FlushString, + }, + references: map[string]plumbing.Hash{ + "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), + }, + peeled: make(map[string]plumbing.Hash), + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "1111111111111111111111111111111111111111 ref/foo\n", + pktline.FlushString, + }, + references: map[string]plumbing.Hash{ + "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), + }, + peeled: make(map[string]plumbing.Hash), + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "1111111111111111111111111111111111111111 ref/foo\n", + "2222222222222222222222222222222222222222 ref/bar", + pktline.FlushString, + }, + references: map[string]plumbing.Hash{ + "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), + "ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"), + }, + peeled: make(map[string]plumbing.Hash), + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "1111111111111111111111111111111111111111 ref/foo^{}\n", + pktline.FlushString, + }, + references: make(map[string]plumbing.Hash), + peeled: map[string]plumbing.Hash{ + "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), + }, + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "1111111111111111111111111111111111111111 ref/foo\n", + "2222222222222222222222222222222222222222 ref/bar^{}", + pktline.FlushString, + }, + references: map[string]plumbing.Hash{ + "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), + }, + peeled: map[string]plumbing.Hash{ + "ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"), + }, + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "51b8b4fb32271d39fbdd760397406177b2b0fd36 refs/pull/10/head\n", + "02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca refs/pull/100/head\n", + "c284c212704c43659bf5913656b8b28e32da1621 refs/pull/100/merge\n", + "3d6537dce68c8b7874333a1720958bd8db3ae8ca refs/pull/101/merge\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + pktline.FlushString, + }, + references: map[string]plumbing.Hash{ + "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), + "refs/pull/10/head": plumbing.NewHash("51b8b4fb32271d39fbdd760397406177b2b0fd36"), + "refs/pull/100/head": plumbing.NewHash("02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca"), + "refs/pull/100/merge": plumbing.NewHash("c284c212704c43659bf5913656b8b28e32da1621"), + "refs/pull/101/merge": plumbing.NewHash("3d6537dce68c8b7874333a1720958bd8db3ae8ca"), + "refs/tags/v2.6.11": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), + "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), + }, + peeled: map[string]plumbing.Hash{ + "refs/tags/v2.6.11": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"), + "refs/tags/v2.6.11-tree": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"), + }, + }, + } { + ar := s.testDecodeOK(c, test.input) + comment := Commentf("input = %v\n", test.input) + c.Assert(ar.References, DeepEquals, test.references, comment) + c.Assert(ar.Peeled, DeepEquals, test.peeled, comment) + } +} + +func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsNoSpace(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8crefs/tags/v2.6.11\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed ref data.*") +} + +func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsMultipleSpaces(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags v2.6.11\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed ref data.*") +} + +func (s *AdvRefsDecodeSuite) TestShallow(c *C) { + for _, test := range [...]struct { + input []string + shallows []plumbing.Hash + }{ + { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + pktline.FlushString, + }, + shallows: []plumbing.Hash{}, + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + "shallow 1111111111111111111111111111111111111111\n", + pktline.FlushString, + }, + shallows: []plumbing.Hash{plumbing.NewHash("1111111111111111111111111111111111111111")}, + }, { + input: []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + "shallow 1111111111111111111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222\n", + pktline.FlushString, + }, + shallows: []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + }, + }, + } { + ar := s.testDecodeOK(c, test.input) + comment := Commentf("input = %v\n", test.input) + c.Assert(ar.Shallows, DeepEquals, test.shallows, comment) + } +} + +func (s *AdvRefsDecodeSuite) TestInvalidShallowHash(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + "shallow 11111111alcortes111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*invalid hash text.*") +} + +func (s *AdvRefsDecodeSuite) TestGarbageAfterShallow(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + "shallow 1111111111111111111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222\n", + "b5be40b90dbaa6bd337f3b77de361bfc0723468b refs/tags/v4.4", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed shallow prefix.*") +} + +func (s *AdvRefsDecodeSuite) TestMalformedShallowHash(c *C) { + payloads := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", + "shallow 1111111111111111111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222 malformed\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed shallow hash.*") +} + +func (s *AdvRefsDecodeSuite) TestEOFRefs(c *C) { + input := strings.NewReader("" + + "005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" + + "003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" + + "00355dc01c595e6c6ec9ccda4f6ffbf614e4d92bb0c7 refs/foo\n", + ) + s.testDecoderErrorMatches(c, input, ".*invalid pkt-len.*") +} + +func (s *AdvRefsDecodeSuite) TestEOFShallows(c *C) { + input := strings.NewReader("" + + "005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" + + "003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" + + "00445dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n" + + "0047c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n" + + "0035shallow 1111111111111111111111111111111111111111\n" + + "0034shallow 222222222222222222222222") + s.testDecoderErrorMatches(c, input, ".*unexpected EOF.*") +} diff --git a/plumbing/protocol/packp/advrefs_decoder_test.go b/plumbing/protocol/packp/advrefs_decoder_test.go deleted file mode 100644 index ee72d56..0000000 --- a/plumbing/protocol/packp/advrefs_decoder_test.go +++ /dev/null @@ -1,515 +0,0 @@ -package packp - -import ( - "bytes" - "io" - "strings" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" - - . "gopkg.in/check.v1" -) - -type AdvRefsDecodeSuite struct{} - -var _ = Suite(&AdvRefsDecodeSuite{}) - -func (s *AdvRefsDecodeSuite) TestEmpty(c *C) { - ar := NewAdvRefs() - var buf bytes.Buffer - d := NewAdvRefsDecoder(&buf) - - err := d.Decode(ar) - c.Assert(err, Equals, ErrEmpty) -} - -func (s *AdvRefsDecodeSuite) TestEmptyFlush(c *C) { - ar := NewAdvRefs() - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - e.Flush() - - d := NewAdvRefsDecoder(&buf) - - err := d.Decode(ar) - c.Assert(err, Equals, ErrEmpty) -} - -func (s *AdvRefsDecodeSuite) TestEmptyPrefixFlush(c *C) { - ar := NewAdvRefs() - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - e.EncodeString("# service=git-upload-pack") - e.Flush() - e.Flush() - - d := NewAdvRefsDecoder(&buf) - - err := d.Decode(ar) - c.Assert(err, Equals, ErrEmpty) -} - -func (s *AdvRefsDecodeSuite) TestShortForHash(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*too short") -} - -func (s *AdvRefsDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) { - ar := NewAdvRefs() - d := NewAdvRefsDecoder(input) - - err := d.Decode(ar) - c.Assert(err, ErrorMatches, pattern) -} - -func (s *AdvRefsDecodeSuite) TestInvalidFirstHash(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796alberto2219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*invalid hash.*") -} - -func (s *AdvRefsDecodeSuite) TestZeroId(c *C) { - payloads := []string{ - "0000000000000000000000000000000000000000 capabilities^{}\x00multi_ack thin-pack\n", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(ar.Head, IsNil) -} - -func (s *AdvRefsDecodeSuite) testDecodeOK(c *C, payloads []string) *AdvRefs { - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - err := e.EncodeString(payloads...) - c.Assert(err, IsNil) - - ar := NewAdvRefs() - d := NewAdvRefsDecoder(&buf) - - err = d.Decode(ar) - c.Assert(err, IsNil) - - return ar -} - -func (s *AdvRefsDecodeSuite) TestMalformedZeroId(c *C) { - payloads := []string{ - "0000000000000000000000000000000000000000 wrong\x00multi_ack thin-pack\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed zero-id.*") -} - -func (s *AdvRefsDecodeSuite) TestShortZeroId(c *C) { - payloads := []string{ - "0000000000000000000000000000000000000000 capabi", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*too short zero-id.*") -} - -func (s *AdvRefsDecodeSuite) TestHead(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(*ar.Head, Equals, - plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) -} - -func (s *AdvRefsDecodeSuite) TestFirstIsNotHead(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 refs/heads/master\x00", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(ar.Head, IsNil) - c.Assert(ar.References["refs/heads/master"], Equals, - plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) -} - -func (s *AdvRefsDecodeSuite) TestShortRef(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 H", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*too short.*") -} - -func (s *AdvRefsDecodeSuite) TestNoNULL(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADofs-delta multi_ack", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*NULL not found.*") -} - -func (s *AdvRefsDecodeSuite) TestNoSpaceAfterHash(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5-HEAD\x00", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*no space after hash.*") -} - -func (s *AdvRefsDecodeSuite) TestNoCaps(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(ar.Capabilities.IsEmpty(), Equals, true) -} - -func (s *AdvRefsDecodeSuite) TestCaps(c *C) { - for _, test := range [...]struct { - input []string - capabilities []Capability - }{ - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00", - pktline.FlushString, - }, - capabilities: []Capability{}, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00\n", - pktline.FlushString, - }, - capabilities: []Capability{}, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta", - pktline.FlushString, - }, - capabilities: []Capability{ - { - Name: "ofs-delta", - Values: []string(nil), - }, - }, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack", - pktline.FlushString, - }, - capabilities: []Capability{ - {Name: "ofs-delta", Values: []string(nil)}, - {Name: "multi_ack", Values: []string(nil)}, - }, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta multi_ack\n", - pktline.FlushString, - }, - capabilities: []Capability{ - {Name: "ofs-delta", Values: []string(nil)}, - {Name: "multi_ack", Values: []string(nil)}, - }, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master agent=foo=bar\n", - pktline.FlushString, - }, - capabilities: []Capability{ - {Name: "symref", Values: []string{"HEAD:refs/heads/master"}}, - {Name: "agent", Values: []string{"foo=bar"}}, - }, - }, - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:refs/heads/master agent=foo=bar agent=new-agent\n", - pktline.FlushString, - }, - capabilities: []Capability{ - {Name: "symref", Values: []string{"HEAD:refs/heads/master"}}, - {Name: "agent", Values: []string{"foo=bar", "new-agent"}}, - }, - }, - } { - ar := s.testDecodeOK(c, test.input) - for _, fixCap := range test.capabilities { - c.Assert(ar.Capabilities.Supports(fixCap.Name), Equals, true, - Commentf("input = %q, capability = %q", test.input, fixCap.Name)) - c.Assert(ar.Capabilities.Get(fixCap.Name).Values, DeepEquals, fixCap.Values, - Commentf("input = %q, capability = %q", test.input, fixCap.Name)) - } - } -} - -func (s *AdvRefsDecodeSuite) TestWithPrefix(c *C) { - payloads := []string{ - "# this is a prefix\n", - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00foo\n", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(len(ar.Prefix), Equals, 1) - c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix")) -} - -func (s *AdvRefsDecodeSuite) TestWithPrefixAndFlush(c *C) { - payloads := []string{ - "# this is a prefix\n", - pktline.FlushString, - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00foo\n", - pktline.FlushString, - } - ar := s.testDecodeOK(c, payloads) - c.Assert(len(ar.Prefix), Equals, 2) - c.Assert(ar.Prefix[0], DeepEquals, []byte("# this is a prefix")) - c.Assert(ar.Prefix[1], DeepEquals, []byte(pktline.FlushString)) -} - -func (s *AdvRefsDecodeSuite) TestOtherRefs(c *C) { - for _, test := range [...]struct { - input []string - references map[string]plumbing.Hash - peeled map[string]plumbing.Hash - }{ - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - pktline.FlushString, - }, - references: make(map[string]plumbing.Hash), - peeled: make(map[string]plumbing.Hash), - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "1111111111111111111111111111111111111111 ref/foo", - pktline.FlushString, - }, - references: map[string]plumbing.Hash{ - "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), - }, - peeled: make(map[string]plumbing.Hash), - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "1111111111111111111111111111111111111111 ref/foo\n", - pktline.FlushString, - }, - references: map[string]plumbing.Hash{ - "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), - }, - peeled: make(map[string]plumbing.Hash), - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "1111111111111111111111111111111111111111 ref/foo\n", - "2222222222222222222222222222222222222222 ref/bar", - pktline.FlushString, - }, - references: map[string]plumbing.Hash{ - "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), - "ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"), - }, - peeled: make(map[string]plumbing.Hash), - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "1111111111111111111111111111111111111111 ref/foo^{}\n", - pktline.FlushString, - }, - references: make(map[string]plumbing.Hash), - peeled: map[string]plumbing.Hash{ - "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), - }, - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "1111111111111111111111111111111111111111 ref/foo\n", - "2222222222222222222222222222222222222222 ref/bar^{}", - pktline.FlushString, - }, - references: map[string]plumbing.Hash{ - "ref/foo": plumbing.NewHash("1111111111111111111111111111111111111111"), - }, - peeled: map[string]plumbing.Hash{ - "ref/bar": plumbing.NewHash("2222222222222222222222222222222222222222"), - }, - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "51b8b4fb32271d39fbdd760397406177b2b0fd36 refs/pull/10/head\n", - "02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca refs/pull/100/head\n", - "c284c212704c43659bf5913656b8b28e32da1621 refs/pull/100/merge\n", - "3d6537dce68c8b7874333a1720958bd8db3ae8ca refs/pull/101/merge\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - pktline.FlushString, - }, - references: map[string]plumbing.Hash{ - "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), - "refs/pull/10/head": plumbing.NewHash("51b8b4fb32271d39fbdd760397406177b2b0fd36"), - "refs/pull/100/head": plumbing.NewHash("02b5a6031ba7a8cbfde5d65ff9e13ecdbc4a92ca"), - "refs/pull/100/merge": plumbing.NewHash("c284c212704c43659bf5913656b8b28e32da1621"), - "refs/pull/101/merge": plumbing.NewHash("3d6537dce68c8b7874333a1720958bd8db3ae8ca"), - "refs/tags/v2.6.11": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), - "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), - }, - peeled: map[string]plumbing.Hash{ - "refs/tags/v2.6.11": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"), - "refs/tags/v2.6.11-tree": plumbing.NewHash("c39ae07f393806ccf406ef966e9a15afc43cc36a"), - }, - }, - } { - ar := s.testDecodeOK(c, test.input) - comment := Commentf("input = %v\n", test.input) - c.Assert(ar.References, DeepEquals, test.references, comment) - c.Assert(ar.Peeled, DeepEquals, test.peeled, comment) - } -} - -func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsNoSpace(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8crefs/tags/v2.6.11\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed ref data.*") -} - -func (s *AdvRefsDecodeSuite) TestMalformedOtherRefsMultipleSpaces(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack thin-pack\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags v2.6.11\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed ref data.*") -} - -func (s *AdvRefsDecodeSuite) TestShallow(c *C) { - for _, test := range [...]struct { - input []string - shallows []plumbing.Hash - }{ - { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - pktline.FlushString, - }, - shallows: []plumbing.Hash{}, - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - "shallow 1111111111111111111111111111111111111111\n", - pktline.FlushString, - }, - shallows: []plumbing.Hash{plumbing.NewHash("1111111111111111111111111111111111111111")}, - }, { - input: []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - "shallow 1111111111111111111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222\n", - pktline.FlushString, - }, - shallows: []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - }, - }, - } { - ar := s.testDecodeOK(c, test.input) - comment := Commentf("input = %v\n", test.input) - c.Assert(ar.Shallows, DeepEquals, test.shallows, comment) - } -} - -func (s *AdvRefsDecodeSuite) TestInvalidShallowHash(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - "shallow 11111111alcortes111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*invalid hash text.*") -} - -func (s *AdvRefsDecodeSuite) TestGarbageAfterShallow(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - "shallow 1111111111111111111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222\n", - "b5be40b90dbaa6bd337f3b77de361bfc0723468b refs/tags/v4.4", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed shallow prefix.*") -} - -func (s *AdvRefsDecodeSuite) TestMalformedShallowHash(c *C) { - payloads := []string{ - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n", - "shallow 1111111111111111111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222 malformed\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed shallow hash.*") -} - -func (s *AdvRefsDecodeSuite) TestEOFRefs(c *C) { - input := strings.NewReader("" + - "005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" + - "003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" + - "00355dc01c595e6c6ec9ccda4f6ffbf614e4d92bb0c7 refs/foo\n", - ) - s.testDecoderErrorMatches(c, input, ".*invalid pkt-len.*") -} - -func (s *AdvRefsDecodeSuite) TestEOFShallows(c *C) { - input := strings.NewReader("" + - "005b6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00ofs-delta symref=HEAD:/refs/heads/master\n" + - "003fa6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n" + - "00445dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n" + - "0047c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11-tree^{}\n" + - "0035shallow 1111111111111111111111111111111111111111\n" + - "0034shallow 222222222222222222222222") - s.testDecoderErrorMatches(c, input, ".*unexpected EOF.*") -} diff --git a/plumbing/protocol/packp/advrefs_encode.go b/plumbing/protocol/packp/advrefs_encode.go new file mode 100644 index 0000000..3c5df19 --- /dev/null +++ b/plumbing/protocol/packp/advrefs_encode.go @@ -0,0 +1,157 @@ +package packp + +import ( + "bytes" + "io" + "sort" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Encode writes the AdvRefs encoding to a writer. +// +// All the payloads will end with a newline character. Capabilities, +// references and shallows are writen in alphabetical order, except for +// peeled references that always follow their corresponding references. +func (a *AdvRefs) Encode(w io.Writer) error { + e := newAdvRefsEncoder(w) + return e.Encode(a) +} + +type advRefsEncoder struct { + data *AdvRefs // data to encode + pe *pktline.Encoder // where to write the encoded data + err error // sticky error +} + +func newAdvRefsEncoder(w io.Writer) *advRefsEncoder { + return &advRefsEncoder{ + pe: pktline.NewEncoder(w), + } +} + +func (e *advRefsEncoder) Encode(v *AdvRefs) error { + e.data = v + + for state := encodePrefix; state != nil; { + state = state(e) + } + + return e.err +} + +type encoderStateFn func(*advRefsEncoder) encoderStateFn + +func encodePrefix(e *advRefsEncoder) encoderStateFn { + for _, p := range e.data.Prefix { + if bytes.Equal(p, pktline.Flush) { + if e.err = e.pe.Flush(); e.err != nil { + return nil + } + continue + } + if e.err = e.pe.Encodef("%s\n", string(p)); e.err != nil { + return nil + } + } + + return encodeFirstLine +} + +// Adds the first pkt-line payload: head hash, head ref and capabilities. +// Also handle the special case when no HEAD ref is found. +func encodeFirstLine(e *advRefsEncoder) encoderStateFn { + head := formatHead(e.data.Head) + separator := formatSeparator(e.data.Head) + capabilities := formatCaps(e.data.Capabilities) + + if e.err = e.pe.Encodef("%s %s\x00%s\n", head, separator, capabilities); e.err != nil { + return nil + } + + return encodeRefs +} + +func formatHead(h *plumbing.Hash) string { + if h == nil { + return plumbing.ZeroHash.String() + } + + return h.String() +} + +func formatSeparator(h *plumbing.Hash) string { + if h == nil { + return noHead + } + + return head +} + +func formatCaps(c *Capabilities) string { + if c == nil { + return "" + } + + c.Sort() + + return c.String() +} + +// Adds the (sorted) refs: hash SP refname EOL +// and their peeled refs if any. +func encodeRefs(e *advRefsEncoder) encoderStateFn { + refs := sortRefs(e.data.References) + for _, r := range refs { + hash, _ := e.data.References[r] + if e.err = e.pe.Encodef("%s %s\n", hash.String(), r); e.err != nil { + return nil + } + + if hash, ok := e.data.Peeled[r]; ok { + if e.err = e.pe.Encodef("%s %s^{}\n", hash.String(), r); e.err != nil { + return nil + } + } + } + + return encodeShallow +} + +func sortRefs(m map[string]plumbing.Hash) []string { + ret := make([]string, 0, len(m)) + for k := range m { + ret = append(ret, k) + } + sort.Strings(ret) + + return ret +} + +// Adds the (sorted) shallows: "shallow" SP hash EOL +func encodeShallow(e *advRefsEncoder) encoderStateFn { + sorted := sortShallows(e.data.Shallows) + for _, hash := range sorted { + if e.err = e.pe.Encodef("shallow %s\n", hash); e.err != nil { + return nil + } + } + + return encodeFlush +} + +func sortShallows(c []plumbing.Hash) []string { + ret := []string{} + for _, h := range c { + ret = append(ret, h.String()) + } + sort.Strings(ret) + + return ret +} + +func encodeFlush(e *advRefsEncoder) encoderStateFn { + e.err = e.pe.Flush() + return nil +} diff --git a/plumbing/protocol/packp/advrefs_encode_test.go b/plumbing/protocol/packp/advrefs_encode_test.go new file mode 100644 index 0000000..207ce58 --- /dev/null +++ b/plumbing/protocol/packp/advrefs_encode_test.go @@ -0,0 +1,237 @@ +package packp + +import ( + "bytes" + "strings" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" + + . "gopkg.in/check.v1" +) + +type AdvRefsEncodeSuite struct{} + +var _ = Suite(&AdvRefsEncodeSuite{}) + +func testEncode(c *C, input *AdvRefs, expected []byte) { + var buf bytes.Buffer + e := newAdvRefsEncoder(&buf) + err := e.Encode(input) + c.Assert(err, IsNil) + obtained := buf.Bytes() + + comment := Commentf("\nobtained = %s\nexpected = %s\n", string(obtained), string(expected)) + + c.Assert(obtained, DeepEquals, expected, comment) +} + +func (s *AdvRefsEncodeSuite) TestZeroValue(c *C) { + ar := &AdvRefs{} + + expected := pktlines(c, + "0000000000000000000000000000000000000000 capabilities^{}\x00\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestHead(c *C) { + hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") + ar := &AdvRefs{ + Head: &hash, + } + + expected := pktlines(c, + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestCapsNoHead(c *C) { + capabilities := NewCapabilities() + capabilities.Add("symref", "HEAD:/refs/heads/master") + capabilities.Add("ofs-delta") + capabilities.Add("multi_ack") + ar := &AdvRefs{ + Capabilities: capabilities, + } + + expected := pktlines(c, + "0000000000000000000000000000000000000000 capabilities^{}\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestCapsWithHead(c *C) { + hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") + capabilities := NewCapabilities() + capabilities.Add("symref", "HEAD:/refs/heads/master") + capabilities.Add("ofs-delta") + capabilities.Add("multi_ack") + ar := &AdvRefs{ + Head: &hash, + Capabilities: capabilities, + } + + expected := pktlines(c, + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestRefs(c *C) { + references := map[string]plumbing.Hash{ + "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), + "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), + "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), + "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), + "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), + } + ar := &AdvRefs{ + References: references, + } + + expected := pktlines(c, + "0000000000000000000000000000000000000000 capabilities^{}\x00\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", + "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", + "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestPeeled(c *C) { + references := map[string]plumbing.Hash{ + "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), + "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), + "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), + "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), + "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), + } + peeled := map[string]plumbing.Hash{ + "refs/tags/v2.7.13-tree": plumbing.NewHash("4444444444444444444444444444444444444444"), + "refs/tags/v2.6.12-tree": plumbing.NewHash("5555555555555555555555555555555555555555"), + } + ar := &AdvRefs{ + References: references, + Peeled: peeled, + } + + expected := pktlines(c, + "0000000000000000000000000000000000000000 capabilities^{}\x00\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", + "5555555555555555555555555555555555555555 refs/tags/v2.6.12-tree^{}\n", + "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", + "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", + "4444444444444444444444444444444444444444 refs/tags/v2.7.13-tree^{}\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestShallow(c *C) { + shallows := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + } + ar := &AdvRefs{ + Shallows: shallows, + } + + expected := pktlines(c, + "0000000000000000000000000000000000000000 capabilities^{}\x00\n", + "shallow 1111111111111111111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222\n", + "shallow 3333333333333333333333333333333333333333\n", + "shallow 4444444444444444444444444444444444444444\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestAll(c *C) { + hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") + + capabilities := NewCapabilities() + capabilities.Add("symref", "HEAD:/refs/heads/master") + capabilities.Add("ofs-delta") + capabilities.Add("multi_ack") + + references := map[string]plumbing.Hash{ + "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), + "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), + "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), + "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), + "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), + } + + peeled := map[string]plumbing.Hash{ + "refs/tags/v2.7.13-tree": plumbing.NewHash("4444444444444444444444444444444444444444"), + "refs/tags/v2.6.12-tree": plumbing.NewHash("5555555555555555555555555555555555555555"), + } + + shallows := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + } + + ar := &AdvRefs{ + Head: &hash, + Capabilities: capabilities, + References: references, + Peeled: peeled, + Shallows: shallows, + } + + expected := pktlines(c, + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", + "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", + "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", + "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", + "5555555555555555555555555555555555555555 refs/tags/v2.6.12-tree^{}\n", + "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", + "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", + "4444444444444444444444444444444444444444 refs/tags/v2.7.13-tree^{}\n", + "shallow 1111111111111111111111111111111111111111\n", + "shallow 2222222222222222222222222222222222222222\n", + "shallow 3333333333333333333333333333333333333333\n", + "shallow 4444444444444444444444444444444444444444\n", + pktline.FlushString, + ) + + testEncode(c, ar, expected) +} + +func (s *AdvRefsEncodeSuite) TestErrorTooLong(c *C) { + references := map[string]plumbing.Hash{ + strings.Repeat("a", pktline.MaxPayloadSize): plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), + } + ar := &AdvRefs{ + References: references, + } + + var buf bytes.Buffer + e := newAdvRefsEncoder(&buf) + err := e.Encode(ar) + c.Assert(err, ErrorMatches, ".*payload is too long.*") +} diff --git a/plumbing/protocol/packp/advrefs_encoder.go b/plumbing/protocol/packp/advrefs_encoder.go deleted file mode 100644 index cdba188..0000000 --- a/plumbing/protocol/packp/advrefs_encoder.go +++ /dev/null @@ -1,154 +0,0 @@ -package packp - -import ( - "bytes" - "io" - "sort" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" -) - -// An AdvRefsEncoder writes AdvRefs values to an output stream. -type AdvRefsEncoder struct { - data *AdvRefs // data to encode - pe *pktline.Encoder // where to write the encoded data - err error // sticky error -} - -// NewAdvRefsEncoder returns a new encoder that writes to w. -func NewAdvRefsEncoder(w io.Writer) *AdvRefsEncoder { - return &AdvRefsEncoder{ - pe: pktline.NewEncoder(w), - } -} - -// Encode writes the AdvRefs encoding of v to the stream. -// -// All the payloads will end with a newline character. Capabilities, -// references and shallows are writen in alphabetical order, except for -// peeled references that always follow their corresponding references. -func (e *AdvRefsEncoder) Encode(v *AdvRefs) error { - e.data = v - - for state := encodePrefix; state != nil; { - state = state(e) - } - - return e.err -} - -type encoderStateFn func(*AdvRefsEncoder) encoderStateFn - -func encodePrefix(e *AdvRefsEncoder) encoderStateFn { - for _, p := range e.data.Prefix { - if bytes.Equal(p, pktline.Flush) { - if e.err = e.pe.Flush(); e.err != nil { - return nil - } - continue - } - if e.err = e.pe.Encodef("%s\n", string(p)); e.err != nil { - return nil - } - } - - return encodeFirstLine -} - -// Adds the first pkt-line payload: head hash, head ref and capabilities. -// Also handle the special case when no HEAD ref is found. -func encodeFirstLine(e *AdvRefsEncoder) encoderStateFn { - head := formatHead(e.data.Head) - separator := formatSeparator(e.data.Head) - capabilities := formatCaps(e.data.Capabilities) - - if e.err = e.pe.Encodef("%s %s\x00%s\n", head, separator, capabilities); e.err != nil { - return nil - } - - return encodeRefs -} - -func formatHead(h *plumbing.Hash) string { - if h == nil { - return plumbing.ZeroHash.String() - } - - return h.String() -} - -func formatSeparator(h *plumbing.Hash) string { - if h == nil { - return noHead - } - - return head -} - -func formatCaps(c *Capabilities) string { - if c == nil { - return "" - } - - c.Sort() - - return c.String() -} - -// Adds the (sorted) refs: hash SP refname EOL -// and their peeled refs if any. -func encodeRefs(e *AdvRefsEncoder) encoderStateFn { - refs := sortRefs(e.data.References) - for _, r := range refs { - hash, _ := e.data.References[r] - if e.err = e.pe.Encodef("%s %s\n", hash.String(), r); e.err != nil { - return nil - } - - if hash, ok := e.data.Peeled[r]; ok { - if e.err = e.pe.Encodef("%s %s^{}\n", hash.String(), r); e.err != nil { - return nil - } - } - } - - return encodeShallow -} - -func sortRefs(m map[string]plumbing.Hash) []string { - ret := make([]string, 0, len(m)) - for k := range m { - ret = append(ret, k) - } - sort.Strings(ret) - - return ret -} - -// Adds the (sorted) shallows: "shallow" SP hash EOL -func encodeShallow(e *AdvRefsEncoder) encoderStateFn { - sorted := sortShallows(e.data.Shallows) - for _, hash := range sorted { - if e.err = e.pe.Encodef("shallow %s\n", hash); e.err != nil { - return nil - } - } - - return encodeFlush -} - -func sortShallows(c []plumbing.Hash) []string { - ret := []string{} - for _, h := range c { - ret = append(ret, h.String()) - } - sort.Strings(ret) - - return ret -} - -func encodeFlush(e *AdvRefsEncoder) encoderStateFn { - e.err = e.pe.Flush() - return nil -} diff --git a/plumbing/protocol/packp/advrefs_encoder_test.go b/plumbing/protocol/packp/advrefs_encoder_test.go deleted file mode 100644 index 222a267..0000000 --- a/plumbing/protocol/packp/advrefs_encoder_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package packp - -import ( - "bytes" - "strings" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" - - . "gopkg.in/check.v1" -) - -type AdvRefsEncodeSuite struct{} - -var _ = Suite(&AdvRefsEncodeSuite{}) - -func testEncode(c *C, input *AdvRefs, expected []byte) { - var buf bytes.Buffer - e := NewAdvRefsEncoder(&buf) - err := e.Encode(input) - c.Assert(err, IsNil) - obtained := buf.Bytes() - - comment := Commentf("\nobtained = %s\nexpected = %s\n", string(obtained), string(expected)) - - c.Assert(obtained, DeepEquals, expected, comment) -} - -func (s *AdvRefsEncodeSuite) TestZeroValue(c *C) { - ar := &AdvRefs{} - - expected := pktlines(c, - "0000000000000000000000000000000000000000 capabilities^{}\x00\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestHead(c *C) { - hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - ar := &AdvRefs{ - Head: &hash, - } - - expected := pktlines(c, - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestCapsNoHead(c *C) { - capabilities := NewCapabilities() - capabilities.Add("symref", "HEAD:/refs/heads/master") - capabilities.Add("ofs-delta") - capabilities.Add("multi_ack") - ar := &AdvRefs{ - Capabilities: capabilities, - } - - expected := pktlines(c, - "0000000000000000000000000000000000000000 capabilities^{}\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestCapsWithHead(c *C) { - hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - capabilities := NewCapabilities() - capabilities.Add("symref", "HEAD:/refs/heads/master") - capabilities.Add("ofs-delta") - capabilities.Add("multi_ack") - ar := &AdvRefs{ - Head: &hash, - Capabilities: capabilities, - } - - expected := pktlines(c, - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestRefs(c *C) { - references := map[string]plumbing.Hash{ - "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), - "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), - "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), - "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), - "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), - } - ar := &AdvRefs{ - References: references, - } - - expected := pktlines(c, - "0000000000000000000000000000000000000000 capabilities^{}\x00\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", - "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", - "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestPeeled(c *C) { - references := map[string]plumbing.Hash{ - "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), - "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), - "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), - "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), - "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), - } - peeled := map[string]plumbing.Hash{ - "refs/tags/v2.7.13-tree": plumbing.NewHash("4444444444444444444444444444444444444444"), - "refs/tags/v2.6.12-tree": plumbing.NewHash("5555555555555555555555555555555555555555"), - } - ar := &AdvRefs{ - References: references, - Peeled: peeled, - } - - expected := pktlines(c, - "0000000000000000000000000000000000000000 capabilities^{}\x00\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", - "5555555555555555555555555555555555555555 refs/tags/v2.6.12-tree^{}\n", - "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", - "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", - "4444444444444444444444444444444444444444 refs/tags/v2.7.13-tree^{}\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestShallow(c *C) { - shallows := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - } - ar := &AdvRefs{ - Shallows: shallows, - } - - expected := pktlines(c, - "0000000000000000000000000000000000000000 capabilities^{}\x00\n", - "shallow 1111111111111111111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222\n", - "shallow 3333333333333333333333333333333333333333\n", - "shallow 4444444444444444444444444444444444444444\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestAll(c *C) { - hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - - capabilities := NewCapabilities() - capabilities.Add("symref", "HEAD:/refs/heads/master") - capabilities.Add("ofs-delta") - capabilities.Add("multi_ack") - - references := map[string]plumbing.Hash{ - "refs/heads/master": plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), - "refs/tags/v2.6.12-tree": plumbing.NewHash("1111111111111111111111111111111111111111"), - "refs/tags/v2.7.13-tree": plumbing.NewHash("3333333333333333333333333333333333333333"), - "refs/tags/v2.6.13-tree": plumbing.NewHash("2222222222222222222222222222222222222222"), - "refs/tags/v2.6.11-tree": plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"), - } - - peeled := map[string]plumbing.Hash{ - "refs/tags/v2.7.13-tree": plumbing.NewHash("4444444444444444444444444444444444444444"), - "refs/tags/v2.6.12-tree": plumbing.NewHash("5555555555555555555555555555555555555555"), - } - - shallows := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - } - - ar := &AdvRefs{ - Head: &hash, - Capabilities: capabilities, - References: references, - Peeled: peeled, - Shallows: shallows, - } - - expected := pktlines(c, - "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00multi_ack ofs-delta symref=HEAD:/refs/heads/master\n", - "a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n", - "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11-tree\n", - "1111111111111111111111111111111111111111 refs/tags/v2.6.12-tree\n", - "5555555555555555555555555555555555555555 refs/tags/v2.6.12-tree^{}\n", - "2222222222222222222222222222222222222222 refs/tags/v2.6.13-tree\n", - "3333333333333333333333333333333333333333 refs/tags/v2.7.13-tree\n", - "4444444444444444444444444444444444444444 refs/tags/v2.7.13-tree^{}\n", - "shallow 1111111111111111111111111111111111111111\n", - "shallow 2222222222222222222222222222222222222222\n", - "shallow 3333333333333333333333333333333333333333\n", - "shallow 4444444444444444444444444444444444444444\n", - pktline.FlushString, - ) - - testEncode(c, ar, expected) -} - -func (s *AdvRefsEncodeSuite) TestErrorTooLong(c *C) { - references := map[string]plumbing.Hash{ - strings.Repeat("a", pktline.MaxPayloadSize): plumbing.NewHash("a6930aaee06755d1bdcfd943fbf614e4d92bb0c7"), - } - ar := &AdvRefs{ - References: references, - } - - var buf bytes.Buffer - e := NewAdvRefsEncoder(&buf) - err := e.Encode(ar) - c.Assert(err, ErrorMatches, ".*payload is too long.*") -} diff --git a/plumbing/protocol/packp/advrefs_test.go b/plumbing/protocol/packp/advrefs_test.go index 1a696d4..6d9f488 100644 --- a/plumbing/protocol/packp/advrefs_test.go +++ b/plumbing/protocol/packp/advrefs_test.go @@ -44,14 +44,10 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) { var obtained []byte { ar := packp.NewAdvRefs() - d := packp.NewAdvRefsDecoder(input) - err = d.Decode(ar) - c.Assert(err, IsNil) + c.Assert(ar.Decode(input), IsNil) var buf bytes.Buffer - e := packp.NewAdvRefsEncoder(&buf) - err := e.Encode(ar) - c.Assert(err, IsNil) + c.Assert(ar.Encode(&buf), IsNil) obtained = buf.Bytes() } @@ -258,12 +254,9 @@ func ExampleDecoder_Decode() { // Use the raw message as our input. input := strings.NewReader(raw) - // Create a Decoder reading from our input. - d := packp.NewAdvRefsDecoder(input) - // Decode the input into a newly allocated AdvRefs value. ar := packp.NewAdvRefs() - _ = d.Decode(ar) // error check ignored for brevity + _ = ar.Decode(input) // error check ignored for brevity // Do something interesting with the AdvRefs, e.g. print its contents. fmt.Println("head =", ar.Head) @@ -303,8 +296,7 @@ func ExampleEncoder_Encode() { // You can encode into stdout too, but you will not be able // see the '\x00' after "HEAD". var buf bytes.Buffer - e := packp.NewAdvRefsEncoder(&buf) - _ = e.Encode(ar) // error checks ignored for brevity + _ = ar.Encode(&buf) // error checks ignored for brevity // Print the contents of the buffer as a quoted string. // Printing is as a non-quoted string will be prettier but you diff --git a/plumbing/protocol/packp/ulreq.go b/plumbing/protocol/packp/ulreq.go index 5870001..6ec5b96 100644 --- a/plumbing/protocol/packp/ulreq.go +++ b/plumbing/protocol/packp/ulreq.go @@ -6,10 +6,11 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing" ) -// UlReq values represent the information transmitted on a +// UploadRequest values represent the information transmitted on a // upload-request message. Values from this type are not zero-value // safe, use the New function instead. -type UlReq struct { +// This is a low level type, use UploadPackRequest instead. +type UploadRequest struct { Capabilities *Capabilities Wants []plumbing.Hash Shallows []plumbing.Hash @@ -39,15 +40,20 @@ type DepthReference string func (d DepthReference) isDepth() {} -// NewUlReq returns a pointer to a new UlReq value, ready to be used. It has +// NewUploadRequest returns a pointer to a new UlReq value, ready to be used. It has // no capabilities, wants or shallows and an infinite depth. Please // note that to encode an upload-request it has to have at least one // wanted hash. -func NewUlReq() *UlReq { - return &UlReq{ +func NewUploadRequest() *UploadRequest { + return &UploadRequest{ Capabilities: NewCapabilities(), Wants: []plumbing.Hash{}, Shallows: []plumbing.Hash{}, Depth: DepthCommits(0), } } + +// Want adds a hash reference to the 'wants' list. +func (r *UploadRequest) Want(h ...plumbing.Hash) { + r.Wants = append(r.Wants, h...) +} diff --git a/plumbing/protocol/packp/ulreq_decode.go b/plumbing/protocol/packp/ulreq_decode.go new file mode 100644 index 0000000..0124cd0 --- /dev/null +++ b/plumbing/protocol/packp/ulreq_decode.go @@ -0,0 +1,259 @@ +package packp + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Decode reads the next upload-request form its input and +// stores it in the UploadRequest. +func (u *UploadRequest) Decode(r io.Reader) error { + d := newUlReqDecoder(r) + return d.Decode(u) +} + +type ulReqDecoder struct { + s *pktline.Scanner // a pkt-line scanner from the input stream + line []byte // current pkt-line contents, use parser.nextLine() to make it advance + nLine int // current pkt-line number for debugging, begins at 1 + err error // sticky error, use the parser.error() method to fill this out + data *UploadRequest // parsed data is stored here +} + +func newUlReqDecoder(r io.Reader) *ulReqDecoder { + return &ulReqDecoder{ + s: pktline.NewScanner(r), + } +} + +func (d *ulReqDecoder) Decode(v *UploadRequest) error { + d.data = v + + for state := d.decodeFirstWant; state != nil; { + state = state() + } + + return d.err +} + +// fills out the parser stiky error +func (d *ulReqDecoder) error(format string, a ...interface{}) { + d.err = fmt.Errorf("pkt-line %d: %s", d.nLine, + fmt.Sprintf(format, a...)) +} + +// Reads a new pkt-line from the scanner, makes its payload available as +// p.line and increments p.nLine. A successful invocation returns true, +// otherwise, false is returned and the sticky error is filled out +// accordingly. Trims eols at the end of the payloads. +func (d *ulReqDecoder) nextLine() bool { + d.nLine++ + + if !d.s.Scan() { + if d.err = d.s.Err(); d.err != nil { + return false + } + + d.error("EOF") + return false + } + + d.line = d.s.Bytes() + d.line = bytes.TrimSuffix(d.line, eol) + + return true +} + +// Expected format: want [ capabilities] +func (d *ulReqDecoder) decodeFirstWant() stateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if !bytes.HasPrefix(d.line, want) { + d.error("missing 'want ' prefix") + return nil + } + d.line = bytes.TrimPrefix(d.line, want) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Wants = append(d.data.Wants, hash) + + return d.decodeCaps +} + +func (d *ulReqDecoder) readHash() (plumbing.Hash, bool) { + if len(d.line) < hashSize { + d.err = fmt.Errorf("malformed hash: %v", d.line) + return plumbing.ZeroHash, false + } + + var hash plumbing.Hash + if _, err := hex.Decode(hash[:], d.line[:hashSize]); err != nil { + d.error("invalid hash text: %s", err) + return plumbing.ZeroHash, false + } + d.line = d.line[hashSize:] + + return hash, true +} + +// Expected format: sp cap1 sp cap2 sp cap3... +func (d *ulReqDecoder) decodeCaps() stateFn { + if len(d.line) == 0 { + return d.decodeOtherWants + } + + d.line = bytes.TrimPrefix(d.line, sp) + + for _, c := range bytes.Split(d.line, sp) { + name, values := readCapability(c) + d.data.Capabilities.Add(name, values...) + } + + return d.decodeOtherWants +} + +// Expected format: want +func (d *ulReqDecoder) decodeOtherWants() stateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if bytes.HasPrefix(d.line, shallow) { + return d.decodeShallow + } + + if bytes.HasPrefix(d.line, deepen) { + return d.decodeDeepen + } + + if len(d.line) == 0 { + return nil + } + + if !bytes.HasPrefix(d.line, want) { + d.error("unexpected payload while expecting a want: %q", d.line) + return nil + } + d.line = bytes.TrimPrefix(d.line, want) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Wants = append(d.data.Wants, hash) + + return d.decodeOtherWants +} + +// Expected format: shallow +func (d *ulReqDecoder) decodeShallow() stateFn { + if bytes.HasPrefix(d.line, deepen) { + return d.decodeDeepen + } + + if len(d.line) == 0 { + return nil + } + + if !bytes.HasPrefix(d.line, shallow) { + d.error("unexpected payload while expecting a shallow: %q", d.line) + return nil + } + d.line = bytes.TrimPrefix(d.line, shallow) + + hash, ok := d.readHash() + if !ok { + return nil + } + d.data.Shallows = append(d.data.Shallows, hash) + + if ok := d.nextLine(); !ok { + return nil + } + + return d.decodeShallow +} + +// Expected format: deepen / deepen-since
    / deepen-not +func (d *ulReqDecoder) decodeDeepen() stateFn { + if bytes.HasPrefix(d.line, deepenCommits) { + return d.decodeDeepenCommits + } + + if bytes.HasPrefix(d.line, deepenSince) { + return d.decodeDeepenSince + } + + if bytes.HasPrefix(d.line, deepenReference) { + return d.decodeDeepenReference + } + + if len(d.line) == 0 { + return nil + } + + d.error("unexpected deepen specification: %q", d.line) + return nil +} + +func (d *ulReqDecoder) decodeDeepenCommits() stateFn { + d.line = bytes.TrimPrefix(d.line, deepenCommits) + + var n int + if n, d.err = strconv.Atoi(string(d.line)); d.err != nil { + return nil + } + if n < 0 { + d.err = fmt.Errorf("negative depth") + return nil + } + d.data.Depth = DepthCommits(n) + + return d.decodeFlush +} + +func (d *ulReqDecoder) decodeDeepenSince() stateFn { + d.line = bytes.TrimPrefix(d.line, deepenSince) + + var secs int64 + secs, d.err = strconv.ParseInt(string(d.line), 10, 64) + if d.err != nil { + return nil + } + t := time.Unix(secs, 0).UTC() + d.data.Depth = DepthSince(t) + + return d.decodeFlush +} + +func (d *ulReqDecoder) decodeDeepenReference() stateFn { + d.line = bytes.TrimPrefix(d.line, deepenReference) + + d.data.Depth = DepthReference(string(d.line)) + + return d.decodeFlush +} + +func (d *ulReqDecoder) decodeFlush() stateFn { + if ok := d.nextLine(); !ok { + return nil + } + + if len(d.line) != 0 { + d.err = fmt.Errorf("unexpected payload while expecting a flush-pkt: %q", d.line) + } + + return nil +} diff --git a/plumbing/protocol/packp/ulreq_decode_test.go b/plumbing/protocol/packp/ulreq_decode_test.go new file mode 100644 index 0000000..eb12c90 --- /dev/null +++ b/plumbing/protocol/packp/ulreq_decode_test.go @@ -0,0 +1,532 @@ +package packp + +import ( + "bytes" + "io" + "sort" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" + + . "gopkg.in/check.v1" +) + +type UlReqDecodeSuite struct{} + +var _ = Suite(&UlReqDecodeSuite{}) + +func (s *UlReqDecodeSuite) TestEmpty(c *C) { + ur := NewUploadRequest() + var buf bytes.Buffer + d := newUlReqDecoder(&buf) + + err := d.Decode(ur) + c.Assert(err, ErrorMatches, "pkt-line 1: EOF") +} + +func (s *UlReqDecodeSuite) TestNoWant(c *C) { + payloads := []string{ + "foobar", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*missing 'want '.*") +} + +func (s *UlReqDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) { + ur := NewUploadRequest() + d := newUlReqDecoder(input) + + err := d.Decode(ur) + c.Assert(err, ErrorMatches, pattern) +} + +func (s *UlReqDecodeSuite) TestInvalidFirstHash(c *C) { + payloads := []string{ + "want 6ecf0ef2c2dffb796alberto2219af86ec6584e5\n", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*invalid hash.*") +} + +func (s *UlReqDecodeSuite) TestWantOK(c *C) { + payloads := []string{ + "want 1111111111111111111111111111111111111111", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + c.Assert(ur.Wants, DeepEquals, []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + }) +} + +func (s *UlReqDecodeSuite) testDecodeOK(c *C, payloads []string) *UploadRequest { + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) + err := e.EncodeString(payloads...) + c.Assert(err, IsNil) + + ur := NewUploadRequest() + d := newUlReqDecoder(&buf) + + err = d.Decode(ur) + c.Assert(err, IsNil) + + return ur +} + +func (s *UlReqDecodeSuite) TestWantWithCapabilities(c *C) { + payloads := []string{ + "want 1111111111111111111111111111111111111111 ofs-delta multi_ack", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + c.Assert(ur.Wants, DeepEquals, []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111")}) + + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) +} + +func (s *UlReqDecodeSuite) TestManyWantsNoCapabilities(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333", + "want 4444444444444444444444444444444444444444", + "want 1111111111111111111111111111111111111111", + "want 2222222222222222222222222222222222222222", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expected := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + } + + sort.Sort(byHash(ur.Wants)) + sort.Sort(byHash(expected)) + c.Assert(ur.Wants, DeepEquals, expected) +} + +type byHash []plumbing.Hash + +func (a byHash) Len() int { return len(a) } +func (a byHash) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byHash) Less(i, j int) bool { + ii := [20]byte(a[i]) + jj := [20]byte(a[j]) + return bytes.Compare(ii[:], jj[:]) < 0 +} + +func (s *UlReqDecodeSuite) TestManyWantsBadWant(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333", + "want 4444444444444444444444444444444444444444", + "foo", + "want 2222222222222222222222222222222222222222", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestManyWantsInvalidHash(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333", + "want 4444444444444444444444444444444444444444", + "want 1234567890abcdef", + "want 2222222222222222222222222222222222222222", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed hash.*") +} + +func (s *UlReqDecodeSuite) TestManyWantsWithCapabilities(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "want 4444444444444444444444444444444444444444", + "want 1111111111111111111111111111111111111111", + "want 2222222222222222222222222222222222222222", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expected := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + } + + sort.Sort(byHash(ur.Wants)) + sort.Sort(byHash(expected)) + c.Assert(ur.Wants, DeepEquals, expected) + + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) +} + +func (s *UlReqDecodeSuite) TestSingleShallowSingleWant(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expectedWants := []plumbing.Hash{ + plumbing.NewHash("3333333333333333333333333333333333333333"), + } + + expectedShallows := []plumbing.Hash{ + plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + } + + c.Assert(ur.Wants, DeepEquals, expectedWants) + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) + + c.Assert(ur.Shallows, DeepEquals, expectedShallows) +} + +func (s *UlReqDecodeSuite) TestSingleShallowManyWants(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "want 4444444444444444444444444444444444444444", + "want 1111111111111111111111111111111111111111", + "want 2222222222222222222222222222222222222222", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expectedWants := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + } + sort.Sort(byHash(expectedWants)) + + expectedShallows := []plumbing.Hash{ + plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + } + + sort.Sort(byHash(ur.Wants)) + c.Assert(ur.Wants, DeepEquals, expectedWants) + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) + + c.Assert(ur.Shallows, DeepEquals, expectedShallows) +} + +func (s *UlReqDecodeSuite) TestManyShallowSingleWant(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "shallow cccccccccccccccccccccccccccccccccccccccc", + "shallow dddddddddddddddddddddddddddddddddddddddd", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expectedWants := []plumbing.Hash{ + plumbing.NewHash("3333333333333333333333333333333333333333"), + } + + expectedShallows := []plumbing.Hash{ + plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), + plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), + } + sort.Sort(byHash(expectedShallows)) + + c.Assert(ur.Wants, DeepEquals, expectedWants) + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) + + sort.Sort(byHash(ur.Shallows)) + c.Assert(ur.Shallows, DeepEquals, expectedShallows) +} + +func (s *UlReqDecodeSuite) TestManyShallowManyWants(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "want 4444444444444444444444444444444444444444", + "want 1111111111111111111111111111111111111111", + "want 2222222222222222222222222222222222222222", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "shallow cccccccccccccccccccccccccccccccccccccccc", + "shallow dddddddddddddddddddddddddddddddddddddddd", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expectedWants := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + } + sort.Sort(byHash(expectedWants)) + + expectedShallows := []plumbing.Hash{ + plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), + plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), + } + sort.Sort(byHash(expectedShallows)) + + sort.Sort(byHash(ur.Wants)) + c.Assert(ur.Wants, DeepEquals, expectedWants) + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) + + sort.Sort(byHash(ur.Shallows)) + c.Assert(ur.Shallows, DeepEquals, expectedShallows) +} + +func (s *UlReqDecodeSuite) TestMalformedShallow(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shalow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestMalformedShallowHash(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*malformed hash.*") +} + +func (s *UlReqDecodeSuite) TestMalformedShallowManyShallows(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "shalow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "shallow cccccccccccccccccccccccccccccccccccccccc", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenSpec(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen-foo 34", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected deepen.*") +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenSingleWant(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "depth 32", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenMultiWant(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "want 2222222222222222222222222222222222222222", + "depth 32", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenWithSingleShallow(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow 2222222222222222222222222222222222222222", + "depth 32", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenWithMultiShallow(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "shallow 2222222222222222222222222222222222222222", + "shallow 5555555555555555555555555555555555555555", + "depth 32", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} + +func (s *UlReqDecodeSuite) TestDeepenCommits(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen 1234", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) + commits, ok := ur.Depth.(DepthCommits) + c.Assert(ok, Equals, true) + c.Assert(int(commits), Equals, 1234) +} + +func (s *UlReqDecodeSuite) TestDeepenCommitsInfiniteInplicit(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen 0", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) + commits, ok := ur.Depth.(DepthCommits) + c.Assert(ok, Equals, true) + c.Assert(int(commits), Equals, 0) +} + +func (s *UlReqDecodeSuite) TestDeepenCommitsInfiniteExplicit(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) + commits, ok := ur.Depth.(DepthCommits) + c.Assert(ok, Equals, true) + c.Assert(int(commits), Equals, 0) +} + +func (s *UlReqDecodeSuite) TestMalformedDeepenCommits(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen -32", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*negative depth.*") +} + +func (s *UlReqDecodeSuite) TestDeepenCommitsEmpty(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen ", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*invalid syntax.*") +} + +func (s *UlReqDecodeSuite) TestDeepenSince(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen-since 1420167845", // 2015-01-02T03:04:05+00:00 + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expected := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) + + c.Assert(ur.Depth, FitsTypeOf, DepthSince(time.Now())) + since, ok := ur.Depth.(DepthSince) + c.Assert(ok, Equals, true) + c.Assert(time.Time(since).Equal(expected), Equals, true, + Commentf("obtained=%s\nexpected=%s", time.Time(since), expected)) +} + +func (s *UlReqDecodeSuite) TestDeepenReference(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen-not refs/heads/master", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expected := "refs/heads/master" + + c.Assert(ur.Depth, FitsTypeOf, DepthReference("")) + reference, ok := ur.Depth.(DepthReference) + c.Assert(ok, Equals, true) + c.Assert(string(reference), Equals, expected) +} + +func (s *UlReqDecodeSuite) TestAll(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "want 4444444444444444444444444444444444444444", + "want 1111111111111111111111111111111111111111", + "want 2222222222222222222222222222222222222222", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "shallow cccccccccccccccccccccccccccccccccccccccc", + "shallow dddddddddddddddddddddddddddddddddddddddd", + "deepen 1234", + pktline.FlushString, + } + ur := s.testDecodeOK(c, payloads) + + expectedWants := []plumbing.Hash{ + plumbing.NewHash("1111111111111111111111111111111111111111"), + plumbing.NewHash("2222222222222222222222222222222222222222"), + plumbing.NewHash("3333333333333333333333333333333333333333"), + plumbing.NewHash("4444444444444444444444444444444444444444"), + } + sort.Sort(byHash(expectedWants)) + sort.Sort(byHash(ur.Wants)) + c.Assert(ur.Wants, DeepEquals, expectedWants) + + c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) + c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) + + expectedShallows := []plumbing.Hash{ + plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), + plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), + } + sort.Sort(byHash(expectedShallows)) + sort.Sort(byHash(ur.Shallows)) + c.Assert(ur.Shallows, DeepEquals, expectedShallows) + + c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) + commits, ok := ur.Depth.(DepthCommits) + c.Assert(ok, Equals, true) + c.Assert(int(commits), Equals, 1234) +} + +func (s *UlReqDecodeSuite) TestExtraData(c *C) { + payloads := []string{ + "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", + "deepen 32", + "foo", + pktline.FlushString, + } + r := toPktLines(c, payloads) + s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") +} diff --git a/plumbing/protocol/packp/ulreq_decoder.go b/plumbing/protocol/packp/ulreq_decoder.go deleted file mode 100644 index 67ba479..0000000 --- a/plumbing/protocol/packp/ulreq_decoder.go +++ /dev/null @@ -1,258 +0,0 @@ -package packp - -import ( - "bytes" - "encoding/hex" - "fmt" - "io" - "strconv" - "time" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" -) - -// A UlReqDecoder reads and decodes AdvRef values from an input stream. -type UlReqDecoder struct { - s *pktline.Scanner // a pkt-line scanner from the input stream - line []byte // current pkt-line contents, use parser.nextLine() to make it advance - nLine int // current pkt-line number for debugging, begins at 1 - err error // sticky error, use the parser.error() method to fill this out - data *UlReq // parsed data is stored here -} - -// NewUlReqDecoder returns a new decoder that reads from r. -// -// Will not read more data from r than necessary. -func NewUlReqDecoder(r io.Reader) *UlReqDecoder { - return &UlReqDecoder{ - s: pktline.NewScanner(r), - } -} - -// Decode reads the next upload-request form its input and -// stores it in the value pointed to by v. -func (d *UlReqDecoder) Decode(v *UlReq) error { - d.data = v - - for state := d.decodeFirstWant; state != nil; { - state = state() - } - - return d.err -} - -// fills out the parser stiky error -func (d *UlReqDecoder) error(format string, a ...interface{}) { - d.err = fmt.Errorf("pkt-line %d: %s", d.nLine, - fmt.Sprintf(format, a...)) -} - -// Reads a new pkt-line from the scanner, makes its payload available as -// p.line and increments p.nLine. A successful invocation returns true, -// otherwise, false is returned and the sticky error is filled out -// accordingly. Trims eols at the end of the payloads. -func (d *UlReqDecoder) nextLine() bool { - d.nLine++ - - if !d.s.Scan() { - if d.err = d.s.Err(); d.err != nil { - return false - } - - d.error("EOF") - return false - } - - d.line = d.s.Bytes() - d.line = bytes.TrimSuffix(d.line, eol) - - return true -} - -// Expected format: want [ capabilities] -func (d *UlReqDecoder) decodeFirstWant() stateFn { - if ok := d.nextLine(); !ok { - return nil - } - - if !bytes.HasPrefix(d.line, want) { - d.error("missing 'want ' prefix") - return nil - } - d.line = bytes.TrimPrefix(d.line, want) - - hash, ok := d.readHash() - if !ok { - return nil - } - d.data.Wants = append(d.data.Wants, hash) - - return d.decodeCaps -} - -func (d *UlReqDecoder) readHash() (plumbing.Hash, bool) { - if len(d.line) < hashSize { - d.err = fmt.Errorf("malformed hash: %v", d.line) - return plumbing.ZeroHash, false - } - - var hash plumbing.Hash - if _, err := hex.Decode(hash[:], d.line[:hashSize]); err != nil { - d.error("invalid hash text: %s", err) - return plumbing.ZeroHash, false - } - d.line = d.line[hashSize:] - - return hash, true -} - -// Expected format: sp cap1 sp cap2 sp cap3... -func (d *UlReqDecoder) decodeCaps() stateFn { - if len(d.line) == 0 { - return d.decodeOtherWants - } - - d.line = bytes.TrimPrefix(d.line, sp) - - for _, c := range bytes.Split(d.line, sp) { - name, values := readCapability(c) - d.data.Capabilities.Add(name, values...) - } - - return d.decodeOtherWants -} - -// Expected format: want -func (d *UlReqDecoder) decodeOtherWants() stateFn { - if ok := d.nextLine(); !ok { - return nil - } - - if bytes.HasPrefix(d.line, shallow) { - return d.decodeShallow - } - - if bytes.HasPrefix(d.line, deepen) { - return d.decodeDeepen - } - - if len(d.line) == 0 { - return nil - } - - if !bytes.HasPrefix(d.line, want) { - d.error("unexpected payload while expecting a want: %q", d.line) - return nil - } - d.line = bytes.TrimPrefix(d.line, want) - - hash, ok := d.readHash() - if !ok { - return nil - } - d.data.Wants = append(d.data.Wants, hash) - - return d.decodeOtherWants -} - -// Expected format: shallow -func (d *UlReqDecoder) decodeShallow() stateFn { - if bytes.HasPrefix(d.line, deepen) { - return d.decodeDeepen - } - - if len(d.line) == 0 { - return nil - } - - if !bytes.HasPrefix(d.line, shallow) { - d.error("unexpected payload while expecting a shallow: %q", d.line) - return nil - } - d.line = bytes.TrimPrefix(d.line, shallow) - - hash, ok := d.readHash() - if !ok { - return nil - } - d.data.Shallows = append(d.data.Shallows, hash) - - if ok := d.nextLine(); !ok { - return nil - } - - return d.decodeShallow -} - -// Expected format: deepen / deepen-since
      / deepen-not -func (d *UlReqDecoder) decodeDeepen() stateFn { - if bytes.HasPrefix(d.line, deepenCommits) { - return d.decodeDeepenCommits - } - - if bytes.HasPrefix(d.line, deepenSince) { - return d.decodeDeepenSince - } - - if bytes.HasPrefix(d.line, deepenReference) { - return d.decodeDeepenReference - } - - if len(d.line) == 0 { - return nil - } - - d.error("unexpected deepen specification: %q", d.line) - return nil -} - -func (d *UlReqDecoder) decodeDeepenCommits() stateFn { - d.line = bytes.TrimPrefix(d.line, deepenCommits) - - var n int - if n, d.err = strconv.Atoi(string(d.line)); d.err != nil { - return nil - } - if n < 0 { - d.err = fmt.Errorf("negative depth") - return nil - } - d.data.Depth = DepthCommits(n) - - return d.decodeFlush -} - -func (d *UlReqDecoder) decodeDeepenSince() stateFn { - d.line = bytes.TrimPrefix(d.line, deepenSince) - - var secs int64 - secs, d.err = strconv.ParseInt(string(d.line), 10, 64) - if d.err != nil { - return nil - } - t := time.Unix(secs, 0).UTC() - d.data.Depth = DepthSince(t) - - return d.decodeFlush -} - -func (d *UlReqDecoder) decodeDeepenReference() stateFn { - d.line = bytes.TrimPrefix(d.line, deepenReference) - - d.data.Depth = DepthReference(string(d.line)) - - return d.decodeFlush -} - -func (d *UlReqDecoder) decodeFlush() stateFn { - if ok := d.nextLine(); !ok { - return nil - } - - if len(d.line) != 0 { - d.err = fmt.Errorf("unexpected payload while expecting a flush-pkt: %q", d.line) - } - - return nil -} diff --git a/plumbing/protocol/packp/ulreq_decoder_test.go b/plumbing/protocol/packp/ulreq_decoder_test.go deleted file mode 100644 index e90962a..0000000 --- a/plumbing/protocol/packp/ulreq_decoder_test.go +++ /dev/null @@ -1,532 +0,0 @@ -package packp - -import ( - "bytes" - "io" - "sort" - "time" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" - - . "gopkg.in/check.v1" -) - -type UlReqDecodeSuite struct{} - -var _ = Suite(&UlReqDecodeSuite{}) - -func (s *UlReqDecodeSuite) TestEmpty(c *C) { - ur := NewUlReq() - var buf bytes.Buffer - d := NewUlReqDecoder(&buf) - - err := d.Decode(ur) - c.Assert(err, ErrorMatches, "pkt-line 1: EOF") -} - -func (s *UlReqDecodeSuite) TestNoWant(c *C) { - payloads := []string{ - "foobar", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*missing 'want '.*") -} - -func (s *UlReqDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) { - ur := NewUlReq() - d := NewUlReqDecoder(input) - - err := d.Decode(ur) - c.Assert(err, ErrorMatches, pattern) -} - -func (s *UlReqDecodeSuite) TestInvalidFirstHash(c *C) { - payloads := []string{ - "want 6ecf0ef2c2dffb796alberto2219af86ec6584e5\n", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*invalid hash.*") -} - -func (s *UlReqDecodeSuite) TestWantOK(c *C) { - payloads := []string{ - "want 1111111111111111111111111111111111111111", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - c.Assert(ur.Wants, DeepEquals, []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - }) -} - -func (s *UlReqDecodeSuite) testDecodeOK(c *C, payloads []string) *UlReq { - var buf bytes.Buffer - e := pktline.NewEncoder(&buf) - err := e.EncodeString(payloads...) - c.Assert(err, IsNil) - - ur := NewUlReq() - d := NewUlReqDecoder(&buf) - - err = d.Decode(ur) - c.Assert(err, IsNil) - - return ur -} - -func (s *UlReqDecodeSuite) TestWantWithCapabilities(c *C) { - payloads := []string{ - "want 1111111111111111111111111111111111111111 ofs-delta multi_ack", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - c.Assert(ur.Wants, DeepEquals, []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111")}) - - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) -} - -func (s *UlReqDecodeSuite) TestManyWantsNoCapabilities(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333", - "want 4444444444444444444444444444444444444444", - "want 1111111111111111111111111111111111111111", - "want 2222222222222222222222222222222222222222", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expected := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - } - - sort.Sort(byHash(ur.Wants)) - sort.Sort(byHash(expected)) - c.Assert(ur.Wants, DeepEquals, expected) -} - -type byHash []plumbing.Hash - -func (a byHash) Len() int { return len(a) } -func (a byHash) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byHash) Less(i, j int) bool { - ii := [20]byte(a[i]) - jj := [20]byte(a[j]) - return bytes.Compare(ii[:], jj[:]) < 0 -} - -func (s *UlReqDecodeSuite) TestManyWantsBadWant(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333", - "want 4444444444444444444444444444444444444444", - "foo", - "want 2222222222222222222222222222222222222222", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestManyWantsInvalidHash(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333", - "want 4444444444444444444444444444444444444444", - "want 1234567890abcdef", - "want 2222222222222222222222222222222222222222", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed hash.*") -} - -func (s *UlReqDecodeSuite) TestManyWantsWithCapabilities(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "want 4444444444444444444444444444444444444444", - "want 1111111111111111111111111111111111111111", - "want 2222222222222222222222222222222222222222", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expected := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - } - - sort.Sort(byHash(ur.Wants)) - sort.Sort(byHash(expected)) - c.Assert(ur.Wants, DeepEquals, expected) - - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) -} - -func (s *UlReqDecodeSuite) TestSingleShallowSingleWant(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expectedWants := []plumbing.Hash{ - plumbing.NewHash("3333333333333333333333333333333333333333"), - } - - expectedShallows := []plumbing.Hash{ - plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - } - - c.Assert(ur.Wants, DeepEquals, expectedWants) - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) - - c.Assert(ur.Shallows, DeepEquals, expectedShallows) -} - -func (s *UlReqDecodeSuite) TestSingleShallowManyWants(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "want 4444444444444444444444444444444444444444", - "want 1111111111111111111111111111111111111111", - "want 2222222222222222222222222222222222222222", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expectedWants := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - } - sort.Sort(byHash(expectedWants)) - - expectedShallows := []plumbing.Hash{ - plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - } - - sort.Sort(byHash(ur.Wants)) - c.Assert(ur.Wants, DeepEquals, expectedWants) - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) - - c.Assert(ur.Shallows, DeepEquals, expectedShallows) -} - -func (s *UlReqDecodeSuite) TestManyShallowSingleWant(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "shallow cccccccccccccccccccccccccccccccccccccccc", - "shallow dddddddddddddddddddddddddddddddddddddddd", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expectedWants := []plumbing.Hash{ - plumbing.NewHash("3333333333333333333333333333333333333333"), - } - - expectedShallows := []plumbing.Hash{ - plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), - plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), - } - sort.Sort(byHash(expectedShallows)) - - c.Assert(ur.Wants, DeepEquals, expectedWants) - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) - - sort.Sort(byHash(ur.Shallows)) - c.Assert(ur.Shallows, DeepEquals, expectedShallows) -} - -func (s *UlReqDecodeSuite) TestManyShallowManyWants(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "want 4444444444444444444444444444444444444444", - "want 1111111111111111111111111111111111111111", - "want 2222222222222222222222222222222222222222", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "shallow cccccccccccccccccccccccccccccccccccccccc", - "shallow dddddddddddddddddddddddddddddddddddddddd", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expectedWants := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - } - sort.Sort(byHash(expectedWants)) - - expectedShallows := []plumbing.Hash{ - plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), - plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), - } - sort.Sort(byHash(expectedShallows)) - - sort.Sort(byHash(ur.Wants)) - c.Assert(ur.Wants, DeepEquals, expectedWants) - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) - - sort.Sort(byHash(ur.Shallows)) - c.Assert(ur.Shallows, DeepEquals, expectedShallows) -} - -func (s *UlReqDecodeSuite) TestMalformedShallow(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shalow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestMalformedShallowHash(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*malformed hash.*") -} - -func (s *UlReqDecodeSuite) TestMalformedShallowManyShallows(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "shalow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "shallow cccccccccccccccccccccccccccccccccccccccc", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenSpec(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen-foo 34", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected deepen.*") -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenSingleWant(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "depth 32", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenMultiWant(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "want 2222222222222222222222222222222222222222", - "depth 32", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenWithSingleShallow(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow 2222222222222222222222222222222222222222", - "depth 32", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenWithMultiShallow(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "shallow 2222222222222222222222222222222222222222", - "shallow 5555555555555555555555555555555555555555", - "depth 32", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} - -func (s *UlReqDecodeSuite) TestDeepenCommits(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen 1234", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) - commits, ok := ur.Depth.(DepthCommits) - c.Assert(ok, Equals, true) - c.Assert(int(commits), Equals, 1234) -} - -func (s *UlReqDecodeSuite) TestDeepenCommitsInfiniteInplicit(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen 0", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) - commits, ok := ur.Depth.(DepthCommits) - c.Assert(ok, Equals, true) - c.Assert(int(commits), Equals, 0) -} - -func (s *UlReqDecodeSuite) TestDeepenCommitsInfiniteExplicit(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) - commits, ok := ur.Depth.(DepthCommits) - c.Assert(ok, Equals, true) - c.Assert(int(commits), Equals, 0) -} - -func (s *UlReqDecodeSuite) TestMalformedDeepenCommits(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen -32", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*negative depth.*") -} - -func (s *UlReqDecodeSuite) TestDeepenCommitsEmpty(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen ", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*invalid syntax.*") -} - -func (s *UlReqDecodeSuite) TestDeepenSince(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen-since 1420167845", // 2015-01-02T03:04:05+00:00 - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expected := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) - - c.Assert(ur.Depth, FitsTypeOf, DepthSince(time.Now())) - since, ok := ur.Depth.(DepthSince) - c.Assert(ok, Equals, true) - c.Assert(time.Time(since).Equal(expected), Equals, true, - Commentf("obtained=%s\nexpected=%s", time.Time(since), expected)) -} - -func (s *UlReqDecodeSuite) TestDeepenReference(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen-not refs/heads/master", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expected := "refs/heads/master" - - c.Assert(ur.Depth, FitsTypeOf, DepthReference("")) - reference, ok := ur.Depth.(DepthReference) - c.Assert(ok, Equals, true) - c.Assert(string(reference), Equals, expected) -} - -func (s *UlReqDecodeSuite) TestAll(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "want 4444444444444444444444444444444444444444", - "want 1111111111111111111111111111111111111111", - "want 2222222222222222222222222222222222222222", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "shallow cccccccccccccccccccccccccccccccccccccccc", - "shallow dddddddddddddddddddddddddddddddddddddddd", - "deepen 1234", - pktline.FlushString, - } - ur := s.testDecodeOK(c, payloads) - - expectedWants := []plumbing.Hash{ - plumbing.NewHash("1111111111111111111111111111111111111111"), - plumbing.NewHash("2222222222222222222222222222222222222222"), - plumbing.NewHash("3333333333333333333333333333333333333333"), - plumbing.NewHash("4444444444444444444444444444444444444444"), - } - sort.Sort(byHash(expectedWants)) - sort.Sort(byHash(ur.Wants)) - c.Assert(ur.Wants, DeepEquals, expectedWants) - - c.Assert(ur.Capabilities.Supports("ofs-delta"), Equals, true) - c.Assert(ur.Capabilities.Supports("multi_ack"), Equals, true) - - expectedShallows := []plumbing.Hash{ - plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), - plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd"), - } - sort.Sort(byHash(expectedShallows)) - sort.Sort(byHash(ur.Shallows)) - c.Assert(ur.Shallows, DeepEquals, expectedShallows) - - c.Assert(ur.Depth, FitsTypeOf, DepthCommits(0)) - commits, ok := ur.Depth.(DepthCommits) - c.Assert(ok, Equals, true) - c.Assert(int(commits), Equals, 1234) -} - -func (s *UlReqDecodeSuite) TestExtraData(c *C) { - payloads := []string{ - "want 3333333333333333333333333333333333333333 ofs-delta multi_ack", - "deepen 32", - "foo", - pktline.FlushString, - } - r := toPktLines(c, payloads) - s.testDecoderErrorMatches(c, r, ".*unexpected payload.*") -} diff --git a/plumbing/protocol/packp/ulreq_encode.go b/plumbing/protocol/packp/ulreq_encode.go new file mode 100644 index 0000000..b422a5f --- /dev/null +++ b/plumbing/protocol/packp/ulreq_encode.go @@ -0,0 +1,141 @@ +package packp + +import ( + "fmt" + "io" + "sort" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// Encode writes the UlReq encoding of u to the stream. +// +// All the payloads will end with a newline character. Wants and +// shallows are sorted alphabetically. A depth of 0 means no depth +// request is sent. +func (u *UploadRequest) Encode(w io.Writer) error { + e := newUlReqEncoder(w) + return e.Encode(u) +} + +type ulReqEncoder struct { + pe *pktline.Encoder // where to write the encoded data + data *UploadRequest // the data to encode + sortedWants []string + err error // sticky error +} + +func newUlReqEncoder(w io.Writer) *ulReqEncoder { + return &ulReqEncoder{ + pe: pktline.NewEncoder(w), + } +} + +func (e *ulReqEncoder) Encode(v *UploadRequest) error { + if len(v.Wants) == 0 { + return fmt.Errorf("empty wants provided") + } + + e.data = v + e.sortedWants = sortHashes(v.Wants) + + for state := e.encodeFirstWant; state != nil; { + state = state() + } + + return e.err +} + +func sortHashes(list []plumbing.Hash) []string { + sorted := make([]string, len(list)) + for i, hash := range list { + sorted[i] = hash.String() + } + sort.Strings(sorted) + + return sorted +} + +func (e *ulReqEncoder) encodeFirstWant() stateFn { + var err error + if e.data.Capabilities.IsEmpty() { + err = e.pe.Encodef("want %s\n", e.sortedWants[0]) + } else { + e.data.Capabilities.Sort() + err = e.pe.Encodef( + "want %s %s\n", + e.sortedWants[0], + e.data.Capabilities.String(), + ) + } + if err != nil { + e.err = fmt.Errorf("encoding first want line: %s", err) + return nil + } + + return e.encodeAditionalWants +} + +func (e *ulReqEncoder) encodeAditionalWants() stateFn { + for _, w := range e.sortedWants[1:] { + if err := e.pe.Encodef("want %s\n", w); err != nil { + e.err = fmt.Errorf("encoding want %q: %s", w, err) + return nil + } + } + + return e.encodeShallows +} + +func (e *ulReqEncoder) encodeShallows() stateFn { + sorted := sortHashes(e.data.Shallows) + for _, s := range sorted { + if err := e.pe.Encodef("shallow %s\n", s); err != nil { + e.err = fmt.Errorf("encoding shallow %q: %s", s, err) + return nil + } + } + + return e.encodeDepth +} + +func (e *ulReqEncoder) encodeDepth() stateFn { + switch depth := e.data.Depth.(type) { + case DepthCommits: + if depth != 0 { + commits := int(depth) + if err := e.pe.Encodef("deepen %d\n", commits); err != nil { + e.err = fmt.Errorf("encoding depth %d: %s", depth, err) + return nil + } + } + case DepthSince: + when := time.Time(depth).UTC() + if err := e.pe.Encodef("deepen-since %d\n", when.Unix()); err != nil { + e.err = fmt.Errorf("encoding depth %s: %s", when, err) + return nil + } + case DepthReference: + reference := string(depth) + if err := e.pe.Encodef("deepen-not %s\n", reference); err != nil { + e.err = fmt.Errorf("encoding depth %s: %s", reference, err) + return nil + } + default: + e.err = fmt.Errorf("unsupported depth type") + return nil + } + + return e.encodeFlush +} + +func (e *ulReqEncoder) encodeFlush() stateFn { + if err := e.pe.Flush(); err != nil { + e.err = fmt.Errorf("encoding flush-pkt: %s", err) + return nil + } + + return nil +} diff --git a/plumbing/protocol/packp/ulreq_encode_test.go b/plumbing/protocol/packp/ulreq_encode_test.go new file mode 100644 index 0000000..1eb3175 --- /dev/null +++ b/plumbing/protocol/packp/ulreq_encode_test.go @@ -0,0 +1,257 @@ +package packp + +import ( + "bytes" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" + + . "gopkg.in/check.v1" +) + +type UlReqEncodeSuite struct{} + +var _ = Suite(&UlReqEncodeSuite{}) + +func testUlReqEncode(c *C, ur *UploadRequest, expectedPayloads []string) { + var buf bytes.Buffer + e := newUlReqEncoder(&buf) + + err := e.Encode(ur) + c.Assert(err, IsNil) + obtained := buf.Bytes() + + expected := pktlines(c, expectedPayloads...) + + comment := Commentf("\nobtained = %s\nexpected = %s\n", string(obtained), string(expected)) + + c.Assert(obtained, DeepEquals, expected, comment) +} + +func testUlReqEncodeError(c *C, ur *UploadRequest, expectedErrorRegEx string) { + var buf bytes.Buffer + e := newUlReqEncoder(&buf) + + err := e.Encode(ur) + c.Assert(err, ErrorMatches, expectedErrorRegEx) +} + +func (s *UlReqEncodeSuite) TestZeroValue(c *C) { + ur := NewUploadRequest() + expectedErrorRegEx := ".*empty wants.*" + + testUlReqEncodeError(c, ur, expectedErrorRegEx) +} + +func (s *UlReqEncodeSuite) TestOneWant(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestOneWantWithCapabilities(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") + ur.Capabilities.Add("multi_ack") + ur.Capabilities.Add("thin-pack") + ur.Capabilities.Add("side-band") + ur.Capabilities.Add("ofs-delta") + + expected := []string{ + "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestWants(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) + ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) + ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + "want 2222222222222222222222222222222222222222\n", + "want 3333333333333333333333333333333333333333\n", + "want 4444444444444444444444444444444444444444\n", + "want 5555555555555555555555555555555555555555\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestWantsWithCapabilities(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) + ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) + ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) + + ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") + ur.Capabilities.Add("multi_ack") + ur.Capabilities.Add("thin-pack") + ur.Capabilities.Add("side-band") + ur.Capabilities.Add("ofs-delta") + + expected := []string{ + "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", + "want 2222222222222222222222222222222222222222\n", + "want 3333333333333333333333333333333333333333\n", + "want 4444444444444444444444444444444444444444\n", + "want 5555555555555555555555555555555555555555\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestShallow(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Capabilities.Add("multi_ack") + ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + expected := []string{ + "want 1111111111111111111111111111111111111111 multi_ack\n", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestManyShallows(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Capabilities.Add("multi_ack") + ur.Shallows = append(ur.Shallows, plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + expected := []string{ + "want 1111111111111111111111111111111111111111 multi_ack\n", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", + "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", + "shallow cccccccccccccccccccccccccccccccccccccccc\n", + "shallow dddddddddddddddddddddddddddddddddddddddd\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestDepthCommits(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Depth = DepthCommits(1234) + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + "deepen 1234\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestDepthSinceUTC(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + since := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) + ur.Depth = DepthSince(since) + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + "deepen-since 1420167845\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestDepthSinceNonUTC(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + berlin, err := time.LoadLocation("Europe/Berlin") + c.Assert(err, IsNil) + since := time.Date(2015, time.January, 2, 3, 4, 5, 0, berlin) + // since value is 2015-01-02 03:04:05 +0100 UTC (Europe/Berlin) or + // 2015-01-02 02:04:05 +0000 UTC, which is 1420164245 Unix seconds. + ur.Depth = DepthSince(since) + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + "deepen-since 1420164245\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestDepthReference(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Depth = DepthReference("refs/heads/feature-foo") + + expected := []string{ + "want 1111111111111111111111111111111111111111\n", + "deepen-not refs/heads/feature-foo\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} + +func (s *UlReqEncodeSuite) TestAll(c *C) { + ur := NewUploadRequest() + ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) + ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) + ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) + ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) + ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) + + ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") + ur.Capabilities.Add("multi_ack") + ur.Capabilities.Add("thin-pack") + ur.Capabilities.Add("side-band") + ur.Capabilities.Add("ofs-delta") + + ur.Shallows = append(ur.Shallows, plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc")) + ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + since := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) + ur.Depth = DepthSince(since) + + expected := []string{ + "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", + "want 2222222222222222222222222222222222222222\n", + "want 3333333333333333333333333333333333333333\n", + "want 4444444444444444444444444444444444444444\n", + "want 5555555555555555555555555555555555555555\n", + "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", + "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", + "shallow cccccccccccccccccccccccccccccccccccccccc\n", + "shallow dddddddddddddddddddddddddddddddddddddddd\n", + "deepen-since 1420167845\n", + pktline.FlushString, + } + + testUlReqEncode(c, ur, expected) +} diff --git a/plumbing/protocol/packp/ulreq_encoder.go b/plumbing/protocol/packp/ulreq_encoder.go deleted file mode 100644 index 9ebc4b5..0000000 --- a/plumbing/protocol/packp/ulreq_encoder.go +++ /dev/null @@ -1,138 +0,0 @@ -package packp - -import ( - "fmt" - "io" - "sort" - "time" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" -) - -// An UlReqEncoder writes UlReq values to an output stream. -type UlReqEncoder struct { - pe *pktline.Encoder // where to write the encoded data - data *UlReq // the data to encode - sortedWants []string - err error // sticky error -} - -// NewUlReqEncoder returns a new encoder that writes to w. -func NewUlReqEncoder(w io.Writer) *UlReqEncoder { - return &UlReqEncoder{ - pe: pktline.NewEncoder(w), - } -} - -// Encode writes the UlReq encoding of v to the stream. -// -// All the payloads will end with a newline character. Wants and -// shallows are sorted alphabetically. A depth of 0 means no depth -// request is sent. -func (e *UlReqEncoder) Encode(v *UlReq) error { - if len(v.Wants) == 0 { - return fmt.Errorf("empty wants provided") - } - - e.data = v - e.sortedWants = sortHashes(v.Wants) - - for state := e.encodeFirstWant; state != nil; { - state = state() - } - - return e.err -} - -func sortHashes(list []plumbing.Hash) []string { - sorted := make([]string, len(list)) - for i, hash := range list { - sorted[i] = hash.String() - } - sort.Strings(sorted) - - return sorted -} - -func (e *UlReqEncoder) encodeFirstWant() stateFn { - var err error - if e.data.Capabilities.IsEmpty() { - err = e.pe.Encodef("want %s\n", e.sortedWants[0]) - } else { - e.data.Capabilities.Sort() - err = e.pe.Encodef( - "want %s %s\n", - e.sortedWants[0], - e.data.Capabilities.String(), - ) - } - if err != nil { - e.err = fmt.Errorf("encoding first want line: %s", err) - return nil - } - - return e.encodeAditionalWants -} - -func (e *UlReqEncoder) encodeAditionalWants() stateFn { - for _, w := range e.sortedWants[1:] { - if err := e.pe.Encodef("want %s\n", w); err != nil { - e.err = fmt.Errorf("encoding want %q: %s", w, err) - return nil - } - } - - return e.encodeShallows -} - -func (e *UlReqEncoder) encodeShallows() stateFn { - sorted := sortHashes(e.data.Shallows) - for _, s := range sorted { - if err := e.pe.Encodef("shallow %s\n", s); err != nil { - e.err = fmt.Errorf("encoding shallow %q: %s", s, err) - return nil - } - } - - return e.encodeDepth -} - -func (e *UlReqEncoder) encodeDepth() stateFn { - switch depth := e.data.Depth.(type) { - case DepthCommits: - if depth != 0 { - commits := int(depth) - if err := e.pe.Encodef("deepen %d\n", commits); err != nil { - e.err = fmt.Errorf("encoding depth %d: %s", depth, err) - return nil - } - } - case DepthSince: - when := time.Time(depth).UTC() - if err := e.pe.Encodef("deepen-since %d\n", when.Unix()); err != nil { - e.err = fmt.Errorf("encoding depth %s: %s", when, err) - return nil - } - case DepthReference: - reference := string(depth) - if err := e.pe.Encodef("deepen-not %s\n", reference); err != nil { - e.err = fmt.Errorf("encoding depth %s: %s", reference, err) - return nil - } - default: - e.err = fmt.Errorf("unsupported depth type") - return nil - } - - return e.encodeFlush -} - -func (e *UlReqEncoder) encodeFlush() stateFn { - if err := e.pe.Flush(); err != nil { - e.err = fmt.Errorf("encoding flush-pkt: %s", err) - return nil - } - - return nil -} diff --git a/plumbing/protocol/packp/ulreq_encoder_test.go b/plumbing/protocol/packp/ulreq_encoder_test.go deleted file mode 100644 index fb83653..0000000 --- a/plumbing/protocol/packp/ulreq_encoder_test.go +++ /dev/null @@ -1,257 +0,0 @@ -package packp - -import ( - "bytes" - "time" - - "gopkg.in/src-d/go-git.v4/plumbing" - "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" - - . "gopkg.in/check.v1" -) - -type UlReqEncodeSuite struct{} - -var _ = Suite(&UlReqEncodeSuite{}) - -func testUlReqEncode(c *C, ur *UlReq, expectedPayloads []string) { - var buf bytes.Buffer - e := NewUlReqEncoder(&buf) - - err := e.Encode(ur) - c.Assert(err, IsNil) - obtained := buf.Bytes() - - expected := pktlines(c, expectedPayloads...) - - comment := Commentf("\nobtained = %s\nexpected = %s\n", string(obtained), string(expected)) - - c.Assert(obtained, DeepEquals, expected, comment) -} - -func testUlReqEncodeError(c *C, ur *UlReq, expectedErrorRegEx string) { - var buf bytes.Buffer - e := NewUlReqEncoder(&buf) - - err := e.Encode(ur) - c.Assert(err, ErrorMatches, expectedErrorRegEx) -} - -func (s *UlReqEncodeSuite) TestZeroValue(c *C) { - ur := NewUlReq() - expectedErrorRegEx := ".*empty wants.*" - - testUlReqEncodeError(c, ur, expectedErrorRegEx) -} - -func (s *UlReqEncodeSuite) TestOneWant(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestOneWantWithCapabilities(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") - ur.Capabilities.Add("multi_ack") - ur.Capabilities.Add("thin-pack") - ur.Capabilities.Add("side-band") - ur.Capabilities.Add("ofs-delta") - - expected := []string{ - "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestWants(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) - ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) - ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - "want 2222222222222222222222222222222222222222\n", - "want 3333333333333333333333333333333333333333\n", - "want 4444444444444444444444444444444444444444\n", - "want 5555555555555555555555555555555555555555\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestWantsWithCapabilities(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) - ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) - ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) - - ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") - ur.Capabilities.Add("multi_ack") - ur.Capabilities.Add("thin-pack") - ur.Capabilities.Add("side-band") - ur.Capabilities.Add("ofs-delta") - - expected := []string{ - "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", - "want 2222222222222222222222222222222222222222\n", - "want 3333333333333333333333333333333333333333\n", - "want 4444444444444444444444444444444444444444\n", - "want 5555555555555555555555555555555555555555\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestShallow(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Capabilities.Add("multi_ack") - ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) - - expected := []string{ - "want 1111111111111111111111111111111111111111 multi_ack\n", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestManyShallows(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Capabilities.Add("multi_ack") - ur.Shallows = append(ur.Shallows, plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) - - expected := []string{ - "want 1111111111111111111111111111111111111111 multi_ack\n", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", - "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", - "shallow cccccccccccccccccccccccccccccccccccccccc\n", - "shallow dddddddddddddddddddddddddddddddddddddddd\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestDepthCommits(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Depth = DepthCommits(1234) - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - "deepen 1234\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestDepthSinceUTC(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - since := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) - ur.Depth = DepthSince(since) - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - "deepen-since 1420167845\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestDepthSinceNonUTC(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - berlin, err := time.LoadLocation("Europe/Berlin") - c.Assert(err, IsNil) - since := time.Date(2015, time.January, 2, 3, 4, 5, 0, berlin) - // since value is 2015-01-02 03:04:05 +0100 UTC (Europe/Berlin) or - // 2015-01-02 02:04:05 +0000 UTC, which is 1420164245 Unix seconds. - ur.Depth = DepthSince(since) - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - "deepen-since 1420164245\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestDepthReference(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Depth = DepthReference("refs/heads/feature-foo") - - expected := []string{ - "want 1111111111111111111111111111111111111111\n", - "deepen-not refs/heads/feature-foo\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} - -func (s *UlReqEncodeSuite) TestAll(c *C) { - ur := NewUlReq() - ur.Wants = append(ur.Wants, plumbing.NewHash("4444444444444444444444444444444444444444")) - ur.Wants = append(ur.Wants, plumbing.NewHash("1111111111111111111111111111111111111111")) - ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) - ur.Wants = append(ur.Wants, plumbing.NewHash("2222222222222222222222222222222222222222")) - ur.Wants = append(ur.Wants, plumbing.NewHash("5555555555555555555555555555555555555555")) - - ur.Capabilities.Add("sysref", "HEAD:/refs/heads/master") - ur.Capabilities.Add("multi_ack") - ur.Capabilities.Add("thin-pack") - ur.Capabilities.Add("side-band") - ur.Capabilities.Add("ofs-delta") - - ur.Shallows = append(ur.Shallows, plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("dddddddddddddddddddddddddddddddddddddddd")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc")) - ur.Shallows = append(ur.Shallows, plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) - - since := time.Date(2015, time.January, 2, 3, 4, 5, 0, time.UTC) - ur.Depth = DepthSince(since) - - expected := []string{ - "want 1111111111111111111111111111111111111111 multi_ack ofs-delta side-band sysref=HEAD:/refs/heads/master thin-pack\n", - "want 2222222222222222222222222222222222222222\n", - "want 3333333333333333333333333333333333333333\n", - "want 4444444444444444444444444444444444444444\n", - "want 5555555555555555555555555555555555555555\n", - "shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", - "shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n", - "shallow cccccccccccccccccccccccccccccccccccccccc\n", - "shallow dddddddddddddddddddddddddddddddddddddddd\n", - "deepen-since 1420167845\n", - pktline.FlushString, - } - - testUlReqEncode(c, ur, expected) -} diff --git a/plumbing/protocol/packp/ulreq_test.go b/plumbing/protocol/packp/ulreq_test.go index 19b6dd0..be02f9d 100644 --- a/plumbing/protocol/packp/ulreq_test.go +++ b/plumbing/protocol/packp/ulreq_test.go @@ -12,7 +12,7 @@ import ( func ExampleUlReqEncoder_Encode() { // Create an empty UlReq with the contents you want... - ur := NewUlReq() + ur := NewUploadRequest() // Add a couple of wants ur.Wants = append(ur.Wants, plumbing.NewHash("3333333333333333333333333333333333333333")) @@ -32,7 +32,7 @@ func ExampleUlReqEncoder_Encode() { ur.Depth = DepthSince(since) // Create a new Encode for the stdout... - e := NewUlReqEncoder(os.Stdout) + e := newUlReqEncoder(os.Stdout) // ...and encode the upload-request to it. _ = e.Encode(ur) // ignoring errors for brevity // Output: @@ -60,10 +60,10 @@ func ExampleUlReqDecoder_Decode() { input := strings.NewReader(raw) // Create the Decoder reading from our input. - d := NewUlReqDecoder(input) + d := newUlReqDecoder(input) // Decode the input into a newly allocated UlReq value. - ur := NewUlReq() + ur := NewUploadRequest() _ = d.Decode(ur) // error check ignored for brevity // Do something interesting with the UlReq, e.g. print its contents. diff --git a/plumbing/protocol/packp/upload_pack_request.go b/plumbing/protocol/packp/upload_pack_request.go new file mode 100644 index 0000000..42033b1 --- /dev/null +++ b/plumbing/protocol/packp/upload_pack_request.go @@ -0,0 +1,75 @@ +package packp + +import ( + "fmt" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" +) + +// UploadHaves is a message to signal the references that a client has in a +// upload-pack. Do not use this directly. Use UploadPackRequest request instead. +type UploadHaves struct { + Haves []plumbing.Hash +} + +// Encode encodes the UploadHaves into the Writer. +func (u *UploadHaves) Encode(w io.Writer) error { + e := pktline.NewEncoder(w) + for _, have := range u.Haves { + if err := e.Encodef("have %s\n", have); err != nil { + return fmt.Errorf("sending haves for %q: %s", have, err) + } + } + + if len(u.Haves) != 0 { + if err := e.Flush(); err != nil { + return fmt.Errorf("sending flush-pkt after haves: %s", err) + } + } + + return nil +} + +// Have adds a hash reference to the 'haves' list. +func (r *UploadHaves) Have(h ...plumbing.Hash) { + r.Haves = append(r.Haves, h...) +} + +// UploadPackRequest represents a upload-pack request. +// Zero-value is not safe, use NewUploadPackRequest instead. +type UploadPackRequest struct { + *UploadRequest + *UploadHaves +} + +// NewUploadPackRequest creates a new UploadPackRequest and returns a pointer. +func NewUploadPackRequest() *UploadPackRequest { + return &UploadPackRequest{ + UploadHaves: &UploadHaves{}, + UploadRequest: NewUploadRequest(), + } +} + +func (r *UploadPackRequest) IsEmpty() bool { + return isSubset(r.Wants, r.Haves) +} + +func isSubset(needle []plumbing.Hash, haystack []plumbing.Hash) bool { + for _, h := range needle { + found := false + for _, oh := range haystack { + if h == oh { + found = true + break + } + } + + if !found { + return false + } + } + + return true +} diff --git a/plumbing/protocol/packp/upload_pack_request_test.go b/plumbing/protocol/packp/upload_pack_request_test.go new file mode 100644 index 0000000..f321736 --- /dev/null +++ b/plumbing/protocol/packp/upload_pack_request_test.go @@ -0,0 +1,33 @@ +package packp + +import ( + "gopkg.in/src-d/go-git.v4/plumbing" + + . "gopkg.in/check.v1" +) + +type UploadPackRequestSuite struct{} + +var _ = Suite(&UploadPackRequestSuite{}) + +func (s *UploadPackRequestSuite) TestUploadPackRequest_IsEmpty(c *C) { + r := NewUploadPackRequest() + r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) + r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) + + c.Assert(r.IsEmpty(), Equals, false) + + r = NewUploadPackRequest() + r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989")) + r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + + c.Assert(r.IsEmpty(), Equals, false) + + r = NewUploadPackRequest() + r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + r.Have(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c")) + + c.Assert(r.IsEmpty(), Equals, true) +} -- cgit