915327a69ec5698752c25aa831e79521375110ab
[platform/upstream/gcc.git] / libgo / go / http / response.go
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
7 package http
8
9 import (
10         "bufio"
11         "io"
12         "net/textproto"
13         "os"
14         "strconv"
15         "strings"
16 )
17
18 var respExcludeHeader = map[string]bool{
19         "Content-Length":    true,
20         "Transfer-Encoding": true,
21         "Trailer":           true,
22 }
23
24 // Response represents the response from an HTTP request.
25 //
26 type Response struct {
27         Status     string // e.g. "200 OK"
28         StatusCode int    // e.g. 200
29         Proto      string // e.g. "HTTP/1.0"
30         ProtoMajor int    // e.g. 1
31         ProtoMinor int    // e.g. 0
32
33         // Header maps header keys to values.  If the response had multiple
34         // headers with the same key, they will be concatenated, with comma
35         // delimiters.  (Section 4.2 of RFC 2616 requires that multiple headers
36         // be semantically equivalent to a comma-delimited sequence.) Values
37         // duplicated by other fields in this struct (e.g., ContentLength) are
38         // omitted from Header.
39         //
40         // Keys in the map are canonicalized (see CanonicalHeaderKey).
41         Header Header
42
43         // Body represents the response body.
44         Body io.ReadCloser
45
46         // ContentLength records the length of the associated content.  The
47         // value -1 indicates that the length is unknown.  Unless RequestMethod
48         // is "HEAD", values >= 0 indicate that the given number of bytes may
49         // be read from Body.
50         ContentLength int64
51
52         // Contains transfer encodings from outer-most to inner-most. Value is
53         // nil, means that "identity" encoding is used.
54         TransferEncoding []string
55
56         // Close records whether the header directed that the connection be
57         // closed after reading Body.  The value is advice for clients: neither
58         // ReadResponse nor Response.Write ever closes a connection.
59         Close bool
60
61         // Trailer maps trailer keys to values, in the same
62         // format as the header.
63         Trailer Header
64
65         // The Request that was sent to obtain this Response.
66         // Request's Body is nil (having already been consumed).
67         // This is only populated for Client requests.
68         Request *Request
69 }
70
71 // Cookies parses and returns the cookies set in the Set-Cookie headers.
72 func (r *Response) Cookies() []*Cookie {
73         return readSetCookies(r.Header)
74 }
75
76 // ReadResponse reads and returns an HTTP response from r.  The
77 // req parameter specifies the Request that corresponds to
78 // this Response.  Clients must call resp.Body.Close when finished
79 // reading resp.Body.  After that call, clients can inspect
80 // resp.Trailer to find key/value pairs included in the response
81 // trailer.
82 func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err os.Error) {
83
84         tp := textproto.NewReader(r)
85         resp = new(Response)
86
87         resp.Request = req
88         resp.Request.Method = strings.ToUpper(resp.Request.Method)
89
90         // Parse the first line of the response.
91         line, err := tp.ReadLine()
92         if err != nil {
93                 if err == os.EOF {
94                         err = io.ErrUnexpectedEOF
95                 }
96                 return nil, err
97         }
98         f := strings.SplitN(line, " ", 3)
99         if len(f) < 2 {
100                 return nil, &badStringError{"malformed HTTP response", line}
101         }
102         reasonPhrase := ""
103         if len(f) > 2 {
104                 reasonPhrase = f[2]
105         }
106         resp.Status = f[1] + " " + reasonPhrase
107         resp.StatusCode, err = strconv.Atoi(f[1])
108         if err != nil {
109                 return nil, &badStringError{"malformed HTTP status code", f[1]}
110         }
111
112         resp.Proto = f[0]
113         var ok bool
114         if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
115                 return nil, &badStringError{"malformed HTTP version", resp.Proto}
116         }
117
118         // Parse the response headers.
119         mimeHeader, err := tp.ReadMIMEHeader()
120         if err != nil {
121                 return nil, err
122         }
123         resp.Header = Header(mimeHeader)
124
125         fixPragmaCacheControl(resp.Header)
126
127         err = readTransfer(resp, r)
128         if err != nil {
129                 return nil, err
130         }
131
132         return resp, nil
133 }
134
135 // RFC2616: Should treat
136 //      Pragma: no-cache
137 // like
138 //      Cache-Control: no-cache
139 func fixPragmaCacheControl(header Header) {
140         if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
141                 if _, presentcc := header["Cache-Control"]; !presentcc {
142                         header["Cache-Control"] = []string{"no-cache"}
143                 }
144         }
145 }
146
147 // ProtoAtLeast returns whether the HTTP protocol used
148 // in the response is at least major.minor.
149 func (r *Response) ProtoAtLeast(major, minor int) bool {
150         return r.ProtoMajor > major ||
151                 r.ProtoMajor == major && r.ProtoMinor >= minor
152 }
153
154 // Writes the response (header, body and trailer) in wire format. This method
155 // consults the following fields of resp:
156 //
157 //  StatusCode
158 //  ProtoMajor
159 //  ProtoMinor
160 //  RequestMethod
161 //  TransferEncoding
162 //  Trailer
163 //  Body
164 //  ContentLength
165 //  Header, values for non-canonical keys will have unpredictable behavior
166 //
167 func (resp *Response) Write(w io.Writer) os.Error {
168
169         // RequestMethod should be upper-case
170         if resp.Request != nil {
171                 resp.Request.Method = strings.ToUpper(resp.Request.Method)
172         }
173
174         // Status line
175         text := resp.Status
176         if text == "" {
177                 var ok bool
178                 text, ok = statusText[resp.StatusCode]
179                 if !ok {
180                         text = "status code " + strconv.Itoa(resp.StatusCode)
181                 }
182         }
183         io.WriteString(w, "HTTP/"+strconv.Itoa(resp.ProtoMajor)+".")
184         io.WriteString(w, strconv.Itoa(resp.ProtoMinor)+" ")
185         io.WriteString(w, strconv.Itoa(resp.StatusCode)+" "+text+"\r\n")
186
187         // Process Body,ContentLength,Close,Trailer
188         tw, err := newTransferWriter(resp)
189         if err != nil {
190                 return err
191         }
192         err = tw.WriteHeader(w)
193         if err != nil {
194                 return err
195         }
196
197         // Rest of header
198         err = resp.Header.WriteSubset(w, respExcludeHeader)
199         if err != nil {
200                 return err
201         }
202
203         // End-of-header
204         io.WriteString(w, "\r\n")
205
206         // Write body and trailer
207         err = tw.WriteBody(w)
208         if err != nil {
209                 return err
210         }
211
212         // Success
213         return nil
214 }