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.
19 var respExcludeHeader
= map[string]bool{
20 "Content-Length": true,
21 "Transfer-Encoding": true,
25 // Response represents the response from an HTTP request.
27 type Response
struct {
28 Status
string // e.g. "200 OK"
29 StatusCode
int // e.g. 200
30 Proto
string // e.g. "HTTP/1.0"
31 ProtoMajor
int // e.g. 1
32 ProtoMinor
int // e.g. 0
34 // RequestMethod records the method used in the HTTP request.
35 // Header fields such as Content-Length have method-specific meaning.
36 RequestMethod
string // e.g. "HEAD", "CONNECT", "GET", etc.
38 // Header maps header keys to values. If the response had multiple
39 // headers with the same key, they will be concatenated, with comma
40 // delimiters. (Section 4.2 of RFC 2616 requires that multiple headers
41 // be semantically equivalent to a comma-delimited sequence.) Values
42 // duplicated by other fields in this struct (e.g., ContentLength) are
43 // omitted from Header.
45 // Keys in the map are canonicalized (see CanonicalHeaderKey).
46 Header
map[string]string
48 // Body represents the response body.
51 // ContentLength records the length of the associated content. The
52 // value -1 indicates that the length is unknown. Unless RequestMethod
53 // is "HEAD", values >= 0 indicate that the given number of bytes may
57 // Contains transfer encodings from outer-most to inner-most. Value is
58 // nil, means that "identity" encoding is used.
59 TransferEncoding
[]string
61 // Close records whether the header directed that the connection be
62 // closed after reading Body. The value is advice for clients: neither
63 // ReadResponse nor Response.Write ever closes a connection.
66 // Trailer maps trailer keys to values. Like for Header, if the
67 // response has multiple trailer lines with the same key, they will be
68 // concatenated, delimited by commas.
69 Trailer
map[string]string
72 // ReadResponse reads and returns an HTTP response from r. The RequestMethod
73 // parameter specifies the method used in the corresponding request (e.g.,
74 // "GET", "HEAD"). Clients must call resp.Body.Close when finished reading
75 // resp.Body. After that call, clients can inspect resp.Trailer to find
76 // key/value pairs included in the response trailer.
77 func ReadResponse(r
*bufio
.Reader
, requestMethod
string) (resp
*Response
, err os
.Error
) {
81 resp
.RequestMethod
= strings
.ToUpper(requestMethod
)
83 // Parse the first line of the response.
84 line
, err
:= readLine(r
)
88 f
:= strings
.Split(line
, " ", 3)
90 return nil, &badStringError
{"malformed HTTP response", line
}
92 resp
.Status
= f
[1] + " " + f
[2]
93 resp
.StatusCode
, err
= strconv
.Atoi(f
[1])
95 return nil, &badStringError
{"malformed HTTP status code", f
[1]}
100 if resp
.ProtoMajor
, resp
.ProtoMinor
, ok
= parseHTTPVersion(resp
.Proto
); !ok
{
101 return nil, &badStringError
{"malformed HTTP version", resp
.Proto
}
104 // Parse the response headers.
106 resp
.Header
= make(map[string]string)
108 key
, value
, err
:= readKeyValue(r
)
113 break // end of response header
115 if nheader
++; nheader
>= maxHeaderLines
{
116 return nil, ErrHeaderTooLong
118 resp
.AddHeader(key
, value
)
121 fixPragmaCacheControl(resp
.Header
)
123 err
= readTransfer(resp
, r
)
131 // RFC2616: Should treat
134 // Cache-Control: no-cache
135 func fixPragmaCacheControl(header
map[string]string) {
136 if header
["Pragma"] == "no-cache" {
137 if _
, presentcc
:= header
["Cache-Control"]; !presentcc
{
138 header
["Cache-Control"] = "no-cache"
143 // AddHeader adds a value under the given key. Keys are not case sensitive.
144 func (r
*Response
) AddHeader(key
, value
string) {
145 key
= CanonicalHeaderKey(key
)
147 oldValues
, oldValuesPresent
:= r
.Header
[key
]
148 if oldValuesPresent
{
149 r
.Header
[key
] = oldValues
+ "," + value
151 r
.Header
[key
] = value
155 // GetHeader returns the value of the response header with the given key.
156 // If there were multiple headers with this key, their values are concatenated,
157 // with a comma delimiter. If there were no response headers with the given
158 // key, GetHeader returns an empty string. Keys are not case sensitive.
159 func (r
*Response
) GetHeader(key
string) (value
string) {
160 return r
.Header
[CanonicalHeaderKey(key
)]
163 // ProtoAtLeast returns whether the HTTP protocol used
164 // in the response is at least major.minor.
165 func (r
*Response
) ProtoAtLeast(major
, minor
int) bool {
166 return r
.ProtoMajor
> major ||
167 r
.ProtoMajor
== major
&& r
.ProtoMinor
>= minor
170 // Writes the response (header, body and trailer) in wire format. This method
171 // consults the following fields of resp:
181 // Header, values for non-canonical keys will have unpredictable behavior
183 func (resp
*Response
) Write(w io
.Writer
) os
.Error
{
185 // RequestMethod should be upper-case
186 resp
.RequestMethod
= strings
.ToUpper(resp
.RequestMethod
)
192 text
, ok
= statusText
[resp
.StatusCode
]
194 text
= "status code " + strconv
.Itoa(resp
.StatusCode
)
197 io
.WriteString(w
, "HTTP/"+strconv
.Itoa(resp
.ProtoMajor
)+".")
198 io
.WriteString(w
, strconv
.Itoa(resp
.ProtoMinor
)+" ")
199 io
.WriteString(w
, strconv
.Itoa(resp
.StatusCode
)+" "+text
+"\r\n")
201 // Process Body,ContentLength,Close,Trailer
202 tw
, err
:= newTransferWriter(resp
)
206 err
= tw
.WriteHeader(w
)
212 err
= writeSortedKeyValue(w
, resp
.Header
, respExcludeHeader
)
218 io
.WriteString(w
, "\r\n")
220 // Write body and trailer
221 err
= tw
.WriteBody(w
)
230 func writeSortedKeyValue(w io
.Writer
, kvm
map[string]string, exclude
map[string]bool) os
.Error
{
231 kva
:= make([]string, len(kvm
))
233 for k
, v
:= range kvm
{
235 kva
[i
] = fmt
.Sprint(k
+ ": " + v
+ "\r\n")
240 sort
.SortStrings(kva
)
241 for _
, l
:= range kva
{
242 if _
, err
:= io
.WriteString(w
, l
); err
!= nil {