2010-02-10 18:29:03 -07:00
|
|
|
// Copyright 2010 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 (
|
|
|
|
"bytes"
|
2011-06-24 17:46:14 -06:00
|
|
|
"fmt"
|
2011-04-14 21:36:52 -06:00
|
|
|
"io"
|
2011-03-12 17:05:07 -07:00
|
|
|
"io/ioutil"
|
2011-04-14 21:36:52 -06:00
|
|
|
"os"
|
|
|
|
"strings"
|
2010-02-10 18:29:03 -07:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
type reqWriteTest struct {
|
2011-03-05 12:35:15 -07:00
|
|
|
Req Request
|
2011-06-24 17:46:14 -06:00
|
|
|
Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body
|
2011-03-05 12:35:15 -07:00
|
|
|
Raw string
|
|
|
|
RawProxy string
|
2010-02-10 18:29:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var reqWriteTests = []reqWriteTest{
|
|
|
|
// HTTP/1.1 => chunked coding; no body; no trailer
|
2010-10-22 11:06:33 -06:00
|
|
|
{
|
2010-02-10 18:29:03 -07:00
|
|
|
Request{
|
|
|
|
Method: "GET",
|
|
|
|
RawURL: "http://www.techcrunch.com/",
|
|
|
|
URL: &URL{
|
2010-09-27 19:54:04 -06:00
|
|
|
Raw: "http://www.techcrunch.com/",
|
|
|
|
Scheme: "http",
|
|
|
|
RawPath: "http://www.techcrunch.com/",
|
|
|
|
RawAuthority: "www.techcrunch.com",
|
|
|
|
RawUserinfo: "",
|
|
|
|
Host: "www.techcrunch.com",
|
|
|
|
Path: "/",
|
|
|
|
RawQuery: "",
|
|
|
|
Fragment: "",
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
2010-03-02 14:46:51 -07:00
|
|
|
Proto: "HTTP/1.1",
|
2010-02-10 18:29:03 -07:00
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
2011-02-22 22:39:25 -07:00
|
|
|
Header: Header{
|
|
|
|
"Accept": {"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
|
|
|
|
"Accept-Charset": {"ISO-8859-1,utf-8;q=0.7,*;q=0.7"},
|
|
|
|
"Accept-Encoding": {"gzip,deflate"},
|
|
|
|
"Accept-Language": {"en-us,en;q=0.5"},
|
|
|
|
"Keep-Alive": {"300"},
|
|
|
|
"Proxy-Connection": {"keep-alive"},
|
2011-06-16 14:02:28 -06:00
|
|
|
"User-Agent": {"Fake"},
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
2011-06-16 14:02:28 -06:00
|
|
|
Body: nil,
|
|
|
|
Close: false,
|
|
|
|
Host: "www.techcrunch.com",
|
|
|
|
Form: map[string][]string{},
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
|
|
|
|
2011-03-05 12:35:15 -07:00
|
|
|
nil,
|
|
|
|
|
2010-02-22 16:39:30 -07:00
|
|
|
"GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
|
2010-02-10 18:29:03 -07:00
|
|
|
"Host: www.techcrunch.com\r\n" +
|
|
|
|
"User-Agent: Fake\r\n" +
|
2011-02-22 22:39:25 -07:00
|
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
|
2010-02-10 18:29:03 -07:00
|
|
|
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
|
|
|
|
"Accept-Encoding: gzip,deflate\r\n" +
|
|
|
|
"Accept-Language: en-us,en;q=0.5\r\n" +
|
|
|
|
"Keep-Alive: 300\r\n" +
|
|
|
|
"Proxy-Connection: keep-alive\r\n\r\n",
|
2011-03-05 12:35:15 -07:00
|
|
|
|
|
|
|
"GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
|
2011-05-20 20:40:23 -06:00
|
|
|
"Host: www.techcrunch.com\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"User-Agent: Fake\r\n" +
|
|
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
|
|
|
|
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
|
|
|
|
"Accept-Encoding: gzip,deflate\r\n" +
|
|
|
|
"Accept-Language: en-us,en;q=0.5\r\n" +
|
|
|
|
"Keep-Alive: 300\r\n" +
|
|
|
|
"Proxy-Connection: keep-alive\r\n\r\n",
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
|
|
|
// HTTP/1.1 => chunked coding; body; empty trailer
|
2010-10-22 11:06:33 -06:00
|
|
|
{
|
2010-02-10 18:29:03 -07:00
|
|
|
Request{
|
|
|
|
Method: "GET",
|
|
|
|
URL: &URL{
|
|
|
|
Scheme: "http",
|
2010-03-02 14:46:51 -07:00
|
|
|
Host: "www.google.com",
|
|
|
|
Path: "/search",
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
2010-03-02 14:46:51 -07:00
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
2011-03-06 21:02:29 -07:00
|
|
|
Header: Header{},
|
2010-02-19 09:38:40 -07:00
|
|
|
TransferEncoding: []string{"chunked"},
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
|
|
|
|
2011-03-05 12:35:15 -07:00
|
|
|
[]byte("abcdef"),
|
|
|
|
|
2010-02-10 18:29:03 -07:00
|
|
|
"GET /search HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
chunk("abcdef") + chunk(""),
|
2011-03-05 12:35:15 -07:00
|
|
|
|
|
|
|
"GET http://www.google.com/search HTTP/1.1\r\n" +
|
2011-05-20 20:40:23 -06:00
|
|
|
"Host: www.google.com\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
chunk("abcdef") + chunk(""),
|
2010-02-10 18:29:03 -07:00
|
|
|
},
|
2010-02-24 16:13:39 -07:00
|
|
|
// HTTP/1.1 POST => chunked coding; body; empty trailer
|
2010-10-22 11:06:33 -06:00
|
|
|
{
|
2010-02-24 16:13:39 -07:00
|
|
|
Request{
|
|
|
|
Method: "POST",
|
|
|
|
URL: &URL{
|
|
|
|
Scheme: "http",
|
2010-03-02 14:46:51 -07:00
|
|
|
Host: "www.google.com",
|
|
|
|
Path: "/search",
|
2010-02-24 16:13:39 -07:00
|
|
|
},
|
2010-03-02 14:46:51 -07:00
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
2011-03-06 21:02:29 -07:00
|
|
|
Header: Header{},
|
2010-03-02 14:46:51 -07:00
|
|
|
Close: true,
|
2010-02-24 16:13:39 -07:00
|
|
|
TransferEncoding: []string{"chunked"},
|
|
|
|
},
|
|
|
|
|
2011-03-05 12:35:15 -07:00
|
|
|
[]byte("abcdef"),
|
|
|
|
|
2010-02-24 16:13:39 -07:00
|
|
|
"POST /search HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Connection: close\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
chunk("abcdef") + chunk(""),
|
2011-03-05 12:35:15 -07:00
|
|
|
|
|
|
|
"POST http://www.google.com/search HTTP/1.1\r\n" +
|
2011-05-20 20:40:23 -06:00
|
|
|
"Host: www.google.com\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Connection: close\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
chunk("abcdef") + chunk(""),
|
2010-02-24 16:13:39 -07:00
|
|
|
},
|
2011-04-14 21:36:52 -06:00
|
|
|
|
|
|
|
// HTTP/1.1 POST with Content-Length, no chunking
|
|
|
|
{
|
|
|
|
Request{
|
|
|
|
Method: "POST",
|
|
|
|
URL: &URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "www.google.com",
|
|
|
|
Path: "/search",
|
|
|
|
},
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{},
|
|
|
|
Close: true,
|
|
|
|
ContentLength: 6,
|
|
|
|
},
|
|
|
|
|
|
|
|
[]byte("abcdef"),
|
|
|
|
|
|
|
|
"POST /search HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Connection: close\r\n" +
|
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
|
|
|
|
"POST http://www.google.com/search HTTP/1.1\r\n" +
|
2011-05-20 20:40:23 -06:00
|
|
|
"Host: www.google.com\r\n" +
|
2011-04-14 21:36:52 -06:00
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Connection: close\r\n" +
|
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
},
|
|
|
|
|
2011-05-31 09:47:03 -06:00
|
|
|
// HTTP/1.1 POST with Content-Length in headers
|
|
|
|
{
|
|
|
|
Request{
|
|
|
|
Method: "POST",
|
|
|
|
RawURL: "http://example.com/",
|
|
|
|
Host: "example.com",
|
|
|
|
Header: Header{
|
|
|
|
"Content-Length": []string{"10"}, // ignored
|
|
|
|
},
|
|
|
|
ContentLength: 6,
|
|
|
|
},
|
|
|
|
|
|
|
|
[]byte("abcdef"),
|
|
|
|
|
|
|
|
"POST http://example.com/ HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
|
|
|
|
"POST http://example.com/ HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
},
|
|
|
|
|
2010-02-24 16:13:39 -07:00
|
|
|
// default to HTTP/1.1
|
2010-10-22 11:06:33 -06:00
|
|
|
{
|
2010-02-24 16:13:39 -07:00
|
|
|
Request{
|
|
|
|
Method: "GET",
|
|
|
|
RawURL: "/search",
|
2010-03-02 14:46:51 -07:00
|
|
|
Host: "www.google.com",
|
2010-02-24 16:13:39 -07:00
|
|
|
},
|
|
|
|
|
2011-03-05 12:35:15 -07:00
|
|
|
nil,
|
|
|
|
|
2010-02-24 16:13:39 -07:00
|
|
|
"GET /search HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"\r\n",
|
2011-03-05 12:35:15 -07:00
|
|
|
|
|
|
|
// Looks weird but RawURL overrides what WriteProxy would choose.
|
|
|
|
"GET /search HTTP/1.1\r\n" +
|
2011-05-20 20:40:23 -06:00
|
|
|
"Host: www.google.com\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"\r\n",
|
2010-02-24 16:13:39 -07:00
|
|
|
},
|
2011-06-24 17:46:14 -06:00
|
|
|
|
|
|
|
// Request with a 0 ContentLength and a 0 byte body.
|
|
|
|
{
|
|
|
|
Request{
|
|
|
|
Method: "POST",
|
|
|
|
RawURL: "/",
|
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 0, // as if unset by user
|
|
|
|
},
|
|
|
|
|
|
|
|
func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) },
|
|
|
|
|
|
|
|
"POST / HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"\r\n",
|
|
|
|
|
|
|
|
"POST / HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"\r\n",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Request with a 0 ContentLength and a 1 byte body.
|
|
|
|
{
|
|
|
|
Request{
|
|
|
|
Method: "POST",
|
|
|
|
RawURL: "/",
|
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 0, // as if unset by user
|
|
|
|
},
|
|
|
|
|
|
|
|
func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) },
|
|
|
|
|
|
|
|
"POST / HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
|
|
|
chunk("x") + chunk(""),
|
|
|
|
|
|
|
|
"POST / HTTP/1.1\r\n" +
|
|
|
|
"Host: example.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
|
|
|
chunk("x") + chunk(""),
|
|
|
|
},
|
2010-02-10 18:29:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRequestWrite(t *testing.T) {
|
|
|
|
for i := range reqWriteTests {
|
|
|
|
tt := &reqWriteTests[i]
|
2011-06-24 17:46:14 -06:00
|
|
|
|
|
|
|
setBody := func() {
|
|
|
|
switch b := tt.Body.(type) {
|
|
|
|
case []byte:
|
|
|
|
tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
|
|
|
case func() io.ReadCloser:
|
|
|
|
tt.Req.Body = b()
|
|
|
|
}
|
|
|
|
}
|
2011-03-05 12:35:15 -07:00
|
|
|
if tt.Body != nil {
|
2011-06-24 17:46:14 -06:00
|
|
|
setBody()
|
2011-03-05 12:35:15 -07:00
|
|
|
}
|
2011-06-16 14:02:28 -06:00
|
|
|
if tt.Req.Header == nil {
|
|
|
|
tt.Req.Header = make(Header)
|
|
|
|
}
|
2010-02-10 18:29:03 -07:00
|
|
|
var braw bytes.Buffer
|
|
|
|
err := tt.Req.Write(&braw)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error writing #%d: %s", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
sraw := braw.String()
|
|
|
|
if sraw != tt.Raw {
|
|
|
|
t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, sraw)
|
|
|
|
continue
|
|
|
|
}
|
2011-03-05 12:35:15 -07:00
|
|
|
|
|
|
|
if tt.Body != nil {
|
2011-06-24 17:46:14 -06:00
|
|
|
setBody()
|
2011-03-05 12:35:15 -07:00
|
|
|
}
|
|
|
|
var praw bytes.Buffer
|
|
|
|
err = tt.Req.WriteProxy(&praw)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error writing #%d: %s", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
sraw = praw.String()
|
|
|
|
if sraw != tt.RawProxy {
|
|
|
|
t.Errorf("Test Proxy %d, expecting:\n%s\nGot:\n%s\n", i, tt.RawProxy, sraw)
|
|
|
|
continue
|
|
|
|
}
|
2010-02-10 18:29:03 -07:00
|
|
|
}
|
|
|
|
}
|
2011-04-14 21:36:52 -06:00
|
|
|
|
|
|
|
type closeChecker struct {
|
|
|
|
io.Reader
|
|
|
|
closed bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *closeChecker) Close() os.Error {
|
|
|
|
rc.closed = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestRequestWriteClosesBody tests that Request.Write does close its request.Body.
|
|
|
|
// It also indirectly tests NewRequest and that it doesn't wrap an existing Closer
|
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
|
|
|
// inside a NopCloser, and that it serializes it correctly.
|
2011-04-14 21:36:52 -06:00
|
|
|
func TestRequestWriteClosesBody(t *testing.T) {
|
|
|
|
rc := &closeChecker{Reader: strings.NewReader("my body")}
|
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
|
|
|
req, _ := NewRequest("POST", "http://foo.com/", rc)
|
2011-06-24 17:46:14 -06:00
|
|
|
if req.ContentLength != 0 {
|
|
|
|
t.Errorf("got req.ContentLength %d, want 0", req.ContentLength)
|
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
|
|
|
}
|
2011-04-14 21:36:52 -06:00
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
req.Write(buf)
|
|
|
|
if !rc.closed {
|
|
|
|
t.Error("body not closed after write")
|
|
|
|
}
|
2011-06-24 17:46:14 -06:00
|
|
|
expected := "POST / HTTP/1.1\r\n" +
|
|
|
|
"Host: foo.com\r\n" +
|
|
|
|
"User-Agent: Go http package\r\n" +
|
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
|
|
|
// TODO: currently we don't buffer before chunking, so we get a
|
|
|
|
// single "m" chunk before the other chunks, as this was the 1-byte
|
|
|
|
// read from our MultiReader where we stiched the Body back together
|
|
|
|
// after sniffing whether the Body was 0 bytes or not.
|
|
|
|
chunk("m") +
|
|
|
|
chunk("y body") +
|
|
|
|
chunk("")
|
|
|
|
if buf.String() != expected {
|
|
|
|
t.Errorf("write:\n got: %s\nwant: %s", buf.String(), expected)
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-24 17:46:14 -06:00
|
|
|
func chunk(s string) string {
|
|
|
|
return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
|
2011-04-14 21:36:52 -06:00
|
|
|
}
|