aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/protocol/packp/updreq_decode.go
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing/protocol/packp/updreq_decode.go')
-rw-r--r--plumbing/protocol/packp/updreq_decode.go231
1 files changed, 231 insertions, 0 deletions
diff --git a/plumbing/protocol/packp/updreq_decode.go b/plumbing/protocol/packp/updreq_decode.go
new file mode 100644
index 0000000..0740871
--- /dev/null
+++ b/plumbing/protocol/packp/updreq_decode.go
@@ -0,0 +1,231 @@
+package packp
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
+)
+
+var (
+ shallowLineLength = len(shallow) + hashSize
+ minCommandLength = hashSize*2 + 2 + 1
+ minCommandAndCapsLenth = minCommandLength + 1
+)
+
+var (
+ ErrEmpty = errors.New("empty update-request message")
+ errNoCommands = errors.New("unexpected EOF before any command")
+ errMissingCapabilitiesDelimiter = errors.New("capabilities delimiter not found")
+)
+
+func errMalformedRequest(reason string) error {
+ return fmt.Errorf("malformed request: %s", reason)
+}
+
+func errInvalidHashSize(got int) error {
+ return fmt.Errorf("invalid hash size: expected %d, got %d",
+ hashSize, got)
+}
+
+func errInvalidHash(err error) error {
+ return fmt.Errorf("invalid hash: %s", err.Error())
+}
+
+func errInvalidShallowLineLength(got int) error {
+ return errMalformedRequest(fmt.Sprintf(
+ "invalid shallow line length: expected %d, got %d",
+ shallowLineLength, got))
+}
+
+func errInvalidCommandCapabilitiesLineLength(got int) error {
+ return errMalformedRequest(fmt.Sprintf(
+ "invalid command and capabilities line length: expected at least %d, got %d",
+ minCommandAndCapsLenth, got))
+}
+
+func errInvalidCommandLineLength(got int) error {
+ return errMalformedRequest(fmt.Sprintf(
+ "invalid command line length: expected at least %d, got %d",
+ minCommandLength, got))
+}
+
+func errInvalidShallowObjId(err error) error {
+ return errMalformedRequest(
+ fmt.Sprintf("invalid shallow object id: %s", err.Error()))
+}
+
+func errInvalidOldObjId(err error) error {
+ return errMalformedRequest(
+ fmt.Sprintf("invalid old object id: %s", err.Error()))
+}
+
+func errInvalidNewObjId(err error) error {
+ return errMalformedRequest(
+ fmt.Sprintf("invalid new object id: %s", err.Error()))
+}
+
+func errMalformedCommand(err error) error {
+ return errMalformedRequest(fmt.Sprintf(
+ "malformed command: %s", err.Error()))
+}
+
+// Decode reads the next update-request message form the reader and wr
+func (req *ReferenceUpdateRequest) Decode(r io.Reader) error {
+ d := &updReqDecoder{s: pktline.NewScanner(r)}
+ return d.Decode(req)
+}
+
+type updReqDecoder struct {
+ s *pktline.Scanner
+ r *ReferenceUpdateRequest
+}
+
+func (d *updReqDecoder) Decode(r *ReferenceUpdateRequest) error {
+ d.r = r
+ funcs := []func() error{
+ d.scanLine,
+ d.decodeShallow,
+ d.decodeCommandAndCapabilities,
+ d.decodeCommands,
+ r.validate,
+ }
+
+ for _, f := range funcs {
+ if err := f(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (d *updReqDecoder) scanLine() error {
+ if ok := d.s.Scan(); !ok {
+ return d.scanErrorOr(ErrEmpty)
+ }
+
+ return nil
+}
+
+func (d *updReqDecoder) decodeShallow() error {
+ b := d.s.Bytes()
+
+ if !bytes.HasPrefix(b, shallowNoSp) {
+ return nil
+ }
+
+ if len(b) != shallowLineLength {
+ return errInvalidShallowLineLength(len(b))
+ }
+
+ h, err := parseHash(string(b[len(shallow):]))
+ if err != nil {
+ return errInvalidShallowObjId(err)
+ }
+
+ if ok := d.s.Scan(); !ok {
+ return d.scanErrorOr(errNoCommands)
+ }
+
+ d.r.Shallow = &h
+
+ return nil
+}
+
+func (d *updReqDecoder) decodeCommands() error {
+ for {
+ b := d.s.Bytes()
+ if bytes.Equal(b, pktline.Flush) {
+ return nil
+ }
+
+ c, err := parseCommand(b)
+ if err != nil {
+ return err
+ }
+
+ d.r.Commands = append(d.r.Commands, c)
+
+ if ok := d.s.Scan(); !ok {
+ return d.s.Err()
+ }
+ }
+}
+
+func (d *updReqDecoder) decodeCommandAndCapabilities() error {
+ b := d.s.Bytes()
+ i := bytes.IndexByte(b, 0)
+ if i == -1 {
+ return errMissingCapabilitiesDelimiter
+ }
+
+ if len(b) < minCommandAndCapsLenth {
+ return errInvalidCommandCapabilitiesLineLength(len(b))
+ }
+
+ cmd, err := parseCommand(b[:i])
+ if err != nil {
+ return err
+ }
+
+ d.r.Commands = append(d.r.Commands, cmd)
+
+ if err := d.r.Capabilities.Decode(b[i+1:]); err != nil {
+ return err
+ }
+
+ if err := d.scanLine(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func parseCommand(b []byte) (*Command, error) {
+ if len(b) < minCommandLength {
+ return nil, errInvalidCommandLineLength(len(b))
+ }
+
+ var os, ns, n string
+ if _, err := fmt.Sscanf(string(b), "%s %s %s", &os, &ns, &n); err != nil {
+ return nil, errMalformedCommand(err)
+ }
+
+ oh, err := parseHash(os)
+ if err != nil {
+ return nil, errInvalidOldObjId(err)
+ }
+
+ nh, err := parseHash(ns)
+ if err != nil {
+ return nil, errInvalidNewObjId(err)
+ }
+
+ return &Command{Old: oh, New: nh, Name: n}, nil
+}
+
+func parseHash(s string) (plumbing.Hash, error) {
+ if len(s) != hashSize {
+ return plumbing.ZeroHash, errInvalidHashSize(len(s))
+ }
+
+ if _, err := hex.DecodeString(s); err != nil {
+ return plumbing.ZeroHash, errInvalidHash(err)
+ }
+
+ h := plumbing.NewHash(s)
+ return h, nil
+}
+
+func (d *updReqDecoder) scanErrorOr(origErr error) error {
+ if err := d.s.Err(); err != nil {
+ return err
+ }
+
+ return origErr
+}