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