github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/net/http/response.go (about)

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