Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149package gitclient
import ( "bytes" "errors" "fmt" "io" "strconv")
type pktKind int
const ( pktData pktKind = iota pktFlush pktDelim pktResponseEnd)
const maxPktLen = 65520
func writePkt(w io.Writer, line string) error { if len(line)+4 > maxPktLen { return fmt.Errorf("pkt-line too long: %d bytes", len(line)) } _, err := fmt.Fprintf(w, "%04x%s", len(line)+4, line) return err}
func writeFlush(w io.Writer) error { _, err := io.WriteString(w, "0000") return err}
func writeDelim(w io.Writer) error { _, err := io.WriteString(w, "0001") return err}
type pktReader struct { r io.Reader buf []byte}
func newPktReader(r io.Reader) *pktReader { return &pktReader{r: r, buf: make([]byte, maxPktLen)}}
// payload is only valid until the next callfunc (p *pktReader) next() (pktKind, []byte, error) { if _, err := io.ReadFull(p.r, p.buf[:4]); err != nil { if errors.Is(err, io.EOF) { err = io.ErrUnexpectedEOF } return 0, nil, err } n, err := strconv.ParseUint(string(p.buf[:4]), 16, 16) if err != nil { return 0, nil, fmt.Errorf("bad pkt-line length %q", p.buf[:4]) } switch { case n == 0: return pktFlush, nil, nil case n == 1: return pktDelim, nil, nil case n == 2: return pktResponseEnd, nil, nil case n < 4 || n > maxPktLen: return 0, nil, fmt.Errorf("bad pkt-line length %d", n) } payload := p.buf[:n-4] if _, err := io.ReadFull(p.r, payload); err != nil { return 0, nil, err } return pktData, payload, nil}
// skips acknowledgments/shallow-info; the reader yields band 1 of the// packfile section, band 3 becomes an errorfunc packfileSection(p *pktReader) (io.Reader, error) { for { kind, line, err := p.next() if err != nil { return nil, err } if kind == pktFlush || kind == pktResponseEnd { return nil, errors.New("fetch response has no packfile section") } if msg, ok := bytes.CutPrefix(line, []byte("ERR ")); ok { return nil, fmt.Errorf("remote: %s", bytes.TrimSpace(msg)) } if string(line) == "packfile\n" { return &sideband{p: p}, nil } }}
type sideband struct { p *pktReader data []byte done bool}
func (s *sideband) Read(b []byte) (int, error) { for len(s.data) == 0 { if s.done { return 0, io.EOF } kind, line, err := s.p.next() if err != nil { return 0, err } if kind != pktData { s.done = true continue } if len(line) == 0 { return 0, errors.New("empty sideband pkt-line") } switch line[0] { case 1: s.data = line[1:] case 2: case 3: return 0, fmt.Errorf("remote: %s", bytes.TrimSpace(line[1:])) default: return 0, fmt.Errorf("bad sideband %d", line[0]) } } n := copy(b, s.data) s.data = s.data[n:] return n, nil}
// errors with ErrTooLarge once more than n bytes come throughtype capReader struct { r io.Reader n int64}
func (c *capReader) Read(b []byte) (int, error) { n, err := c.r.Read(b) c.n -= int64(n) if c.n < 0 { return 0, ErrTooLarge } return n, err}