github.com/catandhorse/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 }