LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // The wire protocol for HTTP's "chunked" Transfer-Encoding. | 5 // The wire protocol for HTTP's "chunked" Transfer-Encoding. |
6 | 6 |
7 // This code is duplicated in httputil/chunked.go. | 7 // This code is duplicated in httputil/chunked.go. |
8 // Please make any changes in both files. | 8 // Please make any changes in both files. |
9 | 9 |
10 package http | 10 package http |
11 | 11 |
12 import ( | 12 import ( |
13 "bufio" | 13 "bufio" |
14 "bytes" | |
15 "errors" | 14 "errors" |
16 "fmt" | 15 "fmt" |
17 "io" | 16 "io" |
18 "strconv" | |
19 ) | 17 ) |
20 | 18 |
21 const maxLineLength = 4096 // assumed <= bufio.defaultBufSize | 19 const maxLineLength = 4096 // assumed <= bufio.defaultBufSize |
22 | 20 |
23 var ErrLineTooLong = errors.New("header line too long") | 21 var ErrLineTooLong = errors.New("header line too long") |
24 | 22 |
25 // newChunkedReader returns a new chunkedReader that translates the data read fr
om r | 23 // newChunkedReader returns a new chunkedReader that translates the data read fr
om r |
26 // out of HTTP "chunked" format before returning it. | 24 // out of HTTP "chunked" format before returning it. |
27 // The chunkedReader returns io.EOF when the final 0-length chunk is read. | 25 // The chunkedReader returns io.EOF when the final 0-length chunk is read. |
28 // | 26 // |
29 // newChunkedReader is not needed by normal applications. The http package | 27 // newChunkedReader is not needed by normal applications. The http package |
30 // automatically decodes chunking when reading response bodies. | 28 // automatically decodes chunking when reading response bodies. |
31 func newChunkedReader(r io.Reader) io.Reader { | 29 func newChunkedReader(r io.Reader) io.Reader { |
32 br, ok := r.(*bufio.Reader) | 30 br, ok := r.(*bufio.Reader) |
33 if !ok { | 31 if !ok { |
34 br = bufio.NewReader(r) | 32 br = bufio.NewReader(r) |
35 } | 33 } |
36 return &chunkedReader{r: br} | 34 return &chunkedReader{r: br} |
37 } | 35 } |
38 | 36 |
39 type chunkedReader struct { | 37 type chunkedReader struct { |
40 r *bufio.Reader | 38 r *bufio.Reader |
41 n uint64 // unread bytes in chunk | 39 n uint64 // unread bytes in chunk |
42 err error | 40 err error |
43 buf [2]byte | 41 buf [2]byte |
44 } | 42 } |
45 | 43 |
46 func (cr *chunkedReader) beginChunk() { | 44 func (cr *chunkedReader) beginChunk() { |
47 // chunk-size CRLF | 45 // chunk-size CRLF |
48 » var line string | 46 » var line []byte |
49 line, cr.err = readLine(cr.r) | 47 line, cr.err = readLine(cr.r) |
50 if cr.err != nil { | 48 if cr.err != nil { |
51 return | 49 return |
52 } | 50 } |
53 » cr.n, cr.err = strconv.ParseUint(line, 16, 64) | 51 » cr.n, cr.err = parseHexUint(line) |
54 if cr.err != nil { | 52 if cr.err != nil { |
55 return | 53 return |
56 } | 54 } |
57 if cr.n == 0 { | 55 if cr.n == 0 { |
58 cr.err = io.EOF | 56 cr.err = io.EOF |
59 } | 57 } |
60 } | 58 } |
61 | 59 |
62 func (cr *chunkedReader) Read(b []uint8) (n int, err error) { | 60 func (cr *chunkedReader) Read(b []uint8) (n int, err error) { |
63 if cr.err != nil { | 61 if cr.err != nil { |
(...skipping 18 matching lines...) Expand all Loading... |
82 } | 80 } |
83 } | 81 } |
84 } | 82 } |
85 return n, cr.err | 83 return n, cr.err |
86 } | 84 } |
87 | 85 |
88 // Read a line of bytes (up to \n) from b. | 86 // Read a line of bytes (up to \n) from b. |
89 // Give up if the line exceeds maxLineLength. | 87 // Give up if the line exceeds maxLineLength. |
90 // The returned bytes are a pointer into storage in | 88 // The returned bytes are a pointer into storage in |
91 // the bufio, so they are only valid until the next bufio read. | 89 // the bufio, so they are only valid until the next bufio read. |
92 func readLineBytes(b *bufio.Reader) (p []byte, err error) { | 90 func readLine(b *bufio.Reader) (p []byte, err error) { |
93 if p, err = b.ReadSlice('\n'); err != nil { | 91 if p, err = b.ReadSlice('\n'); err != nil { |
94 // We always know when EOF is coming. | 92 // We always know when EOF is coming. |
95 // If the caller asked for a line, there should be a line. | 93 // If the caller asked for a line, there should be a line. |
96 if err == io.EOF { | 94 if err == io.EOF { |
97 err = io.ErrUnexpectedEOF | 95 err = io.ErrUnexpectedEOF |
98 } else if err == bufio.ErrBufferFull { | 96 } else if err == bufio.ErrBufferFull { |
99 err = ErrLineTooLong | 97 err = ErrLineTooLong |
100 } | 98 } |
101 return nil, err | 99 return nil, err |
102 } | 100 } |
103 if len(p) >= maxLineLength { | 101 if len(p) >= maxLineLength { |
104 return nil, ErrLineTooLong | 102 return nil, ErrLineTooLong |
105 } | 103 } |
106 | 104 » return trimTrailingWhitespace(p), nil |
107 » // Chop off trailing white space. | |
108 » p = bytes.TrimRight(p, " \r\t\n") | |
109 | |
110 » return p, nil | |
111 } | 105 } |
112 | 106 |
113 // readLineBytes, but convert the bytes into a string. | 107 func trimTrailingWhitespace(b []byte) []byte { |
114 func readLine(b *bufio.Reader) (s string, err error) { | 108 » for len(b) > 0 && isASCIISpace(b[len(b)-1]) { |
115 » p, e := readLineBytes(b) | 109 » » b = b[:len(b)-1] |
116 » if e != nil { | |
117 » » return "", e | |
118 } | 110 } |
119 » return string(p), nil | 111 » return b |
| 112 } |
| 113 |
| 114 func isASCIISpace(b byte) bool { |
| 115 » return b == ' ' || b == '\t' || b == '\n' || b == '\r' |
120 } | 116 } |
121 | 117 |
122 // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP | 118 // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP |
123 // "chunked" format before writing them to w. Closing the returned chunkedWriter | 119 // "chunked" format before writing them to w. Closing the returned chunkedWriter |
124 // sends the final 0-length chunk that marks the end of the stream. | 120 // sends the final 0-length chunk that marks the end of the stream. |
125 // | 121 // |
126 // newChunkedWriter is not needed by normal applications. The http | 122 // newChunkedWriter is not needed by normal applications. The http |
127 // package adds chunking automatically if handlers don't set a | 123 // package adds chunking automatically if handlers don't set a |
128 // Content-Length header. Using newChunkedWriter inside a handler | 124 // Content-Length header. Using newChunkedWriter inside a handler |
129 // would result in double chunking or chunking with a Content-Length | 125 // would result in double chunking or chunking with a Content-Length |
(...skipping 30 matching lines...) Expand all Loading... |
160 } | 156 } |
161 _, err = io.WriteString(cw.Wire, "\r\n") | 157 _, err = io.WriteString(cw.Wire, "\r\n") |
162 | 158 |
163 return | 159 return |
164 } | 160 } |
165 | 161 |
166 func (cw *chunkedWriter) Close() error { | 162 func (cw *chunkedWriter) Close() error { |
167 _, err := io.WriteString(cw.Wire, "0\r\n") | 163 _, err := io.WriteString(cw.Wire, "0\r\n") |
168 return err | 164 return err |
169 } | 165 } |
| 166 |
| 167 func parseHexUint(v []byte) (n uint64, err error) { |
| 168 for _, b := range v { |
| 169 n <<= 4 |
| 170 switch { |
| 171 case '0' <= b && b <= '9': |
| 172 b = b - '0' |
| 173 case 'a' <= b && b <= 'f': |
| 174 b = b - 'a' + 10 |
| 175 case 'A' <= b && b <= 'F': |
| 176 b = b - 'A' + 10 |
| 177 default: |
| 178 return 0, errors.New("invalid byte in chunk length") |
| 179 } |
| 180 n |= uint64(b) |
| 181 } |
| 182 return |
| 183 } |
LEFT | RIGHT |