2010-02-19 09:38:40 -07:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2011-11-02 13:54:16 -06:00
|
|
|
"bytes"
|
2011-11-01 20:04:37 -06:00
|
|
|
"errors"
|
2011-09-19 10:01:32 -06:00
|
|
|
"fmt"
|
2010-02-19 09:38:40 -07:00
|
|
|
"io"
|
2011-04-27 16:47:04 -06:00
|
|
|
"io/ioutil"
|
2011-11-04 10:17:46 -06:00
|
|
|
"net/textproto"
|
2010-02-19 09:38:40 -07:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// transferWriter inspects the fields of a user-supplied Request or Response,
|
|
|
|
// sanitizes them without changing the user object and provides methods for
|
|
|
|
// writing the respective header, body and trailer in wire format.
|
|
|
|
type transferWriter struct {
|
2011-09-19 12:41:09 -06:00
|
|
|
Method string
|
2011-06-24 17:46:14 -06:00
|
|
|
Body io.Reader
|
|
|
|
BodyCloser io.Closer
|
2010-02-19 09:38:40 -07:00
|
|
|
ResponseToHEAD bool
|
2011-09-19 10:01:32 -06:00
|
|
|
ContentLength int64 // -1 means unknown, 0 means exactly none
|
2010-02-19 09:38:40 -07:00
|
|
|
Close bool
|
|
|
|
TransferEncoding []string
|
2011-02-22 22:39:25 -07:00
|
|
|
Trailer Header
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func newTransferWriter(r interface{}) (t *transferWriter, err error) {
|
2010-02-19 09:38:40 -07:00
|
|
|
t = &transferWriter{}
|
|
|
|
|
|
|
|
// Extract relevant fields
|
|
|
|
atLeastHTTP11 := false
|
|
|
|
switch rr := r.(type) {
|
|
|
|
case *Request:
|
2011-09-19 10:01:32 -06:00
|
|
|
if rr.ContentLength != 0 && rr.Body == nil {
|
|
|
|
return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength)
|
|
|
|
}
|
2011-09-19 12:41:09 -06:00
|
|
|
t.Method = rr.Method
|
2010-02-19 09:38:40 -07:00
|
|
|
t.Body = rr.Body
|
2011-06-24 17:46:14 -06:00
|
|
|
t.BodyCloser = rr.Body
|
2010-02-19 09:38:40 -07:00
|
|
|
t.ContentLength = rr.ContentLength
|
|
|
|
t.Close = rr.Close
|
|
|
|
t.TransferEncoding = rr.TransferEncoding
|
|
|
|
t.Trailer = rr.Trailer
|
|
|
|
atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
|
2011-06-24 17:46:14 -06:00
|
|
|
if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
|
|
|
|
if t.ContentLength == 0 {
|
|
|
|
// Test to see if it's actually zero or just unset.
|
|
|
|
var buf [1]byte
|
|
|
|
n, _ := io.ReadFull(t.Body, buf[:])
|
|
|
|
if n == 1 {
|
|
|
|
// Oh, guess there is data in this Body Reader after all.
|
|
|
|
// The ContentLength field just wasn't set.
|
|
|
|
// Stich the Body back together again, re-attaching our
|
|
|
|
// consumed byte.
|
|
|
|
t.ContentLength = -1
|
|
|
|
t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body)
|
|
|
|
} else {
|
|
|
|
// Body is actually empty.
|
|
|
|
t.Body = nil
|
|
|
|
t.BodyCloser = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.ContentLength < 0 {
|
|
|
|
t.TransferEncoding = []string{"chunked"}
|
|
|
|
}
|
http: fix handling of 0-lengthed http requests
Via Russ Ross' bug report on golang-nuts, it was not possible
to send an HTTP request with a zero length body with either a
Content-Length (it was stripped) or chunking (it wasn't set).
This means Go couldn't upload 0-length objects to Amazon S3.
(which aren't as silly as they might sound, as S3 objects can
have key/values associated with them, set in the headers)
Amazon further doesn't supported chunked uploads. (not Go's
problem, but we should be able to let users set an explicit
Content-Length, even if it's zero.)
To fix the ambiguity of an explicit zero Content-Length and
the Request struct's default zero value, users need to
explicit set TransferEncoding to []string{"identity"} to force
the Request.Write to include a Content-Length: 0. identity is
in RFC 2616 but is ignored pretty much everywhere. We don't
even then serialize it on the wire, since it's kinda useless,
except as an internal sentinel value.
The "identity" value is then documented, but most users can
ignore that because NewRequest now sets that.
And adds more tests.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4603041
2011-06-08 16:59:23 -06:00
|
|
|
}
|
2010-02-19 09:38:40 -07:00
|
|
|
case *Response:
|
2012-05-21 12:07:27 -06:00
|
|
|
if rr.Request != nil {
|
|
|
|
t.Method = rr.Request.Method
|
|
|
|
}
|
2010-02-19 09:38:40 -07:00
|
|
|
t.Body = rr.Body
|
2011-06-24 17:46:14 -06:00
|
|
|
t.BodyCloser = rr.Body
|
2010-02-19 09:38:40 -07:00
|
|
|
t.ContentLength = rr.ContentLength
|
|
|
|
t.Close = rr.Close
|
|
|
|
t.TransferEncoding = rr.TransferEncoding
|
|
|
|
t.Trailer = rr.Trailer
|
|
|
|
atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
|
2012-05-21 12:07:27 -06:00
|
|
|
t.ResponseToHEAD = noBodyExpected(t.Method)
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sanitize Body,ContentLength,TransferEncoding
|
|
|
|
if t.ResponseToHEAD {
|
|
|
|
t.Body = nil
|
|
|
|
t.TransferEncoding = nil
|
|
|
|
// ContentLength is expected to hold Content-Length
|
|
|
|
if t.ContentLength < 0 {
|
|
|
|
return nil, ErrMissingContentLength
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if !atLeastHTTP11 || t.Body == nil {
|
|
|
|
t.TransferEncoding = nil
|
|
|
|
}
|
|
|
|
if chunked(t.TransferEncoding) {
|
|
|
|
t.ContentLength = -1
|
|
|
|
} else if t.Body == nil { // no chunking, no body
|
|
|
|
t.ContentLength = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sanitize Trailer
|
|
|
|
if !chunked(t.TransferEncoding) {
|
|
|
|
t.Trailer = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func noBodyExpected(requestMethod string) bool {
|
|
|
|
return requestMethod == "HEAD"
|
|
|
|
}
|
|
|
|
|
2011-09-19 12:41:09 -06:00
|
|
|
func (t *transferWriter) shouldSendContentLength() bool {
|
|
|
|
if chunked(t.TransferEncoding) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if t.ContentLength > 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if t.ResponseToHEAD {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Many servers expect a Content-Length for these methods
|
|
|
|
if t.Method == "POST" || t.Method == "PUT" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if t.ContentLength == 0 && isIdentity(t.TransferEncoding) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
|
2010-02-24 16:13:39 -07:00
|
|
|
if t.Close {
|
|
|
|
_, err = io.WriteString(w, "Connection: close\r\n")
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Write Content-Length and/or Transfer-Encoding whose values are a
|
|
|
|
// function of the sanitized field triple (Body, ContentLength,
|
|
|
|
// TransferEncoding)
|
2011-09-19 12:41:09 -06:00
|
|
|
if t.shouldSendContentLength() {
|
|
|
|
io.WriteString(w, "Content-Length: ")
|
2011-12-05 13:48:46 -07:00
|
|
|
_, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n")
|
2010-02-24 16:13:39 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2011-09-19 12:41:09 -06:00
|
|
|
} else if chunked(t.TransferEncoding) {
|
|
|
|
_, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
|
2010-02-24 16:13:39 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write Trailer header
|
|
|
|
if t.Trailer != nil {
|
|
|
|
// TODO: At some point, there should be a generic mechanism for
|
|
|
|
// writing long headers, using HTTP line splitting
|
|
|
|
io.WriteString(w, "Trailer: ")
|
|
|
|
needComma := false
|
2010-12-08 22:36:56 -07:00
|
|
|
for k := range t.Trailer {
|
2010-02-19 09:38:40 -07:00
|
|
|
k = CanonicalHeaderKey(k)
|
|
|
|
switch k {
|
|
|
|
case "Transfer-Encoding", "Trailer", "Content-Length":
|
|
|
|
return &badStringError{"invalid Trailer key", k}
|
|
|
|
}
|
|
|
|
if needComma {
|
|
|
|
io.WriteString(w, ",")
|
|
|
|
}
|
|
|
|
io.WriteString(w, k)
|
|
|
|
needComma = true
|
|
|
|
}
|
|
|
|
_, err = io.WriteString(w, "\r\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func (t *transferWriter) WriteBody(w io.Writer) (err error) {
|
2011-09-19 10:01:32 -06:00
|
|
|
var ncopy int64
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Write body
|
|
|
|
if t.Body != nil {
|
|
|
|
if chunked(t.TransferEncoding) {
|
2011-11-03 19:12:51 -06:00
|
|
|
cw := newChunkedWriter(w)
|
2010-02-19 09:38:40 -07:00
|
|
|
_, err = io.Copy(cw, t.Body)
|
|
|
|
if err == nil {
|
|
|
|
err = cw.Close()
|
|
|
|
}
|
2010-07-18 22:05:27 -06:00
|
|
|
} else if t.ContentLength == -1 {
|
2011-09-19 10:01:32 -06:00
|
|
|
ncopy, err = io.Copy(w, t.Body)
|
2010-02-19 09:38:40 -07:00
|
|
|
} else {
|
2011-09-19 10:01:32 -06:00
|
|
|
ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
|
|
|
|
nextra, err := io.Copy(ioutil.Discard, t.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ncopy += nextra
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2011-06-24 17:46:14 -06:00
|
|
|
if err = t.BodyCloser.Close(); err != nil {
|
2010-02-19 09:38:40 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-19 10:01:32 -06:00
|
|
|
if t.ContentLength != -1 && t.ContentLength != ncopy {
|
|
|
|
return fmt.Errorf("http: Request.ContentLength=%d with Body length %d",
|
|
|
|
t.ContentLength, ncopy)
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// TODO(petar): Place trailer writer code here.
|
|
|
|
if chunked(t.TransferEncoding) {
|
|
|
|
// Last chunk, empty trailer
|
|
|
|
_, err = io.WriteString(w, "\r\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type transferReader struct {
|
|
|
|
// Input
|
2011-02-22 22:39:25 -07:00
|
|
|
Header Header
|
2010-02-19 09:38:40 -07:00
|
|
|
StatusCode int
|
|
|
|
RequestMethod string
|
|
|
|
ProtoMajor int
|
|
|
|
ProtoMinor int
|
|
|
|
// Output
|
|
|
|
Body io.ReadCloser
|
|
|
|
ContentLength int64
|
|
|
|
TransferEncoding []string
|
|
|
|
Close bool
|
2011-02-22 22:39:25 -07:00
|
|
|
Trailer Header
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
2011-02-08 21:35:02 -07:00
|
|
|
// bodyAllowedForStatus returns whether a given response status code
|
|
|
|
// permits a body. See RFC2616, section 4.4.
|
|
|
|
func bodyAllowedForStatus(status int) bool {
|
|
|
|
switch {
|
|
|
|
case status >= 100 && status <= 199:
|
|
|
|
return false
|
|
|
|
case status == 204:
|
|
|
|
return false
|
|
|
|
case status == 304:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// msg is *Request or *Response.
|
2011-11-01 20:04:37 -06:00
|
|
|
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
|
2010-02-19 09:38:40 -07:00
|
|
|
t := &transferReader{}
|
|
|
|
|
|
|
|
// Unify input
|
2011-06-24 14:48:12 -06:00
|
|
|
isResponse := false
|
2010-02-19 09:38:40 -07:00
|
|
|
switch rr := msg.(type) {
|
|
|
|
case *Response:
|
|
|
|
t.Header = rr.Header
|
|
|
|
t.StatusCode = rr.StatusCode
|
2011-05-13 08:31:24 -06:00
|
|
|
t.RequestMethod = rr.Request.Method
|
2010-02-19 09:38:40 -07:00
|
|
|
t.ProtoMajor = rr.ProtoMajor
|
|
|
|
t.ProtoMinor = rr.ProtoMinor
|
2010-10-19 21:29:25 -06:00
|
|
|
t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
|
2011-06-24 14:48:12 -06:00
|
|
|
isResponse = true
|
2010-02-19 09:38:40 -07:00
|
|
|
case *Request:
|
|
|
|
t.Header = rr.Header
|
|
|
|
t.ProtoMajor = rr.ProtoMajor
|
|
|
|
t.ProtoMinor = rr.ProtoMinor
|
|
|
|
// Transfer semantics for Requests are exactly like those for
|
|
|
|
// Responses with status code 200, responding to a GET method
|
|
|
|
t.StatusCode = 200
|
|
|
|
t.RequestMethod = "GET"
|
2011-06-24 14:48:12 -06:00
|
|
|
default:
|
|
|
|
panic("unexpected type")
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
2010-02-24 16:13:39 -07:00
|
|
|
// Default to HTTP/1.1
|
|
|
|
if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
|
|
|
|
t.ProtoMajor, t.ProtoMinor = 1, 1
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Transfer encoding, content length
|
2011-04-04 20:43:36 -06:00
|
|
|
t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
|
2010-02-19 09:38:40 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2012-12-05 23:36:23 -07:00
|
|
|
realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
|
2010-02-19 09:38:40 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2012-12-05 23:36:23 -07:00
|
|
|
if isResponse && t.RequestMethod == "HEAD" {
|
|
|
|
if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil {
|
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
t.ContentLength = n
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.ContentLength = realLength
|
|
|
|
}
|
2010-02-19 09:38:40 -07:00
|
|
|
|
|
|
|
// Trailer
|
|
|
|
t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2011-02-08 21:35:02 -07:00
|
|
|
// If there is no Content-Length or chunked Transfer-Encoding on a *Response
|
|
|
|
// and the status is not 1xx, 204 or 304, then the body is unbounded.
|
|
|
|
// See RFC2616, section 4.4.
|
|
|
|
switch msg.(type) {
|
|
|
|
case *Response:
|
2012-12-05 23:36:23 -07:00
|
|
|
if realLength == -1 &&
|
2011-02-08 21:35:02 -07:00
|
|
|
!chunked(t.TransferEncoding) &&
|
|
|
|
bodyAllowedForStatus(t.StatusCode) {
|
|
|
|
// Unbounded body.
|
|
|
|
t.Close = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Prepare body reader. ContentLength < 0 means chunked encoding
|
|
|
|
// or close connection when finished, since multipart is not supported yet
|
|
|
|
switch {
|
|
|
|
case chunked(t.TransferEncoding):
|
2011-11-03 19:12:51 -06:00
|
|
|
t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
|
2012-12-05 23:36:23 -07:00
|
|
|
case realLength >= 0:
|
2010-02-19 09:38:40 -07:00
|
|
|
// TODO: limit the Content-Length. This is an easy DoS vector.
|
2012-12-05 23:36:23 -07:00
|
|
|
t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
|
2010-02-19 09:38:40 -07:00
|
|
|
default:
|
2012-12-05 23:36:23 -07:00
|
|
|
// realLength < 0, i.e. "Content-Length" not mentioned in header
|
2010-02-19 09:38:40 -07:00
|
|
|
if t.Close {
|
|
|
|
// Close semantics (i.e. HTTP/1.0)
|
|
|
|
t.Body = &body{Reader: r, closing: t.Close}
|
|
|
|
} else {
|
|
|
|
// Persistent connection (i.e. HTTP/1.1)
|
|
|
|
t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unify output
|
|
|
|
switch rr := msg.(type) {
|
|
|
|
case *Request:
|
|
|
|
rr.Body = t.Body
|
|
|
|
rr.ContentLength = t.ContentLength
|
|
|
|
rr.TransferEncoding = t.TransferEncoding
|
|
|
|
rr.Close = t.Close
|
|
|
|
rr.Trailer = t.Trailer
|
|
|
|
case *Response:
|
|
|
|
rr.Body = t.Body
|
|
|
|
rr.ContentLength = t.ContentLength
|
|
|
|
rr.TransferEncoding = t.TransferEncoding
|
|
|
|
rr.Close = t.Close
|
|
|
|
rr.Trailer = t.Trailer
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks whether chunked is part of the encodings stack
|
|
|
|
func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
|
|
|
|
|
http: fix handling of 0-lengthed http requests
Via Russ Ross' bug report on golang-nuts, it was not possible
to send an HTTP request with a zero length body with either a
Content-Length (it was stripped) or chunking (it wasn't set).
This means Go couldn't upload 0-length objects to Amazon S3.
(which aren't as silly as they might sound, as S3 objects can
have key/values associated with them, set in the headers)
Amazon further doesn't supported chunked uploads. (not Go's
problem, but we should be able to let users set an explicit
Content-Length, even if it's zero.)
To fix the ambiguity of an explicit zero Content-Length and
the Request struct's default zero value, users need to
explicit set TransferEncoding to []string{"identity"} to force
the Request.Write to include a Content-Length: 0. identity is
in RFC 2616 but is ignored pretty much everywhere. We don't
even then serialize it on the wire, since it's kinda useless,
except as an internal sentinel value.
The "identity" value is then documented, but most users can
ignore that because NewRequest now sets that.
And adds more tests.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/4603041
2011-06-08 16:59:23 -06:00
|
|
|
// Checks whether the encoding is explicitly "identity".
|
|
|
|
func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Sanitize transfer encoding
|
2011-11-01 20:04:37 -06:00
|
|
|
func fixTransferEncoding(requestMethod string, header Header) ([]string, error) {
|
2010-02-19 09:38:40 -07:00
|
|
|
raw, present := header["Transfer-Encoding"]
|
|
|
|
if !present {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2011-10-18 07:56:34 -06:00
|
|
|
delete(header, "Transfer-Encoding")
|
2011-04-04 20:43:36 -06:00
|
|
|
|
|
|
|
// Head responses have no bodies, so the transfer encoding
|
|
|
|
// should be ignored.
|
|
|
|
if requestMethod == "HEAD" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2011-06-27 17:43:14 -06:00
|
|
|
encodings := strings.Split(raw[0], ",")
|
2010-02-19 09:38:40 -07:00
|
|
|
te := make([]string, 0, len(encodings))
|
|
|
|
// TODO: Even though we only support "identity" and "chunked"
|
|
|
|
// encodings, the loop below is designed with foresight. One
|
|
|
|
// invariant that must be maintained is that, if present,
|
|
|
|
// chunked encoding must always come first.
|
|
|
|
for _, encoding := range encodings {
|
|
|
|
encoding = strings.ToLower(strings.TrimSpace(encoding))
|
2012-03-01 15:56:05 -07:00
|
|
|
// "identity" encoding is not recorded
|
2010-02-19 09:38:40 -07:00
|
|
|
if encoding == "identity" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if encoding != "chunked" {
|
|
|
|
return nil, &badStringError{"unsupported transfer encoding", encoding}
|
|
|
|
}
|
|
|
|
te = te[0 : len(te)+1]
|
|
|
|
te[len(te)-1] = encoding
|
|
|
|
}
|
|
|
|
if len(te) > 1 {
|
|
|
|
return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
|
|
|
|
}
|
|
|
|
if len(te) > 0 {
|
|
|
|
// Chunked encoding trumps Content-Length. See RFC 2616
|
|
|
|
// Section 4.4. Currently len(te) > 0 implies chunked
|
|
|
|
// encoding.
|
2011-10-18 07:56:34 -06:00
|
|
|
delete(header, "Content-Length")
|
2010-02-19 09:38:40 -07:00
|
|
|
return te, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the expected body length, using RFC 2616 Section 4.4. This
|
|
|
|
// function is not a method, because ultimately it should be shared by
|
|
|
|
// ReadResponse and ReadRequest.
|
2011-11-01 20:04:37 -06:00
|
|
|
func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) {
|
2010-02-19 09:38:40 -07:00
|
|
|
|
|
|
|
// Logic based on response type or status
|
|
|
|
if noBodyExpected(requestMethod) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
if status/100 == 1 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
switch status {
|
|
|
|
case 204, 304:
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logic based on Transfer-Encoding
|
|
|
|
if chunked(te) {
|
|
|
|
return -1, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logic based on Content-Length
|
2012-05-28 12:07:24 -06:00
|
|
|
cl := strings.TrimSpace(header.get("Content-Length"))
|
2011-02-22 22:39:25 -07:00
|
|
|
if cl != "" {
|
2012-12-05 23:36:23 -07:00
|
|
|
n, err := parseContentLength(cl)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
return n, nil
|
|
|
|
} else {
|
|
|
|
header.Del("Content-Length")
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
2011-06-24 14:48:12 -06:00
|
|
|
if !isResponse && requestMethod == "GET" {
|
|
|
|
// RFC 2616 doesn't explicitly permit nor forbid an
|
|
|
|
// entity-body on a GET request so we permit one if
|
|
|
|
// declared, but we default to 0 here (not -1 below)
|
|
|
|
// if there's no mention of a body.
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
2010-02-19 09:38:40 -07:00
|
|
|
// Logic based on media type. The purpose of the following code is just
|
|
|
|
// to detect whether the unsupported "multipart/byteranges" is being
|
|
|
|
// used. A proper Content-Type parser is needed in the future.
|
2012-05-28 12:07:24 -06:00
|
|
|
if strings.Contains(strings.ToLower(header.get("Content-Type")), "multipart/byteranges") {
|
2010-03-30 11:51:11 -06:00
|
|
|
return -1, ErrNotSupported
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Body-EOF logic based on other methods (like closing, or chunked coding)
|
|
|
|
return -1, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine whether to hang up after sending a request and body, or
|
|
|
|
// receiving a response and body
|
2010-09-27 19:55:04 -06:00
|
|
|
// 'header' is the request headers
|
2011-02-22 22:39:25 -07:00
|
|
|
func shouldClose(major, minor int, header Header) bool {
|
2010-09-27 19:55:04 -06:00
|
|
|
if major < 1 {
|
2010-02-19 09:38:40 -07:00
|
|
|
return true
|
2010-09-27 19:55:04 -06:00
|
|
|
} else if major == 1 && minor == 0 {
|
2012-05-28 12:07:24 -06:00
|
|
|
if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") {
|
2010-09-27 19:55:04 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
2011-02-22 22:39:25 -07:00
|
|
|
} else {
|
2010-02-19 09:38:40 -07:00
|
|
|
// TODO: Should split on commas, toss surrounding white space,
|
|
|
|
// and check each field.
|
2012-05-28 12:07:24 -06:00
|
|
|
if strings.ToLower(header.get("Connection")) == "close" {
|
2011-02-22 22:39:25 -07:00
|
|
|
header.Del("Connection")
|
2010-02-19 09:38:40 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the trailer header
|
2011-11-01 20:04:37 -06:00
|
|
|
func fixTrailer(header Header, te []string) (Header, error) {
|
2012-05-28 12:07:24 -06:00
|
|
|
raw := header.get("Trailer")
|
2011-02-22 22:39:25 -07:00
|
|
|
if raw == "" {
|
2010-02-19 09:38:40 -07:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2011-02-22 22:39:25 -07:00
|
|
|
header.Del("Trailer")
|
|
|
|
trailer := make(Header)
|
2011-06-27 17:43:14 -06:00
|
|
|
keys := strings.Split(raw, ",")
|
2010-02-19 09:38:40 -07:00
|
|
|
for _, key := range keys {
|
|
|
|
key = CanonicalHeaderKey(strings.TrimSpace(key))
|
|
|
|
switch key {
|
|
|
|
case "Transfer-Encoding", "Trailer", "Content-Length":
|
|
|
|
return nil, &badStringError{"bad trailer key", key}
|
|
|
|
}
|
2011-02-22 22:39:25 -07:00
|
|
|
trailer.Del(key)
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
if len(trailer) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if !chunked(te) {
|
|
|
|
// Trailer and no chunking
|
|
|
|
return nil, ErrUnexpectedTrailer
|
|
|
|
}
|
|
|
|
return trailer, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// body turns a Reader into a ReadCloser.
|
|
|
|
// Close ensures that the body has been fully read
|
|
|
|
// and then reads the trailer if necessary.
|
|
|
|
type body struct {
|
|
|
|
io.Reader
|
|
|
|
hdr interface{} // non-nil (Response or Request) value means read trailer
|
|
|
|
r *bufio.Reader // underlying wire-format reader for the trailer
|
|
|
|
closing bool // is the connection to be closed after reading body?
|
2011-04-30 20:54:08 -06:00
|
|
|
closed bool
|
2011-08-23 02:17:21 -06:00
|
|
|
|
|
|
|
res *response // response writer for server requests, else nil
|
2011-04-30 20:54:08 -06:00
|
|
|
}
|
|
|
|
|
2012-12-09 23:42:10 -07:00
|
|
|
// ErrBodyReadAfterClose is returned when reading a Request or Response
|
|
|
|
// Body after the body has been closed. This typically happens when the body is
|
2011-04-30 20:54:08 -06:00
|
|
|
// read after an HTTP Handler calls WriteHeader or Write on its
|
|
|
|
// ResponseWriter.
|
2012-12-09 23:42:10 -07:00
|
|
|
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
|
2011-04-30 20:54:08 -06:00
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func (b *body) Read(p []byte) (n int, err error) {
|
2011-04-30 20:54:08 -06:00
|
|
|
if b.closed {
|
2011-05-01 13:37:20 -06:00
|
|
|
return 0, ErrBodyReadAfterClose
|
2011-04-30 20:54:08 -06:00
|
|
|
}
|
2011-11-04 10:17:46 -06:00
|
|
|
n, err = b.Reader.Read(p)
|
|
|
|
|
|
|
|
// Read the final trailer once we hit EOF.
|
|
|
|
if err == io.EOF && b.hdr != nil {
|
2011-11-16 12:35:47 -07:00
|
|
|
if e := b.readTrailer(); e != nil {
|
|
|
|
err = e
|
|
|
|
}
|
2011-11-04 10:17:46 -06:00
|
|
|
b.hdr = nil
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
singleCRLF = []byte("\r\n")
|
|
|
|
doubleCRLF = []byte("\r\n\r\n")
|
|
|
|
)
|
|
|
|
|
|
|
|
func seeUpcomingDoubleCRLF(r *bufio.Reader) bool {
|
|
|
|
for peekSize := 4; ; peekSize++ {
|
|
|
|
// This loop stops when Peek returns an error,
|
|
|
|
// which it does when r's buffer has been filled.
|
|
|
|
buf, err := r.Peek(peekSize)
|
|
|
|
if bytes.HasSuffix(buf, doubleCRLF) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2012-11-13 23:38:25 -07:00
|
|
|
var errTrailerEOF = errors.New("http: unexpected EOF reading trailer")
|
|
|
|
|
2011-11-04 10:17:46 -06:00
|
|
|
func (b *body) readTrailer() error {
|
|
|
|
// The common case, since nobody uses trailers.
|
2012-11-13 23:38:25 -07:00
|
|
|
buf, err := b.r.Peek(2)
|
2011-11-04 10:17:46 -06:00
|
|
|
if bytes.Equal(buf, singleCRLF) {
|
|
|
|
b.r.ReadByte()
|
|
|
|
b.r.ReadByte()
|
|
|
|
return nil
|
|
|
|
}
|
2012-11-13 23:38:25 -07:00
|
|
|
if len(buf) < 2 {
|
|
|
|
return errTrailerEOF
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2011-11-04 10:17:46 -06:00
|
|
|
|
|
|
|
// Make sure there's a header terminator coming up, to prevent
|
|
|
|
// a DoS with an unbounded size Trailer. It's not easy to
|
|
|
|
// slip in a LimitReader here, as textproto.NewReader requires
|
|
|
|
// a concrete *bufio.Reader. Also, we can't get all the way
|
|
|
|
// back up to our conn's LimitedReader that *might* be backing
|
|
|
|
// this bufio.Reader. Instead, a hack: we iteratively Peek up
|
|
|
|
// to the bufio.Reader's max size, looking for a double CRLF.
|
|
|
|
// This limits the trailer to the underlying buffer size, typically 4kB.
|
|
|
|
if !seeUpcomingDoubleCRLF(b.r) {
|
|
|
|
return errors.New("http: suspiciously long trailer after chunked body")
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr, err := textproto.NewReader(b.r).ReadMIMEHeader()
|
|
|
|
if err != nil {
|
2012-11-13 23:38:25 -07:00
|
|
|
if err == io.EOF {
|
|
|
|
return errTrailerEOF
|
|
|
|
}
|
2011-11-04 10:17:46 -06:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
switch rr := b.hdr.(type) {
|
|
|
|
case *Request:
|
|
|
|
rr.Trailer = Header(hdr)
|
|
|
|
case *Response:
|
|
|
|
rr.Trailer = Header(hdr)
|
|
|
|
}
|
|
|
|
return nil
|
2010-02-19 09:38:40 -07:00
|
|
|
}
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func (b *body) Close() error {
|
2011-04-30 20:54:08 -06:00
|
|
|
if b.closed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
b.closed = true
|
|
|
|
}()
|
2010-02-19 09:38:40 -07:00
|
|
|
if b.hdr == nil && b.closing {
|
|
|
|
// no trailer and closing the connection next.
|
|
|
|
// no point in reading to EOF.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2011-08-23 02:17:21 -06:00
|
|
|
// In a server request, don't continue reading from the client
|
|
|
|
// if we've already hit the maximum body size set by the
|
|
|
|
// handler. If this is set, that also means the TCP connection
|
|
|
|
// is about to be closed, so getting to the next HTTP request
|
|
|
|
// in the stream is not necessary.
|
|
|
|
if b.res != nil && b.res.requestBodyLimitHit {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2011-11-04 10:17:46 -06:00
|
|
|
// Fully consume the body, which will also lead to us reading
|
|
|
|
// the trailer headers after the body, if present.
|
2011-04-27 16:47:04 -06:00
|
|
|
if _, err := io.Copy(ioutil.Discard, b); err != nil {
|
2010-02-19 09:38:40 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2012-12-05 23:36:23 -07:00
|
|
|
|
|
|
|
// parseContentLength trims whitespace from s and returns -1 if no value
|
|
|
|
// is set, or the value if it's >= 0.
|
|
|
|
func parseContentLength(cl string) (int64, error) {
|
|
|
|
cl = strings.TrimSpace(cl)
|
|
|
|
if cl == "" {
|
|
|
|
return -1, nil
|
|
|
|
}
|
|
|
|
n, err := strconv.ParseInt(cl, 10, 64)
|
|
|
|
if err != nil || n < 0 {
|
|
|
|
return 0, &badStringError{"bad Content-Length", cl}
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
|
|
|
|
}
|