aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2016-12-01 09:59:19 +0100
committerGitHub <noreply@github.com>2016-12-01 09:59:19 +0100
commitc15bf1dff332873644290db0e186b8f5ad9b8fb2 (patch)
tree1dd79212333823b70f2792ad116864ea127e4a1c /plumbing
parent7de79aef5d4aa3100382d4df3e99525f9949e8d1 (diff)
downloadgo-git-c15bf1dff332873644290db0e186b8f5ad9b8fb2.tar.gz
capabilities: full integration (#151)
* format/pktline: fix readPayloadLen err handling * protocol/pakp: UploadReq validation and creation of capabilities * protocol/pakp: AdvRef tests * protocol/pakp: capability.List.Delete * protocol: filter unsupported capabilities * remote capability negociation * transport: UploadRequest validation * requested changes
Diffstat (limited to 'plumbing')
-rw-r--r--plumbing/protocol/packp/advrefs_test.go98
-rw-r--r--plumbing/protocol/packp/capability/list.go17
-rw-r--r--plumbing/protocol/packp/capability/list_test.go16
-rw-r--r--plumbing/protocol/packp/ulreq.go105
-rw-r--r--plumbing/protocol/packp/ulreq_decode_test.go2
-rw-r--r--plumbing/protocol/packp/ulreq_test.go101
-rw-r--r--plumbing/protocol/packp/upload_pack_request_test.go33
-rw-r--r--plumbing/protocol/packp/uppackreq.go (renamed from plumbing/protocol/packp/upload_pack_request.go)67
-rw-r--r--plumbing/protocol/packp/uppackreq_test.go42
-rw-r--r--plumbing/transport/common.go20
-rw-r--r--plumbing/transport/common_test.go10
-rw-r--r--plumbing/transport/http/fetch_pack.go5
-rw-r--r--plumbing/transport/http/fetch_pack_test.go6
-rw-r--r--plumbing/transport/internal/common/common.go5
-rw-r--r--plumbing/transport/test/common.go40
15 files changed, 468 insertions, 99 deletions
diff --git a/plumbing/protocol/packp/advrefs_test.go b/plumbing/protocol/packp/advrefs_test.go
index 1689938..52ddc5c 100644
--- a/plumbing/protocol/packp/advrefs_test.go
+++ b/plumbing/protocol/packp/advrefs_test.go
@@ -1,27 +1,89 @@
-package packp_test
+package packp
import (
"bytes"
"fmt"
"io"
"strings"
- "testing"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
. "gopkg.in/check.v1"
)
-func Test(t *testing.T) { TestingT(t) }
+type AdvRefSuite struct{}
-type SuiteDecodeEncode struct{}
+var _ = Suite(&AdvRefSuite{})
-var _ = Suite(&SuiteDecodeEncode{})
+func (s *AdvRefSuite) TestAddReferenceSymbolic(c *C) {
+ ref := plumbing.NewSymbolicReference("foo", "bar")
-func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
+ a := NewAdvRefs()
+ err := a.AddReference(ref)
+ c.Assert(err, IsNil)
+
+ values := a.Capabilities.Get(capability.SymRef)
+ c.Assert(values, HasLen, 1)
+ c.Assert(values[0], Equals, "foo:bar")
+}
+
+func (s *AdvRefSuite) TestAddReferenceHash(c *C) {
+ ref := plumbing.NewHashReference("foo", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
+
+ a := NewAdvRefs()
+ err := a.AddReference(ref)
+ c.Assert(err, IsNil)
+
+ c.Assert(a.References, HasLen, 1)
+ c.Assert(a.References["foo"].String(), Equals, "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
+}
+
+func (s *AdvRefSuite) TestAllReferences(c *C) {
+ hash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
+
+ a := NewAdvRefs()
+ err := a.AddReference(plumbing.NewSymbolicReference("foo", "bar"))
+ c.Assert(err, IsNil)
+ err = a.AddReference(plumbing.NewHashReference("bar", hash))
+ c.Assert(err, IsNil)
+
+ refs, err := a.AllReferences()
+ c.Assert(err, IsNil)
+
+ iter, err := refs.IterReferences()
+ c.Assert(err, IsNil)
+
+ var count int
+ iter.ForEach(func(ref *plumbing.Reference) error {
+ count++
+ switch ref.Name() {
+ case "bar":
+ c.Assert(ref.Hash(), Equals, hash)
+ case "foo":
+ c.Assert(ref.Target().String(), Equals, "bar")
+ }
+ return nil
+ })
+
+ c.Assert(count, Equals, 2)
+}
+
+func (s *AdvRefSuite) TestAllReferencesBadSymref(c *C) {
+ a := NewAdvRefs()
+ err := a.Capabilities.Set(capability.SymRef, "foo")
+ c.Assert(err, IsNil)
+
+ _, err = a.AllReferences()
+ c.Assert(err, NotNil)
+}
+
+type AdvRefsDecodeEncodeSuite struct{}
+
+var _ = Suite(&AdvRefsDecodeEncodeSuite{})
+
+func (s *AdvRefsDecodeEncodeSuite) test(c *C, in []string, exp []string) {
var err error
var input io.Reader
{
@@ -44,7 +106,7 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
var obtained []byte
{
- ar := packp.NewAdvRefs()
+ ar := NewAdvRefs()
c.Assert(ar.Decode(input), IsNil)
var buf bytes.Buffer
@@ -56,7 +118,7 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
c.Assert(string(obtained), DeepEquals, string(expected))
}
-func (s *SuiteDecodeEncode) TestNoHead(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestNoHead(c *C) {
input := []string{
"0000000000000000000000000000000000000000 capabilities^{}\x00",
pktline.FlushString,
@@ -70,7 +132,7 @@ func (s *SuiteDecodeEncode) TestNoHead(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestNoHeadSmart(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestNoHeadSmart(c *C) {
input := []string{
"# service=git-upload-pack\n",
"0000000000000000000000000000000000000000 capabilities^{}\x00",
@@ -86,7 +148,7 @@ func (s *SuiteDecodeEncode) TestNoHeadSmart(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestNoHeadSmartBug(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestNoHeadSmartBug(c *C) {
input := []string{
"# service=git-upload-pack\n",
pktline.FlushString,
@@ -104,7 +166,7 @@ func (s *SuiteDecodeEncode) TestNoHeadSmartBug(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestRefs(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestRefs(c *C) {
input := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master",
@@ -124,7 +186,7 @@ func (s *SuiteDecodeEncode) TestRefs(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestPeeled(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestPeeled(c *C) {
input := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack",
"7777777777777777777777777777777777777777 refs/tags/v2.6.12-tree\n",
@@ -148,7 +210,7 @@ func (s *SuiteDecodeEncode) TestPeeled(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestAll(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestAll(c *C) {
input := []string{
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack\n",
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
@@ -176,7 +238,7 @@ func (s *SuiteDecodeEncode) TestAll(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestAllSmart(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestAllSmart(c *C) {
input := []string{
"# service=git-upload-pack\n",
pktline.FlushString,
@@ -208,7 +270,7 @@ func (s *SuiteDecodeEncode) TestAllSmart(c *C) {
s.test(c, input, expected)
}
-func (s *SuiteDecodeEncode) TestAllSmartBug(c *C) {
+func (s *AdvRefsDecodeEncodeSuite) TestAllSmartBug(c *C) {
input := []string{
"# service=git-upload-pack\n",
pktline.FlushString,
@@ -254,7 +316,7 @@ func ExampleDecoder_Decode() {
input := strings.NewReader(raw)
// Decode the input into a newly allocated AdvRefs value.
- ar := packp.NewAdvRefs()
+ ar := NewAdvRefs()
_ = ar.Decode(input) // error check ignored for brevity
// Do something interesting with the AdvRefs, e.g. print its contents.
@@ -270,7 +332,7 @@ func ExampleDecoder_Decode() {
func ExampleEncoder_Encode() {
// Create an AdvRefs with the contents you want...
- ar := packp.NewAdvRefs()
+ ar := NewAdvRefs()
// ...add a hash for the HEAD...
head := plumbing.NewHash("1111111111111111111111111111111111111111")
diff --git a/plumbing/protocol/packp/capability/list.go b/plumbing/protocol/packp/capability/list.go
index ff8d4a4..7197d36 100644
--- a/plumbing/protocol/packp/capability/list.go
+++ b/plumbing/protocol/packp/capability/list.go
@@ -145,6 +145,23 @@ func (l *List) Supports(capability Capability) bool {
return ok
}
+// Delete deletes a capability from the List
+func (l *List) Delete(capability Capability) {
+ if !l.Supports(capability) {
+ return
+ }
+
+ delete(l.m, capability)
+ for i, c := range l.sort {
+ if c != string(capability) {
+ continue
+ }
+
+ l.sort = append(l.sort[:i], l.sort[i+1:]...)
+ return
+ }
+}
+
// String generates the capabilities strings, the capabilities are sorted in
// insertion order
func (l *List) String() string {
diff --git a/plumbing/protocol/packp/capability/list_test.go b/plumbing/protocol/packp/capability/list_test.go
index eeb3173..d6aac16 100644
--- a/plumbing/protocol/packp/capability/list_test.go
+++ b/plumbing/protocol/packp/capability/list_test.go
@@ -97,6 +97,22 @@ func (s *SuiteCapabilities) TestGetEmpty(c *check.C) {
c.Assert(cap.Get(Agent), check.HasLen, 0)
}
+func (s *SuiteCapabilities) TestDelete(c *check.C) {
+ cap := NewList()
+ cap.Delete(SymRef)
+
+ err := cap.Add(Sideband)
+ c.Assert(err, check.IsNil)
+ err = cap.Set(SymRef, "bar")
+ c.Assert(err, check.IsNil)
+ err = cap.Set(Sideband64k)
+ c.Assert(err, check.IsNil)
+
+ cap.Delete(SymRef)
+
+ c.Assert(cap.String(), check.Equals, "side-band side-band-64k")
+}
+
func (s *SuiteCapabilities) TestAdd(c *check.C) {
cap := NewList()
err := cap.Add(SymRef, "foo", "qux")
diff --git a/plumbing/protocol/packp/ulreq.go b/plumbing/protocol/packp/ulreq.go
index be68b26..254e85e 100644
--- a/plumbing/protocol/packp/ulreq.go
+++ b/plumbing/protocol/packp/ulreq.go
@@ -1,12 +1,15 @@
package packp
import (
+ "fmt"
"time"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
)
+const DefaultAgent = "go-git/4.x"
+
// 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.
@@ -41,10 +44,9 @@ type DepthReference string
func (d DepthReference) isDepth() {}
-// 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.
+// NewUploadRequest returns a pointer to a new UploadRequest 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 NewUploadRequest() *UploadRequest {
return &UploadRequest{
Capabilities: capability.NewList(),
@@ -54,7 +56,96 @@ func NewUploadRequest() *UploadRequest {
}
}
-// Want adds a hash reference to the 'wants' list.
-func (r *UploadRequest) Want(h ...plumbing.Hash) {
- r.Wants = append(r.Wants, h...)
+// NewUploadRequestFromCapabilities returns a pointer to a new UploadRequest
+// value, the request capabilities are filled with the most optiomal ones, based
+// on the adv value (advertaised capabilities), the UploadRequest generated it
+// has no wants or shallows and an infinite depth.
+func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest {
+ r := NewUploadRequest()
+
+ if adv.Supports(capability.MultiACKDetailed) {
+ r.Capabilities.Set(capability.MultiACKDetailed)
+ } else if adv.Supports(capability.MultiACK) {
+ r.Capabilities.Set(capability.MultiACK)
+ }
+
+ if adv.Supports(capability.ThinPack) {
+ r.Capabilities.Set(capability.ThinPack)
+ }
+
+ if adv.Supports(capability.OFSDelta) {
+ r.Capabilities.Set(capability.OFSDelta)
+ }
+
+ if adv.Supports(capability.Agent) {
+ r.Capabilities.Set(capability.Agent, DefaultAgent)
+ }
+
+ return r
+}
+
+// Validate validates the content of UploadRequest, following the next rules:
+// - Wants MUST have at least one reference
+// - capability.Shallow MUST be present if Shallows is not empty
+// - is a non-zero DepthCommits is given capability.Shallow MUST be present
+// - is a DepthSince is given capability.Shallow MUST be present
+// - is a DepthReference is given capability.DeepenNot MUST be present
+// - MUST contain only maximum of one of capability.Sideband and capability.Sideband64k
+// - MUST contain only maximum of one of capability.MultiACK and capability.MultiACKDetailed
+func (r *UploadRequest) Validate() error {
+ if len(r.Wants) == 0 {
+ return fmt.Errorf("want can't be empty")
+ }
+
+ if err := r.validateRequiredCapabilities(); err != nil {
+ return err
+ }
+
+ if err := r.validateConflictCapabilities(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (r *UploadRequest) validateRequiredCapabilities() error {
+ msg := "missing capability %s"
+
+ if len(r.Shallows) != 0 && !r.Capabilities.Supports(capability.Shallow) {
+ return fmt.Errorf(msg, capability.Shallow)
+ }
+
+ switch r.Depth.(type) {
+ case DepthCommits:
+ if r.Depth != DepthCommits(0) {
+ if !r.Capabilities.Supports(capability.Shallow) {
+ return fmt.Errorf(msg, capability.Shallow)
+ }
+ }
+ case DepthSince:
+ if !r.Capabilities.Supports(capability.DeepenSince) {
+ return fmt.Errorf(msg, capability.DeepenSince)
+ }
+ case DepthReference:
+ if !r.Capabilities.Supports(capability.DeepenNot) {
+ return fmt.Errorf(msg, capability.DeepenNot)
+ }
+ }
+
+ return nil
+}
+
+func (r *UploadRequest) validateConflictCapabilities() error {
+ msg := "capabilities %s and %s are mutually exclusive"
+ if r.Capabilities.Supports(capability.Sideband) &&
+ r.Capabilities.Supports(capability.Sideband64k) {
+ return fmt.Errorf(msg, capability.Sideband, capability.Sideband64k)
+ }
+
+ if r.Capabilities.Supports(capability.MultiACK) &&
+ r.Capabilities.Supports(capability.MultiACKDetailed) {
+ return fmt.Errorf(msg, capability.MultiACK, capability.MultiACKDetailed)
+ }
+
+ return nil
}
diff --git a/plumbing/protocol/packp/ulreq_decode_test.go b/plumbing/protocol/packp/ulreq_decode_test.go
index e7d9d7c6..82a4e1f 100644
--- a/plumbing/protocol/packp/ulreq_decode_test.go
+++ b/plumbing/protocol/packp/ulreq_decode_test.go
@@ -8,9 +8,9 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
. "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
)
type UlReqDecodeSuite struct{}
diff --git a/plumbing/protocol/packp/ulreq_test.go b/plumbing/protocol/packp/ulreq_test.go
index 5e9e978..53626ee 100644
--- a/plumbing/protocol/packp/ulreq_test.go
+++ b/plumbing/protocol/packp/ulreq_test.go
@@ -9,8 +9,109 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+
+ . "gopkg.in/check.v1"
)
+type UlReqSuite struct{}
+
+var _ = Suite(&UlReqSuite{})
+
+func (s *UlReqSuite) TestNewUploadRequestFromCapabilities(c *C) {
+ cap := capability.NewList()
+ cap.Set(capability.Sideband)
+ cap.Set(capability.Sideband64k)
+ cap.Set(capability.MultiACK)
+ cap.Set(capability.MultiACKDetailed)
+ cap.Set(capability.ThinPack)
+ cap.Set(capability.OFSDelta)
+ cap.Set(capability.Agent, "foo")
+
+ r := NewUploadRequestFromCapabilities(cap)
+ c.Assert(r.Capabilities.String(), Equals,
+ "multi_ack_detailed thin-pack ofs-delta agent=go-git/4.x",
+ )
+}
+
+func (s *UlReqSuite) TestValidateWants(c *C) {
+ r := NewUploadRequest()
+ err := r.Validate()
+ c.Assert(err, NotNil)
+
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ err = r.Validate()
+ c.Assert(err, IsNil)
+}
+
+func (s *UlReqSuite) TestValidateShallows(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Shallows = append(r.Shallows, plumbing.NewHash("2222222222222222222222222222222222222222"))
+ err := r.Validate()
+ c.Assert(err, NotNil)
+
+ r.Capabilities.Set(capability.Shallow)
+ err = r.Validate()
+ c.Assert(err, IsNil)
+}
+
+func (s *UlReqSuite) TestValidateDepthCommits(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Depth = DepthCommits(42)
+
+ err := r.Validate()
+ c.Assert(err, NotNil)
+
+ r.Capabilities.Set(capability.Shallow)
+ err = r.Validate()
+ c.Assert(err, IsNil)
+}
+
+func (s *UlReqSuite) TestValidateDepthReference(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Depth = DepthReference("1111111111111111111111111111111111111111")
+
+ err := r.Validate()
+ c.Assert(err, NotNil)
+
+ r.Capabilities.Set(capability.DeepenNot)
+ err = r.Validate()
+ c.Assert(err, IsNil)
+}
+
+func (s *UlReqSuite) TestValidateDepthSince(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Depth = DepthSince(time.Now())
+
+ err := r.Validate()
+ c.Assert(err, NotNil)
+
+ r.Capabilities.Set(capability.DeepenSince)
+ err = r.Validate()
+ c.Assert(err, IsNil)
+}
+
+func (s *UlReqSuite) TestValidateConflictSideband(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Capabilities.Set(capability.Sideband)
+ r.Capabilities.Set(capability.Sideband64k)
+ err := r.Validate()
+ c.Assert(err, NotNil)
+}
+
+func (s *UlReqSuite) TestValidateConflictMultiACK(c *C) {
+ r := NewUploadRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
+ r.Capabilities.Set(capability.MultiACK)
+ r.Capabilities.Set(capability.MultiACKDetailed)
+ err := r.Validate()
+ c.Assert(err, NotNil)
+}
+
func ExampleUlReqEncoder_Encode() {
// Create an empty UlReq with the contents you want...
ur := NewUploadRequest()
diff --git a/plumbing/protocol/packp/upload_pack_request_test.go b/plumbing/protocol/packp/upload_pack_request_test.go
deleted file mode 100644
index f321736..0000000
--- a/plumbing/protocol/packp/upload_pack_request_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-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)
-}
diff --git a/plumbing/protocol/packp/upload_pack_request.go b/plumbing/protocol/packp/uppackreq.go
index 42033b1..2b1cb84 100644
--- a/plumbing/protocol/packp/upload_pack_request.go
+++ b/plumbing/protocol/packp/uppackreq.go
@@ -6,37 +6,9 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
)
-// 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 {
@@ -52,6 +24,19 @@ func NewUploadPackRequest() *UploadPackRequest {
}
}
+// NewUploadPackRequestFromCapabilities creates a new UploadPackRequest and
+// returns a pointer. The request capabilities are filled with the most optiomal
+// ones, based on the adv value (advertaised capabilities), the UploadPackRequest
+// it has no wants, haves or shallows and an infinite depth
+func NewUploadPackRequestFromCapabilities(adv *capability.List) *UploadPackRequest {
+ return &UploadPackRequest{
+ UploadHaves: &UploadHaves{},
+ UploadRequest: NewUploadRequestFromCapabilities(adv),
+ }
+}
+
+// IsEmpty a request if empty if Haves are contained in the Wants, or if Wants
+// length is zero
func (r *UploadPackRequest) IsEmpty() bool {
return isSubset(r.Wants, r.Haves)
}
@@ -73,3 +58,27 @@ func isSubset(needle []plumbing.Hash, haystack []plumbing.Hash) bool {
return true
}
+
+// 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
+}
diff --git a/plumbing/protocol/packp/uppackreq_test.go b/plumbing/protocol/packp/uppackreq_test.go
new file mode 100644
index 0000000..75b75b4
--- /dev/null
+++ b/plumbing/protocol/packp/uppackreq_test.go
@@ -0,0 +1,42 @@
+package packp
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+
+ . "gopkg.in/check.v1"
+)
+
+type UploadPackRequestSuite struct{}
+
+var _ = Suite(&UploadPackRequestSuite{})
+
+func (s *UploadPackRequestSuite) TestNewUploadPackRequestFromCapabilities(c *C) {
+ cap := capability.NewList()
+ cap.Set(capability.Agent, "foo")
+
+ r := NewUploadPackRequestFromCapabilities(cap)
+ c.Assert(r.Capabilities.String(), Equals, "agent=go-git/4.x")
+}
+
+func (s *UploadPackRequestSuite) TestIsEmpty(c *C) {
+ r := NewUploadPackRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Wants = append(r.Wants, plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
+ r.Haves = append(r.Haves, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+
+ c.Assert(r.IsEmpty(), Equals, false)
+
+ r = NewUploadPackRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Wants = append(r.Wants, plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
+ r.Haves = append(r.Haves, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+
+ c.Assert(r.IsEmpty(), Equals, false)
+
+ r = NewUploadPackRequest()
+ r.Wants = append(r.Wants, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Haves = append(r.Haves, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+
+ c.Assert(r.IsEmpty(), Equals, true)
+}
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
index 9715a39..bfc999f 100644
--- a/plumbing/transport/common.go
+++ b/plumbing/transport/common.go
@@ -21,6 +21,7 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
)
var (
@@ -124,3 +125,22 @@ func transformSCPLikeIfNeeded(endpoint string) string {
return endpoint
}
+
+// UnsupportedCapabilities are the capabilities not supported by any client
+// implementation
+var UnsupportedCapabilities = []capability.Capability{
+ capability.MultiACK,
+ capability.MultiACKDetailed,
+ capability.Sideband,
+ capability.Sideband64k,
+ capability.ThinPack,
+}
+
+// FilterUnsupportedCapabilities it filter out all the UnsupportedCapabilities
+// from a capability.List, the intended usage is on the client implementation
+// to filter the capabilities from an AdvRefs message.
+func FilterUnsupportedCapabilities(list *capability.List) {
+ for _, c := range UnsupportedCapabilities {
+ list.Delete(c)
+ }
+}
diff --git a/plumbing/transport/common_test.go b/plumbing/transport/common_test.go
index 9ca4459..e9a5efa 100644
--- a/plumbing/transport/common_test.go
+++ b/plumbing/transport/common_test.go
@@ -3,6 +3,8 @@ package transport
import (
"testing"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+
. "gopkg.in/check.v1"
)
@@ -29,3 +31,11 @@ func (s *SuiteCommon) TestNewEndpointWrongForgat(c *C) {
c.Assert(err, Not(IsNil))
c.Assert(e.Host, Equals, "")
}
+
+func (s *SuiteCommon) TestFilterUnsupportedCapabilities(c *C) {
+ l := capability.NewList()
+ l.Set(capability.MultiACK)
+
+ FilterUnsupportedCapabilities(l)
+ c.Assert(l.Supports(capability.MultiACK), Equals, false)
+}
diff --git a/plumbing/transport/http/fetch_pack.go b/plumbing/transport/http/fetch_pack.go
index 40c3a7f..f250667 100644
--- a/plumbing/transport/http/fetch_pack.go
+++ b/plumbing/transport/http/fetch_pack.go
@@ -70,6 +70,7 @@ func (s *fetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
return nil, err
}
+ transport.FilterUnsupportedCapabilities(ar.Capabilities)
return ar, nil
}
@@ -78,6 +79,10 @@ func (s *fetchPackSession) FetchPack(r *packp.UploadPackRequest) (io.ReadCloser,
return nil, transport.ErrEmptyUploadPackRequest
}
+ if err := r.Validate(); err != nil {
+ return nil, err
+ }
+
url := fmt.Sprintf(
"%s/%s",
s.endpoint.String(), transport.UploadPackServiceName,
diff --git a/plumbing/transport/http/fetch_pack_test.go b/plumbing/transport/http/fetch_pack_test.go
index 7471208..920b623 100644
--- a/plumbing/transport/http/fetch_pack_test.go
+++ b/plumbing/transport/http/fetch_pack_test.go
@@ -43,9 +43,9 @@ func (s *FetchPackSuite) TestInfoNotExists(c *C) {
func (s *FetchPackSuite) TestuploadPackRequestToReader(c *C) {
r := packp.NewUploadPackRequest()
- r.Want(plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
- r.Want(plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
- r.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ r.Wants = append(r.Wants, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
+ r.Wants = append(r.Wants, plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
+ r.Haves = append(r.Haves, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
sr, err := uploadPackRequestToReader(r)
c.Assert(err, IsNil)
diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go
index 10e395e..56edab0 100644
--- a/plumbing/transport/internal/common/common.go
+++ b/plumbing/transport/internal/common/common.go
@@ -162,6 +162,7 @@ func (s *session) AdvertisedReferences() (*packp.AdvRefs, error) {
return nil, err
}
+ transport.FilterUnsupportedCapabilities(ar.Capabilities)
return ar, nil
}
@@ -172,6 +173,10 @@ func (s *session) FetchPack(req *packp.UploadPackRequest) (io.ReadCloser, error)
return nil, transport.ErrEmptyUploadPackRequest
}
+ if err := req.Validate(); err != nil {
+ return nil, err
+ }
+
if !s.advRefsRun {
if _, err := s.AdvertisedReferences(); err != nil {
return nil, err
diff --git a/plumbing/transport/test/common.go b/plumbing/transport/test/common.go
index ed2e141..3b7f05f 100644
--- a/plumbing/transport/test/common.go
+++ b/plumbing/transport/test/common.go
@@ -43,7 +43,7 @@ func (s *FetchPackSuite) TestInfoNotExists(c *C) {
r, err = s.Client.NewFetchPackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
reader, err := r.FetchPack(req)
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
c.Assert(reader, IsNil)
@@ -70,6 +70,16 @@ func (s *FetchPackSuite) TestDefaultBranch(c *C) {
c.Assert(symrefs[0], Equals, "HEAD:refs/heads/master")
}
+func (s *FetchPackSuite) TestAdvertisedReferencesFilterUnsupported(c *C) {
+ r, err := s.Client.NewFetchPackSession(s.Endpoint)
+ c.Assert(err, IsNil)
+ defer func() { c.Assert(r.Close(), IsNil) }()
+
+ info, err := r.AdvertisedReferences()
+ c.Assert(err, IsNil)
+ c.Assert(info.Capabilities.Supports(capability.MultiACK), Equals, false)
+}
+
func (s *FetchPackSuite) TestCapabilities(c *C) {
r, err := s.Client.NewFetchPackSession(s.Endpoint)
c.Assert(err, IsNil)
@@ -90,7 +100,7 @@ func (s *FetchPackSuite) TestFullFetchPack(c *C) {
c.Assert(info, NotNil)
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
reader, err := r.FetchPack(req)
c.Assert(err, IsNil)
@@ -104,7 +114,7 @@ func (s *FetchPackSuite) TestFetchPack(c *C) {
defer func() { c.Assert(r.Close(), IsNil) }()
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
reader, err := r.FetchPack(req)
c.Assert(err, IsNil)
@@ -112,14 +122,28 @@ func (s *FetchPackSuite) TestFetchPack(c *C) {
s.checkObjectNumber(c, reader, 28)
}
+func (s *FetchPackSuite) TestFetchPackInvalidReq(c *C) {
+ r, err := s.Client.NewFetchPackSession(s.Endpoint)
+ c.Assert(err, IsNil)
+ defer func() { c.Assert(r.Close(), IsNil) }()
+
+ req := packp.NewUploadPackRequest()
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Capabilities.Set(capability.Sideband)
+ req.Capabilities.Set(capability.Sideband64k)
+
+ _, err = r.FetchPack(req)
+ c.Assert(err, NotNil)
+}
+
func (s *FetchPackSuite) TestFetchPackNoChanges(c *C) {
r, err := s.Client.NewFetchPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- req.Have(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Haves = append(req.Haves, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
reader, err := r.FetchPack(req)
c.Assert(err, Equals, transport.ErrEmptyUploadPackRequest)
@@ -132,8 +156,8 @@ func (s *FetchPackSuite) TestFetchPackMulti(c *C) {
defer func() { c.Assert(r.Close(), IsNil) }()
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- req.Want(plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"))
+ req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
+ req.Wants = append(req.Wants, plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"))
reader, err := r.FetchPack(req)
c.Assert(err, IsNil)
@@ -146,7 +170,7 @@ func (s *FetchPackSuite) TestFetchError(c *C) {
c.Assert(err, IsNil)
req := packp.NewUploadPackRequest()
- req.Want(plumbing.NewHash("1111111111111111111111111111111111111111"))
+ req.Wants = append(req.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
reader, err := r.FetchPack(req)
c.Assert(err, Equals, transport.ErrEmptyUploadPackRequest)