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

Golang

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