~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/Azure/azure-sdk-for-go/core/http/response.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        "bytes"
 
12
        "errors"
 
13
        "github.com/Azure/azure-sdk-for-go/core/tls"
 
14
        "io"
 
15
        "net/textproto"
 
16
        "net/url"
 
17
        "strconv"
 
18
        "strings"
 
19
)
 
20
 
 
21
var respExcludeHeader = map[string]bool{
 
22
        "Content-Length":    true,
 
23
        "Transfer-Encoding": true,
 
24
        "Trailer":           true,
 
25
}
 
26
 
 
27
// Response represents the response from an HTTP request.
 
28
//
 
29
type Response struct {
 
30
        Status     string // e.g. "200 OK"
 
31
        StatusCode int    // e.g. 200
 
32
        Proto      string // e.g. "HTTP/1.0"
 
33
        ProtoMajor int    // e.g. 1
 
34
        ProtoMinor int    // e.g. 0
 
35
 
 
36
        // Header maps header keys to values.  If the response had multiple
 
37
        // headers with the same key, they may be concatenated, with comma
 
38
        // delimiters.  (Section 4.2 of RFC 2616 requires that multiple headers
 
39
        // be semantically equivalent to a comma-delimited sequence.) Values
 
40
        // duplicated by other fields in this struct (e.g., ContentLength) are
 
41
        // omitted from Header.
 
42
        //
 
43
        // Keys in the map are canonicalized (see CanonicalHeaderKey).
 
44
        Header Header
 
45
 
 
46
        // Body represents the response body.
 
47
        //
 
48
        // The http Client and Transport guarantee that Body is always
 
49
        // non-nil, even on responses without a body or responses with
 
50
        // a zero-length body. It is the caller's responsibility to
 
51
        // close Body.
 
52
        //
 
53
        // The Body is automatically dechunked if the server replied
 
54
        // with a "chunked" Transfer-Encoding.
 
55
        Body io.ReadCloser
 
56
 
 
57
        // ContentLength records the length of the associated content.  The
 
58
        // value -1 indicates that the length is unknown.  Unless Request.Method
 
59
        // is "HEAD", values >= 0 indicate that the given number of bytes may
 
60
        // be read from Body.
 
61
        ContentLength int64
 
62
 
 
63
        // Contains transfer encodings from outer-most to inner-most. Value is
 
64
        // nil, means that "identity" encoding is used.
 
65
        TransferEncoding []string
 
66
 
 
67
        // Close records whether the header directed that the connection be
 
68
        // closed after reading Body.  The value is advice for clients: neither
 
69
        // ReadResponse nor Response.Write ever closes a connection.
 
70
        Close bool
 
71
 
 
72
        // Trailer maps trailer keys to values, in the same
 
73
        // format as the header.
 
74
        Trailer Header
 
75
 
 
76
        // The Request that was sent to obtain this Response.
 
77
        // Request's Body is nil (having already been consumed).
 
78
        // This is only populated for Client requests.
 
79
        Request *Request
 
80
 
 
81
        // TLS contains information about the TLS connection on which the
 
82
        // response was received. It is nil for unencrypted responses.
 
83
        // The pointer is shared between responses and should not be
 
84
        // modified.
 
85
        TLS *tls.ConnectionState
 
86
}
 
87
 
 
88
// Cookies parses and returns the cookies set in the Set-Cookie headers.
 
89
func (r *Response) Cookies() []*Cookie {
 
90
        return readSetCookies(r.Header)
 
91
}
 
92
 
 
93
var ErrNoLocation = errors.New("http: no Location header in response")
 
94
 
 
95
// Location returns the URL of the response's "Location" header,
 
96
// if present.  Relative redirects are resolved relative to
 
97
// the Response's Request.  ErrNoLocation is returned if no
 
98
// Location header is present.
 
99
func (r *Response) Location() (*url.URL, error) {
 
100
        lv := r.Header.Get("Location")
 
101
        if lv == "" {
 
102
                return nil, ErrNoLocation
 
103
        }
 
104
        if r.Request != nil && r.Request.URL != nil {
 
105
                return r.Request.URL.Parse(lv)
 
106
        }
 
107
        return url.Parse(lv)
 
108
}
 
109
 
 
110
// ReadResponse reads and returns an HTTP response from r.
 
111
// The req parameter optionally specifies the Request that corresponds
 
112
// to this Response. If nil, a GET request is assumed.
 
113
// Clients must call resp.Body.Close when finished reading resp.Body.
 
114
// After that call, clients can inspect resp.Trailer to find key/value
 
115
// pairs included in the response trailer.
 
116
func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
 
117
        tp := textproto.NewReader(r)
 
118
        resp := &Response{
 
119
                Request: req,
 
120
        }
 
121
 
 
122
        // Parse the first line of the response.
 
123
        line, err := tp.ReadLine()
 
124
        if err != nil {
 
125
                if err == io.EOF {
 
126
                        err = io.ErrUnexpectedEOF
 
127
                }
 
128
                return nil, err
 
129
        }
 
130
        f := strings.SplitN(line, " ", 3)
 
131
        if len(f) < 2 {
 
132
                return nil, &badStringError{"malformed HTTP response", line}
 
133
        }
 
134
        reasonPhrase := ""
 
135
        if len(f) > 2 {
 
136
                reasonPhrase = f[2]
 
137
        }
 
138
        resp.Status = f[1] + " " + reasonPhrase
 
