Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c15bf1d

Browse files
authored
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
1 parent 7de79ae commit c15bf1d

File tree

17 files changed

+478
-102
lines changed

17 files changed

+478
-102
lines changed

common_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ func (c *MockFetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
119119
func (c *MockFetchPackSession) FetchPack(
120120
r *packp.UploadPackRequest) (io.ReadCloser, error) {
121121

122+
if !r.Capabilities.Supports(capability.Agent) {
123+
return nil, fmt.Errorf("" +
124+
"invalid test rquest, missing Agent capability, the request" +
125+
"should be created using NewUploadPackRequestFromCapabilities",
126+
)
127+
}
128+
122129
f := fixtures.ByURL(c.endpoint.String())
123130

124131
if len(r.Wants) == 1 {

plumbing/protocol/packp/advrefs_test.go

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,89 @@
1-
package packp_test
1+
package packp
22

33
import (
44
"bytes"
55
"fmt"
66
"io"
77
"strings"
8-
"testing"
98

109
"gopkg.in/src-d/go-git.v4/plumbing"
1110
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
12-
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
1311
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
1412

1513
. "gopkg.in/check.v1"
1614
)
1715

18-
func Test(t *testing.T) { TestingT(t) }
16+
type AdvRefSuite struct{}
1917

20-
type SuiteDecodeEncode struct{}
18+
var _ = Suite(&AdvRefSuite{})
2119

22-
var _ = Suite(&SuiteDecodeEncode{})
20+
func (s *AdvRefSuite) TestAddReferenceSymbolic(c *C) {
21+
ref := plumbing.NewSymbolicReference("foo", "bar")
2322

24-
func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
23+
a := NewAdvRefs()
24+
err := a.AddReference(ref)
25+
c.Assert(err, IsNil)
26+
27+
values := a.Capabilities.Get(capability.SymRef)
28+
c.Assert(values, HasLen, 1)
29+
c.Assert(values[0], Equals, "foo:bar")
30+
}
31+
32+
func (s *AdvRefSuite) TestAddReferenceHash(c *C) {
33+
ref := plumbing.NewHashReference("foo", plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c"))
34+
35+
a := NewAdvRefs()
36+
err := a.AddReference(ref)
37+
c.Assert(err, IsNil)
38+
39+
c.Assert(a.References, HasLen, 1)
40+
c.Assert(a.References["foo"].String(), Equals, "5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
41+
}
42+
43+
func (s *AdvRefSuite) TestAllReferences(c *C) {
44+
hash := plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c")
45+
46+
a := NewAdvRefs()
47+
err := a.AddReference(plumbing.NewSymbolicReference("foo", "bar"))
48+
c.Assert(err, IsNil)
49+
err = a.AddReference(plumbing.NewHashReference("bar", hash))
50+
c.Assert(err, IsNil)
51+
52+
refs, err := a.AllReferences()
53+
c.Assert(err, IsNil)
54+
55+
iter, err := refs.IterReferences()
56+
c.Assert(err, IsNil)
57+
58+
var count int
59+
iter.ForEach(func(ref *plumbing.Reference) error {
60+
count++
61+
switch ref.Name() {
62+
case "bar":
63+
c.Assert(ref.Hash(), Equals, hash)
64+
case "foo":
65+
c.Assert(ref.Target().String(), Equals, "bar")
66+
}
67+
return nil
68+
})
69+
70+
c.Assert(count, Equals, 2)
71+
}
72+
73+
func (s *AdvRefSuite) TestAllReferencesBadSymref(c *C) {
74+
a := NewAdvRefs()
75+
err := a.Capabilities.Set(capability.SymRef, "foo")
76+
c.Assert(err, IsNil)
77+
78+
_, err = a.AllReferences()
79+
c.Assert(err, NotNil)
80+
}
81+
82+
type AdvRefsDecodeEncodeSuite struct{}
83+
84+
var _ = Suite(&AdvRefsDecodeEncodeSuite{})
85+
86+
func (s *AdvRefsDecodeEncodeSuite) test(c *C, in []string, exp []string) {
2587
var err error
2688
var input io.Reader
2789
{
@@ -44,7 +106,7 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
44106

45107
var obtained []byte
46108
{
47-
ar := packp.NewAdvRefs()
109+
ar := NewAdvRefs()
48110
c.Assert(ar.Decode(input), IsNil)
49111

50112
var buf bytes.Buffer
@@ -56,7 +118,7 @@ func (s *SuiteDecodeEncode) test(c *C, in []string, exp []string) {
56118
c.Assert(string(obtained), DeepEquals, string(expected))
57119
}
58120

59-
func (s *SuiteDecodeEncode) TestNoHead(c *C) {
121+
func (s *AdvRefsDecodeEncodeSuite) TestNoHead(c *C) {
60122
input := []string{
61123
"0000000000000000000000000000000000000000 capabilities^{}\x00",
62124
pktline.FlushString,
@@ -70,7 +132,7 @@ func (s *SuiteDecodeEncode) TestNoHead(c *C) {
70132
s.test(c, input, expected)
71133
}
72134

73-
func (s *SuiteDecodeEncode) TestNoHeadSmart(c *C) {
135+
func (s *AdvRefsDecodeEncodeSuite) TestNoHeadSmart(c *C) {
74136
input := []string{
75137
"# service=git-upload-pack\n",
76138
"0000000000000000000000000000000000000000 capabilities^{}\x00",
@@ -86,7 +148,7 @@ func (s *SuiteDecodeEncode) TestNoHeadSmart(c *C) {
86148
s.test(c, input, expected)
87149
}
88150

89-
func (s *SuiteDecodeEncode) TestNoHeadSmartBug(c *C) {
151+
func (s *AdvRefsDecodeEncodeSuite) TestNoHeadSmartBug(c *C) {
90152
input := []string{
91153
"# service=git-upload-pack\n",
92154
pktline.FlushString,
@@ -104,7 +166,7 @@ func (s *SuiteDecodeEncode) TestNoHeadSmartBug(c *C) {
104166
s.test(c, input, expected)
105167
}
106168

107-
func (s *SuiteDecodeEncode) TestRefs(c *C) {
169+
func (s *AdvRefsDecodeEncodeSuite) TestRefs(c *C) {
108170
input := []string{
109171
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack",
110172
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master",
@@ -124,7 +186,7 @@ func (s *SuiteDecodeEncode) TestRefs(c *C) {
124186
s.test(c, input, expected)
125187
}
126188

127-
func (s *SuiteDecodeEncode) TestPeeled(c *C) {
189+
func (s *AdvRefsDecodeEncodeSuite) TestPeeled(c *C) {
128190
input := []string{
129191
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack",
130192
"7777777777777777777777777777777777777777 refs/tags/v2.6.12-tree\n",
@@ -148,7 +210,7 @@ func (s *SuiteDecodeEncode) TestPeeled(c *C) {
148210
s.test(c, input, expected)
149211
}
150212

151-
func (s *SuiteDecodeEncode) TestAll(c *C) {
213+
func (s *AdvRefsDecodeEncodeSuite) TestAll(c *C) {
152214
input := []string{
153215
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEAD\x00symref=HEAD:/refs/heads/master ofs-delta multi_ack\n",
154216
"a6930aaee06755d1bdcfd943fbf614e4d92bb0c7 refs/heads/master\n",
@@ -176,7 +238,7 @@ func (s *SuiteDecodeEncode) TestAll(c *C) {
176238
s.test(c, input, expected)
177239
}
178240

179-
func (s *SuiteDecodeEncode) TestAllSmart(c *C) {
241+
func (s *AdvRefsDecodeEncodeSuite) TestAllSmart(c *C) {
180242
input := []string{
181243
"# service=git-upload-pack\n",
182244
pktline.FlushString,
@@ -208,7 +270,7 @@ func (s *SuiteDecodeEncode) TestAllSmart(c *C) {
208270
s.test(c, input, expected)
209271
}
210272

211-
func (s *SuiteDecodeEncode) TestAllSmartBug(c *C) {
273+
func (s *AdvRefsDecodeEncodeSuite) TestAllSmartBug(c *C) {
212274
input := []string{
213275
"# service=git-upload-pack\n",
214276
pktline.FlushString,
@@ -254,7 +316,7 @@ func ExampleDecoder_Decode() {
254316
input := strings.NewReader(raw)
255317

256318
// Decode the input into a newly allocated AdvRefs value.
257-
ar := packp.NewAdvRefs()
319+
ar := NewAdvRefs()
258320
_ = ar.Decode(input) // error check ignored for brevity
259321

260322
// Do something interesting with the AdvRefs, e.g. print its contents.
@@ -270,7 +332,7 @@ func ExampleDecoder_Decode() {
270332

271333
func ExampleEncoder_Encode() {
272334
// Create an AdvRefs with the contents you want...
273-
ar := packp.NewAdvRefs()
335+
ar := NewAdvRefs()
274336

275337
// ...add a hash for the HEAD...
276338
head := plumbing.NewHash("1111111111111111111111111111111111111111")

plumbing/protocol/packp/capability/list.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ func (l *List) Supports(capability Capability) bool {
145145
return ok
146146
}
147147

148+
// Delete deletes a capability from the List
149+
func (l *List) Delete(capability Capability) {
150+
if !l.Supports(capability) {
151+
return
152+
}
153+
154+
delete(l.m, capability)
155+
for i, c := range l.sort {
156+
if c != string(capability) {
157+
continue
158+
}
159+
160+
l.sort = append(l.sort[:i], l.sort[i+1:]...)
161+
return
162+
}
163+
}
164+
148165
// String generates the capabilities strings, the capabilities are sorted in
149166
// insertion order
150167
func (l *List) String() string {

plumbing/protocol/packp/capability/list_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,22 @@ func (s *SuiteCapabilities) TestGetEmpty(c *check.C) {
9797
c.Assert(cap.Get(Agent), check.HasLen, 0)
9898
}
9999

100+
func (s *SuiteCapabilities) TestDelete(c *check.C) {
101+
cap := NewList()
102+
cap.Delete(SymRef)
103+
104+
err := cap.Add(Sideband)
105+
c.Assert(err, check.IsNil)
106+
err = cap.Set(SymRef, "bar")
107+
c.Assert(err, check.IsNil)
108+
err = cap.Set(Sideband64k)
109+
c.Assert(err, check.IsNil)
110+
111+
cap.Delete(SymRef)
112+
113+
c.Assert(cap.String(), check.Equals, "side-band side-band-64k")
114+
}
115+
100116
func (s *SuiteCapabilities) TestAdd(c *check.C) {
101117
cap := NewList()
102118
err := cap.Add(SymRef, "foo", "qux")

plumbing/protocol/packp/ulreq.go

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package packp
22

33
import (
4+
"fmt"
45
"time"
56

67
"gopkg.in/src-d/go-git.v4/plumbing"
78
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
89
)
910

11+
const DefaultAgent = "go-git/4.x"
12+
1013
// UploadRequest values represent the information transmitted on a
1114
// upload-request message. Values from this type are not zero-value
1215
// safe, use the New function instead.
@@ -41,10 +44,9 @@ type DepthReference string
4144

4245
func (d DepthReference) isDepth() {}
4346

44-
// NewUploadRequest returns a pointer to a new UlReq value, ready to be used. It has
45-
// no capabilities, wants or shallows and an infinite depth. Please
46-
// note that to encode an upload-request it has to have at least one
47-
// wanted hash.
47+
// NewUploadRequest returns a pointer to a new UploadRequest value, ready to be
48+
// used. It has no capabilities, wants or shallows and an infinite depth. Please
49+
// note that to encode an upload-request it has to have at least one wanted hash.
4850
func NewUploadRequest() *UploadRequest {
4951
return &UploadRequest{
5052
Capabilities: capability.NewList(),
@@ -54,7 +56,96 @@ func NewUploadRequest() *UploadRequest {
5456
}
5557
}
5658

57-
// Want adds a hash reference to the 'wants' list.
58-
func (r *UploadRequest) Want(h ...plumbing.Hash) {
59-
r.Wants = append(r.Wants, h...)
59+
// NewUploadRequestFromCapabilities returns a pointer to a new UploadRequest
60+
// value, the request capabilities are filled with the most optiomal ones, based
61+
// on the adv value (advertaised capabilities), the UploadRequest generated it
62+
// has no wants or shallows and an infinite depth.
63+
func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest {
64+
r := NewUploadRequest()
65+
66+
if adv.Supports(capability.MultiACKDetailed) {
67+
r.Capabilities.Set(capability.MultiACKDetailed)
68+
} else if adv.Supports(capability.MultiACK) {
69+
r.Capabilities.Set(capability.MultiACK)
70+
}
71+
72+
if adv.Supports(capability.ThinPack) {
73+
r.Capabilities.Set(capability.ThinPack)
74+
}
75+
76+
if adv.Supports(capability.OFSDelta) {
77+
r.Capabilities.Set(capability.OFSDelta)
78+
}
79+
80+
if adv.Supports(capability.Agent) {
81+
r.Capabilities.Set(capability.Agent, DefaultAgent)
82+
}
83+
84+
return r
85+
}
86+
87+
// Validate validates the content of UploadRequest, following the next rules:
88+
// - Wants MUST have at least one reference
89+
// - capability.Shallow MUST be present if Shallows is not empty
90+
// - is a non-zero DepthCommits is given capability.Shallow MUST be present
91+
// - is a DepthSince is given capability.Shallow MUST be present
92+
// - is a DepthReference is given capability.DeepenNot MUST be present
93+
// - MUST contain only maximum of one of capability.Sideband and capability.Sideband64k
94+
// - MUST contain only maximum of one of capability.MultiACK and capability.MultiACKDetailed
95+
func (r *UploadRequest) Validate() error {
96+
if len(r.Wants) == 0 {
97+
return fmt.Errorf("want can't be empty")
98+
}
99+
100+
if err := r.validateRequiredCapabilities(); err != nil {
101+
return err
102+
}
103+
104+
if err := r.validateConflictCapabilities(); err != nil {
105+
return err
106+
}
107+
108+
return nil
109+
}
110+
111+
func (r *UploadRequest) validateRequiredCapabilities() error {
112+
msg := "missing capability %s"
113+
114+
if len(r.Shallows) != 0 && !r.Capabilities.Supports(capability.Shallow) {
115+
return fmt.Errorf(msg, capability.Shallow)
116+
}
117+
118+
switch r.Depth.(type) {
119+
case DepthCommits:
120+
if r.Depth != DepthCommits(0) {
121+
if !r.Capabilities.Supports(capability.Shallow) {
122+
return fmt.Errorf(msg, capability.Shallow)
123+
}
124+
}
125+
case DepthSince:
126+
if !r.Capabilities.Supports(capability.DeepenSince) {
127+
return fmt.Errorf(msg, capability.DeepenSince)
128+
}
129+
case DepthReference:
130+
if !r.Capabilities.Supports(capability.DeepenNot) {
131+
return fmt.Errorf(msg, capability.DeepenNot)
132+
}
133+
}
134+
135+
return nil
136+
}
137+
138+
func (r *UploadRequest) validateConflictCapabilities() error {
139+
msg := "capabilities %s and %s are mutually exclusive"
140+
if r.Capabilities.Supports(capability.Sideband) &&
141+
r.Capabilities.Supports(capability.Sideband64k) {
142+
return fmt.Errorf(msg, capability.Sideband, capability.Sideband64k)
143+
}
144+
145+
if r.Capabilities.Supports(capability.MultiACK) &&
146+
r.Capabilities.Supports(capability.MultiACKDetailed) {
147+
return fmt.Errorf(msg, capability.MultiACK, capability.MultiACKDetailed)
148+
}
149+
150+
return nil
60151
}

plumbing/protocol/packp/ulreq_decode_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88

99
"gopkg.in/src-d/go-git.v4/plumbing"
1010
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
11+
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
1112

1213
. "gopkg.in/check.v1"
13-
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
1414
)
1515

1616
type UlReqDecodeSuite struct{}

0 commit comments

Comments
 (0)