The Go Programming Language

Source file src/pkg/http/transfer.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	package http
     6	
     7	import (
     8		"bytes"
     9		"bufio"
    10		"io"
    11		"io/ioutil"
    12		"os"
    13		"strconv"
    14		"strings"
    15	)
    16	
    17	// transferWriter inspects the fields of a user-supplied Request or Response,
    18	// sanitizes them without changing the user object and provides methods for
    19	// writing the respective header, body and trailer in wire format.
    20	type transferWriter struct {
    21		Body             io.Reader
    22		BodyCloser       io.Closer
    23		ResponseToHEAD   bool
    24		ContentLength    int64
    25		Close            bool
    26		TransferEncoding []string
    27		Trailer          Header
    28	}
    29	
    30	func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) {
    31		t = &transferWriter{}
    32	
    33		// Extract relevant fields
    34		atLeastHTTP11 := false
    35		switch rr := r.(type) {
    36		case *Request:
    37			t.Body = rr.Body
    38			t.BodyCloser = rr.Body
    39			t.ContentLength = rr.ContentLength
    40			t.Close = rr.Close
    41			t.TransferEncoding = rr.TransferEncoding
    42			t.Trailer = rr.Trailer
    43			atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
    44			if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
    45				if t.ContentLength == 0 {
    46					// Test to see if it's actually zero or just unset.
    47					var buf [1]byte
    48					n, _ := io.ReadFull(t.Body, buf[:])
    49					if n == 1 {
    50						// Oh, guess there is data in this Body Reader after all.
    51						// The ContentLength field just wasn't set.
    52						// Stich the Body back together again, re-attaching our
    53						// consumed byte.
    54						t.ContentLength = -1
    55						t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body)
    56					} else {
    57						// Body is actually empty.
    58						t.Body = nil
    59						t.BodyCloser = nil
    60					}
    61				}
    62				if t.ContentLength < 0 {
    63					t.TransferEncoding = []string{"chunked"}
    64				}
    65			}
    66		case *Response:
    67			t.Body = rr.Body
    68			t.BodyCloser = rr.Body
    69			t.ContentLength = rr.ContentLength
    70			t.Close = rr.Close
    71			t.TransferEncoding = rr.TransferEncoding
    72			t.Trailer = rr.Trailer
    73			atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
    74			t.ResponseToHEAD = noBodyExpected(rr.Request.Method)
    75		}
    76	
    77		// Sanitize Body,ContentLength,TransferEncoding
    78		if t.ResponseToHEAD {
    79			t.Body = nil
    80			t.TransferEncoding = nil
    81			// ContentLength is expected to hold Content-Length
    82			if t.ContentLength < 0 {
    83				return nil, ErrMissingContentLength
    84			}
    85		} else {
    86			if !atLeastHTTP11 || t.Body == nil {
    87				t.TransferEncoding = nil
    88			}
    89			if chunked(t.TransferEncoding) {
    90				t.ContentLength = -1
    91			} else if t.Body == nil { // no chunking, no body
    92				t.ContentLength = 0
    93			}
    94		}
    95	
    96		// Sanitize Trailer
    97		if !chunked(t.TransferEncoding) {
    98			t.Trailer = nil
    99		}
   100	
   101		return t, nil
   102	}
   103	
   104	func noBodyExpected(requestMethod string) bool {
   105		return requestMethod == "HEAD"
   106	}
   107	
   108	func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) {
   109		if t.Close {
   110			_, err = io.WriteString(w, "Connection: close\r\n")
   111			if err != nil {
   112				return
   113			}
   114		}
   115	
   116		// Write Content-Length and/or Transfer-Encoding whose values are a
   117		// function of the sanitized field triple (Body, ContentLength,
   118		// TransferEncoding)
   119		if chunked(t.TransferEncoding) {
   120			_, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
   121			if err != nil {
   122				return
   123			}
   124		} else if t.ContentLength > 0 || t.ResponseToHEAD || (t.ContentLength == 0 && isIdentity(t.TransferEncoding)) {
   125			io.WriteString(w, "Content-Length: ")
   126			_, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n")
   127			if err != nil {
   128				return
   129			}
   130		}
   131	
   132		// Write Trailer header
   133		if t.Trailer != nil {
   134			// TODO: At some point, there should be a generic mechanism for
   135			// writing long headers, using HTTP line splitting
   136			io.WriteString(w, "Trailer: ")
   137			needComma := false
   138			for k := range t.Trailer {
   139				k = CanonicalHeaderKey(k)
   140				switch k {
   141				case "Transfer-Encoding", "Trailer", "Content-Length":
   142					return &badStringError{"invalid Trailer key", k}
   143				}
   144				if needComma {
   145					io.WriteString(w, ",")
   146				}
   147				io.WriteString(w, k)
   148				needComma = true
   149			}
   150			_, err = io.WriteString(w, "\r\n")
   151		}
   152	
   153		return
   154	}
   155	
   156	func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) {
   157		// Write body
   158		if t.Body != nil {
   159			if chunked(t.TransferEncoding) {
   160				cw := NewChunkedWriter(w)
   161				_, err = io.Copy(cw, t.Body)
   162				if err == nil {
   163					err = cw.Close()
   164				}
   165			} else if t.ContentLength == -1 {
   166				_, err = io.Copy(w, t.Body)
   167			} else {
   168				_, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
   169			}
   170			if err != nil {
   171				return err
   172			}
   173			if err = t.BodyCloser.Close(); err != nil {
   174				return err
   175			}
   176		}
   177	
   178		// TODO(petar): Place trailer writer code here.
   179		if chunked(t.TransferEncoding) {
   180			// Last chunk, empty trailer
   181			_, err = io.WriteString(w, "\r\n")
   182		}
   183	
   184		return
   185	}
   186	
   187	type transferReader struct {
   188		// Input
   189		Header        Header
   190		StatusCode    int
   191		RequestMethod string
   192		ProtoMajor    int
   193		ProtoMinor    int
   194		// Output
   195		Body             io.ReadCloser
   196		ContentLength    int64
   197		TransferEncoding []string
   198		Close            bool
   199		Trailer          Header
   200	}
   201	
   202	// bodyAllowedForStatus returns whether a given response status code
   203	// permits a body.  See RFC2616, section 4.4.
   204	func bodyAllowedForStatus(status int) bool {
   205		switch {
   206		case status >= 100 && status <= 199:
   207			return false
   208		case status == 204:
   209			return false
   210		case status == 304:
   211			return false
   212		}
   213		return true
   214	}
   215	
   216	// msg is *Request or *Response.
   217	func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
   218		t := &transferReader{}
   219	
   220		// Unify input
   221		isResponse := false
   222		switch rr := msg.(type) {
   223		case *Response:
   224			t.Header = rr.Header
   225			t.StatusCode = rr.StatusCode
   226			t.RequestMethod = rr.Request.Method
   227			t.ProtoMajor = rr.ProtoMajor
   228			t.ProtoMinor = rr.ProtoMinor
   229			t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
   230			isResponse = true
   231		case *Request:
   232			t.Header = rr.Header
   233			t.ProtoMajor = rr.ProtoMajor
   234			t.ProtoMinor = rr.ProtoMinor
   235			// Transfer semantics for Requests are exactly like those for
   236			// Responses with status code 200, responding to a GET method
   237			t.StatusCode = 200
   238			t.RequestMethod = "GET"
   239		default:
   240			panic("unexpected type")
   241		}
   242	
   243		// Default to HTTP/1.1
   244		if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
   245			t.ProtoMajor, t.ProtoMinor = 1, 1
   246		}
   247	
   248		// Transfer encoding, content length
   249		t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
   250		if err != nil {
   251			return err
   252		}
   253	
   254		t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
   255		if err != nil {
   256			return err
   257		}
   258	
   259		// Trailer
   260		t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
   261		if err != nil {
   262			return err
   263		}
   264	
   265		// If there is no Content-Length or chunked Transfer-Encoding on a *Response
   266		// and the status is not 1xx, 204 or 304, then the body is unbounded.
   267		// See RFC2616, section 4.4.
   268		switch msg.(type) {
   269		case *Response:
   270			if t.ContentLength == -1 &&
   271				!chunked(t.TransferEncoding) &&
   272				bodyAllowedForStatus(t.StatusCode) {
   273				// Unbounded body.
   274				t.Close = true
   275			}
   276		}
   277	
   278		// Prepare body reader.  ContentLength < 0 means chunked encoding
   279		// or close connection when finished, since multipart is not supported yet
   280		switch {
   281		case chunked(t.TransferEncoding):
   282			t.Body = &body{Reader: NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
   283		case t.ContentLength >= 0:
   284			// TODO: limit the Content-Length. This is an easy DoS vector.
   285			t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
   286		default:
   287			// t.ContentLength < 0, i.e. "Content-Length" not mentioned in header
   288			if t.Close {
   289				// Close semantics (i.e. HTTP/1.0)
   290				t.Body = &body{Reader: r, closing: t.Close}
   291			} else {
   292				// Persistent connection (i.e. HTTP/1.1)
   293				t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
   294			}
   295		}
   296	
   297		// Unify output
   298		switch rr := msg.(type) {
   299		case *Request:
   300			rr.Body = t.Body
   301			rr.ContentLength = t.ContentLength
   302			rr.TransferEncoding = t.TransferEncoding
   303			rr.Close = t.Close
   304			rr.Trailer = t.Trailer
   305		case *Response:
   306			rr.Body = t.Body
   307			rr.ContentLength = t.ContentLength
   308			rr.TransferEncoding = t.TransferEncoding
   309			rr.Close = t.Close
   310			rr.Trailer = t.Trailer
   311		}
   312	
   313		return nil
   314	}
   315	
   316	// Checks whether chunked is part of the encodings stack
   317	func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
   318	
   319	// Checks whether the encoding is explicitly "identity".
   320	func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
   321	
   322	// Sanitize transfer encoding
   323	func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) {
   324		raw, present := header["Transfer-Encoding"]
   325		if !present {
   326			return nil, nil
   327		}
   328	
   329		header["Transfer-Encoding"] = nil, false
   330	
   331		// Head responses have no bodies, so the transfer encoding
   332		// should be ignored.
   333		if requestMethod == "HEAD" {
   334			return nil, nil
   335		}
   336	
   337		encodings := strings.Split(raw[0], ",")
   338		te := make([]string, 0, len(encodings))
   339		// TODO: Even though we only support "identity" and "chunked"
   340		// encodings, the loop below is designed with foresight. One
   341		// invariant that must be maintained is that, if present,
   342		// chunked encoding must always come first.
   343		for _, encoding := range encodings {
   344			encoding = strings.ToLower(strings.TrimSpace(encoding))
   345			// "identity" encoding is not recored
   346			if encoding == "identity" {
   347				break
   348			}
   349			if encoding != "chunked" {
   350				return nil, &badStringError{"unsupported transfer encoding", encoding}
   351			}
   352			te = te[0 : len(te)+1]
   353			te[len(te)-1] = encoding
   354		}
   355		if len(te) > 1 {
   356			return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
   357		}
   358		if len(te) > 0 {
   359			// Chunked encoding trumps Content-Length. See RFC 2616
   360			// Section 4.4. Currently len(te) > 0 implies chunked
   361			// encoding.
   362			header["Content-Length"] = nil, false
   363			return te, nil
   364		}
   365	
   366		return nil, nil
   367	}
   368	
   369	// Determine the expected body length, using RFC 2616 Section 4.4. This
   370	// function is not a method, because ultimately it should be shared by
   371	// ReadResponse and ReadRequest.
   372	func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, os.Error) {
   373	
   374		// Logic based on response type or status
   375		if noBodyExpected(requestMethod) {
   376			return 0, nil
   377		}
   378		if status/100 == 1 {
   379			return 0, nil
   380		}
   381		switch status {
   382		case 204, 304:
   383			return 0, nil
   384		}
   385	
   386		// Logic based on Transfer-Encoding
   387		if chunked(te) {
   388			return -1, nil
   389		}
   390	
   391		// Logic based on Content-Length
   392		cl := strings.TrimSpace(header.Get("Content-Length"))
   393		if cl != "" {
   394			n, err := strconv.Atoi64(cl)
   395			if err != nil || n < 0 {
   396				return -1, &badStringError{"bad Content-Length", cl}
   397			}
   398			return n, nil
   399		} else {
   400			header.Del("Content-Length")
   401		}
   402	
   403		if !isResponse && requestMethod == "GET" {
   404			// RFC 2616 doesn't explicitly permit nor forbid an
   405			// entity-body on a GET request so we permit one if
   406			// declared, but we default to 0 here (not -1 below)
   407			// if there's no mention of a body.
   408			return 0, nil
   409		}
   410	
   411		// Logic based on media type. The purpose of the following code is just
   412		// to detect whether the unsupported "multipart/byteranges" is being
   413		// used. A proper Content-Type parser is needed in the future.
   414		if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
   415			return -1, ErrNotSupported
   416		}
   417	
   418		// Body-EOF logic based on other methods (like closing, or chunked coding)
   419		return -1, nil
   420	}
   421	
   422	// Determine whether to hang up after sending a request and body, or
   423	// receiving a response and body
   424	// 'header' is the request headers
   425	func shouldClose(major, minor int, header Header) bool {
   426		if major < 1 {
   427			return true
   428		} else if major == 1 && minor == 0 {
   429			if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
   430				return true
   431			}
   432			return false
   433		} else {
   434			// TODO: Should split on commas, toss surrounding white space,
   435			// and check each field.
   436			if strings.ToLower(header.Get("Connection")) == "close" {
   437				header.Del("Connection")
   438				return true
   439			}
   440		}
   441		return false
   442	}
   443	
   444	// Parse the trailer header
   445	func fixTrailer(header Header, te []string) (Header, os.Error) {
   446		raw := header.Get("Trailer")
   447		if raw == "" {
   448			return nil, nil
   449		}
   450	
   451		header.Del("Trailer")
   452		trailer := make(Header)
   453		keys := strings.Split(raw, ",")
   454		for _, key := range keys {
   455			key = CanonicalHeaderKey(strings.TrimSpace(key))
   456			switch key {
   457			case "Transfer-Encoding", "Trailer", "Content-Length":
   458				return nil, &badStringError{"bad trailer key", key}
   459			}
   460			trailer.Del(key)
   461		}
   462		if len(trailer) == 0 {
   463			return nil, nil
   464		}
   465		if !chunked(te) {
   466			// Trailer and no chunking
   467			return nil, ErrUnexpectedTrailer
   468		}
   469		return trailer, nil
   470	}
   471	
   472	// body turns a Reader into a ReadCloser.
   473	// Close ensures that the body has been fully read
   474	// and then reads the trailer if necessary.
   475	type body struct {
   476		io.Reader
   477		hdr     interface{}   // non-nil (Response or Request) value means read trailer
   478		r       *bufio.Reader // underlying wire-format reader for the trailer
   479		closing bool          // is the connection to be closed after reading body?
   480		closed  bool
   481	}
   482	
   483	// ErrBodyReadAfterClose is returned when reading a Request Body after
   484	// the body has been closed. This typically happens when the body is
   485	// read after an HTTP Handler calls WriteHeader or Write on its
   486	// ResponseWriter.
   487	var ErrBodyReadAfterClose = os.NewError("http: invalid Read on closed request Body")
   488	
   489	func (b *body) Read(p []byte) (n int, err os.Error) {
   490		if b.closed {
   491			return 0, ErrBodyReadAfterClose
   492		}
   493		return b.Reader.Read(p)
   494	}
   495	
   496	func (b *body) Close() os.Error {
   497		if b.closed {
   498			return nil
   499		}
   500		defer func() {
   501			b.closed = true
   502		}()
   503		if b.hdr == nil && b.closing {
   504			// no trailer and closing the connection next.
   505			// no point in reading to EOF.
   506			return nil
   507		}
   508	
   509		if _, err := io.Copy(ioutil.Discard, b); err != nil {
   510			return err
   511		}
   512	
   513		if b.hdr == nil { // not reading trailer
   514			return nil
   515		}
   516	
   517		// TODO(petar): Put trailer reader code here
   518	
   519		return nil
   520	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.