139
        resp.StatusCode, err = strconv.Atoi(f[1])
 
140
        if err != nil {
 
141
                return nil, &badStringError{"malformed HTTP status code", f[1]}
 
142
        }
 
143
 
 
144
        resp.Proto = f[0]
 
145
        var ok bool
 
146
        if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok {
 
147
                return nil, &badStringError{"malformed HTTP version", resp.Proto}
 
148
        }
 
149
 
 
150
        // Parse the response headers.
 
151
        mimeHeader, err := tp.ReadMIMEHeader()
 
152
        if err != nil {
 
153
                if err == io.EOF {
 
154
                        err = io.ErrUnexpectedEOF
 
155
                }
 
156
                return nil, err
 
157
        }
 
158
        resp.Header = Header(mimeHeader)
 
159
 
 
160
        fixPragmaCacheControl(resp.Header)
 
161
 
 
162
        err = readTransfer(resp, r)
 
163
        if err != nil {
 
164
                return nil, err
 
165
        }
 
166
 
 
167
        return resp, nil
 
168
}
 
169
 
 
170
// RFC2616: Should treat
 
171
//      Pragma: no-cache
 
172
// like
 
173
//      Cache-Control: no-cache
 
174
func fixPragmaCacheControl(header Header) {
 
175
        if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
 
176
                if _, presentcc := header["Cache-Control"]; !presentcc {
 
177
                        header["Cache-Control"] = []string{"no-cache"}
 
178
                }
 
179
        }
 
180
}
 
181
 
 
182
// ProtoAtLeast reports whether the HTTP protocol used
 
183
// in the response is at least major.minor.
 
184
func (r *Response) ProtoAtLeast(major, minor int) bool {
 
185
        return r.ProtoMajor > major ||
 
186
                r.ProtoMajor == major && r.ProtoMinor >= minor
 
187
}
 
188
 
 
189
// Writes the response (header, body and trailer) in wire format. This method
 
190
// consults the following fields of the response:
 
191
//
 
192
//  StatusCode
 
193
//  ProtoMajor
 
194
//  ProtoMinor
 
195
//  Request.Method
 
196
//  TransferEncoding
 
197
//  Trailer
 
198
//  Body
 
199
//  ContentLength
 
200
//  Header, values for non-canonical keys will have unpredictable behavior
 
201
//
 
202
// Body is closed after it is sent.
 
203
func (r *Response) Write(w io.Writer) error {
 
204
        // Status line
 
205
        text := r.Status
 
206
        if text == "" {
 
207
                var ok bool
 
208
                text, ok = statusText[r.StatusCode]
 
209
                if !ok {
 
210
                        text = "status code " + strconv.Itoa(r.StatusCode)
 
211
                }
 
212
        }
 
213
        protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
 
214
        statusCode := strconv.Itoa(r.StatusCode) + " "
 
215
        text = strings.TrimPrefix(text, statusCode)
 
216
        if _, err := io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n"); err != nil {
 
217
                return err
 
218
        }
 
219
 
 
220
        // Clone it, so we can modify r1 as needed.
 
221
        r1 := new(Response)
 
222
        *r1 = *r
 
223
        if r1.ContentLength == 0 && r1.Body != nil {
 
224
                // Is it actually 0 length? Or just unknown?
 
225
                var buf [1]byte
 
226
                n, err := r1.Body.Read(buf[:])
 
227
                if err != nil && err != io.EOF {
 
228
                        return err
 
229
                }
 
230
                if n == 0 {
 
231
                        // Reset it to a known zero reader, in case underlying one
 
232
                        // is unhappy being read repeatedly.
 
233
                        r1.Body = eofReader
 
234
                } else {
 
235
                        r1.ContentLength = -1
 
236
                        r1.Body = struct {
 
237
                                io.Reader
 
238
                                io.Closer
 
239
                        }{
 
240
                                io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
 
241
                                r.Body,
 
242
                        }
 
243
                }
 
244
        }
 
245
        // If we're sending a non-chunked HTTP/1.1 response without a
 
246
        // content-length, the only way to do that is the old HTTP/1.0
 
247
        // way, by noting the EOF with a connection close, so we need
 
248
        // to set Close.
 
249
        if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) {
 
250
                r1.Close = true
 
251
        }
 
252
 
 
253
        // Process Body,ContentLength,Close,Trailer
 
254
        tw, err := newTransferWriter(r1)
 
255
        if err != nil {
 
256
                return err
 
257
        }
 
258
        err = tw.WriteHeader(w)
 
259
        if err != nil {
 
260
                return err
 
261
        }
 
262
 
 
263
        // Rest of header
 
264
        err = r.Header.WriteSubset(w, respExcludeHeader)
 
265
        if err != nil {
 
266
                return err
 
267
        }
 
268
 
 
269
        // contentLengthAlreadySent may have been already sent for
 
270
        // POST/PUT requests, even if zero length. See Issue 8180.
 
271
        contentLengthAlreadySent := tw.shouldSendContentLength()
 
272
        if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
 
273
                if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
 
274
                        return err
 
275
                }
 
276
        }
 
277
 
 
278
        // End-of-header
 
279
        if _, err := io.WriteString(w, "\r\n"); err != nil {
 
280
                return err
 
281
        }
 
282
 
 
283
        // Write body and trailer
 
284
        err = tw.WriteBody(w)
 
285
        if err != nil {
 
286
                return err
 
287
        }
 
288
 
 
289
        // Success
 
290
        return nil
 
291
}