1
2
3
4
5 package http
6
7 import (
8 "bytes"
9 "bufio"
10 "io"
11 "io/ioutil"
12 "os"
13 "strconv"
14 "strings"
15 )
16
17
18
19
20 type transferWriter struct {
21 Body io.Reader
22 BodyCloser io.Closer
23 ResponseToHEAD bool
24 ContentLength int64
25 Close bool
26 TransferEncoding []string
27 Trailer Header
28 }
29
30 func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) {
31 t = &transferWriter{}
32
33
34 atLeastHTTP11 := false
35 switch rr := r.(type) {
36 case *Request:
37 t.Body = rr.Body
38 t.BodyCloser = rr.Body
39 t.ContentLength = rr.ContentLength
40 t.Close = rr.Close
41 t.TransferEncoding = rr.TransferEncoding
42 t.Trailer = rr.Trailer
43 atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
44 if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
45 if t.ContentLength == 0 {
46
47 var buf [1]byte
48 n, _ := io.ReadFull(t.Body, buf[:])
49 if n == 1 {
50
51
52
53
54 t.ContentLength = -1
55 t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body)
56 } else {
57
58 t.Body = nil
59 t.BodyCloser = nil
60 }
61 }
62 if t.ContentLength < 0 {
63 t.TransferEncoding = []string{"chunked"}
64 }
65 }
66 case *Response:
67 t.Body = rr.Body
68 t.BodyCloser = rr.Body
69 t.ContentLength = rr.ContentLength
70 t.Close = rr.Close
71 t.TransferEncoding = rr.TransferEncoding
72 t.Trailer = rr.Trailer
73 atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
74 t.ResponseToHEAD = noBodyExpected(rr.Request.Method)
75 }
76
77
78 if t.ResponseToHEAD {
79 t.Body = nil
80 t.TransferEncoding = nil
81
82 if t.ContentLength < 0 {
83 return nil, ErrMissingContentLength
84 }
85 } else {
86 if !atLeastHTTP11 || t.Body == nil {
87 t.TransferEncoding = nil
88 }
89 if chunked(t.TransferEncoding) {
90 t.ContentLength = -1
91 } else if t.Body == nil {
92 t.ContentLength = 0
93 }
94 }
95
96
97 if !chunked(t.TransferEncoding) {
98 t.Trailer = nil
99 }
100
101 return t, nil
102 }
103
104 func noBodyExpected(requestMethod string) bool {
105 return requestMethod == "HEAD"
106 }
107
108 func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) {
109 if t.Close {
110 _, err = io.WriteString(w, "Connection: close\r\n")
111 if err != nil {
112 return
113 }
114 }
115
116
117
118
119 if chunked(t.TransferEncoding) {
120 _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
121 if err != nil {
122 return
123 }
124 } else if t.ContentLength > 0 || t.ResponseToHEAD || (t.ContentLength == 0 && isIdentity(t.TransferEncoding)) {
125 io.WriteString(w, "Content-Length: ")
126 _, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n")
127 if err != nil {
128 return
129 }
130 }
131
132
133 if t.Trailer != nil {
134
135
136 io.WriteString(w, "Trailer: ")
137 needComma := false
138 for k := range t.Trailer {
139 k = CanonicalHeaderKey(k)
140 switch k {
141 case "Transfer-Encoding", "Trailer", "Content-Length":
142 return &badStringError{"invalid Trailer key", k}
143 }
144 if needComma {
145 io.WriteString(w, ",")
146 }
147 io.WriteString(w, k)
148 needComma = true
149 }
150 _, err = io.WriteString(w, "\r\n")
151 }
152
153 return
154 }
155
156 func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) {
157
158 if t.Body != nil {
159 if chunked(t.TransferEncoding) {
160 cw := NewChunkedWriter(w)
161 _, err = io.Copy(cw, t.Body)
162 if err == nil {
163 err = cw.Close()
164 }
165 } else if t.ContentLength == -1 {
166 _, err = io.Copy(w, t.Body)
167 } else {
168 _, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
169 }
170 if err != nil {
171 return err
172 }
173 if err = t.BodyCloser.Close(); err != nil {
174 return err
175 }
176 }
177
178
179 if chunked(t.TransferEncoding) {
180
181 _, err = io.WriteString(w, "\r\n")
182 }
183
184 return
185 }
186
187 type transferReader struct {
188
189 Header Header
190 StatusCode int
191 RequestMethod string
192 ProtoMajor int
193 ProtoMinor int
194
195 Body io.ReadCloser
196 ContentLength int64
197 TransferEncoding []string
198 Close bool
199 Trailer Header
200 }
201
202
203
204 func bodyAllowedForStatus(status int) bool {
205 switch {
206 case status >= 100 && status <= 199:
207 return false
208 case status == 204:
209 return false
210 case status == 304:
211 return false
212 }
213 return true
214 }
215
216
217 func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
218 t := &transferReader{}
219
220
221 isResponse := false
222 switch rr := msg.(type) {
223 case *Response:
224 t.Header = rr.Header
225 t.StatusCode = rr.StatusCode
226 t.RequestMethod = rr.Request.Method
227 t.ProtoMajor = rr.ProtoMajor
228 t.ProtoMinor = rr.ProtoMinor
229 t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
230 isResponse = true
231 case *Request:
232 t.Header = rr.Header
233 t.ProtoMajor = rr.ProtoMajor
234 t.ProtoMinor = rr.ProtoMinor
235
236
237 t.StatusCode = 200
238 t.RequestMethod = "GET"
239 default:
240 panic("unexpected type")
241 }
242
243
244 if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
245 t.ProtoMajor, t.ProtoMinor = 1, 1
246 }
247
248
249 t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
250 if err != nil {
251 return err
252 }
253
254 t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
255 if err != nil {
256 return err
257 }
258
259
260 t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
261 if err != nil {
262 return err
263 }
264
265
266
267
268 switch msg.(type) {
269 case *Response:
270 if t.ContentLength == -1 &&
271 !chunked(t.TransferEncoding) &&
272 bodyAllowedForStatus(t.StatusCode) {
273
274 t.Close = true
275 }
276 }
277
278
279
280 switch {
281 case chunked(t.TransferEncoding):
282 t.Body = &body{Reader: NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
283 case t.ContentLength >= 0:
284
285 t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
286 default:
287
288 if t.Close {
289
290 t.Body = &body{Reader: r, closing: t.Close}
291 } else {
292
293 t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
294 }
295 }
296
297
298 switch rr := msg.(type) {
299 case *Request:
300 rr.Body = t.Body
301 rr.ContentLength = t.ContentLength
302 rr.TransferEncoding = t.TransferEncoding
303 rr.Close = t.Close
304 rr.Trailer = t.Trailer
305 case *Response:
306 rr.Body = t.Body
307 rr.ContentLength = t.ContentLength
308 rr.TransferEncoding = t.TransferEncoding
309 rr.Close = t.Close
310 rr.Trailer = t.Trailer
311 }
312
313 return nil
314 }
315
316
317 func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
318
319
320 func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
321
322
323 func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) {
324 raw, present := header["Transfer-Encoding"]
325 if !present {
326 return nil, nil
327 }
328
329 header["Transfer-Encoding"] = nil, false
330
331
332
333 if requestMethod == "HEAD" {
334 return nil, nil
335 }
336
337 encodings := strings.Split(raw[0], ",")
338 te := make([]string, 0, len(encodings))
339
340
341
342
343 for _, encoding := range encodings {
344 encoding = strings.ToLower(strings.TrimSpace(encoding))
345
346 if encoding == "identity" {
347 break
348 }
349 if encoding != "chunked" {
350 return nil, &badStringError{"unsupported transfer encoding", encoding}
351 }
352 te = te[0 : len(te)+1]
353 te[len(te)-1] = encoding
354 }
355 if len(te) > 1 {
356 return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
357 }
358 if len(te) > 0 {
359
360
361
362 header["Content-Length"] = nil, false
363 return te, nil
364 }
365
366 return nil, nil
367 }
368
369
370
371
372 func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, os.Error) {
373
374
375 if noBodyExpected(requestMethod) {
376 return 0, nil
377 }
378 if status/100 == 1 {
379 return 0, nil
380 }
381 switch status {
382 case 204, 304:
383 return 0, nil
384 }
385
386
387 if chunked(te) {
388 return -1, nil
389 }
390
391
392 cl := strings.TrimSpace(header.Get("Content-Length"))
393 if cl != "" {
394 n, err := strconv.Atoi64(cl)
395 if err != nil || n < 0 {
396 return -1, &badStringError{"bad Content-Length", cl}
397 }
398 return n, nil
399 } else {
400 header.Del("Content-Length")
401 }
402
403 if !isResponse && requestMethod == "GET" {
404
405
406
407
408 return 0, nil
409 }
410
411
412
413
414 if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
415 return -1, ErrNotSupported
416 }
417
418
419 return -1, nil
420 }
421
422
423
424
425 func shouldClose(major, minor int, header Header) bool {
426 if major < 1 {
427 return true
428 } else if major == 1 && minor == 0 {
429 if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
430 return true
431 }
432 return false
433 } else {
434
435
436 if strings.ToLower(header.Get("Connection")) == "close" {
437 header.Del("Connection")
438 return true
439 }
440 }
441 return false
442 }
443
444
445 func fixTrailer(header Header, te []string) (Header, os.Error) {
446 raw := header.Get("Trailer")
447 if raw == "" {
448 return nil, nil
449 }
450
451 header.Del("Trailer")
452 trailer := make(Header)
453 keys := strings.Split(raw, ",")
454 for _, key := range keys {
455 key = CanonicalHeaderKey(strings.TrimSpace(key))
456 switch key {
457 case "Transfer-Encoding", "Trailer", "Content-Length":
458 return nil, &badStringError{"bad trailer key", key}
459 }
460 trailer.Del(key)
461 }
462 if len(trailer) == 0 {
463 return nil, nil
464 }
465 if !chunked(te) {
466
467 return nil, ErrUnexpectedTrailer
468 }
469 return trailer, nil
470 }
471
472
473
474
475 type body struct {
476 io.Reader
477 hdr interface{}
478 r *bufio.Reader
479 closing bool
480 closed bool
481 }
482
483
484
485
486
487 var ErrBodyReadAfterClose = os.NewError("http: invalid Read on closed request Body")
488
489 func (b *body) Read(p []byte) (n int, err os.Error) {
490 if b.closed {
491 return 0, ErrBodyReadAfterClose
492 }
493 return b.Reader.Read(p)
494 }
495
496 func (b *body) Close() os.Error {
497 if b.closed {
498 return nil
499 }
500 defer func() {
501 b.closed = true
502 }()
503 if b.hdr == nil && b.closing {
504
505
506 return nil
507 }
508
509 if _, err := io.Copy(ioutil.Discard, b); err != nil {
510 return err
511 }
512
513 if b.hdr == nil {
514 return nil
515 }
516
517
518
519 return nil
520 }