Skip to content

Commit

Permalink
plumbing: protocol/packp, Decode push options in update request
Browse files Browse the repository at this point in the history
Signed-off-by: Nikolas Sepos <nikolas.sepos@gmail.com>
  • Loading branch information
thegrumpylion committed Dec 13, 2023
1 parent 952f1ba commit eae2f12
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
50 changes: 50 additions & 0 deletions plumbing/protocol/packp/updreq_decode.go
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
)

var (
Expand Down Expand Up @@ -74,6 +75,11 @@ func errMalformedCommand(err error) error {
"malformed command: %s", err.Error()))
}

func errInvalidPushOption(err error) error {
return errMalformedRequest(fmt.Sprintf(
"invalid push option: %s", err.Error()))
}

// Decode reads the next update-request message form the reader and wr
func (req *ReferenceUpdateRequest) Decode(r io.Reader) error {
var rc io.ReadCloser
Expand All @@ -100,6 +106,7 @@ func (d *updReqDecoder) Decode(req *ReferenceUpdateRequest) error {
d.decodeShallow,
d.decodeCommandAndCapabilities,
d.decodeCommands,
d.decodePushOptions,
d.setPackfile,
req.validate,
}
Expand Down Expand Up @@ -201,6 +208,35 @@ func (d *updReqDecoder) setPackfile() error {
return nil
}

func (d *updReqDecoder) decodePushOptions() error {
if !d.req.Capabilities.Supports(capability.PushOptions) {
return nil
}

if ok := d.s.Scan(); !ok {
return d.s.Err()
}

for {
b := d.s.Bytes()

if bytes.Equal(b, pktline.Flush) {
return nil
}

o, err := parsePushOption(b)
if err != nil {
return err
}

d.req.Options = append(d.req.Options, o)

if ok := d.s.Scan(); !ok {
return d.s.Err()
}
}
}

func parseCommand(b []byte) (*Command, error) {
if len(b) < minCommandLength {
return nil, errInvalidCommandLineLength(len(b))
Expand Down Expand Up @@ -247,3 +283,17 @@ func (d *updReqDecoder) scanErrorOr(origErr error) error {

return origErr
}

func parsePushOption(b []byte) (*Option, error) {
i := bytes.IndexByte(b, '=')
if i == -1 {
return &Option{Key: string(b)}, nil
}
if i == 0 {
return nil, errInvalidPushOption(errors.New("empty option key"))
}
if i == len(b)-1 {
return &Option{Key: string(b[:i])}, nil
}
return &Option{Key: string(b[:i]), Value: string(b[i+1:])}, nil
}
44 changes: 44 additions & 0 deletions plumbing/protocol/packp/updreq_decode_test.go
Expand Up @@ -260,6 +260,50 @@ func (s *UpdReqDecodeSuite) TestWithPackfile(c *C) {
s.testDecodeOkRaw(c, expected, buf.Bytes())
}

func (s *UpdReqDecodeSuite) TestPushOptions(c *C) {
hash1 := plumbing.NewHash("1ecf0ef2c2dffb796033e5a02219af86ec6584e5")
hash2 := plumbing.NewHash("2ecf0ef2c2dffb796033e5a02219af86ec6584e5")

expected := NewReferenceUpdateRequest()
expected.Commands = []*Command{
{Name: plumbing.ReferenceName("myref1"), Old: hash1, New: hash2},
}
expected.Capabilities.Add("push-options")
expected.Options = append(expected.Options, &Option{Key: "key1", Value: "value1"})
expected.Options = append(expected.Options, &Option{Key: "key2", Value: "value2"})
expected.Options = append(expected.Options, &Option{Key: "key3", Value: ""})
expected.Options = append(expected.Options, &Option{Key: "key4", Value: ""})
expected.Packfile = io.NopCloser(bytes.NewReader([]byte{}))

payloads := []string{
"1ecf0ef2c2dffb796033e5a02219af86ec6584e5 2ecf0ef2c2dffb796033e5a02219af86ec6584e5 myref1\x00push-options",
pktline.FlushString,
"key1=value1",
"key2=value2",
"key3",
"key4=",
pktline.FlushString,
}

s.testDecodeOkExpected(c, expected, payloads)
}

func (s *UpdReqDecodeSuite) TestInvalidPushOptionMissingKey(c *C) {
payloads := []string{
"1ecf0ef2c2dffb796033e5a02219af86ec6584e5 2ecf0ef2c2dffb796033e5a02219af86ec6584e5 myref1\x00push-options",
pktline.FlushString,
"=value",
pktline.FlushString,
}

var buf bytes.Buffer
e := pktline.NewEncoder(&buf)
err := e.EncodeString(payloads...)
c.Assert(err, IsNil)

s.testDecoderErrorMatches(c, &buf, "^malformed request: invalid push option: empty option key")
}

func (s *UpdReqDecodeSuite) testDecoderErrorMatches(c *C, input io.Reader, pattern string) {
r := NewReferenceUpdateRequest()
c.Assert(r.Decode(input), ErrorMatches, pattern)
Expand Down
4 changes: 4 additions & 0 deletions plumbing/transport/server/server.go
Expand Up @@ -367,6 +367,10 @@ func (*rpSession) setSupportedCapabilities(c *capability.List) error {
return err
}

if err := c.Set(capability.PushOptions); err != nil {
return err
}

return c.Set(capability.ReportStatus)
}

Expand Down

0 comments on commit eae2f12

Please sign in to comment.