Merge from mainline (167278:168000).
[official-gcc/graphite-test-results.git] / libgo / go / http / response.go
blob6a209c9f88ddeceef21aa7a89c32a7e8ca5f2b06
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.
7 package http
9 import (
10 "bufio"
11 "fmt"
12 "io"
13 "os"
14 "sort"
15 "strconv"
16 "strings"
19 var respExcludeHeader = map[string]bool{
20 "Content-Length": true,
21 "Transfer-Encoding": true,
22 "Trailer": 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.
49 Body io.ReadCloser
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
54 // be read from Body.
55 ContentLength int64
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.
64 Close bool
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) {
79 resp = new(Response)
81 resp.RequestMethod = strings.ToUpper(requestMethod)
83 // Parse the first line of the response.
84 line, err := readLine(r)
85 if err != nil {
86 return nil, err
88 f := strings.Split(line, " ", 3)
89 if len(f) < 3 {
90 return nil, &badStringError{"malformed HTTP response", line}
92 resp.Status = f[1] + " " + f[2]
93 resp.StatusCode, err = strconv.Atoi(f[1])
94 if err != nil {
95 return nil, &badStringError{"malformed HTTP status code", f[1]}
98 resp.Proto = f[0]
99 var ok bool
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.
105 nheader := 0
106 resp.Header = make(map[string]string)
107 for {
108 key, value, err := readKeyValue(r)
109 if err != nil {
110 return nil, err
112 if key == "" {
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)
124 if err != nil {
125 return nil, err
128 return resp, nil
131 // RFC2616: Should treat
132 // Pragma: no-cache
133 // like
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
150 } else {
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:
173 // StatusCode
174 // ProtoMajor
175 // ProtoMinor
176 // RequestMethod
177 // TransferEncoding
178 // Trailer
179 // Body
180 // ContentLength
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)
188 // Status line
189 text := resp.Status
190 if text == "" {
191 var ok bool
192 text, ok = statusText[resp.StatusCode]
193 if !ok {
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)
203 if err != nil {
204 return err
206 err = tw.WriteHeader(w)
207 if err != nil {
208 return err
211 // Rest of header
212 err = writeSortedKeyValue(w, resp.Header, respExcludeHeader)
213 if err != nil {
214 return err
217 // End-of-header
218 io.WriteString(w, "\r\n")
220 // Write body and trailer
221 err = tw.WriteBody(w)
222 if err != nil {
223 return err
226 // Success
227 return nil
230 func writeSortedKeyValue(w io.Writer, kvm map[string]string, exclude map[string]bool) os.Error {
231 kva := make([]string, len(kvm))
232 i := 0
233 for k, v := range kvm {
234 if !exclude[k] {
235 kva[i] = fmt.Sprint(k + ": " + v + "\r\n")
239 kva = kva[0:i]
240 sort.SortStrings(kva)
241 for _, l := range kva {
242 if _, err := io.WriteString(w, l); err != nil {
243 return err
246 return nil