Source file src/pkg/net/http/chunked.go
1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // The wire protocol for HTTP's "chunked" Transfer-Encoding. 6 7 // This code is duplicated in httputil/chunked.go. 8 // Please make any changes in both files. 9 10 package http 11 12 import ( 13 "bufio" 14 "bytes" 15 "errors" 16 "io" 17 "strconv" 18 ) 19 20 const maxLineLength = 4096 // assumed <= bufio.defaultBufSize 21 22 var ErrLineTooLong = errors.New("header line too long") 23 24 // newChunkedReader returns a new chunkedReader that translates the data read from r 25 // out of HTTP "chunked" format before returning it. 26 // The chunkedReader returns io.EOF when the final 0-length chunk is read. 27 // 28 // newChunkedReader is not needed by normal applications. The http package 29 // automatically decodes chunking when reading response bodies. 30 func newChunkedReader(r io.Reader) io.Reader { 31 br, ok := r.(*bufio.Reader) 32 if !ok { 33 br = bufio.NewReader(r) 34 } 35 return &chunkedReader{r: br} 36 } 37 38 type chunkedReader struct { 39 r *bufio.Reader 40 n uint64 // unread bytes in chunk 41 err error 42 } 43 44 func (cr *chunkedReader) beginChunk() { 45 // chunk-size CRLF 46 var line string 47 line, cr.err = readLine(cr.r) 48 if cr.err != nil { 49 return 50 } 51 cr.n, cr.err = strconv.ParseUint(line, 16, 64) 52 if cr.err != nil { 53 return 54 } 55 if cr.n == 0 { 56 cr.err = io.EOF 57 } 58 } 59 60 func (cr *chunkedReader) Read(b []uint8) (n int, err error) { 61 if cr.err != nil { 62 return 0, cr.err 63 } 64 if cr.n == 0 { 65 cr.beginChunk() 66 if cr.err != nil { 67 return 0, cr.err 68 } 69 } 70 if uint64(len(b)) > cr.n { 71 b = b[0:cr.n] 72 } 73 n, cr.err = cr.r.Read(b) 74 cr.n -= uint64(n) 75 if cr.n == 0 && cr.err == nil { 76 // end of chunk (CRLF) 77 b := make([]byte, 2) 78 if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { 79 if b[0] != '\r' || b[1] != '\n' { 80 cr.err = errors.New("malformed chunked encoding") 81 } 82 } 83 } 84 return n, cr.err 85 } 86 87 // Read a line of bytes (up to \n) from b. 88 // Give up if the line exceeds maxLineLength. 89 // The returned bytes are a pointer into storage in 90 // the bufio, so they are only valid until the next bufio read. 91 func readLineBytes(b *bufio.Reader) (p []byte, err error) { 92 if p, err = b.ReadSlice('\n'); err != nil { 93 // We always know when EOF is coming. 94 // If the caller asked for a line, there should be a line. 95 if err == io.EOF { 96 err = io.ErrUnexpectedEOF 97 } else if err == bufio.ErrBufferFull { 98 err = ErrLineTooLong 99 } 100 return nil, err 101 } 102 if len(p) >= maxLineLength { 103 return nil, ErrLineTooLong 104 } 105 106 // Chop off trailing white space. 107 p = bytes.TrimRight(p, " \r\t\n") 108 109 return p, nil 110 } 111 112 // readLineBytes, but convert the bytes into a string. 113 func readLine(b *bufio.Reader) (s string, err error) { 114 p, e := readLineBytes(b) 115 if e != nil { 116 return "", e 117 } 118 return string(p), nil 119 } 120 121 // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP 122 // "chunked" format before writing them to w. Closing the returned chunkedWriter 123 // sends the final 0-length chunk that marks the end of the stream. 124 // 125 // newChunkedWriter is not needed by normal applications. The http 126 // package adds chunking automatically if handlers don't set a 127 // Content-Length header. Using newChunkedWriter inside a handler 128 // would result in double chunking or chunking with a Content-Length 129 // length, both of which are wrong. 130 func newChunkedWriter(w io.Writer) io.WriteCloser { 131 return &chunkedWriter{w} 132 } 133 134 // Writing to chunkedWriter translates to writing in HTTP chunked Transfer 135 // Encoding wire format to the underlying Wire chunkedWriter. 136 type chunkedWriter struct { 137 Wire io.Writer 138 } 139 140 // Write the contents of data as one chunk to Wire. 141 // NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has 142 // a bug since it does not check for success of io.WriteString 143 func (cw *chunkedWriter) Write(data []byte) (n int, err error) { 144 145 // Don't send 0-length data. It looks like EOF for chunked encoding. 146 if len(data) == 0 { 147 return 0, nil 148 } 149 150 head := strconv.FormatInt(int64(len(data)), 16) + "\r\n" 151 152 if _, err = io.WriteString(cw.Wire, head); err != nil { 153 return 0, err 154 } 155 if n, err = cw.Wire.Write(data); err != nil { 156 return 157 } 158 if n != len(data) { 159 err = io.ErrShortWrite 160 return 161 } 162 _, err = io.WriteString(cw.Wire, "\r\n") 163 164 return 165 } 166 167 func (cw *chunkedWriter) Close() error { 168 _, err := io.WriteString(cw.Wire, "0\r\n") 169 return err 170 }