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.
5
// HTTP Response reading and parsing.
20
var respExcludeHeader = map[string]bool{
21
"Content-Length": true,
22
"Transfer-Encoding": true,
26
// Response represents the response from an HTTP request.
28
type Response struct {
29
Status string // e.g. "200 OK"
30
StatusCode int // e.g. 200
31
Proto string // e.g. "HTTP/1.0"
32
ProtoMajor int // e.g. 1
33
ProtoMinor int // e.g. 0
35
// Header maps header keys to values. If the response had multiple
36
// headers with the same key, they may be concatenated, with comma
37
// delimiters. (Section 4.2 of RFC 2616 requires that multiple headers
38
// be semantically equivalent to a comma-delimited sequence.) Values
39
// duplicated by other fields in this struct (e.g., ContentLength) are
40
// omitted from Header.
42
// Keys in the map are canonicalized (see CanonicalHeaderKey).
45
// Body represents the response body.
47
// The http Client and Transport guarantee that Body is always
48
// non-nil, even on responses without a body or responses with
49
// a zero-lengthed body.
51
// The Body is automatically dechunked if the server replied
52
// with a "chunked" Transfer-Encoding.
55
// ContentLength records the length of the associated content. The
56
// value -1 indicates that the length is unknown. Unless Request.Method
57
// is "HEAD", values >= 0 indicate that the given number of bytes may
61
// Contains transfer encodings from outer-most to inner-most. Value is
62
// nil, means that "identity" encoding is used.
63
TransferEncoding []string
65
// Close records whether the header directed that the connection be
66
// closed after reading Body. The value is advice for clients: neither
67
// ReadResponse nor Response.Write ever closes a connection.
70
// Trailer maps trailer keys to values, in the same
71
// format as the header.
74
// The Request that was sent to obtain this Response.
75
// Request's Body is nil (having already been consumed).
76
// This is only populated for Client requests.
79
// TLS contains information about the TLS connection on which the
80
// response was received. It is nil for unencrypted responses.
81
// The pointer is shared between responses and should not be
83
TLS *tls.ConnectionState
86
// Cookies parses and returns the cookies set in the Set-Cookie headers.
87
func (r *Response) Cookies() []*Cookie {
88
return readSetCookies(r.Header)
91
var ErrNoLocation = errors.New("http: no Location header in response")
93
// Location returns the URL of the response's "Location" header,
94
// if present. Relative redirects are resolved relative to
95
// the Response's Request. ErrNoLocation is returned if no
96
// Location header is present.
97
func (r *Response) Location() (*url.URL, error) {
98
lv := r.Header.Get("Location")
100
return nil, ErrNoLocation
102
if r.Request != nil && r.Request.URL != nil {
103
return r.Request.URL.Parse(lv)
108
// ReadResponse reads and returns an HTTP response from r.
109
// The req parameter optionally specifies the Request that corresponds
110
// to this Response. If nil, a GET request is assumed.
111
// Clients must call resp.Body.Close when finished reading resp.Body.
112
// After that call, clients can inspect resp.Trailer to find key/value
113
// pairs included in the response trailer.
114
func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
115
tp := textproto.NewReader(r)
120
// Parse the first line of the response.
121
line, err := tp.ReadLine()
124
err = io.ErrUnexpectedEOF
128
f := strings.SplitN(line, " ", 3)
130
return nil, &badStringError{"malformed HTTP response", line}
136
resp.Status = f[1] + " " + reasonPhrase
137
resp.StatusCode, err = strconv.Atoi(f[1])
139
return nil, &badStringError{"malformed HTTP status code", f[1]}
144
if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
145
return nil, &badStringError{"malformed HTTP version", resp.Proto}
148
// Parse the response headers.
149
mimeHeader, err := tp.ReadMIMEHeader()
152
err = io.ErrUnexpectedEOF
156
resp.Header = Header(mimeHeader)
158
fixPragmaCacheControl(resp.Header)
160
err = readTransfer(resp, r)
168
// RFC2616: Should treat
171
// Cache-Control: no-cache
172
func fixPragmaCacheControl(header Header) {
173
if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
174
if _, presentcc := header["Cache-Control"]; !presentcc {
175
header["Cache-Control"] = []string{"no-cache"}
180
// ProtoAtLeast reports whether the HTTP protocol used
181
// in the response is at least major.minor.
182
func (r *Response) ProtoAtLeast(major, minor int) bool {
183
return r.ProtoMajor > major ||
184
r.ProtoMajor == major && r.ProtoMinor >= minor
187
// Writes the response (header, body and trailer) in wire format. This method
188
// consults the following fields of the response:
198
// Header, values for non-canonical keys will have unpredictable behavior
200
// Body is closed after it is sent.
201
func (r *Response) Write(w io.Writer) error {
207
text, ok = statusText[r.StatusCode]
209
text = "status code " + strconv.Itoa(r.StatusCode)
212
protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
213
statusCode := strconv.Itoa(r.StatusCode) + " "
214
text = strings.TrimPrefix(text, statusCode)
215
io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
217
// Process Body,ContentLength,Close,Trailer
218
tw, err := newTransferWriter(r)
222
err = tw.WriteHeader(w)
228
err = r.Header.WriteSubset(w, respExcludeHeader)
234
io.WriteString(w, "\r\n")
236
// Write body and trailer
237
err = tw.WriteBody(w)