libgo: Update to Go 1.1.1.
[official-gcc.git] / libgo / go / net / http / requestwrite_test.go
blobb27b1f7ce3b7d0915744231d62e4d2d5db32f5df
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.
5 package http
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "net/url"
14 "strings"
15 "testing"
18 type reqWriteTest struct {
19 Req Request
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
32 Req: Request{
33 Method: "GET",
34 URL: &url.URL{
35 Scheme: "http",
36 Host: "www.techcrunch.com",
37 Path: "/",
39 Proto: "HTTP/1.1",
40 ProtoMajor: 1,
41 ProtoMinor: 1,
42 Header: Header{
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"},
51 Body: nil,
52 Close: false,
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
79 Req: Request{
80 Method: "GET",
81 URL: &url.URL{
82 Scheme: "http",
83 Host: "www.google.com",
84 Path: "/search",
86 ProtoMajor: 1,
87 ProtoMinor: 1,
88 Header: Header{},
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
108 Req: Request{
109 Method: "POST",
110 URL: &url.URL{
111 Scheme: "http",
112 Host: "www.google.com",
113 Path: "/search",
115 ProtoMajor: 1,
116 ProtoMinor: 1,
117 Header: Header{},
118 Close: true,
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
141 Req: Request{
142 Method: "POST",
143 URL: &url.URL{
144 Scheme: "http",
145 Host: "www.google.com",
146 Path: "/search",
148 ProtoMajor: 1,
149 ProtoMinor: 1,
150 Header: Header{},
151 Close: true,
152 ContentLength: 6,
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" +
162 "\r\n" +
163 "abcdef",
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" +
170 "\r\n" +
171 "abcdef",
174 // HTTP/1.1 POST with Content-Length in headers
176 Req: Request{
177 Method: "POST",
178 URL: mustParseURL("http://example.com/"),
179 Host: "example.com",
180 Header: Header{
181 "Content-Length": []string{"10"}, // ignored
183 ContentLength: 6,
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" +
192 "\r\n" +
193 "abcdef",
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" +
199 "\r\n" +
200 "abcdef",
203 // default to HTTP/1.1
205 Req: Request{
206 Method: "GET",
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" +
214 "\r\n",
217 // Request with a 0 ContentLength and a 0 byte body.
219 Req: Request{
220 Method: "POST",
221 URL: mustParseURL("/"),
222 Host: "example.com",
223 ProtoMajor: 1,
224 ProtoMinor: 1,
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" +
237 "\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" +
243 "\r\n",
246 // Request with a 0 ContentLength and a 1 byte body.
248 Req: Request{
249 Method: "POST",
250 URL: mustParseURL("/"),
251 Host: "example.com",
252 ProtoMajor: 1,
253 ProtoMinor: 1,
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.
274 Req: Request{
275 Method: "POST",
276 URL: mustParseURL("/"),
277 Host: "example.com",
278 ProtoMajor: 1,
279 ProtoMinor: 1,
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.
288 Req: Request{
289 Method: "POST",
290 URL: mustParseURL("/"),
291 Host: "example.com",
292 ProtoMajor: 1,
293 ProtoMinor: 1,
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.
302 Req: Request{
303 Method: "POST",
304 URL: mustParseURL("/"),
305 Host: "example.com",
306 ProtoMajor: 1,
307 ProtoMinor: 1,
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.
316 Req: Request{
317 Method: "GET",
318 URL: mustParseURL("/foo"),
319 ProtoMajor: 1,
320 ProtoMinor: 0,
321 Header: Header{
322 "X-Foo": []string{"X-Bar"},
326 WantWrite: "GET /foo HTTP/1.1\r\n" +
327 "Host: \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.
337 Req: Request{
338 Method: "GET",
339 Host: "",
340 URL: &url.URL{
341 Scheme: "http",
342 Host: "",
343 Path: "/search",
345 ProtoMajor: 1,
346 ProtoMinor: 1,
347 Header: Header{
348 "Host": []string{"bad.example.com"},
352 WantWrite: "GET /search HTTP/1.1\r\n" +
353 "Host: \r\n" +
354 "User-Agent: Go 1.1 package http\r\n\r\n",
357 // Opaque test #1 from golang.org/issue/4860
359 Req: Request{
360 Method: "GET",
361 URL: &url.URL{
362 Scheme: "http",
363 Host: "www.google.com",
364 Opaque: "/%2F/%2F/",
366 ProtoMajor: 1,
367 ProtoMinor: 1,
368 Header: Header{},
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
378 Req: Request{
379 Method: "GET",
380 URL: &url.URL{
381 Scheme: "http",
382 Host: "x.google.com",
383 Opaque: "//y.google.com/%2F/%2F/",
385 ProtoMajor: 1,
386 ProtoMinor: 1,
387 Header: Header{},
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.
397 Req: Request{
398 Method: "GET",
399 URL: &url.URL{
400 Scheme: "http",
401 Host: "www.google.com",
402 Path: "/",
404 Proto: "HTTP/1.1",
405 ProtoMajor: 1,
406 ProtoMinor: 1,
407 Header: Header{
408 "ALL-CAPS": {"x"},
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" +
415 "ALL-CAPS: x\r\n" +
416 "\r\n",
420 func TestRequestWrite(t *testing.T) {
421 for i := range reqWriteTests {
422 tt := &reqWriteTests[i]
424 setBody := func() {
425 if tt.Body == nil {
426 return
428 switch b := tt.Body.(type) {
429 case []byte:
430 tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
431 case func() io.ReadCloser:
432 tt.Req.Body = b()
435 setBody()
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)
444 continue
446 if err != nil {
447 continue
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)
454 continue
458 if tt.WantProxy != "" {
459 setBody()
460 var praw bytes.Buffer
461 err = tt.Req.WriteProxy(&praw)
462 if err != nil {
463 t.Errorf("WriteProxy #%d: %s", i, err)
464 continue
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)
469 continue
475 type closeChecker struct {
476 io.Reader
477 closed bool
480 func (rc *closeChecker) Close() error {
481 rc.closed = true
482 return nil
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)
495 req.Write(buf)
496 if !rc.closed {
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.
507 chunk("m") +
508 chunk("y body") +
509 chunk("")
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)
521 if err != nil {
522 panic(fmt.Sprintf("Error parsing URL %q: %v", s, err))
524 return u