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-11-01 20:04:37 -06:00
|
|
|
"errors"
|
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-11-08 16:41:54 -07:00
|
|
|
"net/url"
|
2011-04-14 21:36:52 -06:00
|
|
|
"strings"
|
2010-02-10 18:29:03 -07:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
type reqWriteTest struct {
|
2011-09-19 11:22:53 -06:00
|
|
|
Req Request
|
|
|
|
Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body
|
|
|
|
|
|
|
|
// Any of these three may be empty to skip that test.
|
|
|
|
WantWrite string // Request.Write
|
|
|
|
WantProxy string // Request.WriteProxy
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
WantError error // wanted error from Request.Write
|
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
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2010-02-10 18:29:03 -07:00
|
|
|
Method: "GET",
|
2011-08-16 21:36:02 -06:00
|
|
|
URL: &url.URL{
|
2012-01-16 19:49:05 -07:00
|
|
|
Scheme: "http",
|
|
|
|
Host: "www.techcrunch.com",
|
|
|
|
Path: "/",
|
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
|
|
|
},
|
|
|
|
|
2012-01-16 19:49:05 -07:00
|
|
|
WantWrite: "GET / 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
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "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
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2010-02-10 18:29:03 -07:00
|
|
|
Method: "GET",
|
2011-08-16 21:36:02 -06:00
|
|
|
URL: &url.URL{
|
2010-02-10 18:29:03 -07:00
|
|
|
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-09-19 11:22:53 -06:00
|
|
|
Body: []byte("abcdef"),
|
2011-03-05 12:35:15 -07:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "GET /search HTTP/1.1\r\n" +
|
2010-02-10 18:29:03 -07:00
|
|
|
"Host: www.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2010-02-10 18:29:03 -07:00
|
|
|
"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
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "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" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"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
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2010-02-24 16:13:39 -07:00
|
|
|
Method: "POST",
|
2011-08-16 21:36:02 -06:00
|
|
|
URL: &url.URL{
|
2010-02-24 16:13:39 -07:00
|
|
|
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-09-19 11:22:53 -06:00
|
|
|
Body: []byte("abcdef"),
|
2011-03-05 12:35:15 -07:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "POST /search HTTP/1.1\r\n" +
|
2010-02-24 16:13:39 -07:00
|
|
|
"Host: www.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2010-02-24 16:13:39 -07:00
|
|
|
"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
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "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" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-03-05 12:35:15 -07:00
|
|
|
"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
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-04-14 21:36:52 -06:00
|
|
|
Method: "POST",
|
2011-08-16 21:36:02 -06:00
|
|
|
URL: &url.URL{
|
2011-04-14 21:36:52 -06:00
|
|
|
Scheme: "http",
|
|
|
|
Host: "www.google.com",
|
|
|
|
Path: "/search",
|
|
|
|
},
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{},
|
|
|
|
Close: true,
|
|
|
|
ContentLength: 6,
|
|
|
|
},
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: []byte("abcdef"),
|
2011-04-14 21:36:52 -06:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "POST /search HTTP/1.1\r\n" +
|
2011-04-14 21:36:52 -06:00
|
|
|
"Host: www.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-04-14 21:36:52 -06:00
|
|
|
"Connection: close\r\n" +
|
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "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" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-04-14 21:36:52 -06:00
|
|
|
"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
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-05-31 09:47:03 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("http://example.com/"),
|
2011-05-31 09:47:03 -06:00
|
|
|
Host: "example.com",
|
|
|
|
Header: Header{
|
|
|
|
"Content-Length": []string{"10"}, // ignored
|
|
|
|
},
|
|
|
|
ContentLength: 6,
|
|
|
|
},
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: []byte("abcdef"),
|
2011-05-31 09:47:03 -06:00
|
|
|
|
2011-10-12 12:48:25 -06:00
|
|
|
WantWrite: "POST / HTTP/1.1\r\n" +
|
2011-05-31 09:47:03 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-05-31 09:47:03 -06:00
|
|
|
"Content-Length: 6\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"abcdef",
|
|
|
|
|
2012-01-16 19:49:05 -07:00
|
|
|
WantProxy: "POST http://example.com/ HTTP/1.1\r\n" +
|
2011-05-31 09:47:03 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-05-31 09:47:03 -06:00
|
|
|
"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
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2010-02-24 16:13:39 -07:00
|
|
|
Method: "GET",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/search"),
|
2010-03-02 14:46:51 -07:00
|
|
|
Host: "www.google.com",
|
2010-02-24 16:13:39 -07:00
|
|
|
},
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "GET /search HTTP/1.1\r\n" +
|
2010-02-24 16:13:39 -07:00
|
|
|
"Host: www.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2010-02-24 16:13:39 -07:00
|
|
|
"\r\n",
|
|
|
|
},
|
2011-06-24 17:46:14 -06:00
|
|
|
|
|
|
|
// Request with a 0 ContentLength and a 0 byte body.
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-06-24 17:46:14 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/"),
|
2011-06-24 17:46:14 -06:00
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 0, // as if unset by user
|
|
|
|
},
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) },
|
2011-06-24 17:46:14 -06:00
|
|
|
|
2011-09-19 12:41:09 -06:00
|
|
|
// RFC 2616 Section 14.13 says Content-Length should be specified
|
|
|
|
// unless body is prohibited by the request method.
|
|
|
|
// Also, nginx expects it for POST and PUT.
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "POST / HTTP/1.1\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-09-19 12:41:09 -06:00
|
|
|
"Content-Length: 0\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"\r\n",
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "POST / HTTP/1.1\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-09-19 12:41:09 -06:00
|
|
|
"Content-Length: 0\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"\r\n",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Request with a 0 ContentLength and a 1 byte body.
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-06-24 17:46:14 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/"),
|
2011-06-24 17:46:14 -06:00
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 0, // as if unset by user
|
|
|
|
},
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) },
|
2011-06-24 17:46:14 -06:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantWrite: "POST / HTTP/1.1\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
|
|
|
chunk("x") + chunk(""),
|
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
WantProxy: "POST / HTTP/1.1\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Host: example.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"Transfer-Encoding: chunked\r\n\r\n" +
|
|
|
|
chunk("x") + chunk(""),
|
2011-09-19 10:01:32 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Request with a ContentLength of 10 but a 5 byte body.
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-09-19 10:01:32 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/"),
|
2011-09-19 10:01:32 -06:00
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 10, // but we're going to send only 5 bytes
|
|
|
|
},
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: []byte("12345"),
|
2011-11-01 20:04:37 -06:00
|
|
|
WantError: errors.New("http: Request.ContentLength=10 with Body length 5"),
|
2011-09-19 10:01:32 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Request with a ContentLength of 4 but an 8 byte body.
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-09-19 10:01:32 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/"),
|
2011-09-19 10:01:32 -06:00
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 4, // but we're going to try to send 8 bytes
|
|
|
|
},
|
2011-09-19 11:22:53 -06:00
|
|
|
Body: []byte("12345678"),
|
2011-11-01 20:04:37 -06:00
|
|
|
WantError: errors.New("http: Request.ContentLength=4 with Body length 8"),
|
2011-09-19 10:01:32 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// Request with a 5 ContentLength and nil body.
|
|
|
|
{
|
2011-09-19 11:22:53 -06:00
|
|
|
Req: Request{
|
2011-09-19 10:01:32 -06:00
|
|
|
Method: "POST",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/"),
|
2011-09-19 10:01:32 -06:00
|
|
|
Host: "example.com",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
ContentLength: 5, // but we'll omit the body
|
|
|
|
},
|
2011-11-01 20:04:37 -06:00
|
|
|
WantError: errors.New("http: Request.ContentLength=5 with nil Body"),
|
2011-09-19 11:22:53 -06:00
|
|
|
},
|
2011-09-19 10:01:32 -06:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
// Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,
|
|
|
|
// and doesn't add a User-Agent.
|
|
|
|
{
|
|
|
|
Req: Request{
|
|
|
|
Method: "GET",
|
2011-10-12 12:48:25 -06:00
|
|
|
URL: mustParseURL("/foo"),
|
2011-09-19 11:22:53 -06:00
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
|
|
|
Header: Header{
|
|
|
|
"X-Foo": []string{"X-Bar"},
|
|
|
|
},
|
|
|
|
},
|
2011-09-19 10:01:32 -06:00
|
|
|
|
2011-10-12 12:48:25 -06:00
|
|
|
WantWrite: "GET /foo HTTP/1.1\r\n" +
|
|
|
|
"Host: \r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-10-12 12:48:25 -06:00
|
|
|
"X-Foo: X-Bar\r\n\r\n",
|
2011-06-24 17:46:14 -06:00
|
|
|
},
|
2013-02-13 19:33:15 -07:00
|
|
|
|
|
|
|
// If no Request.Host and no Request.URL.Host, we send
|
|
|
|
// an empty Host header, and don't use
|
|
|
|
// Request.Header["Host"]. This is just testing that
|
|
|
|
// we don't change Go 1.0 behavior.
|
|
|
|
{
|
|
|
|
Req: Request{
|
|
|
|
Method: "GET",
|
|
|
|
Host: "",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "",
|
|
|
|
Path: "/search",
|
|
|
|
},
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{
|
|
|
|
"Host": []string{"bad.example.com"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
WantWrite: "GET /search HTTP/1.1\r\n" +
|
|
|
|
"Host: \r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n\r\n",
|
2013-02-13 19:33:15 -07:00
|
|
|
},
|
2013-02-21 13:01:47 -07:00
|
|
|
|
|
|
|
// Opaque test #1 from golang.org/issue/4860
|
|
|
|
{
|
|
|
|
Req: Request{
|
|
|
|
Method: "GET",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "www.google.com",
|
|
|
|
Opaque: "/%2F/%2F/",
|
|
|
|
},
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{},
|
|
|
|
},
|
|
|
|
|
|
|
|
WantWrite: "GET /%2F/%2F/ HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n\r\n",
|
2013-02-21 13:01:47 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// Opaque test #2 from golang.org/issue/4860
|
|
|
|
{
|
|
|
|
Req: Request{
|
|
|
|
Method: "GET",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "x.google.com",
|
|
|
|
Opaque: "//y.google.com/%2F/%2F/",
|
|
|
|
},
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{},
|
|
|
|
},
|
|
|
|
|
|
|
|
WantWrite: "GET http://y.google.com/%2F/%2F/ HTTP/1.1\r\n" +
|
|
|
|
"Host: x.google.com\r\n" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n\r\n",
|
2013-02-21 13:01:47 -07:00
|
|
|
},
|
2013-03-11 12:10:43 -06:00
|
|
|
|
|
|
|
// Testing custom case in header keys. Issue 5022.
|
|
|
|
{
|
|
|
|
Req: Request{
|
|
|
|
Method: "GET",
|
|
|
|
URL: &url.URL{
|
|
|
|
Scheme: "http",
|
|
|
|
Host: "www.google.com",
|
|
|
|
Path: "/",
|
|
|
|
},
|
|
|
|
Proto: "HTTP/1.1",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Header: Header{
|
|
|
|
"ALL-CAPS": {"x"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
WantWrite: "GET / HTTP/1.1\r\n" +
|
|
|
|
"Host: www.google.com\r\n" +
|
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
|
|
|
"ALL-CAPS: x\r\n" +
|
|
|
|
"\r\n",
|
|
|
|
},
|
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() {
|
2011-09-19 11:22:53 -06:00
|
|
|
if tt.Body == nil {
|
|
|
|
return
|
|
|
|
}
|
2011-06-24 17:46:14 -06:00
|
|
|
switch b := tt.Body.(type) {
|
|
|
|
case []byte:
|
|
|
|
tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
|
|
|
case func() io.ReadCloser:
|
|
|
|
tt.Req.Body = b()
|
|
|
|
}
|
|
|
|
}
|
2011-09-19 11:22:53 -06:00
|
|
|
setBody()
|
2011-06-16 14:02:28 -06:00
|
|
|
if tt.Req.Header == nil {
|
|
|
|
tt.Req.Header = make(Header)
|
|
|
|
}
|
2011-09-19 11:22:53 -06:00
|
|
|
|
2010-02-10 18:29:03 -07:00
|
|
|
var braw bytes.Buffer
|
|
|
|
err := tt.Req.Write(&braw)
|
2011-09-19 10:01:32 -06:00
|
|
|
if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.WantError); g != e {
|
|
|
|
t.Errorf("writing #%d, err = %q, want %q", i, g, e)
|
|
|
|
continue
|
|
|
|
}
|
2010-02-10 18:29:03 -07:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2011-09-19 10:01:32 -06:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
if tt.WantWrite != "" {
|
|
|
|
sraw := braw.String()
|
|
|
|
if sraw != tt.WantWrite {
|
|
|
|
t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantWrite, sraw)
|
|
|
|
continue
|
|
|
|
}
|
2010-02-10 18:29:03 -07:00
|
|
|
}
|
2011-03-05 12:35:15 -07:00
|
|
|
|
2011-09-19 11:22:53 -06:00
|
|
|
if tt.WantProxy != "" {
|
2011-06-24 17:46:14 -06:00
|
|
|
setBody()
|
2011-09-19 11:22:53 -06:00
|
|
|
var praw bytes.Buffer
|
|
|
|
err = tt.Req.WriteProxy(&praw)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("WriteProxy #%d: %s", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
sraw := praw.String()
|
|
|
|
if sraw != tt.WantProxy {
|
|
|
|
t.Errorf("Test Proxy %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantProxy, sraw)
|
|
|
|
continue
|
|
|
|
}
|
2011-03-05 12:35:15 -07:00
|
|
|
}
|
2010-02-10 18:29:03 -07:00
|
|
|
}
|
|
|
|
}
|
2011-04-14 21:36:52 -06:00
|
|
|
|
|
|
|
type closeChecker struct {
|
|
|
|
io.Reader
|
|
|
|
closed bool
|
|
|
|
}
|
|
|
|
|
2011-11-01 20:04:37 -06:00
|
|
|
func (rc *closeChecker) Close() error {
|
2011-04-14 21:36:52 -06:00
|
|
|
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" +
|
2013-03-06 14:48:20 -07:00
|
|
|
"User-Agent: Go 1.1 package http\r\n" +
|
2011-06-24 17:46:14 -06:00
|
|
|
"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
|
|
|
}
|
2011-10-12 12:48:25 -06:00
|
|
|
|
|
|
|
func mustParseURL(s string) *url.URL {
|
|
|
|
u, err := url.Parse(s)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Error parsing URL %q: %v", s, err))
|
|
|
|
}
|
|
|
|
return u
|
|
|
|
}
|