1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 type reqWriteTest
struct {
20 Body
interface{} // optional []byte or func() io.ReadCloser to populate Req.Body
22 // Any of these three may be empty to skip that test.
23 WantWrite
string // Request.Write
24 WantProxy
string // Request.WriteProxy
26 WantError error
// wanted error from Request.Write
29 var reqWriteTests
= []reqWriteTest
{
30 // HTTP/1.1 => chunked coding; no body; no trailer
36 Host
: "www.techcrunch.com",
43 "Accept": {"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
44 "Accept-Charset": {"ISO-8859-1,utf-8;q=0.7,*;q=0.7"},
45 "Accept-Encoding": {"gzip,deflate"},
46 "Accept-Language": {"en-us,en;q=0.5"},
47 "Keep-Alive": {"300"},
48 "Proxy-Connection": {"keep-alive"},
49 "User-Agent": {"Fake"},
53 Host
: "www.techcrunch.com",
54 Form
: map[string][]string{},
57 WantWrite
: "GET / HTTP/1.1\r\n" +
58 "Host: www.techcrunch.com\r\n" +
59 "User-Agent: Fake\r\n" +
60 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
61 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
62 "Accept-Encoding: gzip,deflate\r\n" +
63 "Accept-Language: en-us,en;q=0.5\r\n" +
64 "Keep-Alive: 300\r\n" +
65 "Proxy-Connection: keep-alive\r\n\r\n",
67 WantProxy
: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
68 "Host: www.techcrunch.com\r\n" +
69 "User-Agent: Fake\r\n" +
70 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
71 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
72 "Accept-Encoding: gzip,deflate\r\n" +
73 "Accept-Language: en-us,en;q=0.5\r\n" +
74 "Keep-Alive: 300\r\n" +
75 "Proxy-Connection: keep-alive\r\n\r\n",
77 // HTTP/1.1 => chunked coding; body; empty trailer
83 Host
: "www.google.com",
89 TransferEncoding
: []string{"chunked"},
92 Body
: []byte("abcdef"),
94 WantWrite
: "GET /search HTTP/1.1\r\n" +
95 "Host: www.google.com\r\n" +
96 "User-Agent: Go 1.1 package http\r\n" +
97 "Transfer-Encoding: chunked\r\n\r\n" +
98 chunk("abcdef") + chunk(""),
100 WantProxy
: "GET http://www.google.com/search HTTP/1.1\r\n" +
101 "Host: www.google.com\r\n" +
102 "User-Agent: Go 1.1 package http\r\n" +
103 "Transfer-Encoding: chunked\r\n\r\n" +
104 chunk("abcdef") + chunk(""),
106 // HTTP/1.1 POST => chunked coding; body; empty trailer
112 Host
: "www.google.com",
119 TransferEncoding
: []string{"chunked"},
122 Body
: []byte("abcdef"),
124 WantWrite
: "POST /search HTTP/1.1\r\n" +
125 "Host: www.google.com\r\n" +
126 "User-Agent: Go 1.1 package http\r\n" +
127 "Connection: close\r\n" +
128 "Transfer-Encoding: chunked\r\n\r\n" +
129 chunk("abcdef") + chunk(""),
131 WantProxy
: "POST http://www.google.com/search HTTP/1.1\r\n" +
132 "Host: www.google.com\r\n" +
133 "User-Agent: Go 1.1 package http\r\n" +
134 "Connection: close\r\n" +
135 "Transfer-Encoding: chunked\r\n\r\n" +
136 chunk("abcdef") + chunk(""),
139 // HTTP/1.1 POST with Content-Length, no chunking
145 Host
: "www.google.com",
155 Body
: []byte("abcdef"),
157 WantWrite
: "POST /search HTTP/1.1\r\n" +
158 "Host: www.google.com\r\n" +
159 "User-Agent: Go 1.1 package http\r\n" +
160 "Connection: close\r\n" +
161 "Content-Length: 6\r\n" +
165 WantProxy
: "POST http://www.google.com/search HTTP/1.1\r\n" +
166 "Host: www.google.com\r\n" +
167 "User-Agent: Go 1.1 package http\r\n" +
168 "Connection: close\r\n" +
169 "Content-Length: 6\r\n" +
174 // HTTP/1.1 POST with Content-Length in headers
178 URL
: mustParseURL("http://example.com/"),
181 "Content-Length": []string{"10"}, // ignored
186 Body
: []byte("abcdef"),
188 WantWrite
: "POST / HTTP/1.1\r\n" +
189 "Host: example.com\r\n" +
190 "User-Agent: Go 1.1 package http\r\n" +
191 "Content-Length: 6\r\n" +
195 WantProxy
: "POST http://example.com/ HTTP/1.1\r\n" +
196 "Host: example.com\r\n" +
197 "User-Agent: Go 1.1 package http\r\n" +
198 "Content-Length: 6\r\n" +
203 // default to HTTP/1.1
207 URL
: mustParseURL("/search"),
208 Host
: "www.google.com",
211 WantWrite
: "GET /search HTTP/1.1\r\n" +
212 "Host: www.google.com\r\n" +
213 "User-Agent: Go 1.1 package http\r\n" +
217 // Request with a 0 ContentLength and a 0 byte body.
221 URL
: mustParseURL("/"),
225 ContentLength
: 0, // as if unset by user
228 Body
: func() io
.ReadCloser
{ return ioutil
.NopCloser(io
.LimitReader(strings
.NewReader("xx"), 0)) },
230 // RFC 2616 Section 14.13 says Content-Length should be specified
231 // unless body is prohibited by the request method.
232 // Also, nginx expects it for POST and PUT.
233 WantWrite
: "POST / HTTP/1.1\r\n" +
234 "Host: example.com\r\n" +
235 "User-Agent: Go 1.1 package http\r\n" +
236 "Content-Length: 0\r\n" +
239 WantProxy
: "POST / HTTP/1.1\r\n" +
240 "Host: example.com\r\n" +
241 "User-Agent: Go 1.1 package http\r\n" +
242 "Content-Length: 0\r\n" +
246 // Request with a 0 ContentLength and a 1 byte body.
250 URL
: mustParseURL("/"),
254 ContentLength
: 0, // as if unset by user
257 Body
: func() io
.ReadCloser
{ return ioutil
.NopCloser(io
.LimitReader(strings
.NewReader("xx"), 1)) },
259 WantWrite
: "POST / HTTP/1.1\r\n" +
260 "Host: example.com\r\n" +
261 "User-Agent: Go 1.1 package http\r\n" +
262 "Transfer-Encoding: chunked\r\n\r\n" +
263 chunk("x") + chunk(""),
265 WantProxy
: "POST / HTTP/1.1\r\n" +
266 "Host: example.com\r\n" +
267 "User-Agent: Go 1.1 package http\r\n" +
268 "Transfer-Encoding: chunked\r\n\r\n" +
269 chunk("x") + chunk(""),
272 // Request with a ContentLength of 10 but a 5 byte body.
276 URL
: mustParseURL("/"),
280 ContentLength
: 10, // but we're going to send only 5 bytes
282 Body
: []byte("12345"),
283 WantError
: errors
.New("http: Request.ContentLength=10 with Body length 5"),
286 // Request with a ContentLength of 4 but an 8 byte body.
290 URL
: mustParseURL("/"),
294 ContentLength
: 4, // but we're going to try to send 8 bytes
296 Body
: []byte("12345678"),
297 WantError
: errors
.New("http: Request.ContentLength=4 with Body length 8"),
300 // Request with a 5 ContentLength and nil body.
304 URL
: mustParseURL("/"),
308 ContentLength
: 5, // but we'll omit the body
310 WantError
: errors
.New("http: Request.ContentLength=5 with nil Body"),
313 // Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,
314 // and doesn't add a User-Agent.
318 URL
: mustParseURL("/foo"),
322 "X-Foo": []string{"X-Bar"},
326 WantWrite
: "GET /foo HTTP/1.1\r\n" +
328 "User-Agent: Go 1.1 package http\r\n" +
329 "X-Foo: X-Bar\r\n\r\n",
332 // If no Request.Host and no Request.URL.Host, we send
333 // an empty Host header, and don't use
334 // Request.Header["Host"]. This is just testing that
335 // we don't change Go 1.0 behavior.
348 "Host": []string{"bad.example.com"},
352 WantWrite
: "GET /search HTTP/1.1\r\n" +
354 "User-Agent: Go 1.1 package http\r\n\r\n",
357 // Opaque test #1 from golang.org/issue/4860
363 Host
: "www.google.com",
371 WantWrite
: "GET /%2F/%2F/ HTTP/1.1\r\n" +
372 "Host: www.google.com\r\n" +
373 "User-Agent: Go 1.1 package http\r\n\r\n",
376 // Opaque test #2 from golang.org/issue/4860
382 Host
: "x.google.com",
383 Opaque
: "//y.google.com/%2F/%2F/",
390 WantWrite
: "GET http://y.google.com/%2F/%2F/ HTTP/1.1\r\n" +
391 "Host: x.google.com\r\n" +
392 "User-Agent: Go 1.1 package http\r\n\r\n",
395 // Testing custom case in header keys. Issue 5022.
401 Host
: "www.google.com",
412 WantWrite
: "GET / HTTP/1.1\r\n" +
413 "Host: www.google.com\r\n" +
414 "User-Agent: Go 1.1 package http\r\n" +
420 func TestRequestWrite(t
*testing
.T
) {
421 for i
:= range reqWriteTests
{
422 tt
:= &reqWriteTests
[i
]
428 switch b
:= tt
.Body
.(type) {
430 tt
.Req
.Body
= ioutil
.NopCloser(bytes
.NewBuffer(b
))
431 case func() io
.ReadCloser
:
436 if tt
.Req
.Header
== nil {
437 tt
.Req
.Header
= make(Header
)
440 var braw bytes
.Buffer
441 err
:= tt
.Req
.Write(&braw
)
442 if g
, e
:= fmt
.Sprintf("%v", err
), fmt
.Sprintf("%v", tt
.WantError
); g
!= e
{
443 t
.Errorf("writing #%d, err = %q, want %q", i
, g
, e
)
450 if tt
.WantWrite
!= "" {
451 sraw
:= braw
.String()
452 if sraw
!= tt
.WantWrite
{
453 t
.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i
, tt
.WantWrite
, sraw
)
458 if tt
.WantProxy
!= "" {
460 var praw bytes
.Buffer
461 err
= tt
.Req
.WriteProxy(&praw
)
463 t
.Errorf("WriteProxy #%d: %s", i
, err
)
466 sraw
:= praw
.String()
467 if sraw
!= tt
.WantProxy
{
468 t
.Errorf("Test Proxy %d, expecting:\n%s\nGot:\n%s\n", i
, tt
.WantProxy
, sraw
)
475 type closeChecker
struct {
480 func (rc
*closeChecker
) Close() error
{
485 // TestRequestWriteClosesBody tests that Request.Write does close its request.Body.
486 // It also indirectly tests NewRequest and that it doesn't wrap an existing Closer
487 // inside a NopCloser, and that it serializes it correctly.
488 func TestRequestWriteClosesBody(t
*testing
.T
) {
489 rc
:= &closeChecker
{Reader
: strings
.NewReader("my body")}
490 req
, _
:= NewRequest("POST", "http://foo.com/", rc
)
491 if req
.ContentLength
!= 0 {
492 t
.Errorf("got req.ContentLength %d, want 0", req
.ContentLength
)
494 buf
:= new(bytes
.Buffer
)
497 t
.Error("body not closed after write")
499 expected
:= "POST / HTTP/1.1\r\n" +
500 "Host: foo.com\r\n" +
501 "User-Agent: Go 1.1 package http\r\n" +
502 "Transfer-Encoding: chunked\r\n\r\n" +
503 // TODO: currently we don't buffer before chunking, so we get a
504 // single "m" chunk before the other chunks, as this was the 1-byte
505 // read from our MultiReader where we stiched the Body back together
506 // after sniffing whether the Body was 0 bytes or not.
510 if buf
.String() != expected
{
511 t
.Errorf("write:\n got: %s\nwant: %s", buf
.String(), expected
)
515 func chunk(s
string) string {
516 return fmt
.Sprintf("%x\r\n%s\r\n", len(s
), s
)
519 func mustParseURL(s
string) *url
.URL
{
520 u
, err
:= url
.Parse(s
)
522 panic(fmt
.Sprintf("Error parsing URL %q: %v", s
, err
))