OSDN Git Service

Update Go library to last weekly.
[pf3gnuchains/gcc-fork.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         "url"
17 )
18
19 var respExcludeHeader = map[string]bool{
20         "Content-Length":    true,
21         "Transfer-Encoding": true,
22         "Trailer":           true,
23 }
24
25 // Response represents the response from an HTTP request.
26 //
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
33
34         // Header maps header keys to values.  If the response had multiple
35         // headers with the same key, they will be concatenated, with comma
36         // delimiters.  (Section 4.2 of RFC 2616 requires that multiple headers
37         // be semantically equivalent to a comma-delimited sequence.) Values
38         // duplicated by other fields in this struct (e.g., ContentLength) are
39         // omitted from Header.
40         //
41         // Keys in the map are canonicalized (see CanonicalHeaderKey).
42         Header Header
43
44         // Body represents the response body.
45         //
46         // The http Client and Transport guarantee that Body is always
47         // non-nil, even on responses without a body or responses with
48         // a zero-lengthed body.
49         Body io.ReadCloser
50
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
56
57         // Contains transfer encodings from outer-most to inner-most. Value is
58         // nil, means that "identity" encoding is used.
59         TransferEncoding []string
60
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
65
66         // Trailer maps trailer keys to values, in the same
67         // format as the header.
68         Trailer Header
69
70         // The Request that was sent to obtain this Response.
71         // Request's Body is nil (having already been consumed).
72         // This is only populated for Client requests.
73         Request *Request
74 }
75
76 // Cookies parses and returns the cookies set in the Set-Cookie headers.
77 func (r *Response) Cookies() []*Cookie {
78         return readSetCookies(r.Header)
79 }
80
81 var ErrNoLocation = os.NewError("http: no Location header in response")
82
83 // Location returns the URL of the response's "Location" header,
84 // if present.  Relative redirects are resolved relative to
85 // the Response's Request.  ErrNoLocation is returned if no
86 // Location header is present.
87 func (r *Response) Location() (*url.URL, os.Error) {
88         lv := r.Header.Get("Location")
89         if lv == "" {
90                 return nil, ErrNoLocation
91         }
92         if r.Request != nil && r.Request.URL != nil {
93                 return r.Request.URL.Parse(lv)
94         }
95         return url.Parse(lv)
96 }
97
98 // ReadResponse reads and returns an HTTP response from r.  The
99 // req parameter specifies the Request that corresponds to
100 // this Response.  Clients must call resp.Body.Close when finished
101 // reading resp.Body.  After that call, clients can inspect
102 // resp.Trailer to find key/value pairs included in the response
103 // trailer.
104 func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err os.Error) {
105
106         tp := textproto.NewReader(r)
107         resp = new(Response)
108
109         resp.Request = req
110         resp.Request.Method = strings.ToUpper(resp.Request.Method)
111
112         // Parse the first line of the response.
113         line, err := tp.ReadLine()
114         if err != nil {
115                 if err == os.EOF {
116                         err = io.ErrUnexpectedEOF
117                 }
118                 return nil, err
119         }
120         f := strings.SplitN(line, " ", 3)
121         if len(f) < 2 {
122                 return nil, &badStringError{"malformed HTTP response", line}
123         }
124         reasonPhrase := ""
125         if len(f) > 2 {
126                 reasonPhrase = f[2]
127         }
128         resp.Status = f[1] + " " + reasonPhrase
129         resp.StatusCode, err = strconv.Atoi(f[1])
130         if err != nil {
131                 return nil, &badStringError{"malformed HTTP status code", f[1]}
132         }
133
134         resp.Proto = f[0]
135         var ok bool
136         if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
137                 return nil, &badStringError{"malformed HTTP version", resp.Proto}
138         }
139
140         // Parse the response headers.
141         mimeHeader, err := tp.ReadMIMEHeader()
142         if err != nil {
143                 return nil, err
144         }
145         resp.Header = Header(mimeHeader)
146
147         fixPragmaCacheControl(resp.Header)
148
149         err = readTransfer(resp, r)
150         if err != nil {
151                 return nil, err
152         }
153
154         return resp, nil
155 }
156
157 // RFC2616: Should treat
158 //      Pragma: no-cache
159 // like
160 //      Cache-Control: no-cache
161 func fixPragmaCacheControl(header Header) {
162         if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
163                 if _, presentcc := header["Cache-Control"]; !presentcc {
164                         header["Cache-Control"] = []string{"no-cache"}
165                 }
166         }
167 }
168
169 // ProtoAtLeast returns whether the HTTP protocol used
170 // in the response is at least major.minor.
171 func (r *Response) ProtoAtLeast(major, minor int) bool {
172         return r.ProtoMajor > major ||
173                 r.ProtoMajor == major && r.ProtoMinor >= minor
174 }
175
176 // Writes the response (header, body and trailer) in wire format. This method
177 // consults the following fields of resp:
178 //
179 //  StatusCode
180 //  ProtoMajor
181 //  ProtoMinor
182 //  RequestMethod
183 //  TransferEncoding
184 //  Trailer
185 //  Body
186 //  ContentLength
187 //  Header, values for non-canonical keys will have unpredictable behavior
188 //
189 func (resp *Response) Write(w io.Writer) os.Error {
190
191         // RequestMethod should be upper-case
192         if resp.Request != nil {
193                 resp.Request.Method = strings.ToUpper(resp.Request.Method)
194         }
195
196         // Status line
197         text := resp.Status
198         if text == "" {
199                 var ok bool
200                 text, ok = statusText[resp.StatusCode]
201                 if !ok {
202                         text = "status code " + strconv.Itoa(resp.StatusCode)
203                 }
204         }
205         io.WriteString(w, "HTTP/"+strconv.Itoa(resp.ProtoMajor)+".")
206         io.WriteString(w, strconv.Itoa(resp.ProtoMinor)+" ")
207         io.WriteString(w, strconv.Itoa(resp.StatusCode)+" "+text+"\r\n")
208
209         // Process Body,ContentLength,Close,Trailer
210         tw, err := newTransferWriter(resp)
211         if err != nil {
212                 return err
213         }
214         err = tw.WriteHeader(w)
215         if err != nil {
216                 return err
217         }
218
219         // Rest of header
220         err = resp.Header.WriteSubset(w, respExcludeHeader)
221         if err != nil {
222                 return err
223         }
224
225         // End-of-header
226         io.WriteString(w, "\r\n")
227
228         // Write body and trailer
229         err = tw.WriteBody(w)
230         if err != nil {
231                 return err
232         }
233
234         // Success
235         return nil
236 }