Source File
response.go
Belonging Package
net/http
// 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 httpimport ()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. 200Proto string // e.g. "HTTP/1.0"ProtoMajor int // e.g. 1ProtoMinor 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.//// 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 ( *Response) () []*Cookie {return readSetCookies(.Header)}// ErrNoLocation is returned by Response's 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// the Response's Request. ErrNoLocation is returned if no// Location header is present.func ( *Response) () (*url.URL, error) {:= .Header.Get("Location")if == "" {return nil, ErrNoLocation}if .Request != nil && .Request.URL != nil {return .Request.URL.Parse()}return url.Parse()}// 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 ( *bufio.Reader, *Request) (*Response, error) {:= textproto.NewReader():= &Response{Request: ,}// Parse the first line of the response., := .ReadLine()if != nil {if == io.EOF {= io.ErrUnexpectedEOF}return nil,}if := strings.IndexByte(, ' '); == -1 {return nil, badStringError("malformed HTTP response", )} else {.Proto = [:].Status = strings.TrimLeft([+1:], " ")}:= .Statusif := strings.IndexByte(.Status, ' '); != -1 {= .Status[:]}if len() != 3 {return nil, badStringError("malformed HTTP status code", )}.StatusCode, = strconv.Atoi()if != nil || .StatusCode < 0 {return nil, badStringError("malformed HTTP status code", )}var boolif .ProtoMajor, .ProtoMinor, = ParseHTTPVersion(.Proto); ! {return nil, badStringError("malformed HTTP version", .Proto)}// Parse the response headers., := .ReadMIMEHeader()if != nil {if == io.EOF {= io.ErrUnexpectedEOF}return nil,}.Header = Header()fixPragmaCacheControl(.Header)= readTransfer(, )if != nil {return nil,}return , nil}// RFC 7234, section 5.4: Should treat// Pragma: no-cache// like// Cache-Control: no-cachefunc ( Header) {if , := ["Pragma"]; && len() > 0 && [0] == "no-cache" {if , := ["Cache-Control"]; ! {["Cache-Control"] = []string{"no-cache"}}}}// ProtoAtLeast reports whether the HTTP protocol used// in the response is at least major.minor.func ( *Response) (, int) bool {return .ProtoMajor > ||.ProtoMajor == && .ProtoMinor >=}// 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 ( *Response) ( io.Writer) error {// Status line:= .Statusif == "" {var bool, = statusText[.StatusCode]if ! {= "status code " + strconv.Itoa(.StatusCode)}} else {// Just to reduce stutter, if user set r.Status to "200 OK" and StatusCode to 200.// Not important.= strings.TrimPrefix(, strconv.Itoa(.StatusCode)+" ")}if , := fmt.Fprintf(, "HTTP/%d.%d %03d %s\r\n", .ProtoMajor, .ProtoMinor, .StatusCode, ); != nil {return}// Clone it, so we can modify r1 as needed.:= new(Response)* = *if .ContentLength == 0 && .Body != nil {// Is it actually 0 length? Or just unknown?var [1]byte, := .Body.Read([:])if != nil && != io.EOF {return}if == 0 {// Reset it to a known zero reader, in case underlying one// is unhappy being read repeatedly..Body = NoBody} else {.ContentLength = -1.Body = struct {io.Readerio.Closer}{io.MultiReader(bytes.NewReader([:1]), .Body),.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 .ContentLength == -1 && !.Close && .ProtoAtLeast(1, 1) && !chunked(.TransferEncoding) && !.Uncompressed {.Close = true}// Process Body,ContentLength,Close,Trailer, := newTransferWriter()if != nil {return}= .writeHeader(, nil)if != nil {return}// Rest of header= .Header.WriteSubset(, respExcludeHeader)if != nil {return}// contentLengthAlreadySent may have been already sent for// POST/PUT requests, even if zero length. See Issue 8180.:= .shouldSendContentLength()if .ContentLength == 0 && !chunked(.TransferEncoding) && ! && bodyAllowedForStatus(.StatusCode) {if , := io.WriteString(, "Content-Length: 0\r\n"); != nil {return}}// End-of-headerif , := io.WriteString(, "\r\n"); != nil {return}// Write body and trailer= .writeBody()if != nil {return}// Successreturn nil}func ( *Response) () {if .Body != nil {.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 ( *Response) () bool {, := .Body.(io.Writer)return}// isProtocolSwitch reports whether the response code and header// indicate a successful protocol upgrade response.func ( *Response) () bool {return isProtocolSwitchResponse(.StatusCode, .Header)}// isProtocolSwitchResponse reports whether the response code and// response header indicate a successful protocol upgrade response.func ( int, Header) bool {return == StatusSwitchingProtocols && isProtocolSwitchHeader()}// isProtocolSwitchHeader reports whether the request or response header// is for a protocol switch.func ( Header) bool {return .Get("Upgrade") != "" &&httpguts.HeaderValuesContainsToken(["Connection"], "Upgrade")}