...
Run Format

Source file src/pkg/net/http/response.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	// HTTP Response reading and parsing.
     6	
     7	package http
     8	
     9	import (
    10		"bufio"
    11		"bytes"
    12		"crypto/tls"
    13		"errors"
    14		"io"
    15		"net/textproto"
    16		"net/url"
    17		"strconv"
    18		"strings"
    19	)
    20	
    21	var respExcludeHeader = map[string]bool{
    22		"Content-Length":    true,
    23		"Transfer-Encoding": true,
    24		"Trailer":           true,
    25	}
    26	
    27	// Response represents the response from an HTTP request.
    28	//
    29	type Response struct {
    30		Status     string // e.g. "200 OK"
    31		StatusCode int    // e.g. 200
    32		Proto      string // e.g. "HTTP/1.0"
    33		ProtoMajor int    // e.g. 1
    34		ProtoMinor int    // e.g. 0
    35	
    36		// Header maps header keys to values.  If the response had multiple
    37		// headers with the same key, they may be concatenated, with comma
    38		// delimiters.  (Section 4.2 of RFC 2616 requires that multiple headers
    39		// be semantically equivalent to a comma-delimited sequence.) Values
    40		// duplicated by other fields in this struct (e.g., ContentLength) are
    41		// omitted from Header.
    42		//
    43		// Keys in the map are canonicalized (see CanonicalHeaderKey).
    44		Header Header
    45	
    46		// Body represents the response body.
    47		//
    48		// The http Client and Transport guarantee that Body is always
    49		// non-nil, even on responses without a body or responses with
    50		// a zero-length body. It is the caller's responsibility to
    51		// close Body.
    52		//
    53		// The Body is automatically dechunked if the server replied
    54		// with a "chunked" Transfer-Encoding.
    55		Body io.ReadCloser
    56	
    57		// ContentLength records the length of the associated content.  The
    58		// value -1 indicates that the length is unknown.  Unless Request.Method
    59		// is "HEAD", values >= 0 indicate that the given number of bytes may
    60		// be read from Body.
    61		ContentLength int64
    62	
    63		// Contains transfer encodings from outer-most to inner-most. Value is
    64		// nil, means that "identity" encoding is used.
    65		TransferEncoding []string
    66	
    67		// Close records whether the header directed that the connection be
    68		// closed after reading Body.  The value is advice for clients: neither
    69		// ReadResponse nor Response.Write ever closes a connection.
    70		Close bool
    71	
    72		// Trailer maps trailer keys to values, in the same
    73		// format as the header.
    74		Trailer Header
    75	
    76		// The Request that was sent to obtain this Response.
    77		// Request's Body is nil (having already been consumed).
    78		// This is only populated for Client requests.
    79		Request *Request
    80	
    81		// TLS contains information about the TLS connection on which the
    82		// response was received. It is nil for unencrypted responses.
    83		// The pointer is shared between responses and should not be
    84		// modified.
    85		TLS *tls.ConnectionState
    86	}
    87	
    88	// Cookies parses and returns the cookies set in the Set-Cookie headers.
    89	func (r *Response) Cookies() []*Cookie {
    90		return readSetCookies(r.Header)
    91	}
    92	
    93	var ErrNoLocation = errors.New("http: no Location header in response")
    94	
    95	// Location returns the URL of the response's "Location" header,
    96	// if present.  Relative redirects are resolved relative to
    97	// the Response's Request.  ErrNoLocation is returned if no
    98	// Location header is present.
    99	func (r *Response) Location() (*url.URL, error) {
   100		lv := r.Header.Get("Location")
   101		if lv == "" {
   102			return nil, ErrNoLocation
   103		}
   104		if r.Request != nil && r.Request.URL != nil {
   105			return r.Request.URL.Parse(lv)
   106		}
   107		return url.Parse(lv)
   108	}
   109	
   110	// ReadResponse reads and returns an HTTP response from r.
   111	// The req parameter optionally specifies the Request that corresponds
   112	// to this Response. If nil, a GET request is assumed.
   113	// Clients must call resp.Body.Close when finished reading resp.Body.
   114	// After that call, clients can inspect resp.Trailer to find key/value
   115	// pairs included in the response trailer.
   116	func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
   117		tp := textproto.NewReader(r)
   118		resp := &Response{
   119			Request: req,
   120		}
   121	
   122		// Parse the first line of the response.
   123		line, err := tp.ReadLine()
   124		if err != nil {
   125			if err == io.EOF {
   126				err = io.ErrUnexpectedEOF
   127			}
   128			return nil, err
   129		}
   130		f := strings.SplitN(line, " ", 3)
   131		if len(f) < 2 {
   132			return nil, &badStringError{"malformed HTTP response", line}
   133		}
   134		reasonPhrase := ""
   135		if len(f) > 2 {
   136			reasonPhrase = f[2]
   137		}
   138		resp.Status = f[1] + " " + reasonPhrase
   139		resp.StatusCode, err = strconv.Atoi(f[1])
   140		if err != nil {
   141			return nil, &badStringError{"malformed HTTP status code", f[1]}
   142		}
   143	
   144		resp.Proto = f[0]
   145		var ok bool
   146		if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
   147			return nil, &badStringError{"malformed HTTP version", resp.Proto}
   148		}
   149	
   150		// Parse the response headers.
   151		mimeHeader, err := tp.ReadMIMEHeader()
   152		if err != nil {
   153			if err == io.EOF {
   154				err = io.ErrUnexpectedEOF
   155			}
   156			return nil, err
   157		}
   158		resp.Header = Header(mimeHeader)
   159	
   160		fixPragmaCacheControl(resp.Header)
   161	
   162		err = readTransfer(resp, r)
   163		if err != nil {
   164			return nil, err
   165		}
   166	
   167		return resp, nil
   168	}
   169	
   170	// RFC2616: Should treat
   171	//	Pragma: no-cache
   172	// like
   173	//	Cache-Control: no-cache
   174	func fixPragmaCacheControl(header Header) {
   175		if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
   176			if _, presentcc := header["Cache-Control"]; !presentcc {
   177				header["Cache-Control"] = []string{"no-cache"}
   178			}
   179		}
   180	}
   181	
   182	// ProtoAtLeast reports whether the HTTP protocol used
   183	// in the response is at least major.minor.
   184	func (r *Response) ProtoAtLeast(major, minor int) bool {
   185		return r.ProtoMajor > major ||
   186			r.ProtoMajor == major && r.ProtoMinor >= minor
   187	}
   188	
   189	// Writes the response (header, body and trailer) in wire format. This method
   190	// consults the following fields of the response:
   191	//
   192	//  StatusCode
   193	//  ProtoMajor
   194	//  ProtoMinor
   195	//  Request.Method
   196	//  TransferEncoding
   197	//  Trailer
   198	//  Body
   199	//  ContentLength
   200	//  Header, values for non-canonical keys will have unpredictable behavior
   201	//
   202	// Body is closed after it is sent.
   203	func (r *Response) Write(w io.Writer) error {
   204		// Status line
   205		text := r.Status
   206		if text == "" {
   207			var ok bool
   208			text, ok = statusText[r.StatusCode]
   209			if !ok {
   210				text = "status code " + strconv.Itoa(r.StatusCode)
   211			}
   212		}
   213		protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
   214		statusCode := strconv.Itoa(r.StatusCode) + " "
   215		text = strings.TrimPrefix(text, statusCode)
   216		if _, err := io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n"); err != nil {
   217			return err
   218		}
   219	
   220		// Clone it, so we can modify r1 as needed.
   221		r1 := new(Response)
   222		*r1 = *r
   223		if r1.ContentLength == 0 && r1.Body != nil {
   224			// Is it actually 0 length? Or just unknown?
   225			var buf [1]byte
   226			n, err := r1.Body.Read(buf[:])
   227			if err != nil && err != io.EOF {
   228				return err
   229			}
   230			if n == 0 {
   231				// Reset it to a known zero reader, in case underlying one
   232				// is unhappy being read repeatedly.
   233				r1.Body = eofReader
   234			} else {
   235				r1.ContentLength = -1
   236				r1.Body = struct {
   237					io.Reader
   238					io.Closer
   239				}{
   240					io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
   241					r.Body,
   242				}
   243			}
   244		}
   245		// If we're sending a non-chunked HTTP/1.1 response without a
   246		// content-length, the only way to do that is the old HTTP/1.0
   247		// way, by noting the EOF with a connection close, so we need
   248		// to set Close.
   249		if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) {
   250			r1.Close = true
   251		}
   252	
   253		// Process Body,ContentLength,Close,Trailer
   254		tw, err := newTransferWriter(r1)
   255		if err != nil {
   256			return err
   257		}
   258		err = tw.WriteHeader(w)
   259		if err != nil {
   260			return err
   261		}
   262	
   263		// Rest of header
   264		err = r.Header.WriteSubset(w, respExcludeHeader)
   265		if err != nil {
   266			return err
   267		}
   268	
   269		// contentLengthAlreadySent may have been already sent for
   270		// POST/PUT requests, even if zero length. See Issue 8180.
   271		contentLengthAlreadySent := tw.shouldSendContentLength()
   272		if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
   273			if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
   274				return err
   275			}
   276		}
   277	
   278		// End-of-header
   279		if _, err := io.WriteString(w, "\r\n"); err != nil {
   280			return err
   281		}
   282	
   283		// Write body and trailer
   284		err = tw.WriteBody(w)
   285		if err != nil {
   286			return err
   287		}
   288	
   289		// Success
   290		return nil
   291	}
   292	

View as plain text