aboutsummaryrefslogblamecommitdiffstats
path: root/plumbing/protocol/packp/srvresp.go
blob: 0c89e47da93ed5f7b68c36cb9f662c5fe2f3465c (plain) (tree)
1
2
3
4
5
6
7
8
9
10







                

                                                          




                                                                 






                                                                             
                                                                                














































                                                                                       













                                                                                       
package packp

import (
	"bytes"
	"errors"
	"fmt"
	"io"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
)

const ackLineLen = 44

// ServerResponse object acknowledgement from upload-pack service
type ServerResponse struct {
	ACKs []plumbing.Hash
}

// Decode decodes the response into the struct, isMultiACK should be true, if
// the request was done with multi_ack or multi_ack_detailed capabilities
func (r *ServerResponse) Decode(reader io.Reader, isMultiACK bool) error {
	// TODO: implement support for multi_ack or multi_ack_detailed responses
	if isMultiACK {
		return errors.New("multi_ack and multi_ack_detailed are not supported")
	}

	s := pktline.NewScanner(reader)

	for s.Scan() {
		line := s.Bytes()

		if err := r.decodeLine(line); err != nil {
			return err
		}

		if !isMultiACK {
			break
		}
	}

	return s.Err()
}

func (r *ServerResponse) decodeLine(line []byte) error {
	if len(line) == 0 {
		return fmt.Errorf("unexpected flush")
	}

	if bytes.Compare(line[0:3], ack) == 0 {
		return r.decodeACKLine(line)
	}

	if bytes.Compare(line[0:3], nak) == 0 {
		return nil
	}

	return fmt.Errorf("unexpected content %q", string(line))
}

func (r *ServerResponse) decodeACKLine(line []byte) error {
	if len(line) < ackLineLen {
		return fmt.Errorf("malformed ACK %q", line)
	}

	sp := bytes.Index(line, []byte(" "))
	h := plumbing.NewHash(string(line[sp+1 : sp+41]))
	r.ACKs = append(r.ACKs, h)
	return nil
}

// Encode encodes the ServerResponse into a writer.
func (r *ServerResponse) Encode(w io.Writer) error {
	if len(r.ACKs) > 1 {
		return errors.New("multi_ack and multi_ack_detailed are not supported")
	}

	e := pktline.NewEncoder(w)
	if len(r.ACKs) == 0 {
		return e.Encodef("%s\n", nak)
	}

	return e.Encodef("%s %s\n", ack, r.ACKs[0].String())
}