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

Commit 09cd5cc

Browse files
authored
Merge pull request #469 from mcuadros/fix-multiple-ack
plumbing: protocol, fix handling multiple ACK on upload-pack
2 parents 6b69a16 + a3dc077 commit 09cd5cc

File tree

4 files changed

+76
-17
lines changed

4 files changed

+76
-17
lines changed

plumbing/protocol/packp/srvresp.go

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package packp
22

33
import (
4+
"bufio"
45
"bytes"
56
"errors"
67
"fmt"
@@ -18,8 +19,8 @@ type ServerResponse struct {
1819
}
1920

2021
// Decode decodes the response into the struct, isMultiACK should be true, if
21-
// the request was done with multi_ack or multi_ack_detailed capabilities
22-
func (r *ServerResponse) Decode(reader io.Reader, isMultiACK bool) error {
22+
// the request was done with multi_ack or multi_ack_detailed capabilities.
23+
func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
2324
// TODO: implement support for multi_ack or multi_ack_detailed responses
2425
if isMultiACK {
2526
return errors.New("multi_ack and multi_ack_detailed are not supported")
@@ -34,14 +35,56 @@ func (r *ServerResponse) Decode(reader io.Reader, isMultiACK bool) error {
3435
return err
3536
}
3637

37-
if !isMultiACK {
38+
// we need to detect when the end of a response header and the begining
39+
// of a packfile header happend, some requests to the git daemon
40+
// produces a duplicate ACK header even when multi_ack is not supported.
41+
isEnd, err := r.endReached(reader)
42+
if err != nil {
43+
return err
44+
}
45+
46+
if isEnd {
3847
break
3948
}
4049
}
4150

4251
return s.Err()
4352
}
4453

54+
func (r *ServerResponse) endReached(reader *bufio.Reader) (bool, error) {
55+
isPack, err := r.isPACKHeader(reader)
56+
if err == io.EOF {
57+
return true, nil
58+
}
59+
60+
return isPack, err
61+
62+
}
63+
64+
// isPACKHeader detects when a header of a packfile is found, with this goal
65+
// the function is reading from the reader without moving the read pointer.
66+
func (r *ServerResponse) isPACKHeader(reader *bufio.Reader) (bool, error) {
67+
ahead, err := reader.Peek(9)
68+
if err != nil {
69+
return false, err
70+
}
71+
72+
if len(ahead) == 0 {
73+
return true, nil
74+
}
75+
76+
if len(ahead) > 4 && string(ahead[0:4]) == "PACK" {
77+
return true, nil
78+
}
79+
80+
if len(ahead) == 9 && string(ahead[5:]) == "PACK" {
81+
return true, nil
82+
}
83+
84+
return true, nil
85+
86+
}
87+
4588
func (r *ServerResponse) decodeLine(line []byte) error {
4689
if len(line) == 0 {
4790
return fmt.Errorf("unexpected flush")

plumbing/protocol/packp/srvresp_test.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package packp
22

33
import (
4+
"bufio"
45
"bytes"
56

67
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -16,7 +17,7 @@ func (s *ServerResponseSuite) TestDecodeNAK(c *C) {
1617
raw := "0008NAK\n"
1718

1819
sr := &ServerResponse{}
19-
err := sr.Decode(bytes.NewBufferString(raw), false)
20+
err := sr.Decode(bufio.NewReader(bytes.NewBufferString(raw)), false)
2021
c.Assert(err, IsNil)
2122

2223
c.Assert(sr.ACKs, HasLen, 0)
@@ -26,7 +27,18 @@ func (s *ServerResponseSuite) TestDecodeACK(c *C) {
2627
raw := "0031ACK 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n"
2728

2829
sr := &ServerResponse{}
29-
err := sr.Decode(bytes.NewBufferString(raw), false)
30+
err := sr.Decode(bufio.NewReader(bytes.NewBufferString(raw)), false)
31+
c.Assert(err, IsNil)
32+
33+
c.Assert(sr.ACKs, HasLen, 1)
34+
c.Assert(sr.ACKs[0], Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
35+
}
36+
37+
func (s *ServerResponseSuite) TestDecodeMultipleACK(c *C) {
38+
raw := "0031ACK 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n0031ACK 6ecf0ef2c2dffb796033e5a02219af86ec6584e5\n"
39+
40+
sr := &ServerResponse{}
41+
err := sr.Decode(bufio.NewReader(bytes.NewBufferString(raw)), false)
3042
c.Assert(err, IsNil)
3143

3244
c.Assert(sr.ACKs, HasLen, 1)
@@ -37,12 +49,12 @@ func (s *ServerResponseSuite) TestDecodeMalformed(c *C) {
3749
raw := "0029ACK 6ecf0ef2c2dffb796033e5a02219af86ec6584e\n"
3850

3951
sr := &ServerResponse{}
40-
err := sr.Decode(bytes.NewBufferString(raw), false)
52+
err := sr.Decode(bufio.NewReader(bytes.NewBufferString(raw)), false)
4153
c.Assert(err, NotNil)
4254
}
4355

4456
func (s *ServerResponseSuite) TestDecodeMultiACK(c *C) {
4557
sr := &ServerResponse{}
46-
err := sr.Decode(bytes.NewBuffer(nil), true)
58+
err := sr.Decode(bufio.NewReader(bytes.NewBuffer(nil)), true)
4759
c.Assert(err, NotNil)
4860
}

plumbing/protocol/packp/uppackresp.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"errors"
55
"io"
66

7+
"bufio"
8+
79
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
810
"gopkg.in/src-d/go-git.v4/utils/ioutil"
911
)
@@ -51,18 +53,20 @@ func NewUploadPackResponseWithPackfile(req *UploadPackRequest,
5153
// Decode decodes all the responses sent by upload-pack service into the struct
5254
// and prepares it to read the packfile using the Read method
5355
func (r *UploadPackResponse) Decode(reader io.ReadCloser) error {
56+
buf := bufio.NewReader(reader)
57+
5458
if r.isShallow {
55-
if err := r.ShallowUpdate.Decode(reader); err != nil {
59+
if err := r.ShallowUpdate.Decode(buf); err != nil {
5660
return err
5761
}
5862
}
5963

60-
if err := r.ServerResponse.Decode(reader, r.isMultiACK); err != nil {
64+
if err := r.ServerResponse.Decode(buf, r.isMultiACK); err != nil {
6165
return err
6266
}
6367

6468
// now the reader is ready to read the packfile content
65-
r.r = reader
69+
r.r = ioutil.NewReadCloser(buf, reader)
6670

6771
return nil
6872
}

plumbing/protocol/packp/uppackresp_test.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type UploadPackResponseSuite struct{}
1515
var _ = Suite(&UploadPackResponseSuite{})
1616

1717
func (s *UploadPackResponseSuite) TestDecodeNAK(c *C) {
18-
raw := "0008NAK\n[PACK]"
18+
raw := "0008NAK\nPACK"
1919

2020
req := NewUploadPackRequest()
2121
res := NewUploadPackResponse(req)
@@ -26,11 +26,11 @@ func (s *UploadPackResponseSuite) TestDecodeNAK(c *C) {
2626

2727
pack, err := ioutil.ReadAll(res)
2828
c.Assert(err, IsNil)
29-
c.Assert(pack, DeepEquals, []byte("[PACK]"))
29+
c.Assert(pack, DeepEquals, []byte("PACK"))
3030
}
3131

3232
func (s *UploadPackResponseSuite) TestDecodeDepth(c *C) {
33-
raw := "00000008NAK\n[PACK]"
33+
raw := "00000008NAK\nPACK"
3434

3535
req := NewUploadPackRequest()
3636
req.Depth = DepthCommits(1)
@@ -43,11 +43,11 @@ func (s *UploadPackResponseSuite) TestDecodeDepth(c *C) {
4343

4444
pack, err := ioutil.ReadAll(res)
4545
c.Assert(err, IsNil)
46-
c.Assert(pack, DeepEquals, []byte("[PACK]"))
46+
c.Assert(pack, DeepEquals, []byte("PACK"))
4747
}
4848

4949
func (s *UploadPackResponseSuite) TestDecodeMalformed(c *C) {
50-
raw := "00000008ACK\n[PACK]"
50+
raw := "00000008ACK\nPACK"
5151

5252
req := NewUploadPackRequest()
5353
req.Depth = DepthCommits(1)
@@ -96,7 +96,7 @@ func (s *UploadPackResponseSuite) TestEncodeNAK(c *C) {
9696
}
9797

9898
func (s *UploadPackResponseSuite) TestEncodeDepth(c *C) {
99-
pf := ioutil.NopCloser(bytes.NewBuffer([]byte("[PACK]")))
99+
pf := ioutil.NopCloser(bytes.NewBuffer([]byte("PACK")))
100100
req := NewUploadPackRequest()
101101
req.Depth = DepthCommits(1)
102102

@@ -106,7 +106,7 @@ func (s *UploadPackResponseSuite) TestEncodeDepth(c *C) {
106106
b := bytes.NewBuffer(nil)
107107
c.Assert(res.Encode(b), IsNil)
108108

109-
expected := "00000008NAK\n[PACK]"
109+
expected := "00000008NAK\nPACK"
110110
c.Assert(string(b.Bytes()), Equals, expected)
111111
}
112112

0 commit comments

Comments
 (0)