github.com/2lambda123/git-lfs@v2.5.2+incompatible/git/pkt_line.go (about)

     1  package git
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  const (
    14  	// MaxPacketLength is the maximum total (header+payload) length
    15  	// encode-able within one packet using Git's pkt-line protocol.
    16  	MaxPacketLength = 65516
    17  )
    18  
    19  type pktline struct {
    20  	r *bufio.Reader
    21  	w *bufio.Writer
    22  }
    23  
    24  func newPktline(r io.Reader, w io.Writer) *pktline {
    25  	return &pktline{
    26  		r: bufio.NewReader(r),
    27  		w: bufio.NewWriter(w),
    28  	}
    29  }
    30  
    31  // readPacket reads a single packet entirely and returns the data encoded within
    32  // it. Errors can occur in several cases, as described below.
    33  //
    34  // 1) If no data was present in the reader, and no more data could be read (the
    35  //    pipe was closed, etc) than an io.EOF will be returned.
    36  // 2) If there was some data to be read, but the pipe or reader was closed
    37  //    before an entire packet (or header) could be ingested, an
    38  //    io.ErrShortBuffer error will be returned.
    39  // 3) If there was a valid header, but no body associated with the packet, an
    40  //    "Invalid packet length." error will be returned.
    41  // 4) If the data in the header could not be parsed as a hexadecimal length in
    42  //    the Git pktline format, the parse error will be returned.
    43  //
    44  // If none of the above cases fit the state of the data on the wire, the packet
    45  // is returned along with a nil error.
    46  func (p *pktline) readPacket() ([]byte, error) {
    47  	var pktLenHex [4]byte
    48  	if n, err := io.ReadFull(p.r, pktLenHex[:]); err != nil {
    49  		return nil, err
    50  	} else if n != 4 {
    51  		return nil, io.ErrShortBuffer
    52  	}
    53  
    54  	pktLen, err := strconv.ParseInt(string(pktLenHex[:]), 16, 0)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	// pktLen==0: flush packet
    60  	if pktLen == 0 {
    61  		return nil, nil
    62  	}
    63  	if pktLen <= 4 {
    64  		return nil, errors.New("Invalid packet length.")
    65  	}
    66  
    67  	payload, err := ioutil.ReadAll(io.LimitReader(p.r, pktLen-4))
    68  	return payload, err
    69  }
    70  
    71  // readPacketText follows identical semantics to the `readPacket()` function,
    72  // but additionally removes the trailing `\n` LF from the end of the packet, if
    73  // present.
    74  func (p *pktline) readPacketText() (string, error) {
    75  	data, err := p.readPacket()
    76  	return strings.TrimSuffix(string(data), "\n"), err
    77  }
    78  
    79  // readPacketList reads as many packets as possible using the `readPacketText`
    80  // function before encountering a flush packet. It returns a slice of all the
    81  // packets it read, or an error if one was encountered.
    82  func (p *pktline) readPacketList() ([]string, error) {
    83  	var list []string
    84  	for {
    85  		data, err := p.readPacketText()
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  
    90  		if len(data) == 0 {
    91  			break
    92  		}
    93  
    94  		list = append(list, data)
    95  	}
    96  
    97  	return list, nil
    98  }
    99  
   100  // writePacket writes the given data in "data" to the underlying data stream
   101  // using Git's `pkt-line` format.
   102  //
   103  // If the data was longer than MaxPacketLength, an error will be returned. If
   104  // there was any error encountered while writing any component of the packet
   105  // (hdr, payload), it will be returned.
   106  //
   107  // NB: writePacket does _not_ flush the underlying buffered writer. See instead:
   108  // `writeFlush()`.
   109  func (p *pktline) writePacket(data []byte) error {
   110  	if len(data) > MaxPacketLength {
   111  		return errors.New("Packet length exceeds maximal length")
   112  	}
   113  
   114  	if _, err := p.w.WriteString(fmt.Sprintf("%04x", len(data)+4)); err != nil {
   115  		return err
   116  	}
   117  
   118  	if _, err := p.w.Write(data); err != nil {
   119  		return err
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  // writeFlush writes the terminating "flush" packet and then flushes the
   126  // underlying buffered writer.
   127  //
   128  // If any error was encountered along the way, it will be returned immediately
   129  func (p *pktline) writeFlush() error {
   130  	if _, err := p.w.WriteString(fmt.Sprintf("%04x", 0)); err != nil {
   131  		return err
   132  	}
   133  
   134  	if err := p.w.Flush(); err != nil {
   135  		return err
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // writePacketText follows the same semantics as `writePacket`, but appends a
   142  // trailing "\n" LF character to the end of the data.
   143  func (p *pktline) writePacketText(data string) error {
   144  	return p.writePacket([]byte(data + "\n"))
   145  }
   146  
   147  // writePacketList writes a slice of strings using the semantics of
   148  // and then writes a terminating flush sequence afterwords.
   149  //
   150  // If any error was encountered, it will be returned immediately.
   151  func (p *pktline) writePacketList(list []string) error {
   152  	for _, i := range list {
   153  		if err := p.writePacketText(i); err != nil {
   154  			return err
   155  		}
   156  	}
   157  
   158  	return p.writeFlush()
   159  }