| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // HTTP Response reading and parsing. | |
| package http | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "crypto/tls" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "math" | |
| "net/textproto" | |
| "net/url" | |
| "strconv" | |
| "strings" | |
| "golang.org/x/net/http/httpguts" | |
| ) | |
| var respExcludeHeader = map[string]bool{ | |
| "Content-Length": true, | |
| "Transfer-Encoding": true, | |
| "Trailer": true, | |
| } | |
| // Response represents the response from an HTTP request. | |
| // | |
| // The [Client] and [Transport] return Responses from servers once | |
| // the response headers have been received. The response body | |
| // is streamed on demand as the Body field is read. | |
| type Response struct { | |
| Status string // e.g. "200 OK" | |
| StatusCode int // e.g. 200 | |
| Proto string // e.g. "HTTP/1.0" | |
| ProtoMajor int // e.g. 1 | |
| ProtoMinor int // e.g. 0 | |
| // Header maps header keys to values. If the response had multiple | |
| // headers with the same key, they may be concatenated, with comma | |
| // delimiters. (RFC 7230, section 3.2.2 requires that multiple headers | |
| // be semantically equivalent to a comma-delimited sequence.) When | |
| // Header values are duplicated by other fields in this struct (e.g., | |
| // ContentLength, TransferEncoding, Trailer), the field values are | |
| // authoritative. | |
| // | |
| // Keys in the map are canonicalized (see CanonicalHeaderKey). | |
| Header Header | |
| // Body represents the response body. | |
| // | |
| // The response body is streamed on demand as the Body field | |
| // is read. If the network connection fails or the server | |
| // terminates the response, Body.Read calls return an error. | |
| // | |
| // The http Client and Transport guarantee that Body is always | |
| // non-nil, even on responses without a body or responses with | |
| // a zero-length body. It is the caller's responsibility to | |
| // close Body. The default HTTP client's Transport may not | |
| // reuse HTTP/1.x "keep-alive" TCP connections if the Body is | |
| // not read to completion and closed; however, manually reading | |
| // the body to completion should not be needed in most cases, | |
| // as closing the body will also cause the body to be read to | |
| // completion asynchronously, up to a conservative limit. | |
| // | |
| // The Body is automatically dechunked if the server replied | |
| // with a "chunked" Transfer-Encoding. | |
| // | |
| // As of Go 1.12, the Body will also implement io.Writer | |
| // on a successful "101 Switching Protocols" response, | |
| // as used by WebSockets and HTTP/2's "h2c" mode. | |
| Body io.ReadCloser | |
| // ContentLength records the length of the associated content. The | |
| // value -1 indicates that the length is unknown. Unless Request.Method | |
| // is "HEAD", values >= 0 indicate that the given number of bytes may | |
| // be read from Body. | |
| ContentLength int64 | |
| // Contains transfer encodings from outer-most to inner-most. Value is | |
| // nil, means that "identity" encoding is used. | |
| TransferEncoding []string | |
| // Close records whether the header directed that the connection be | |
| // closed after reading Body. The value is advice for clients: neither | |
| // ReadResponse nor Response.Write ever closes a connection. | |
| Close bool | |
| // Uncompressed reports whether the response was sent compressed but | |
| // was decompressed by the http package. When true, reading from | |
| // Body yields the uncompressed content instead of the compressed | |
| // content actually set from the server, ContentLength is set to -1, | |
| // and the "Content-Length" and "Content-Encoding" fields are deleted | |
| // from the responseHeader. To get the original response from | |
| // the server, set Transport.DisableCompression to true. | |
| Uncompressed bool | |
| // Trailer maps trailer keys to values in the same | |
| // format as Header. | |
| // | |
| // The Trailer initially contains only nil values, one for | |
| // each key specified in the server's "Trailer" header | |
| // value. Those values are not added to Header. | |
| // | |
| // Trailer must not be accessed concurrently with Read calls | |
| // on the Body. | |
| // | |
| // After Body.Read has returned io.EOF, Trailer will contain | |
| // any trailer values sent by the server. | |
| Trailer Header | |
| // Request is the request that was sent to obtain this Response. | |
| // Request's Body is nil (having already been consumed). | |
| // This is only populated for Client requests. | |
| Request *Request | |
| // TLS contains information about the TLS connection on which the | |
| // response was received. It is nil for unencrypted responses. | |
| // The pointer is shared between responses and should not be | |
| // modified. | |
| TLS *tls.ConnectionState | |
| } | |
| // Cookies parses and returns the cookies set in the Set-Cookie headers. | |
| func (r *Response) Cookies() []*Cookie { | |
| return readSetCookies(r.Header) | |
| } | |
| // ErrNoLocation is returned by the [Response.Location] method | |
| // when no Location header is present. | |
| var ErrNoLocation = errors.New("http: no Location header in response") | |
| // Location returns the URL of the response's "Location" header, | |
| // if present. Relative redirects are resolved relative to | |
| // [Response.Request]. [ErrNoLocation] is returned if no | |
| // Location header is present. | |
| func (r *Response) Location() (*url.URL, error) { | |
| lv := r.Header.Get("Location") | |
| if lv == "" { | |
| return nil, ErrNoLocation | |
| } | |
| if r.Request != nil && r.Request.URL != nil { | |
| return r.Request.URL.Parse(lv) | |
| } | |
| return url.Parse(lv) | |
| } | |
| // ReadResponse reads and returns an HTTP response from r. | |
| // The req parameter optionally specifies the [Request] that corresponds | |
| // to this [Response]. If nil, a GET request is assumed. | |
| // Clients must call resp.Body.Close when finished reading resp.Body. | |
| // After that call, clients can inspect resp.Trailer to find key/value | |
| // pairs included in the response trailer. | |
| func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { | |
| tp := textproto.NewReader(r) | |
| resp := &Response{ | |
| Request: req, | |
| } | |
| // Parse the first line of the response. | |
| line, err := tp.ReadLine() | |
| if err != nil { | |
| if err == io.EOF { | |
| err = io.ErrUnexpectedEOF | |
| } | |
| return nil, err | |
| } | |
| proto, status, ok := strings.Cut(line, " ") | |
| if !ok { | |
| return nil, badStringError("malformed HTTP response", line) | |
| } | |
| resp.Proto = proto | |
| resp.Status = strings.TrimLeft(status, " ") | |
| statusCode, _, _ := strings.Cut(resp.Status, " ") | |
| if len(statusCode) != 3 { | |
| return nil, badStringError("malformed HTTP status code", statusCode) | |
| } | |
| resp.StatusCode, err = strconv.Atoi(statusCode) | |
| if err != nil || resp.StatusCode < 0 { | |
| return nil, badStringError("malformed HTTP status code", statusCode) | |
| } | |
| if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok { | |
| return nil, badStringError("malformed HTTP version", resp.Proto) | |
| } | |
| // Parse the response headers. | |
| mimeHeader, err := tp.ReadMIMEHeader() | |
| if err != nil { | |
| if err == io.EOF { | |
| err = io.ErrUnexpectedEOF | |
| } | |
| return nil, err | |
| } | |
| resp.Header = Header(mimeHeader) | |
| fixPragmaCacheControl(resp.Header) | |
| err = readTransfer(resp, r, math.MaxInt64) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return resp, nil | |
| } | |
| // RFC 7234, section 5.4: Should treat | |
| // | |
| // Pragma: no-cache | |
| // | |
| // like | |
| // | |
| // Cache-Control: no-cache | |
| func fixPragmaCacheControl(header Header) { | |
| if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { | |
| if _, presentcc := header["Cache-Control"]; !presentcc { | |
| header["Cache-Control"] = []string{"no-cache"} | |
| } | |
| } | |
| } | |
| // ProtoAtLeast reports whether the HTTP protocol used | |
| // in the response is at least major.minor. | |
| func (r *Response) ProtoAtLeast(major, minor int) bool { | |
| return r.ProtoMajor > major || | |
| r.ProtoMajor == major && r.ProtoMinor >= minor | |
| } | |
| // Write writes r to w in the HTTP/1.x server response format, | |
| // including the status line, headers, body, and optional trailer. | |
| // | |
| // This method consults the following fields of the response r: | |
| // | |
| // StatusCode | |
| // ProtoMajor | |
| // ProtoMinor | |
| // Request.Method | |
| // TransferEncoding | |
| // Trailer | |
| // Body | |
| // ContentLength | |
| // Header, values for non-canonical keys will have unpredictable behavior | |
| // | |
| // The Response Body is closed after it is sent. | |
| func (r *Response) Write(w io.Writer) error { | |
| // Status line | |
| text := r.Status | |
| if text == "" { | |
| text = StatusText(r.StatusCode) | |
| if text == "" { | |
| text = "status code " + strconv.Itoa(r.StatusCode) | |
| } | |
| } else { | |
| // Just to reduce stutter, if user set r.Status to "200 OK" and StatusCode to 200. | |
| // Not important. | |
| text = strings.TrimPrefix(text, strconv.Itoa(r.StatusCode)+" ") | |
| } | |
| if _, err := fmt.Fprintf(w, "HTTP/%d.%d %03d %s\r\n", r.ProtoMajor, r.ProtoMinor, r.StatusCode, text); err != nil { | |
| return err | |
| } | |
| // Clone it, so we can modify r1 as needed. | |
| r1 := new(Response) | |
| *r1 = *r | |
| if r1.ContentLength == 0 && r1.Body != nil { | |
| // Is it actually 0 length? Or just unknown? | |
| var buf [1]byte | |
| n, err := r1.Body.Read(buf[:]) | |
| if err != nil && err != io.EOF { | |
| return err | |
| } | |
| if n == 0 { | |
| // Reset it to a known zero reader, in case underlying one | |
| // is unhappy being read repeatedly. | |
| r1.Body = NoBody | |
| } else { | |
| r1.ContentLength = -1 | |
| r1.Body = struct { | |
| io.Reader | |
| io.Closer | |
| }{ | |
| io.MultiReader(bytes.NewReader(buf[:1]), r.Body), | |
| r.Body, | |
| } | |
| } | |
| } | |
| // If we're sending a non-chunked HTTP/1.1 response without a | |
| // content-length, the only way to do that is the old HTTP/1.0 | |
| // way, by noting the EOF with a connection close, so we need | |
| // to set Close. | |
| if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) && !r1.Uncompressed { | |
| r1.Close = true | |
| } | |
| // Process Body,ContentLength,Close,Trailer | |
| tw, err := newTransferWriter(r1) | |
| if err != nil { | |
| return err | |
| } | |
| err = tw.writeHeader(w, nil) | |
| if err != nil { | |
| return err | |
| } | |
| // Rest of header | |
| err = r.Header.WriteSubset(w, respExcludeHeader) | |
| if err != nil { | |
| return err | |
| } | |
| // contentLengthAlreadySent may have been already sent for | |
| // POST/PUT requests, even if zero length. See Issue 8180. | |
| contentLengthAlreadySent := tw.shouldSendContentLength() | |
| if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent && bodyAllowedForStatus(r.StatusCode) { | |
| if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil { | |
| return err | |
| } | |
| } | |
| // End-of-header | |
| if _, err := io.WriteString(w, "\r\n"); err != nil { | |
| return err | |
| } | |
| // Write body and trailer | |
| err = tw.writeBody(w) | |
| if err != nil { | |
| return err | |
| } | |
| // Success | |
| return nil | |
| } | |
| func (r *Response) closeBody() { | |
| if r.Body != nil { | |
| r.Body.Close() | |
| } | |
| } | |
| // bodyIsWritable reports whether the Body supports writing. The | |
| // Transport returns Writable bodies for 101 Switching Protocols | |
| // responses. | |
| // The Transport uses this method to determine whether a persistent | |
| // connection is done being managed from its perspective. Once we | |
| // return a writable response body to a user, the net/http package is | |
| // done managing that connection. | |
| func (r *Response) bodyIsWritable() bool { | |
| _, ok := r.Body.(io.Writer) | |
| return ok | |
| } | |
| // isProtocolSwitch reports whether the response code and header | |
| // indicate a successful protocol upgrade response. | |
| func (r *Response) isProtocolSwitch() bool { | |
| return isProtocolSwitchResponse(r.StatusCode, r.Header) | |
| } | |
| // isProtocolSwitchResponse reports whether the response code and | |
| // response header indicate a successful protocol upgrade response. | |
| func isProtocolSwitchResponse(code int, h Header) bool { | |
| return code == StatusSwitchingProtocols && isProtocolSwitchHeader(h) | |
| } | |
| // isProtocolSwitchHeader reports whether the request or response header | |
| // is for a protocol switch. | |
| func isProtocolSwitchHeader(h Header) bool { | |
| return h.Get("Upgrade") != "" && | |
| httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") | |
| } |