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