1 // Copyright 2009 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.
32 var robotsTxtHandler
= HandlerFunc(func(w ResponseWriter
, r
*Request
) {
33 w
.Header().Set("Last-Modified", "sometime")
34 fmt
.Fprintf(w
, "User-agent: go\nDisallow: /something/")
37 // pedanticReadAll works like ioutil.ReadAll but additionally
38 // verifies that r obeys the documented io.Reader contract.
39 func pedanticReadAll(r io
.Reader
) (b
[]byte, err error
) {
44 if n
== 0 && err
== nil {
45 return nil, fmt
.Errorf("Read: n=0 with err=nil")
47 b
= append(b
, buf
[:n
]...)
50 if n
!= 0 || err
!= io
.EOF
{
51 return nil, fmt
.Errorf("Read: n=%d err=%#v after EOF", n
, err
)
61 type chanWriter
chan string
63 func (w chanWriter
) Write(p
[]byte) (n
int, err error
) {
68 func TestClient(t
*testing
.T
) {
70 ts
:= httptest
.NewServer(robotsTxtHandler
)
76 b
, err
= pedanticReadAll(r
.Body
)
81 } else if s
:= string(b
); !strings
.HasPrefix(s
, "User-agent:") {
82 t
.Errorf("Incorrect page body (did not begin with User-agent): %q", s
)
86 func TestClientHead(t
*testing
.T
) {
88 ts
:= httptest
.NewServer(robotsTxtHandler
)
91 r
, err
:= Head(ts
.URL
)
95 if _
, ok
:= r
.Header
["Last-Modified"]; !ok
{
96 t
.Error("Last-Modified header not found.")
100 type recordingTransport
struct {
104 func (t
*recordingTransport
) RoundTrip(req
*Request
) (resp
*Response
, err error
) {
106 return nil, errors
.New("dummy impl")
109 func TestGetRequestFormat(t
*testing
.T
) {
111 tr
:= &recordingTransport
{}
112 client
:= &Client
{Transport
: tr
}
113 url
:= "http://dummy.faketld/"
114 client
.Get(url
) // Note: doesn't hit network
115 if tr
.req
.Method
!= "GET" {
116 t
.Errorf("expected method %q; got %q", "GET", tr
.req
.Method
)
118 if tr
.req
.URL
.String() != url
{
119 t
.Errorf("expected URL %q; got %q", url
, tr
.req
.URL
.String())
121 if tr
.req
.Header
== nil {
122 t
.Errorf("expected non-nil request Header")
126 func TestPostRequestFormat(t
*testing
.T
) {
128 tr
:= &recordingTransport
{}
129 client
:= &Client
{Transport
: tr
}
131 url
:= "http://dummy.faketld/"
132 json
:= `{"key":"value"}`
133 b
:= strings
.NewReader(json
)
134 client
.Post(url
, "application/json", b
) // Note: doesn't hit network
136 if tr
.req
.Method
!= "POST" {
137 t
.Errorf("got method %q, want %q", tr
.req
.Method
, "POST")
139 if tr
.req
.URL
.String() != url
{
140 t
.Errorf("got URL %q, want %q", tr
.req
.URL
.String(), url
)
142 if tr
.req
.Header
== nil {
143 t
.Fatalf("expected non-nil request Header")
146 t
.Error("got Close true, want false")
148 if g
, e
:= tr
.req
.ContentLength
, int64(len(json
)); g
!= e
{
149 t
.Errorf("got ContentLength %d, want %d", g
, e
)
153 func TestPostFormRequestFormat(t
*testing
.T
) {
155 tr
:= &recordingTransport
{}
156 client
:= &Client
{Transport
: tr
}
158 urlStr
:= "http://dummy.faketld/"
159 form
:= make(url
.Values
)
160 form
.Set("foo", "bar")
161 form
.Add("foo", "bar2")
162 form
.Set("bar", "baz")
163 client
.PostForm(urlStr
, form
) // Note: doesn't hit network
165 if tr
.req
.Method
!= "POST" {
166 t
.Errorf("got method %q, want %q", tr
.req
.Method
, "POST")
168 if tr
.req
.URL
.String() != urlStr
{
169 t
.Errorf("got URL %q, want %q", tr
.req
.URL
.String(), urlStr
)
171 if tr
.req
.Header
== nil {
172 t
.Fatalf("expected non-nil request Header")
174 if g
, e
:= tr
.req
.Header
.Get("Content-Type"), "application/x-www-form-urlencoded"; g
!= e
{
175 t
.Errorf("got Content-Type %q, want %q", g
, e
)
178 t
.Error("got Close true, want false")
180 // Depending on map iteration, body can be either of these.
181 expectedBody
:= "foo=bar&foo=bar2&bar=baz"
182 expectedBody1
:= "bar=baz&foo=bar&foo=bar2"
183 if g
, e
:= tr
.req
.ContentLength
, int64(len(expectedBody
)); g
!= e
{
184 t
.Errorf("got ContentLength %d, want %d", g
, e
)
186 bodyb
, err
:= ioutil
.ReadAll(tr
.req
.Body
)
188 t
.Fatalf("ReadAll on req.Body: %v", err
)
190 if g
:= string(bodyb
); g
!= expectedBody
&& g
!= expectedBody1
{
191 t
.Errorf("got body %q, want %q or %q", g
, expectedBody
, expectedBody1
)
195 func TestClientRedirects(t
*testing
.T
) {
197 var ts
*httptest
.Server
198 ts
= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
199 n
, _
:= strconv
.Atoi(r
.FormValue("n"))
200 // Test Referer header. (7 is arbitrary position to test at)
202 if g
, e
:= r
.Referer(), ts
.URL
+"/?n=6"; e
!= g
{
203 t
.Errorf("on request ?n=7, expected referer of %q; got %q", e
, g
)
207 Redirect(w
, r
, fmt
.Sprintf("/?n=%d", n
+1), StatusFound
)
210 fmt
.Fprintf(w
, "n=%d", n
)
215 _
, err
:= c
.Get(ts
.URL
)
216 if e
, g
:= "Get /?n=10: stopped after 10 redirects", fmt
.Sprintf("%v", err
); e
!= g
{
217 t
.Errorf("with default client Get, expected error %q, got %q", e
, g
)
220 // HEAD request should also have the ability to follow redirects.
221 _
, err
= c
.Head(ts
.URL
)
222 if e
, g
:= "Head /?n=10: stopped after 10 redirects", fmt
.Sprintf("%v", err
); e
!= g
{
223 t
.Errorf("with default client Head, expected error %q, got %q", e
, g
)
226 // Do should also follow redirects.
227 greq
, _
:= NewRequest("GET", ts
.URL
, nil)
229 if e
, g
:= "Get /?n=10: stopped after 10 redirects", fmt
.Sprintf("%v", err
); e
!= g
{
230 t
.Errorf("with default client Do, expected error %q, got %q", e
, g
)
234 var lastVia
[]*Request
235 c
= &Client
{CheckRedirect
: func(_
*Request
, via
[]*Request
) error
{
239 res
, err
:= c
.Get(ts
.URL
)
241 t
.Fatalf("Get error: %v", err
)
244 finalUrl
:= res
.Request
.URL
.String()
245 if e
, g
:= "<nil>", fmt
.Sprintf("%v", err
); e
!= g
{
246 t
.Errorf("with custom client, expected error %q, got %q", e
, g
)
248 if !strings
.HasSuffix(finalUrl
, "/?n=15") {
249 t
.Errorf("expected final url to end in /?n=15; got url %q", finalUrl
)
251 if e
, g
:= 15, len(lastVia
); e
!= g
{
252 t
.Errorf("expected lastVia to have contained %d elements; got %d", e
, g
)
255 checkErr
= errors
.New("no redirects allowed")
256 res
, err
= c
.Get(ts
.URL
)
257 if urlError
, ok
:= err
.(*url
.Error
); !ok || urlError
.Err
!= checkErr
{
258 t
.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err
, err
)
261 t
.Fatalf("Expected a non-nil Response on CheckRedirect failure (http://golang.org/issue/3795)")
264 if res
.Header
.Get("Location") == "" {
265 t
.Errorf("no Location header in Response")
269 func TestPostRedirects(t
*testing
.T
) {
275 var ts
*httptest
.Server
276 ts
= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
278 fmt
.Fprintf(&log
.Buffer
, "%s %s ", r
.Method
, r
.RequestURI
)
280 if v
:= r
.URL
.Query().Get("code"); v
!= "" {
281 code
, _
:= strconv
.Atoi(v
)
283 w
.Header().Set("Location", ts
.URL
)
291 want
int // response code
299 for _
, tt
:= range tests
{
300 res
, err
:= Post(ts
.URL
+tt
.suffix
, "text/plain", strings
.NewReader("Some content"))
304 if res
.StatusCode
!= tt
.want
{
305 t
.Errorf("POST %s: status code = %d; want %d", tt
.suffix
, res
.StatusCode
, tt
.want
)
311 want
:= "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
313 t
.Errorf("Log differs.\n Got: %q\nWant: %q", got
, want
)
317 var expectedCookies
= []*Cookie
{
318 {Name
: "ChocolateChip", Value
: "tasty"},
319 {Name
: "First", Value
: "Hit"},
320 {Name
: "Second", Value
: "Hit"},
323 var echoCookiesRedirectHandler
= HandlerFunc(func(w ResponseWriter
, r
*Request
) {
324 for _
, cookie
:= range r
.Cookies() {
327 if r
.URL
.Path
== "/" {
328 SetCookie(w
, expectedCookies
[1])
329 Redirect(w
, r
, "/second", StatusMovedPermanently
)
331 SetCookie(w
, expectedCookies
[2])
332 w
.Write([]byte("hello"))
336 func TestClientSendsCookieFromJar(t
*testing
.T
) {
337 tr
:= &recordingTransport
{}
338 client
:= &Client
{Transport
: tr
}
339 client
.Jar
= &TestJar
{perURL
: make(map[string][]*Cookie
)}
340 us
:= "http://dummy.faketld/"
341 u
, _
:= url
.Parse(us
)
342 client
.Jar
.SetCookies(u
, expectedCookies
)
344 client
.Get(us
) // Note: doesn't hit network
345 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
347 client
.Head(us
) // Note: doesn't hit network
348 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
350 client
.Post(us
, "text/plain", strings
.NewReader("body")) // Note: doesn't hit network
351 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
353 client
.PostForm(us
, url
.Values
{}) // Note: doesn't hit network
354 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
356 req
, _
:= NewRequest("GET", us
, nil)
357 client
.Do(req
) // Note: doesn't hit network
358 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
360 req
, _
= NewRequest("POST", us
, nil)
361 client
.Do(req
) // Note: doesn't hit network
362 matchReturnedCookies(t
, expectedCookies
, tr
.req
.Cookies())
365 // Just enough correctness for our redirect tests. Uses the URL.Host as the
366 // scope of all cookies.
367 type TestJar
struct {
369 perURL
map[string][]*Cookie
372 func (j
*TestJar
) SetCookies(u
*url
.URL
, cookies
[]*Cookie
) {
376 j
.perURL
= make(map[string][]*Cookie
)
378 j
.perURL
[u
.Host
] = cookies
381 func (j
*TestJar
) Cookies(u
*url
.URL
) []*Cookie
{
384 return j
.perURL
[u
.Host
]
387 func TestRedirectCookiesJar(t
*testing
.T
) {
389 var ts
*httptest
.Server
390 ts
= httptest
.NewServer(echoCookiesRedirectHandler
)
395 u
, _
:= url
.Parse(ts
.URL
)
396 c
.Jar
.SetCookies(u
, []*Cookie
{expectedCookies
[0]})
397 resp
, err
:= c
.Get(ts
.URL
)
399 t
.Fatalf("Get: %v", err
)
402 matchReturnedCookies(t
, expectedCookies
, resp
.Cookies())
405 func matchReturnedCookies(t
*testing
.T
, expected
, given
[]*Cookie
) {
406 if len(given
) != len(expected
) {
407 t
.Logf("Received cookies: %v", given
)
408 t
.Errorf("Expected %d cookies, got %d", len(expected
), len(given
))
410 for _
, ec
:= range expected
{
412 for _
, c
:= range given
{
413 if ec
.Name
== c
.Name
&& ec
.Value
== c
.Value
{
419 t
.Errorf("Missing cookie %v", ec
)
424 func TestJarCalls(t
*testing
.T
) {
426 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
427 pathSuffix
:= r
.RequestURI
[1:]
428 if r
.RequestURI
== "/nosetcookie" {
429 return // dont set cookies for this path
431 SetCookie(w
, &Cookie
{Name
: "name" + pathSuffix
, Value
: "val" + pathSuffix
})
432 if r
.RequestURI
== "/" {
433 Redirect(w
, r
, "http://secondhost.fake/secondpath", 302)
437 jar
:= new(RecordingJar
)
440 Transport
: &Transport
{
441 Dial
: func(_
string, _
string) (net
.Conn
, error
) {
442 return net
.Dial("tcp", ts
.Listener
.Addr().String())
446 _
, err
:= c
.Get("http://firsthost.fake/")
450 _
, err
= c
.Get("http://firsthost.fake/nosetcookie")
454 got
:= jar
.log
.String()
455 want
:= `Cookies("http://firsthost.fake/")
456 SetCookie("http://firsthost.fake/", [name=val])
457 Cookies("http://secondhost.fake/secondpath")
458 SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
459 Cookies("http://firsthost.fake/nosetcookie")
462 t
.Errorf("Got Jar calls:\n%s\nWant:\n%s", got
, want
)
466 // RecordingJar keeps a log of calls made to it, without
467 // tracking any cookies.
468 type RecordingJar
struct {
473 func (j
*RecordingJar
) SetCookies(u
*url
.URL
, cookies
[]*Cookie
) {
474 j
.logf("SetCookie(%q, %v)\n", u
, cookies
)
477 func (j
*RecordingJar
) Cookies(u
*url
.URL
) []*Cookie
{
478 j
.logf("Cookies(%q)\n", u
)
482 func (j
*RecordingJar
) logf(format
string, args
...interface{}) {
485 fmt
.Fprintf(&j
.log
, format
, args
...)
488 func TestStreamingGet(t
*testing
.T
) {
490 say
:= make(chan string)
491 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
493 for str
:= range say
{
501 res
, err
:= c
.Get(ts
.URL
)
506 for _
, str
:= range []string{"i", "am", "also", "known", "as", "comet"} {
508 n
, err
:= io
.ReadFull(res
.Body
, buf
[0:len(str
)])
510 t
.Fatalf("ReadFull on %q: %v", str
, err
)
513 t
.Fatalf("Receiving %q, only read %d bytes", str
, n
)
515 got
:= string(buf
[0:n
])
517 t
.Fatalf("Expected %q, got %q", str
, got
)
521 _
, err
= io
.ReadFull(res
.Body
, buf
[0:1])
523 t
.Fatalf("at end expected EOF, got %v", err
)
527 type writeCountingConn
struct {
532 func (c
*writeCountingConn
) Write(p
[]byte) (int, error
) {
534 return c
.Conn
.Write(p
)
537 // TestClientWrites verifies that client requests are buffered and we
538 // don't send a TCP packet per line of the http request + body.
539 func TestClientWrites(t
*testing
.T
) {
541 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
546 dialer
:= func(netz
string, addr
string) (net
.Conn
, error
) {
547 c
, err
:= net
.Dial(netz
, addr
)
549 c
= &writeCountingConn
{c
, &writes
}
553 c
:= &Client
{Transport
: &Transport
{Dial
: dialer
}}
555 _
, err
:= c
.Get(ts
.URL
)
560 t
.Errorf("Get request did %d Write calls, want 1", writes
)
564 _
, err
= c
.PostForm(ts
.URL
, url
.Values
{"foo": {"bar"}})
569 t
.Errorf("Post request did %d Write calls, want 1", writes
)
573 func TestClientInsecureTransport(t
*testing
.T
) {
575 ts
:= httptest
.NewTLSServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
576 w
.Write([]byte("Hello"))
578 errc
:= make(chanWriter
, 10) // but only expecting 1
579 ts
.Config
.ErrorLog
= log
.New(errc
, "", 0)
582 // TODO(bradfitz): add tests for skipping hostname checks too?
583 // would require a new cert for testing, and probably
584 // redundant with these tests.
585 for _
, insecure
:= range []bool{true, false} {
587 TLSClientConfig
: &tls
.Config
{
588 InsecureSkipVerify
: insecure
,
591 defer tr
.CloseIdleConnections()
592 c
:= &Client
{Transport
: tr
}
593 res
, err
:= c
.Get(ts
.URL
)
594 if (err
== nil) != insecure
{
595 t
.Errorf("insecure=%v: got unexpected err=%v", insecure
, err
)
604 if !strings
.Contains(v
, "TLS handshake error") {
605 t
.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v
)
607 case <-time
.After(5 * time
.Second
):
608 t
.Errorf("timeout waiting for logged error")
613 func TestClientErrorWithRequestURI(t
*testing
.T
) {
615 req
, _
:= NewRequest("GET", "http://localhost:1234/", nil)
616 req
.RequestURI
= "/this/field/is/illegal/and/should/error/"
617 _
, err
:= DefaultClient
.Do(req
)
619 t
.Fatalf("expected an error")
621 if !strings
.Contains(err
.Error(), "RequestURI") {
622 t
.Errorf("wanted error mentioning RequestURI; got error: %v", err
)
626 func newTLSTransport(t
*testing
.T
, ts
*httptest
.Server
) *Transport
{
627 certs
:= x509
.NewCertPool()
628 for _
, c
:= range ts
.TLS
.Certificates
{
629 roots
, err
:= x509
.ParseCertificates(c
.Certificate
[len(c
.Certificate
)-1])
631 t
.Fatalf("error parsing server's root cert: %v", err
)
633 for _
, root
:= range roots
{
638 TLSClientConfig
: &tls
.Config
{RootCAs
: certs
},
642 func TestClientWithCorrectTLSServerName(t
*testing
.T
) {
644 ts
:= httptest
.NewTLSServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
645 if r
.TLS
.ServerName
!= "127.0.0.1" {
646 t
.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r
.TLS
.ServerName
)
651 c
:= &Client
{Transport
: newTLSTransport(t
, ts
)}
652 if _
, err
:= c
.Get(ts
.URL
); err
!= nil {
653 t
.Fatalf("expected successful TLS connection, got error: %v", err
)
657 func TestClientWithIncorrectTLSServerName(t
*testing
.T
) {
659 ts
:= httptest
.NewTLSServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {}))
661 errc
:= make(chanWriter
, 10) // but only expecting 1
662 ts
.Config
.ErrorLog
= log
.New(errc
, "", 0)
664 trans
:= newTLSTransport(t
, ts
)
665 trans
.TLSClientConfig
.ServerName
= "badserver"
666 c
:= &Client
{Transport
: trans
}
667 _
, err
:= c
.Get(ts
.URL
)
669 t
.Fatalf("expected an error")
671 if !strings
.Contains(err
.Error(), "127.0.0.1") ||
!strings
.Contains(err
.Error(), "badserver") {
672 t
.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err
)
676 if !strings
.Contains(v
, "TLS handshake error") {
677 t
.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v
)
679 case <-time
.After(5 * time
.Second
):
680 t
.Errorf("timeout waiting for logged error")
684 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
687 // tls.Config.ServerName (non-empty, set to "example.com") takes
688 // precedence over "some-other-host.tld" which previously incorrectly
689 // took precedence. We don't actually connect to (or even resolve)
690 // "some-other-host.tld", though, because of the Transport.Dial hook.
692 // The httptest.Server has a cert with "example.com" as its name.
693 func TestTransportUsesTLSConfigServerName(t
*testing
.T
) {
695 ts
:= httptest
.NewTLSServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
696 w
.Write([]byte("Hello"))
700 tr
:= newTLSTransport(t
, ts
)
701 tr
.TLSClientConfig
.ServerName
= "example.com" // one of httptest's Server cert names
702 tr
.Dial
= func(netw
, addr
string) (net
.Conn
, error
) {
703 return net
.Dial(netw
, ts
.Listener
.Addr().String())
705 defer tr
.CloseIdleConnections()
706 c
:= &Client
{Transport
: tr
}
707 res
, err
:= c
.Get("https://some-other-host.tld/")
714 func TestResponseSetsTLSConnectionState(t
*testing
.T
) {
716 ts
:= httptest
.NewTLSServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
717 w
.Write([]byte("Hello"))
721 tr
:= newTLSTransport(t
, ts
)
722 tr
.TLSClientConfig
.CipherSuites
= []uint16{tls
.TLS_RSA_WITH_3DES_EDE_CBC_SHA
}
723 tr
.Dial
= func(netw
, addr
string) (net
.Conn
, error
) {
724 return net
.Dial(netw
, ts
.Listener
.Addr().String())
726 defer tr
.CloseIdleConnections()
727 c
:= &Client
{Transport
: tr
}
728 res
, err
:= c
.Get("https://example.com/")
732 defer res
.Body
.Close()
734 t
.Fatal("Response didn't set TLS Connection State.")
736 if got
, want
:= res
.TLS
.CipherSuite
, tls
.TLS_RSA_WITH_3DES_EDE_CBC_SHA
; got
!= want
{
737 t
.Errorf("TLS Cipher Suite = %d; want %d", got
, want
)
741 // Verify Response.ContentLength is populated. http://golang.org/issue/4126
742 func TestClientHeadContentLength(t
*testing
.T
) {
744 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
745 if v
:= r
.FormValue("cl"); v
!= "" {
746 w
.Header().Set("Content-Length", v
)
758 for _
, tt
:= range tests
{
759 req
, _
:= NewRequest("HEAD", ts
.URL
+tt
.suffix
, nil)
760 res
, err
:= DefaultClient
.Do(req
)
764 if res
.ContentLength
!= tt
.want
{
765 t
.Errorf("Content-Length = %d; want %d", res
.ContentLength
, tt
.want
)
767 bs
, err
:= ioutil
.ReadAll(res
.Body
)
772 t
.Errorf("Unexpected content: %q", bs
)
777 func TestEmptyPasswordAuth(t
*testing
.T
) {
780 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
781 auth
:= r
.Header
.Get("Authorization")
782 if strings
.HasPrefix(auth
, "Basic ") {
784 decoded
, err
:= base64
.StdEncoding
.DecodeString(encoded
)
788 expected
:= gopher
+ ":"
791 t
.Errorf("Invalid Authorization header. Got %q, wanted %q", s
, expected
)
794 t
.Errorf("Invalid auth %q", auth
)
799 req
, err
:= NewRequest("GET", ts
.URL
, nil)
803 req
.URL
.User
= url
.User(gopher
)
804 resp
, err
:= c
.Do(req
)
808 defer resp
.Body
.Close()
811 func TestBasicAuth(t
*testing
.T
) {
813 tr
:= &recordingTransport
{}
814 client
:= &Client
{Transport
: tr
}
816 url
:= "http://My%20User:My%20Pass@dummy.faketld/"
817 expected
:= "My User:My Pass"
820 if tr
.req
.Method
!= "GET" {
821 t
.Errorf("got method %q, want %q", tr
.req
.Method
, "GET")
823 if tr
.req
.URL
.String() != url
{
824 t
.Errorf("got URL %q, want %q", tr
.req
.URL
.String(), url
)
826 if tr
.req
.Header
== nil {
827 t
.Fatalf("expected non-nil request Header")
829 auth
:= tr
.req
.Header
.Get("Authorization")
830 if strings
.HasPrefix(auth
, "Basic ") {
832 decoded
, err
:= base64
.StdEncoding
.DecodeString(encoded
)
838 t
.Errorf("Invalid Authorization header. Got %q, wanted %q", s
, expected
)
841 t
.Errorf("Invalid auth %q", auth
)
845 func TestClientTimeout(t
*testing
.T
) {
847 t
.Skip("skipping in short mode")
850 sawRoot
:= make(chan bool, 1)
851 sawSlow
:= make(chan bool, 1)
852 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
853 if r
.URL
.Path
== "/" {
855 Redirect(w
, r
, "/slow", StatusFound
)
858 if r
.URL
.Path
== "/slow" {
859 w
.Write([]byte("Hello"))
862 time
.Sleep(2 * time
.Second
)
867 const timeout
= 500 * time
.Millisecond
872 res
, err
:= c
.Get(ts
.URL
)
881 t
.Fatal("handler never got / request")
888 t
.Fatal("handler never got /slow request")
891 errc
:= make(chan error
, 1)
893 _
, err
:= ioutil
.ReadAll(res
.Body
)
898 const failTime
= timeout
* 2
902 t
.Error("expected error from ReadAll")
905 case <-time
.After(failTime
):
906 t
.Errorf("timeout after %v waiting for timeout of %v", failTime
, timeout
)
910 func TestClientRedirectEatsBody(t
*testing
.T
) {
912 saw
:= make(chan string, 2)
913 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
915 if r
.URL
.Path
== "/" {
916 Redirect(w
, r
, "/foo", StatusFound
) // which includes a body
921 res
, err
:= Get(ts
.URL
)
925 _
, err
= ioutil
.ReadAll(res
.Body
)
935 t
.Fatal("server didn't see a request")
942 t
.Fatal("server didn't see a second request")
946 t
.Fatal("server saw different client ports before & after the redirect")
950 // eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
951 type eofReaderFunc
func()
953 func (f eofReaderFunc
) Read(p
[]byte) (n
int, err error
) {
958 func TestClientTrailers(t
*testing
.T
) {
960 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
961 w
.Header().Set("Connection", "close")
962 w
.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
963 w
.Header().Add("Trailer", "Server-Trailer-C")
966 for k
:= range r
.Trailer
{
967 decl
= append(decl
, k
)
971 slurp
, err
:= ioutil
.ReadAll(r
.Body
)
973 t
.Errorf("Server reading request body: %v", err
)
975 if string(slurp
) != "foo" {
976 t
.Errorf("Server read request body %q; want foo", slurp
)
978 if r
.Trailer
== nil {
979 io
.WriteString(w
, "nil Trailer")
981 fmt
.Fprintf(w
, "decl: %v, vals: %s, %s",
983 r
.Trailer
.Get("Client-Trailer-A"),
984 r
.Trailer
.Get("Client-Trailer-B"))
987 // TODO: golang.org/issue/7759: there's no way yet for
988 // the server to set trailers without hijacking, so do
989 // that for now, just to test the client. Later, in
990 // Go 1.4, it should be implicit that any mutations
991 // to w.Header() after the initial write are the
992 // trailers to be sent, if and only if they were
993 // previously declared with w.Header().Set("Trailer",
996 conn
, buf
, _
:= w
.(Hijacker
).Hijack()
998 t
.Set("Server-Trailer-A", "valuea")
999 t
.Set("Server-Trailer-C", "valuec") // skipping B
1000 buf
.WriteString("0\r\n") // eof
1002 buf
.WriteString("\r\n") // end of trailers
1009 req
, _
= NewRequest("POST", ts
.URL
, io
.MultiReader(
1010 eofReaderFunc(func() {
1011 req
.Trailer
["Client-Trailer-A"] = []string{"valuea"}
1013 strings
.NewReader("foo"),
1014 eofReaderFunc(func() {
1015 req
.Trailer
["Client-Trailer-B"] = []string{"valueb"}
1018 req
.Trailer
= Header
{
1019 "Client-Trailer-A": nil, // to be set later
1020 "Client-Trailer-B": nil, // to be set later
1022 req
.ContentLength
= -1
1023 res
, err
:= DefaultClient
.Do(req
)
1027 if err
:= wantBody(res
, err
, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err
!= nil {
1031 "Server-Trailer-A": []string{"valuea"},
1032 "Server-Trailer-B": nil,
1033 "Server-Trailer-C": []string{"valuec"},
1035 if !reflect
.DeepEqual(res
.Trailer
, want
) {
1036 t
.Errorf("Response trailers = %#v; want %#v", res
.Trailer
, want
)