mirror of
https://github.com/go-gitea/gitea.git
synced 2024-10-19 06:43:41 -04:00
258 lines
5.3 KiB
Go
258 lines
5.3 KiB
Go
|
package packp
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||
|
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
|
||
|
)
|
||
|
|
||
|
// Decode reads the next upload-request form its input and
|
||
|
// stores it in the UploadRequest.
|
||
|
func (u *UploadRequest) Decode(r io.Reader) error {
|
||
|
d := newUlReqDecoder(r)
|
||
|
return d.Decode(u)
|
||
|
}
|
||
|
|
||
|
type ulReqDecoder struct {
|
||
|
s *pktline.Scanner // a pkt-line scanner from the input stream
|
||
|
line []byte // current pkt-line contents, use parser.nextLine() to make it advance
|
||
|
nLine int // current pkt-line number for debugging, begins at 1
|
||
|
err error // sticky error, use the parser.error() method to fill this out
|
||
|
data *UploadRequest // parsed data is stored here
|
||
|
}
|
||
|
|
||
|
func newUlReqDecoder(r io.Reader) *ulReqDecoder {
|
||
|
return &ulReqDecoder{
|
||
|
s: pktline.NewScanner(r),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) Decode(v *UploadRequest) error {
|
||
|
d.data = v
|
||
|
|
||
|
for state := d.decodeFirstWant; state != nil; {
|
||
|
state = state()
|
||
|
}
|
||
|
|
||
|
return d.err
|
||
|
}
|
||
|
|
||
|
// fills out the parser stiky error
|
||
|
func (d *ulReqDecoder) error(format string, a ...interface{}) {
|
||
|
msg := fmt.Sprintf(
|
||
|
"pkt-line %d: %s", d.nLine,
|
||
|
fmt.Sprintf(format, a...),
|
||
|
)
|
||
|
|
||
|
d.err = NewErrUnexpectedData(msg, d.line)
|
||
|
}
|
||
|
|
||
|
// Reads a new pkt-line from the scanner, makes its payload available as
|
||
|
// p.line and increments p.nLine. A successful invocation returns true,
|
||
|
// otherwise, false is returned and the sticky error is filled out
|
||
|
// accordingly. Trims eols at the end of the payloads.
|
||
|
func (d *ulReqDecoder) nextLine() bool {
|
||
|
d.nLine++
|
||
|
|
||
|
if !d.s.Scan() {
|
||
|
if d.err = d.s.Err(); d.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
d.error("EOF")
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
d.line = d.s.Bytes()
|
||
|
d.line = bytes.TrimSuffix(d.line, eol)
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Expected format: want <hash>[ capabilities]
|
||
|
func (d *ulReqDecoder) decodeFirstWant() stateFn {
|
||
|
if ok := d.nextLine(); !ok {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if !bytes.HasPrefix(d.line, want) {
|
||
|
d.error("missing 'want ' prefix")
|
||
|
return nil
|
||
|
}
|
||
|
d.line = bytes.TrimPrefix(d.line, want)
|
||
|
|
||
|
hash, ok := d.readHash()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
d.data.Wants = append(d.data.Wants, hash)
|
||
|
|
||
|
return d.decodeCaps
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) readHash() (plumbing.Hash, bool) {
|
||
|
if len(d.line) < hashSize {
|
||
|
d.err = fmt.Errorf("malformed hash: %v", d.line)
|
||
|
return plumbing.ZeroHash, false
|
||
|
}
|
||
|
|
||
|
var hash plumbing.Hash
|
||
|
if _, err := hex.Decode(hash[:], d.line[:hashSize]); err != nil {
|
||
|
d.error("invalid hash text: %s", err)
|
||
|
return plumbing.ZeroHash, false
|
||
|
}
|
||
|
d.line = d.line[hashSize:]
|
||
|
|
||
|
return hash, true
|
||
|
}
|
||
|
|
||
|
// Expected format: sp cap1 sp cap2 sp cap3...
|
||
|
func (d *ulReqDecoder) decodeCaps() stateFn {
|
||
|
d.line = bytes.TrimPrefix(d.line, sp)
|
||
|
if err := d.data.Capabilities.Decode(d.line); err != nil {
|
||
|
d.error("invalid capabilities: %s", err)
|
||
|
}
|
||
|
|
||
|
return d.decodeOtherWants
|
||
|
}
|
||
|
|
||
|
// Expected format: want <hash>
|
||
|
func (d *ulReqDecoder) decodeOtherWants() stateFn {
|
||
|
if ok := d.nextLine(); !ok {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if bytes.HasPrefix(d.line, shallow) {
|
||
|
return d.decodeShallow
|
||
|
}
|
||
|
|
||
|
if bytes.HasPrefix(d.line, deepen) {
|
||
|
return d.decodeDeepen
|
||
|
}
|
||
|
|
||
|
if len(d.line) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if !bytes.HasPrefix(d.line, want) {
|
||
|
d.error("unexpected payload while expecting a want: %q", d.line)
|
||
|
return nil
|
||
|
}
|
||
|
d.line = bytes.TrimPrefix(d.line, want)
|
||
|
|
||
|
hash, ok := d.readHash()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
d.data.Wants = append(d.data.Wants, hash)
|
||
|
|
||
|
return d.decodeOtherWants
|
||
|
}
|
||
|
|
||
|
// Expected format: shallow <hash>
|
||
|
func (d *ulReqDecoder) decodeShallow() stateFn {
|
||
|
if bytes.HasPrefix(d.line, deepen) {
|
||
|
return d.decodeDeepen
|
||
|
}
|
||
|
|
||
|
if len(d.line) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if !bytes.HasPrefix(d.line, shallow) {
|
||
|
d.error("unexpected payload while expecting a shallow: %q", d.line)
|
||
|
return nil
|
||
|
}
|
||
|
d.line = bytes.TrimPrefix(d.line, shallow)
|
||
|
|
||
|
hash, ok := d.readHash()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
d.data.Shallows = append(d.data.Shallows, hash)
|
||
|
|
||
|
if ok := d.nextLine(); !ok {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return d.decodeShallow
|
||
|
}
|
||
|
|
||
|
// Expected format: deepen <n> / deepen-since <ul> / deepen-not <ref>
|
||
|
func (d *ulReqDecoder) decodeDeepen() stateFn {
|
||
|
if bytes.HasPrefix(d.line, deepenCommits) {
|
||
|
return d.decodeDeepenCommits
|
||
|
}
|
||
|
|
||
|
if bytes.HasPrefix(d.line, deepenSince) {
|
||
|
return d.decodeDeepenSince
|
||
|
}
|
||
|
|
||
|
if bytes.HasPrefix(d.line, deepenReference) {
|
||
|
return d.decodeDeepenReference
|
||
|
}
|
||
|
|
||
|
if len(d.line) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
d.error("unexpected deepen specification: %q", d.line)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) decodeDeepenCommits() stateFn {
|
||
|
d.line = bytes.TrimPrefix(d.line, deepenCommits)
|
||
|
|
||
|
var n int
|
||
|
if n, d.err = strconv.Atoi(string(d.line)); d.err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
if n < 0 {
|
||
|
d.err = fmt.Errorf("negative depth")
|
||
|
return nil
|
||
|
}
|
||
|
d.data.Depth = DepthCommits(n)
|
||
|
|
||
|
return d.decodeFlush
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) decodeDeepenSince() stateFn {
|
||
|
d.line = bytes.TrimPrefix(d.line, deepenSince)
|
||
|
|
||
|
var secs int64
|
||
|
secs, d.err = strconv.ParseInt(string(d.line), 10, 64)
|
||
|
if d.err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
t := time.Unix(secs, 0).UTC()
|
||
|
d.data.Depth = DepthSince(t)
|
||
|
|
||
|
return d.decodeFlush
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) decodeDeepenReference() stateFn {
|
||
|
d.line = bytes.TrimPrefix(d.line, deepenReference)
|
||
|
|
||
|
d.data.Depth = DepthReference(string(d.line))
|
||
|
|
||
|
return d.decodeFlush
|
||
|
}
|
||
|
|
||
|
func (d *ulReqDecoder) decodeFlush() stateFn {
|
||
|
if ok := d.nextLine(); !ok {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if len(d.line) != 0 {
|
||
|
d.err = fmt.Errorf("unexpected payload while expecting a flush-pkt: %q", d.line)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|