github.com/rohankumardubey/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/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  	"errors"
    12  	"io"
    13  	"net/textproto"
    14  	"net/url"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  var respExcludeHeader = map[string]bool{
    20  	"Content-Length":    true,
    21  	"Transfer-Encoding": true,
    22  	"Trailer":           true,
    23  }
    24  
    25  // Response represents the response from an HTTP request.
    26  //
    27  type Response struct {
    28  	Status     string // e.g. "200 OK"
    29  	StatusCode int    // e.g. 200
    30  	Proto      string // e.g. "HTTP/1.0"
    31  	ProtoMajor int    // e.g. 1
    32  	ProtoMinor int    // e.g. 0
    33  
    34  	// Header maps header keys to values.  If the response had multiple
    35  	// headers with the same key, they may be concatenated, with comma
    36  	// delimiters.  (Section 4.2 of RFC 2616 requires that multiple headers
    37  	// be semantically equivalent to a comma-delimited sequence.) Values
    38  	// duplicated by other fields in this struct (e.g., ContentLength) are
    39  	// omitted from Header.
    40  	//
    41  	// Keys in the map are canonicalized (see CanonicalHeaderKey).
    42  	Header Header
    43  
    44  	// Body represents the response body.
    45  	//
    46  	// The http Client and Transport guarantee that Body is always
    47  	// non-nil, even on responses without a body or responses with
    48  	// a zero-lengthed body.
    49  	//
    50  	// The Body is automatically dechunked if the server replied
    51  	// with a "chunked" Transfer-Encoding.
    52  	Body io.ReadCloser
    53  
    54  	// ContentLength records the length of the associated content.  The
    55  	// value -1 indicates that the length is unknown.  Unless Request.Method
    56  	// is "HEAD", values >= 0 indicate that the given number of bytes may
    57  	// be read from Body.
    58  	ContentLength int64
    59  
    60  	// Contains transfer encodings from outer-most to inner-most. Value is
    61  	// nil, means that "identity" encoding is used.
    62  	TransferEncoding []string
    63  
    64  	// Close records whether the header directed that the connection be
    65  	// closed after reading Body.  The value is advice for clients: neither
    66  	// ReadResponse nor Response.Write ever closes a connection.
    67  	Close bool
    68  
    69  	// Trailer maps trailer keys to values, in the same
    70  	// format as the header.
    71  	Trailer Header
    72  
    73  	// The Request that was sent to obtain this Response.
    74  	// Request's Body is nil (having already been consumed).
    75  	// This is only populated for Client requests.
    76  	Request *Request
    77  }
    78  
    79  // Cookies parses and returns the cookies set in the Set-Cookie headers.
    80  func (r *Response) Cookies() []*Cookie {
    81  	return readSetCookies(r.Header)
    82  }
    83  
    84  var ErrNoLocation = errors.New("http: no Location header in response")
    85  
    86  // Location returns the URL of the response's "Location" header,
    87  // if present.  Relative redirects are resolved relative to
    88  // the Response's Request.  ErrNoLocation is returned if no
    89  // Location header is present.
    90  func (r *Response) Location() (*url.URL, error) {
    91  	lv := r.Header.Get("Location")
    92  	if lv == "" {
    93  		return nil, ErrNoLocation
    94  	}
    95  	if r.Request != nil && r.Request.URL != nil {
    96  		return r.Request.URL.Parse(lv)
    97  	}
    98  	return url.Parse(lv)
    99  }
   100  
   101  // ReadResponse reads and returns an HTTP response from r.
   102  // The req parameter optionally specifies the Request that corresponds
   103  // to this Response. If nil, a GET request is assumed.
   104  // Clients must call resp.Body.Close when finished reading resp.Body.
   105  // After that call, clients can inspect resp.Trailer to find key/value
   106  // pairs included in the response trailer.
   107  func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
   108  	tp := textproto.NewReader(r)
   109  	resp := &Response{
   110  		Request: req,
   111  	}
   112  
   113  	// Parse the first line of the response.
   114  	line, err := tp.ReadLine()
   115  	if err != nil {
   116  		if err == io.EOF {
   117  			err = io.ErrUnexpectedEOF
   118  		}
   119  		return nil, err
   120  	}
   121  	f := strings.SplitN(line, " ", 3)
   122  	if len(f) < 2 {
   123  		return nil, &badStringError{"malformed HTTP response", line}
   124  	}
   125  	reasonPhrase := ""
   126  	if len(f) > 2 {
   127  		reasonPhrase = f[2]
   128  	}
   129  	resp.Status = f[1] + " " + reasonPhrase
   130  	resp.StatusCode, err = strconv.Atoi(f[1])
   131  	if err != nil {
   132  		return nil, &badStringError{"malformed HTTP status code", f[1]}
   133  	}
   134  
   135  	resp.Proto = f[0]
   136  	var ok bool
   137  	if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
   138  		return nil, &badStringError{"malformed HTTP version", resp.Proto}
   139  	}
   140  
   141  	// Parse the response headers.
   142  	mimeHeader, err := tp.ReadMIMEHeader()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	resp.Header = Header(mimeHeader)
   147  
   148  	fixPragmaCacheControl(resp.Header)
   149  
   150  	err = readTransfer(resp, r)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	return resp, nil
   156  }
   157  
   158  // RFC2616: Should treat
   159  //	Pragma: no-cache
   160  // like
   161  //	Cache-Control: no-cache
   162  func fixPragmaCacheControl(header Header) {
   163  	if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
   164  		if _, presentcc := header["Cache-Control"]; !presentcc {
   165  			header["Cache-Control"] = []string{"no-cache"}
   166  		}
   167  	}
   168  }
   169  
   170  // ProtoAtLeast reports whether the HTTP protocol used
   171  // in the response is at least major.minor.
   172  func (r *Response) ProtoAtLeast(major, minor int) bool {
   173  	return r.ProtoMajor > major ||
   174  		r.ProtoMajor == major && r.ProtoMinor >= minor
   175  }
   176  
   177  // Writes the response (header, body and trailer) in wire format. This method
   178  // consults the following fields of the response:
   179  //
   180  //  StatusCode
   181  //  ProtoMajor
   182  //  ProtoMinor
   183  //  Request.Method
   184  //  TransferEncoding
   185  //  Trailer
   186  //  Body
   187  //  ContentLength
   188  //  Header, values for non-canonical keys will have unpredictable behavior
   189  //
   190  func (r *Response) Write(w io.Writer) error {
   191  
   192  	// Status line
   193  	text := r.Status
   194  	if text == "" {
   195  		var ok bool
   196  		text, ok = statusText[r.StatusCode]
   197  		if !ok {
   198  			text = "status code " + strconv.Itoa(r.StatusCode)
   199  		}
   200  	}
   201  	protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
   202  	statusCode := strconv.Itoa(r.StatusCode) + " "
   203  	text = strings.TrimPrefix(text, statusCode)
   204  	io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
   205  
   206  	// Process Body,ContentLength,Close,Trailer
   207  	tw, err := newTransferWriter(r)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	err = tw.WriteHeader(w)
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	// Rest of header
   217  	err = r.Header.WriteSubset(w, respExcludeHeader)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	// End-of-header
   223  	io.WriteString(w, "\r\n")
   224  
   225  	// Write body and trailer
   226  	err = tw.WriteBody(w)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	// Success
   232  	return nil
   233  }