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	"fmt"
15	"io"
16	"net/textproto"
17	"net/url"
18	"strconv"
19	"strings"
20
21	"golang.org/x/net/http/httpguts"
22)
23
24var respExcludeHeader = map[string]bool{
25	"Content-Length":    true,
26	"Transfer-Encoding": true,
27	"Trailer":           true,
28}
29
30// Response represents the response from an HTTP request.
31//
32// The [Client] and [Transport] return Responses from servers once
33// the response headers have been received. The response body
34// is streamed on demand as the Body field is read.
35type Response struct {
36	Status     string // e.g. "200 OK"
37	StatusCode int    // e.g. 200
38	Proto      string // e.g. "HTTP/1.0"
39	ProtoMajor int    // e.g. 1
40	ProtoMinor int    // e.g. 0
41
42	// Header maps header keys to values. If the response had multiple
43	// headers with the same key, they may be concatenated, with comma
44	// delimiters.  (RFC 7230, section 3.2.2 requires that multiple headers
45	// be semantically equivalent to a comma-delimited sequence.) When
46	// Header values are duplicated by other fields in this struct (e.g.,
47	// ContentLength, TransferEncoding, Trailer), the field values are
48	// authoritative.
49	//
50	// Keys in the map are canonicalized (see CanonicalHeaderKey).
51	Header Header
52
53	// Body represents the response body.
54	//
55	// The response body is streamed on demand as the Body field
56	// is read. If the network connection fails or the server
57	// terminates the response, Body.Read calls return an error.
58	//
59	// The http Client and Transport guarantee that Body is always
60	// non-nil, even on responses without a body or responses with
61	// a zero-length body. It is the caller's responsibility to
62	// close Body. The default HTTP client's Transport may not
63	// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
64	// not read to completion and closed.
65	//
66	// The Body is automatically dechunked if the server replied
67	// with a "chunked" Transfer-Encoding.
68	//
69	// As of Go 1.12, the Body will also implement io.Writer
70	// on a successful "101 Switching Protocols" response,
71	// as used by WebSockets and HTTP/2's "h2c" mode.
72	Body io.ReadCloser
73
74	// ContentLength records the length of the associated content. The
75	// value -1 indicates that the length is unknown. Unless Request.Method
76	// is "HEAD", values >= 0 indicate that the given number of bytes may
77	// be read from Body.
78	ContentLength int64
79
80	// Contains transfer encodings from outer-most to inner-most. Value is
81	// nil, means that "identity" encoding is used.
82	TransferEncoding []string
83
84	// Close records whether the header directed that the connection be
85	// closed after reading Body. The value is advice for clients: neither
86	// ReadResponse nor Response.Write ever closes a connection.
87	Close bool
88
89	// Uncompressed reports whether the response was sent compressed but
90	// was decompressed by the http package. When true, reading from
91	// Body yields the uncompressed content instead of the compressed
92	// content actually set from the server, ContentLength is set to -1,
93	// and the "Content-Length" and "Content-Encoding" fields are deleted
94	// from the responseHeader. To get the original response from
95	// the server, set Transport.DisableCompression to true.
96	Uncompressed bool
97
98	// Trailer maps trailer keys to values in the same
99	// format as Header.
100	//
101	// The Trailer initially contains only nil values, one for
102	// each key specified in the server's "Trailer" header
103	// value. Those values are not added to Header.
104	//
105	// Trailer must not be accessed concurrently with Read calls
106	// on the Body.
107	//
108	// After Body.Read has returned io.EOF, Trailer will contain
109	// any trailer values sent by the server.
110	Trailer Header
111
112	// Request is the request that was sent to obtain this Response.
113	// Request's Body is nil (having already been consumed).
114	// This is only populated for Client requests.
115	Request *Request
116
117	// TLS contains information about the TLS connection on which the
118	// response was received. It is nil for unencrypted responses.
119	// The pointer is shared between responses and should not be
120	// modified.
121	TLS *tls.ConnectionState
122}
123
124// Cookies parses and returns the cookies set in the Set-Cookie headers.
125func (r *Response) Cookies() []*Cookie {
126	return readSetCookies(r.Header)
127}
128
129// ErrNoLocation is returned by the [Response.Location] method
130// when no Location header is present.
131var ErrNoLocation = errors.New("http: no Location header in response")
132
133// Location returns the URL of the response's "Location" header,
134// if present. Relative redirects are resolved relative to
135// [Response.Request]. [ErrNoLocation] is returned if no
136// Location header is present.
137func (r *Response) Location() (*url.URL, error) {
138	lv := r.Header.Get("Location")
139	if lv == "" {
140		return nil, ErrNoLocation
141	}
142	if r.Request != nil && r.Request.URL != nil {
143		return r.Request.URL.Parse(lv)
144	}
145	return url.Parse(lv)
146}
147
148// ReadResponse reads and returns an HTTP response from r.
149// The req parameter optionally specifies the [Request] that corresponds
150// to this [Response]. If nil, a GET request is assumed.
151// Clients must call resp.Body.Close when finished reading resp.Body.
152// After that call, clients can inspect resp.Trailer to find key/value
153// pairs included in the response trailer.
154func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
155	tp := textproto.NewReader(r)
156	resp := &Response{
157		Request: req,
158	}
159
160	// Parse the first line of the response.
161	line, err := tp.ReadLine()
162	if err != nil {
163		if err == io.EOF {
164			err = io.ErrUnexpectedEOF
165		}
166		return nil, err
167	}
168	proto, status, ok := strings.Cut(line, " ")
169	if !ok {
170		return nil, badStringError("malformed HTTP response", line)
171	}
172	resp.Proto = proto
173	resp.Status = strings.TrimLeft(status, " ")
174
175	statusCode, _, _ := strings.Cut(resp.Status, " ")
176	if len(statusCode) != 3 {
177		return nil, badStringError("malformed HTTP status code", statusCode)
178	}
179	resp.StatusCode, err = strconv.Atoi(statusCode)
180	if err != nil || resp.StatusCode < 0 {
181		return nil, badStringError("malformed HTTP status code", statusCode)
182	}
183	if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
184		return nil, badStringError("malformed HTTP version", resp.Proto)
185	}
186
187	// Parse the response headers.
188	mimeHeader, err := tp.ReadMIMEHeader()
189	if err != nil {
190		if err == io.EOF {
191			err = io.ErrUnexpectedEOF
192		}
193		return nil, err
194	}
195	resp.Header = Header(mimeHeader)
196
197	fixPragmaCacheControl(resp.Header)
198
199	err = readTransfer(resp, r)
200	if err != nil {
201		return nil, err
202	}
203
204	return resp, nil
205}
206
207// RFC 7234, section 5.4: Should treat
208//
209//	Pragma: no-cache
210//
211// like
212//
213//	Cache-Control: no-cache
214func fixPragmaCacheControl(header Header) {
215	if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
216		if _, presentcc := header["Cache-Control"]; !presentcc {
217			header["Cache-Control"] = []string{"no-cache"}
218		}
219	}
220}
221
222// ProtoAtLeast reports whether the HTTP protocol used
223// in the response is at least major.minor.
224func (r *Response) ProtoAtLeast(major, minor int) bool {
225	return r.ProtoMajor > major ||
226		r.ProtoMajor == major && r.ProtoMinor >= minor
227}
228
229// Write writes r to w in the HTTP/1.x server response format,
230// including the status line, headers, body, and optional trailer.
231//
232// This method consults the following fields of the response r:
233//
234//	StatusCode
235//	ProtoMajor
236//	ProtoMinor
237//	Request.Method
238//	TransferEncoding
239//	Trailer
240//	Body
241//	ContentLength
242//	Header, values for non-canonical keys will have unpredictable behavior
243//
244// The Response Body is closed after it is sent.
245func (r *Response) Write(w io.Writer) error {
246	// Status line
247	text := r.Status
248	if text == "" {
249		text = StatusText(r.StatusCode)
250		if text == "" {
251			text = "status code " + strconv.Itoa(r.StatusCode)
252		}
253	} else {
254		// Just to reduce stutter, if user set r.Status to "200 OK" and StatusCode to 200.
255		// Not important.
256		text = strings.TrimPrefix(text, strconv.Itoa(r.StatusCode)+" ")
257	}
258
259	if _, err := fmt.Fprintf(w, "HTTP/%d.%d %03d %s\r\n", r.ProtoMajor, r.ProtoMinor, r.StatusCode, text); err != nil {
260		return err
261	}
262
263	// Clone it, so we can modify r1 as needed.
264	r1 := new(Response)
265	*r1 = *r
266	if r1.ContentLength == 0 && r1.Body != nil {
267		// Is it actually 0 length? Or just unknown?
268		var buf [1]byte
269		n, err := r1.Body.Read(buf[:])
270		if err != nil && err != io.EOF {
271			return err
272		}
273		if n == 0 {
274			// Reset it to a known zero reader, in case underlying one
275			// is unhappy being read repeatedly.
276			r1.Body = NoBody
277		} else {
278			r1.ContentLength = -1
279			r1.Body = struct {
280				io.Reader
281				io.Closer
282			}{
283				io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
284				r.Body,
285			}
286		}
287	}
288	// If we're sending a non-chunked HTTP/1.1 response without a
289	// content-length, the only way to do that is the old HTTP/1.0
290	// way, by noting the EOF with a connection close, so we need
291	// to set Close.
292	if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) && !r1.Uncompressed {
293		r1.Close = true
294	}
295
296	// Process Body,ContentLength,Close,Trailer
297	tw, err := newTransferWriter(r1)
298	if err != nil {
299		return err
300	}
301	err = tw.writeHeader(w, nil)
302	if err != nil {
303		return err
304	}
305
306	// Rest of header
307	err = r.Header.WriteSubset(w, respExcludeHeader)
308	if err != nil {
309		return err
310	}
311
312	// contentLengthAlreadySent may have been already sent for
313	// POST/PUT requests, even if zero length. See Issue 8180.
314	contentLengthAlreadySent := tw.shouldSendContentLength()
315	if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent && bodyAllowedForStatus(r.StatusCode) {
316		if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
317			return err
318		}
319	}
320
321	// End-of-header
322	if _, err := io.WriteString(w, "\r\n"); err != nil {
323		return err
324	}
325
326	// Write body and trailer
327	err = tw.writeBody(w)
328	if err != nil {
329		return err
330	}
331
332	// Success
333	return nil
334}
335
336func (r *Response) closeBody() {
337	if r.Body != nil {
338		r.Body.Close()
339	}
340}
341
342// bodyIsWritable reports whether the Body supports writing. The
343// Transport returns Writable bodies for 101 Switching Protocols
344// responses.
345// The Transport uses this method to determine whether a persistent
346// connection is done being managed from its perspective. Once we
347// return a writable response body to a user, the net/http package is
348// done managing that connection.
349func (r *Response) bodyIsWritable() bool {
350	_, ok := r.Body.(io.Writer)
351	return ok
352}
353
354// isProtocolSwitch reports whether the response code and header
355// indicate a successful protocol upgrade response.
356func (r *Response) isProtocolSwitch() bool {
357	return isProtocolSwitchResponse(r.StatusCode, r.Header)
358}
359
360// isProtocolSwitchResponse reports whether the response code and
361// response header indicate a successful protocol upgrade response.
362func isProtocolSwitchResponse(code int, h Header) bool {
363	return code == StatusSwitchingProtocols && isProtocolSwitchHeader(h)
364}
365
366// isProtocolSwitchHeader reports whether the request or response header
367// is for a protocol switch.
368func isProtocolSwitchHeader(h Header) bool {
369	return h.Get("Upgrade") != "" &&
370		httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade")
371}
372