src/pkg/net/http/chunked.go - The Go Programming Language

Golang

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	}