~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/pkg/http/transfer.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-08-03 17:04:59 UTC
  • mfrom: (14.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110803170459-wzd99m3567y80ila
Tags: 1:59-1
* Imported Upstream version 59
* Refresh patches to a new release
* Fix FTBFS on ARM (Closes: #634270)
* Update version.bash to work with Debian packaging and not hg
  repository

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
package http
6
6
 
7
7
import (
 
8
        "bytes"
8
9
        "bufio"
9
10
        "io"
10
11
        "io/ioutil"
17
18
// sanitizes them without changing the user object and provides methods for
18
19
// writing the respective header, body and trailer in wire format.
19
20
type transferWriter struct {
20
 
        Body             io.ReadCloser
 
21
        Body             io.Reader
 
22
        BodyCloser       io.Closer
21
23
        ResponseToHEAD   bool
22
24
        ContentLength    int64
23
25
        Close            bool
33
35
        switch rr := r.(type) {
34
36
        case *Request:
35
37
                t.Body = rr.Body
 
38
                t.BodyCloser = rr.Body
36
39
                t.ContentLength = rr.ContentLength
37
40
                t.Close = rr.Close
38
41
                t.TransferEncoding = rr.TransferEncoding
39
42
                t.Trailer = rr.Trailer
40
43
                atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
41
 
                if t.Body != nil && t.ContentLength <= 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
42
 
                        t.TransferEncoding = []string{"chunked"}
 
44
                if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
 
45
                        if t.ContentLength == 0 {
 
46
                                // Test to see if it's actually zero or just unset.
 
47
                                var buf [1]byte
 
48
                                n, _ := io.ReadFull(t.Body, buf[:])
 
49
                                if n == 1 {
 
50
                                        // Oh, guess there is data in this Body Reader after all.
 
51
                                        // The ContentLength field just wasn't set.
 
52
                                        // Stich the Body back together again, re-attaching our
 
53
                                        // consumed byte.
 
54
                                        t.ContentLength = -1
 
55
                                        t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body)
 
56
                                } else {
 
57
                                        // Body is actually empty.
 
58
                                        t.Body = nil
 
59
                                        t.BodyCloser = nil
 
60
                                }
 
61
                        }
 
62
                        if t.ContentLength < 0 {
 
63
                                t.TransferEncoding = []string{"chunked"}
 
64
                        }
43
65
                }
44
66
        case *Response:
45
67
                t.Body = rr.Body
 
68
                t.BodyCloser = rr.Body
46
69
                t.ContentLength = rr.ContentLength
47
70
                t.Close = rr.Close
48
71
                t.TransferEncoding = rr.TransferEncoding
147
170
                if err != nil {
148
171
                        return err
149
172
                }
150
 
                if err = t.Body.Close(); err != nil {
 
173
                if err = t.BodyCloser.Close(); err != nil {
151
174
                        return err
152
175
                }
153
176
        }
195
218
        t := &transferReader{}
196
219
 
197
220
        // Unify input
 
221
        isResponse := false
198
222
        switch rr := msg.(type) {
199
223
        case *Response:
200
224
                t.Header = rr.Header
203
227
                t.ProtoMajor = rr.ProtoMajor
204
228
                t.ProtoMinor = rr.ProtoMinor
205
229
                t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
 
230
                isResponse = true
206
231
        case *Request:
207
232
                t.Header = rr.Header
208
233
                t.ProtoMajor = rr.ProtoMajor
211
236
                // Responses with status code 200, responding to a GET method
212
237
                t.StatusCode = 200
213
238
                t.RequestMethod = "GET"
 
239
        default:
 
240
                panic("unexpected type")
214
241
        }
215
242
 
216
243
        // Default to HTTP/1.1
224
251
                return err
225
252
        }
226
253
 
227
 
        t.ContentLength, err = fixLength(t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
 
254
        t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
228
255
        if err != nil {
229
256
                return err
230
257
        }
252
279
        // or close connection when finished, since multipart is not supported yet
253
280
        switch {
254
281
        case chunked(t.TransferEncoding):
255
 
                t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
 
282
                t.Body = &body{Reader: NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
256
283
        case t.ContentLength >= 0:
257
284
                // TODO: limit the Content-Length. This is an easy DoS vector.
258
285
                t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
265
292
                        // Persistent connection (i.e. HTTP/1.1)
266
293
                        t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
267
294
                }
268
 
                // TODO(petar): It may be a good idea, for extra robustness, to
269
 
                // assume ContentLength=0 for GET requests (and other special
270
 
                // cases?). This logic should be in fixLength().
271
295
        }
272
296
 
273
297
        // Unify output
310
334
                return nil, nil
311
335
        }
312
336
 
313
 
        encodings := strings.Split(raw[0], ",", -1)
 
337
        encodings := strings.Split(raw[0], ",")
314
338
        te := make([]string, 0, len(encodings))
315
339
        // TODO: Even though we only support "identity" and "chunked"
316
340
        // encodings, the loop below is designed with foresight. One
345
369
// Determine the expected body length, using RFC 2616 Section 4.4. This
346
370
// function is not a method, because ultimately it should be shared by
347
371
// ReadResponse and ReadRequest.
348
 
func fixLength(status int, requestMethod string, header Header, te []string) (int64, os.Error) {
 
372
func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, os.Error) {
349
373
 
350
374
        // Logic based on response type or status
351
375
        if noBodyExpected(requestMethod) {
376
400
                header.Del("Content-Length")
377
401
        }
378
402
 
 
403
        if !isResponse && requestMethod == "GET" {
 
404
                // RFC 2616 doesn't explicitly permit nor forbid an
 
405
                // entity-body on a GET request so we permit one if
 
406
                // declared, but we default to 0 here (not -1 below)
 
407
                // if there's no mention of a body.
 
408
                return 0, nil
 
409
        }
 
410
 
379
411
        // Logic based on media type. The purpose of the following code is just
380
412
        // to detect whether the unsupported "multipart/byteranges" is being
381
413
        // used. A proper Content-Type parser is needed in the future.
418
450
 
419
451
        header.Del("Trailer")
420
452
        trailer := make(Header)
421
 
        keys := strings.Split(raw, ",", -1)
 
453
        keys := strings.Split(raw, ",")
422
454
        for _, key := range keys {
423
455
                key = CanonicalHeaderKey(strings.TrimSpace(key))
424
456
                switch key {