...
Source file src/pkg/net/http/internal/chunked.go
1
2
3
4
5
6
7
8
9 package internal
10
11 import (
12 "bufio"
13 "bytes"
14 "errors"
15 "fmt"
16 "io"
17 )
18
19 const maxLineLength = 4096
20
21 var ErrLineTooLong = errors.New("header line too long")
22
23
24
25
26
27
28
29 func NewChunkedReader(r io.Reader) io.Reader {
30 br, ok := r.(*bufio.Reader)
31 if !ok {
32 br = bufio.NewReader(r)
33 }
34 return &chunkedReader{r: br}
35 }
36
37 type chunkedReader struct {
38 r *bufio.Reader
39 n uint64
40 err error
41 buf [2]byte
42 checkEnd bool
43 }
44
45 func (cr *chunkedReader) beginChunk() {
46
47 var line []byte
48 line, cr.err = readChunkLine(cr.r)
49 if cr.err != nil {
50 return
51 }
52 cr.n, cr.err = parseHexUint(line)
53 if cr.err != nil {
54 return
55 }
56 if cr.n == 0 {
57 cr.err = io.EOF
58 }
59 }
60
61 func (cr *chunkedReader) chunkHeaderAvailable() bool {
62 n := cr.r.Buffered()
63 if n > 0 {
64 peek, _ := cr.r.Peek(n)
65 return bytes.IndexByte(peek, '\n') >= 0
66 }
67 return false
68 }
69
70 func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
71 for cr.err == nil {
72 if cr.checkEnd {
73 if n > 0 && cr.r.Buffered() < 2 {
74
75
76
77 break
78 }
79 if _, cr.err = io.ReadFull(cr.r, cr.buf[:2]); cr.err == nil {
80 if string(cr.buf[:]) != "\r\n" {
81 cr.err = errors.New("malformed chunked encoding")
82 break
83 }
84 }
85 cr.checkEnd = false
86 }
87 if cr.n == 0 {
88 if n > 0 && !cr.chunkHeaderAvailable() {
89
90
91 break
92 }
93 cr.beginChunk()
94 continue
95 }
96 if len(b) == 0 {
97 break
98 }
99 rbuf := b
100 if uint64(len(rbuf)) > cr.n {
101 rbuf = rbuf[:cr.n]
102 }
103 var n0 int
104 n0, cr.err = cr.r.Read(rbuf)
105 n += n0
106 b = b[n0:]
107 cr.n -= uint64(n0)
108
109
110 if cr.n == 0 && cr.err == nil {
111 cr.checkEnd = true
112 }
113 }
114 return n, cr.err
115 }
116
117
118
119
120
121 func readChunkLine(b *bufio.Reader) ([]byte, error) {
122 p, err := b.ReadSlice('\n')
123 if err != nil {
124
125
126 if err == io.EOF {
127 err = io.ErrUnexpectedEOF
128 } else if err == bufio.ErrBufferFull {
129 err = ErrLineTooLong
130 }
131 return nil, err
132 }
133 if len(p) >= maxLineLength {
134 return nil, ErrLineTooLong
135 }
136 p = trimTrailingWhitespace(p)
137 p, err = removeChunkExtension(p)
138 if err != nil {
139 return nil, err
140 }
141 return p, nil
142 }
143
144 func trimTrailingWhitespace(b []byte) []byte {
145 for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
146 b = b[:len(b)-1]
147 }
148 return b
149 }
150
151 func isASCIISpace(b byte) bool {
152 return b == ' ' || b == '\t' || b == '\n' || b == '\r'
153 }
154
155
156
157
158
159
160
161 func removeChunkExtension(p []byte) ([]byte, error) {
162 semi := bytes.IndexByte(p, ';')
163 if semi == -1 {
164 return p, nil
165 }
166
167
168
169 return p[:semi], nil
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183 func NewChunkedWriter(w io.Writer) io.WriteCloser {
184 return &chunkedWriter{w}
185 }
186
187
188
189 type chunkedWriter struct {
190 Wire io.Writer
191 }
192
193
194
195
196 func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
197
198
199 if len(data) == 0 {
200 return 0, nil
201 }
202
203 if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
204 return 0, err
205 }
206 if n, err = cw.Wire.Write(data); err != nil {
207 return
208 }
209 if n != len(data) {
210 err = io.ErrShortWrite
211 return
212 }
213 if _, err = io.WriteString(cw.Wire, "\r\n"); err != nil {
214 return
215 }
216 if bw, ok := cw.Wire.(*FlushAfterChunkWriter); ok {
217 err = bw.Flush()
218 }
219 return
220 }
221
222 func (cw *chunkedWriter) Close() error {
223 _, err := io.WriteString(cw.Wire, "0\r\n")
224 return err
225 }
226
227
228
229
230
231
232 type FlushAfterChunkWriter struct {
233 *bufio.Writer
234 }
235
236 func parseHexUint(v []byte) (n uint64, err error) {
237 for i, b := range v {
238 switch {
239 case '0' <= b && b <= '9':
240 b = b - '0'
241 case 'a' <= b && b <= 'f':
242 b = b - 'a' + 10
243 case 'A' <= b && b <= 'F':
244 b = b - 'A' + 10
245 default:
246 return 0, errors.New("invalid byte in chunk length")
247 }
248 if i == 16 {
249 return 0, errors.New("http chunk length too large")
250 }
251 n <<= 4
252 n |= uint64(b)
253 }
254 return
255 }
256
View as plain text