654 lines · 15.7 KB · Go
RawDownload
// 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"	"errors"	"fmt"	"io"	"io/ioutil"	"net/url"	"strings"	"testing") type reqWriteTest struct {	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 	WantError error // wanted error from Request.Write} var reqWriteTests = []reqWriteTest{	// HTTP/1.1 => chunked coding; no body; no trailer	{		Req: Request{			Method: "GET",			URL: &url.URL{				Scheme: "http",				Host:   "www.techcrunch.com",				Path:   "/",			},			Proto:      "HTTP/1.1",			ProtoMajor: 1,			ProtoMinor: 1,			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"},				"User-Agent":       {"Fake"},			},			Body:  nil,			Close: false,			Host:  "www.techcrunch.com",			Form:  map[string][]string{},		}, 		WantWrite: "GET / HTTP/1.1\r\n" +			"Host: www.techcrunch.com\r\n" +			"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", 		WantProxy: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" +			"Host: www.techcrunch.com\r\n" +			"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",	},	// HTTP/1.1 => chunked coding; body; empty trailer	{		Req: Request{			Method: "GET",			URL: &url.URL{				Scheme: "http",				Host:   "www.google.com",				Path:   "/search",			},			ProtoMajor:       1,			ProtoMinor:       1,			Header:           Header{},			TransferEncoding: []string{"chunked"},		}, 		Body: []byte("abcdef"), 		WantWrite: "GET /search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("abcdef") + chunk(""), 		WantProxy: "GET http://www.google.com/search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("abcdef") + chunk(""),	},	// HTTP/1.1 POST => chunked coding; body; empty trailer	{		Req: Request{			Method: "POST",			URL: &url.URL{				Scheme: "http",				Host:   "www.google.com",				Path:   "/search",			},			ProtoMajor:       1,			ProtoMinor:       1,			Header:           Header{},			Close:            true,			TransferEncoding: []string{"chunked"},		}, 		Body: []byte("abcdef"), 		WantWrite: "POST /search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Connection: close\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("abcdef") + chunk(""), 		WantProxy: "POST http://www.google.com/search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Connection: close\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("abcdef") + chunk(""),	}, 	// HTTP/1.1 POST with Content-Length, no chunking	{		Req: Request{			Method: "POST",			URL: &url.URL{				Scheme: "http",				Host:   "www.google.com",				Path:   "/search",			},			ProtoMajor:    1,			ProtoMinor:    1,			Header:        Header{},			Close:         true,			ContentLength: 6,		}, 		Body: []byte("abcdef"), 		WantWrite: "POST /search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Connection: close\r\n" +			"Content-Length: 6\r\n" +			"\r\n" +			"abcdef", 		WantProxy: "POST http://www.google.com/search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Connection: close\r\n" +			"Content-Length: 6\r\n" +			"\r\n" +			"abcdef",	}, 	// HTTP/1.1 POST with Content-Length in headers	{		Req: Request{			Method: "POST",			URL:    mustParseURL("http://example.com/"),			Host:   "example.com",			Header: Header{				"Content-Length": []string{"10"}, // ignored			},			ContentLength: 6,		}, 		Body: []byte("abcdef"), 		WantWrite: "POST / HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Content-Length: 6\r\n" +			"\r\n" +			"abcdef", 		WantProxy: "POST http://example.com/ HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Content-Length: 6\r\n" +			"\r\n" +			"abcdef",	}, 	// default to HTTP/1.1	{		Req: Request{			Method: "GET",			URL:    mustParseURL("/search"),			Host:   "www.google.com",		}, 		WantWrite: "GET /search HTTP/1.1\r\n" +			"Host: www.google.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"\r\n",	}, 	// Request with a 0 ContentLength and a 0 byte body.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 0, // as if unset by user		}, 		Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) }, 		// 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.		WantWrite: "POST / HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Content-Length: 0\r\n" +			"\r\n", 		WantProxy: "POST / HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Content-Length: 0\r\n" +			"\r\n",	}, 	// Request with a 0 ContentLength and a 1 byte body.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 0, // as if unset by user		}, 		Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) }, 		WantWrite: "POST / HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("x") + chunk(""), 		WantProxy: "POST / HTTP/1.1\r\n" +			"Host: example.com\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"Transfer-Encoding: chunked\r\n\r\n" +			chunk("x") + chunk(""),	}, 	// Request with a ContentLength of 10 but a 5 byte body.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 10, // but we're going to send only 5 bytes		},		Body:      []byte("12345"),		WantError: errors.New("http: ContentLength=10 with Body length 5"),	}, 	// Request with a ContentLength of 4 but an 8 byte body.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 4, // but we're going to try to send 8 bytes		},		Body:      []byte("12345678"),		WantError: errors.New("http: ContentLength=4 with Body length 8"),	}, 	// Request with a 5 ContentLength and nil body.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 5, // but we'll omit the body		},		WantError: errors.New("http: Request.ContentLength=5 with nil Body"),	}, 	// Request with a 0 ContentLength and a body with 1 byte content and an error.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 0, // as if unset by user		}, 		Body: func() io.ReadCloser {			err := errors.New("Custom reader error")			errReader := &errorReader{err}			return ioutil.NopCloser(io.MultiReader(strings.NewReader("x"), errReader))		}, 		WantError: errors.New("Custom reader error"),	}, 	// Request with a 0 ContentLength and a body without content and an error.	{		Req: Request{			Method:        "POST",			URL:           mustParseURL("/"),			Host:          "example.com",			ProtoMajor:    1,			ProtoMinor:    1,			ContentLength: 0, // as if unset by user		}, 		Body: func() io.ReadCloser {			err := errors.New("Custom reader error")			errReader := &errorReader{err}			return ioutil.NopCloser(errReader)		}, 		WantError: errors.New("Custom reader error"),	}, 	// Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,	// and doesn't add a User-Agent.	{		Req: Request{			Method:     "GET",			URL:        mustParseURL("/foo"),			ProtoMajor: 1,			ProtoMinor: 0,			Header: Header{				"X-Foo": []string{"X-Bar"},			},		}, 		WantWrite: "GET /foo HTTP/1.1\r\n" +			"Host: \r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"X-Foo: X-Bar\r\n\r\n",	}, 	// 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" +			"User-Agent: Go-http-client/1.1\r\n\r\n",	}, 	// 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" +			"User-Agent: Go-http-client/1.1\r\n\r\n",	}, 	// 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" +			"User-Agent: Go-http-client/1.1\r\n\r\n",	}, 	// 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-http-client/1.1\r\n" +			"ALL-CAPS: x\r\n" +			"\r\n",	}, 	// Request with host header field; IPv6 address with zone identifier	{		Req: Request{			Method: "GET",			URL: &url.URL{				Host: "[fe80::1%en0]",			},		}, 		WantWrite: "GET / HTTP/1.1\r\n" +			"Host: [fe80::1]\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"\r\n",	}, 	// Request with optional host header field; IPv6 address with zone identifier	{		Req: Request{			Method: "GET",			URL: &url.URL{				Host: "www.example.com",			},			Host: "[fe80::1%en0]:8080",		}, 		WantWrite: "GET / HTTP/1.1\r\n" +			"Host: [fe80::1]:8080\r\n" +			"User-Agent: Go-http-client/1.1\r\n" +			"\r\n",	},} func TestRequestWrite(t *testing.T) {	for i := range reqWriteTests {		tt := &reqWriteTests[i] 		setBody := func() {			if tt.Body == nil {				return			}			switch b := tt.Body.(type) {			case []byte:

Lines 1–500 of 654.