diff options
Diffstat (limited to 'clients/common/common.go')
-rw-r--r-- | clients/common/common.go | 268 |
1 files changed, 62 insertions, 206 deletions
diff --git a/clients/common/common.go b/clients/common/common.go index 0179bfd..c723cdd 100644 --- a/clients/common/common.go +++ b/clients/common/common.go @@ -2,6 +2,7 @@ package common import ( + "bytes" "errors" "fmt" "io" @@ -11,6 +12,8 @@ import ( "strings" "gopkg.in/src-d/go-git.v4/core" + "gopkg.in/src-d/go-git.v4/formats/packp" + "gopkg.in/src-d/go-git.v4/formats/packp/advrefs" "gopkg.in/src-d/go-git.v4/formats/packp/pktline" "gopkg.in/src-d/go-git.v4/storage/memory" ) @@ -75,246 +78,100 @@ func (e *Endpoint) String() string { return u.String() } -// Capabilities contains all the server capabilities -// https://github.com/git/git/blob/master/Documentation/technical/protocol-capabilities.txt -type Capabilities struct { - m map[string]*Capability - o []string -} - -// Capability represents a server capability -type Capability struct { - Name string - Values []string +type GitUploadPackInfo struct { + Capabilities *packp.Capabilities + Refs memory.ReferenceStorage } -// NewCapabilities returns a new Capabilities struct -func NewCapabilities() *Capabilities { - return &Capabilities{ - m: make(map[string]*Capability, 0), - } +func NewGitUploadPackInfo() *GitUploadPackInfo { + return &GitUploadPackInfo{Capabilities: packp.NewCapabilities()} } -// Decode decodes a string -func (c *Capabilities) Decode(raw string) { - params := strings.Split(raw, " ") - for _, p := range params { - s := strings.SplitN(p, "=", 2) - - var value string - if len(s) == 2 { - value = s[1] +func (i *GitUploadPackInfo) Decode(r io.Reader) error { + d := advrefs.NewDecoder(r) + ar := advrefs.New() + if err := d.Decode(ar); err != nil { + if err == advrefs.ErrEmpty { + return core.NewPermanentError(err) } - - c.Add(s[0], value) - } -} - -// Get returns the values for a capability -func (c *Capabilities) Get(capability string) *Capability { - return c.m[capability] -} - -// Set sets a capability removing the values -func (c *Capabilities) Set(capability string, values ...string) { - if _, ok := c.m[capability]; ok { - delete(c.m, capability) + return core.NewUnexpectedError(err) } - c.Add(capability, values...) -} - -// Add adds a capability, values are optional -func (c *Capabilities) Add(capability string, values ...string) { - if !c.Supports(capability) { - c.m[capability] = &Capability{Name: capability} - c.o = append(c.o, capability) - } + i.Capabilities = ar.Capabilities - if len(values) == 0 { - return + if err := i.addRefs(ar); err != nil { + return core.NewUnexpectedError(err) } - c.m[capability].Values = append(c.m[capability].Values, values...) -} - -// Supports returns true if capability is present -func (c *Capabilities) Supports(capability string) bool { - _, ok := c.m[capability] - return ok + return nil } -// SymbolicReference returns the reference for a given symbolic reference -func (c *Capabilities) SymbolicReference(sym string) string { - if !c.Supports("symref") { - return "" - } - - for _, symref := range c.Get("symref").Values { - parts := strings.Split(symref, ":") - if len(parts) != 2 { - continue - } - - if parts[0] == sym { - return parts[1] - } +func (i *GitUploadPackInfo) addRefs(ar *advrefs.AdvRefs) error { + i.Refs = make(memory.ReferenceStorage, 0) + for name, hash := range ar.References { + ref := core.NewReferenceFromStrings(name, hash.String()) + i.Refs.Set(ref) } - return "" + return i.addSymbolicRefs(ar) } -func (c *Capabilities) String() string { - if len(c.o) == 0 { - return "" - } - - var o string - for _, key := range c.o { - cap := c.m[key] - - added := false - for _, value := range cap.Values { - if value == "" { - continue - } - - added = true - o += fmt.Sprintf("%s=%s ", key, value) - } - - if len(cap.Values) == 0 || !added { - o += key + " " - } - } - - if len(o) == 0 { - return o +func (i *GitUploadPackInfo) addSymbolicRefs(ar *advrefs.AdvRefs) error { + if !hasSymrefs(ar) { + return nil } - return o[:len(o)-1] -} - -type GitUploadPackInfo struct { - Capabilities *Capabilities - Refs memory.ReferenceStorage -} - -func NewGitUploadPackInfo() *GitUploadPackInfo { - return &GitUploadPackInfo{Capabilities: NewCapabilities()} -} - -func (r *GitUploadPackInfo) Decode(s *pktline.Scanner) error { - if err := r.read(s); err != nil { - if err == ErrEmptyGitUploadPack { - return core.NewPermanentError(err) + for _, symref := range ar.Capabilities.Get("symref").Values { + chunks := strings.Split(symref, ":") + if len(chunks) != 2 { + err := fmt.Errorf("bad number of `:` in symref value (%q)", symref) + return core.NewUnexpectedError(err) } - - return core.NewUnexpectedError(err) + name := core.ReferenceName(chunks[0]) + target := core.ReferenceName(chunks[1]) + ref := core.NewSymbolicReference(name, target) + i.Refs.Set(ref) } return nil } -func (r *GitUploadPackInfo) read(s *pktline.Scanner) error { - isEmpty := true - r.Refs = make(memory.ReferenceStorage, 0) - smartCommentIgnore := false - for s.Scan() { - line := string(s.Bytes()) - - if smartCommentIgnore { - // some servers like Github add a flush-pkt after the smart http comment - // that we must ignore to prevent a premature termination of the read. - if len(line) == 0 { - continue - } - smartCommentIgnore = false - } - - // exit on first flush-pkt - if len(line) == 0 { - break - } - - if isSmartHttpComment(line) { - smartCommentIgnore = true - continue - } - - if err := r.readLine(line); err != nil { - return err - } - - isEmpty = false - } - - if isEmpty { - return ErrEmptyGitUploadPack - } - - return s.Err() +func hasSymrefs(ar *advrefs.AdvRefs) bool { + return ar.Capabilities.Supports("symref") } -func isSmartHttpComment(line string) bool { - return line[0] == '#' +func (i *GitUploadPackInfo) Head() *core.Reference { + ref, _ := core.ResolveReference(i.Refs, core.HEAD) + return ref } -func (r *GitUploadPackInfo) readLine(line string) error { - hashEnd := strings.Index(line, " ") - hash := line[:hashEnd] - - zeroID := strings.Index(line, string([]byte{0})) - if zeroID == -1 { - name := line[hashEnd+1 : len(line)-1] - ref := core.NewReferenceFromStrings(name, hash) - return r.Refs.Set(ref) - } - - name := line[hashEnd+1 : zeroID] - r.Capabilities.Decode(line[zeroID+1 : len(line)-1]) - if !r.Capabilities.Supports("symref") { - ref := core.NewReferenceFromStrings(name, hash) - return r.Refs.Set(ref) - } - - target := r.Capabilities.SymbolicReference(name) - ref := core.NewSymbolicReference(core.ReferenceName(name), core.ReferenceName(target)) - return r.Refs.Set(ref) +func (i *GitUploadPackInfo) String() string { + return string(i.Bytes()) } -func (r *GitUploadPackInfo) Head() *core.Reference { - ref, _ := core.ResolveReference(r.Refs, core.HEAD) - return ref -} +func (i *GitUploadPackInfo) Bytes() []byte { + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) -func (r *GitUploadPackInfo) String() string { - return string(r.Bytes()) -} + _ = e.EncodeString("# service=git-upload-pack\n") -func (r *GitUploadPackInfo) Bytes() []byte { - p := pktline.New() - _ = p.AddString("# service=git-upload-pack\n") // inserting a flush-pkt here violates the protocol spec, but some // servers do it, like Github.com - p.AddFlush() + e.Flush() - firstLine := fmt.Sprintf("%s HEAD\x00%s\n", r.Head().Hash(), r.Capabilities.String()) - _ = p.AddString(firstLine) + _ = e.Encodef("%s HEAD\x00%s\n", i.Head().Hash(), i.Capabilities.String()) - for _, ref := range r.Refs { + for _, ref := range i.Refs { if ref.Type() != core.HashReference { continue } - ref := fmt.Sprintf("%s %s\n", ref.Hash(), ref.Name()) - _ = p.AddString(ref) + _ = e.Encodef("%s %s\n", ref.Hash(), ref.Name()) } - p.AddFlush() - b, _ := ioutil.ReadAll(p) + e.Flush() - return b + return buf.Bytes() } type GitUploadPackRequest struct { @@ -337,24 +194,23 @@ func (r *GitUploadPackRequest) String() string { } func (r *GitUploadPackRequest) Reader() *strings.Reader { - p := pktline.New() + var buf bytes.Buffer + e := pktline.NewEncoder(&buf) for _, want := range r.Wants { - _ = p.AddString(fmt.Sprintf("want %s\n", want)) + _ = e.Encodef("want %s\n", want) } for _, have := range r.Haves { - _ = p.AddString(fmt.Sprintf("have %s\n", have)) + _ = e.Encodef("have %s\n", have) } if r.Depth != 0 { - _ = p.AddString(fmt.Sprintf("deepen %d\n", r.Depth)) + _ = e.Encodef("deepen %d\n", r.Depth) } - p.AddFlush() - _ = p.AddString("done\n") - - b, _ := ioutil.ReadAll(p) + _ = e.Flush() + _ = e.EncodeString("done\n") - return strings.NewReader(string(b)) + return strings.NewReader(buf.String()) } |