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.
30 var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
31 w.Header().Set("Last-Modified", "sometime")
32 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
35 // pedanticReadAll works like ioutil.ReadAll but additionally
36 // verifies that r obeys the documented io.Reader contract.
37 func pedanticReadAll(r io.Reader) (b []byte, err error) {
42 if n == 0 && err == nil {
43 return nil, fmt.Errorf("Read: n=0 with err=nil")
45 b = append(b, buf[:n]...)
48 if n != 0 || err != io.EOF {
49 return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err)
59 type chanWriter chan string
61 func (w chanWriter) Write(p []byte) (n int, err error) {
66 func TestClient(t *testing.T) {
68 ts := httptest.NewServer(robotsTxtHandler)
74 b, err = pedanticReadAll(r.Body)
79 } else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
80 t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
84 func TestClientHead_h1(t *testing.T) { testClientHead(t, h1Mode) }
85 func TestClientHead_h2(t *testing.T) { testClientHead(t, h2Mode) }
87 func testClientHead(t *testing.T, h2 bool) {
89 cst := newClientServerTest(t, h2, robotsTxtHandler)
92 r, err := cst.c.Head(cst.ts.URL)
96 if _, ok := r.Header["Last-Modified"]; !ok {
97 t.Error("Last-Modified header not found.")
101 type recordingTransport struct {
105 func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error) {
107 return nil, errors.New("dummy impl")
110 func TestGetRequestFormat(t *testing.T) {
112 tr := &recordingTransport{}
113 client := &Client{Transport: tr}
114 url := "http://dummy.faketld/"
115 client.Get(url) // Note: doesn't hit network
116 if tr.req.Method != "GET" {
117 t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
119 if tr.req.URL.String() != url {
120 t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
122 if tr.req.Header == nil {
123 t.Errorf("expected non-nil request Header")
127 func TestPostRequestFormat(t *testing.T) {
129 tr := &recordingTransport{}
130 client := &Client{Transport: tr}
132 url := "http://dummy.faketld/"
133 json := `{"key":"value"}`
134 b := strings.NewReader(json)
135 client.Post(url, "application/json", b) // Note: doesn't hit network
137 if tr.req.Method != "POST" {
138 t.Errorf("got method %q, want %q", tr.req.Method, "POST")
140 if tr.req.URL.String() != url {
141 t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
143 if tr.req.Header == nil {
144 t.Fatalf("expected non-nil request Header")
147 t.Error("got Close true, want false")
149 if g, e := tr.req.ContentLength, int64(len(json)); g != e {
150 t.Errorf("got ContentLength %d, want %d", g, e)
154 func TestPostFormRequestFormat(t *testing.T) {
156 tr := &recordingTransport{}
157 client := &Client{Transport: tr}
159 urlStr := "http://dummy.faketld/"
160 form := make(url.Values)
161 form.Set("foo", "bar")
162 form.Add("foo", "bar2")
163 form.Set("bar", "baz")
164 client.PostForm(urlStr, form) // Note: doesn't hit network
166 if tr.req.Method != "POST" {
167 t.Errorf("got method %q, want %q", tr.req.Method, "POST")
169 if tr.req.URL.String() != urlStr {
170 t.Errorf("got URL %q, want %q", tr.req.URL.String(), urlStr)
172 if tr.req.Header == nil {
173 t.Fatalf("expected non-nil request Header")
175 if g, e := tr.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; g != e {
176 t.Errorf("got Content-Type %q, want %q", g, e)
179 t.Error("got Close true, want false")
181 // Depending on map iteration, body can be either of these.
182 expectedBody := "foo=bar&foo=bar2&bar=baz"
183 expectedBody1 := "bar=baz&foo=bar&foo=bar2"
184 if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e {
185 t.Errorf("got ContentLength %d, want %d", g, e)
187 bodyb, err := ioutil.ReadAll(tr.req.Body)
189 t.Fatalf("ReadAll on req.Body: %v", err)
191 if g := string(bodyb); g != expectedBody && g != expectedBody1 {
192 t.Errorf("got body %q, want %q or %q", g, expectedBody, expectedBody1)
196 func TestClientRedirects(t *testing.T) {
198 var ts *httptest.Server
199 ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
200 n, _ := strconv.Atoi(r.FormValue("n"))
201 // Test Referer header. (7 is arbitrary position to test at)
203 if g, e := r.Referer(), ts.URL+"/?n=6"; e != g {
204 t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
208 Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
211 fmt.Fprintf(w, "n=%d", n)
216 _, err := c.Get(ts.URL)
217 if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
218 t.Errorf("with default client Get, expected error %q, got %q", e, g)
221 // HEAD request should also have the ability to follow redirects.
222 _, err = c.Head(ts.URL)
223 if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
224 t.Errorf("with default client Head, expected error %q, got %q", e, g)
227 // Do should also follow redirects.
228 greq, _ := NewRequest("GET", ts.URL, nil)
230 if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
231 t.Errorf("with default client Do, expected error %q, got %q", e, g)
234 // Requests with an empty Method should also redirect (Issue 12705)
237 if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
238 t.Errorf("with default client Do and empty Method, expected error %q, got %q", e, g)
242 var lastVia []*Request
244 c = &Client{CheckRedirect: func(req *Request, via []*Request) error {
249 res, err := c.Get(ts.URL)
251 t.Fatalf("Get error: %v", err)
254 finalUrl := res.Request.URL.String()
255 if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
256 t.Errorf("with custom client, expected error %q, got %q", e, g)
258 if !strings.HasSuffix(finalUrl, "/?n=15") {
259 t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
261 if e, g := 15, len(lastVia); e != g {
262 t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
265 // Test that Request.Cancel is propagated between requests (Issue 14053)
266 creq, _ := NewRequest("HEAD", ts.URL, nil)
267 cancel := make(chan struct{})
269 if _, err := c.Do(creq); err != nil {
273 t.Fatal("didn't see redirect")
275 if lastReq.Cancel != cancel {
276 t.Errorf("expected lastReq to have the cancel channel set on the initial req")
279 checkErr = errors.New("no redirects allowed")
280 res, err = c.Get(ts.URL)
281 if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
282 t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
285 t.Fatalf("Expected a non-nil Response on CheckRedirect failure (https://golang.org/issue/3795)")
288 if res.Header.Get("Location") == "" {
289 t.Errorf("no Location header in Response")
293 func TestPostRedirects(t *testing.T) {
299 var ts *httptest.Server
300 ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
302 fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
304 if v := r.URL.Query().Get("code"); v != "" {
305 code, _ := strconv.Atoi(v)
307 w.Header().Set("Location", ts.URL)
315 want int // response code
323 for _, tt := range tests {
324 res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
328 if res.StatusCode != tt.want {
329 t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
335 want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
337 t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
341 var expectedCookies = []*Cookie{
342 {Name: "ChocolateChip", Value: "tasty"},
343 {Name: "First", Value: "Hit"},
344 {Name: "Second", Value: "Hit"},
347 var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
348 for _, cookie := range r.Cookies() {
351 if r.URL.Path == "/" {
352 SetCookie(w, expectedCookies[1])
353 Redirect(w, r, "/second", StatusMovedPermanently)
355 SetCookie(w, expectedCookies[2])
356 w.Write([]byte("hello"))
360 func TestClientSendsCookieFromJar(t *testing.T) {
362 tr := &recordingTransport{}
363 client := &Client{Transport: tr}
364 client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
365 us := "http://dummy.faketld/"
366 u, _ := url.Parse(us)
367 client.Jar.SetCookies(u, expectedCookies)
369 client.Get(us) // Note: doesn't hit network
370 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
372 client.Head(us) // Note: doesn't hit network
373 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
375 client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
376 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
378 client.PostForm(us, url.Values{}) // Note: doesn't hit network
379 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
381 req, _ := NewRequest("GET", us, nil)
382 client.Do(req) // Note: doesn't hit network
383 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
385 req, _ = NewRequest("POST", us, nil)
386 client.Do(req) // Note: doesn't hit network
387 matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
390 // Just enough correctness for our redirect tests. Uses the URL.Host as the
391 // scope of all cookies.
392 type TestJar struct {
394 perURL map[string][]*Cookie
397 func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
401 j.perURL = make(map[string][]*Cookie)
403 j.perURL[u.Host] = cookies
406 func (j *TestJar) Cookies(u *url.URL) []*Cookie {
409 return j.perURL[u.Host]
412 func TestRedirectCookiesJar(t *testing.T) {
414 var ts *httptest.Server
415 ts = httptest.NewServer(echoCookiesRedirectHandler)
420 u, _ := url.Parse(ts.URL)
421 c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
422 resp, err := c.Get(ts.URL)
424 t.Fatalf("Get: %v", err)
427 matchReturnedCookies(t, expectedCookies, resp.Cookies())
430 func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
431 if len(given) != len(expected) {
432 t.Logf("Received cookies: %v", given)
433 t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
435 for _, ec := range expected {
437 for _, c := range given {
438 if ec.Name == c.Name && ec.Value == c.Value {
444 t.Errorf("Missing cookie %v", ec)
449 func TestJarCalls(t *testing.T) {
451 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
452 pathSuffix := r.RequestURI[1:]
453 if r.RequestURI == "/nosetcookie" {
454 return // don't set cookies for this path
456 SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
457 if r.RequestURI == "/" {
458 Redirect(w, r, "http://secondhost.fake/secondpath", 302)
462 jar := new(RecordingJar)
465 Transport: &Transport{
466 Dial: func(_ string, _ string) (net.Conn, error) {
467 return net.Dial("tcp", ts.Listener.Addr().String())
471 _, err := c.Get("http://firsthost.fake/")
475 _, err = c.Get("http://firsthost.fake/nosetcookie")
479 got := jar.log.String()
480 want := `Cookies("http://firsthost.fake/")
481 SetCookie("http://firsthost.fake/", [name=val])
482 Cookies("http://secondhost.fake/secondpath")
483 SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
484 Cookies("http://firsthost.fake/nosetcookie")
487 t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
491 // RecordingJar keeps a log of calls made to it, without
492 // tracking any cookies.
493 type RecordingJar struct {
498 func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
499 j.logf("SetCookie(%q, %v)\n", u, cookies)
502 func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
503 j.logf("Cookies(%q)\n", u)
507 func (j *RecordingJar) logf(format string, args ...interface{}) {
510 fmt.Fprintf(&j.log, format, args...)
513 func TestStreamingGet_h1(t *testing.T) { testStreamingGet(t, h1Mode) }
514 func TestStreamingGet_h2(t *testing.T) { testStreamingGet(t, h2Mode) }
516 func testStreamingGet(t *testing.T, h2 bool) {
518 say := make(chan string)
519 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
521 for str := range say {
529 res, err := c.Get(cst.ts.URL)
534 for _, str := range []string{"i", "am", "also", "known", "as", "comet"} {
536 n, err := io.ReadFull(res.Body, buf[0:len(str)])
538 t.Fatalf("ReadFull on %q: %v", str, err)
541 t.Fatalf("Receiving %q, only read %d bytes", str, n)
543 got := string(buf[0:n])
545 t.Fatalf("Expected %q, got %q", str, got)
549 _, err = io.ReadFull(res.Body, buf[0:1])
551 t.Fatalf("at end expected EOF, got %v", err)
555 type writeCountingConn struct {
560 func (c *writeCountingConn) Write(p []byte) (int, error) {
562 return c.Conn.Write(p)
565 // TestClientWrites verifies that client requests are buffered and we
566 // don't send a TCP packet per line of the http request + body.
567 func TestClientWrites(t *testing.T) {
569 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
574 dialer := func(netz string, addr string) (net.Conn, error) {
575 c, err := net.Dial(netz, addr)
577 c = &writeCountingConn{c, &writes}
581 c := &Client{Transport: &Transport{Dial: dialer}}
583 _, err := c.Get(ts.URL)
588 t.Errorf("Get request did %d Write calls, want 1", writes)
592 _, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}})
597 t.Errorf("Post request did %d Write calls, want 1", writes)
601 func TestClientInsecureTransport(t *testing.T) {
603 ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
604 w.Write([]byte("Hello"))
606 errc := make(chanWriter, 10) // but only expecting 1
607 ts.Config.ErrorLog = log.New(errc, "", 0)
610 // TODO(bradfitz): add tests for skipping hostname checks too?
611 // would require a new cert for testing, and probably
612 // redundant with these tests.
613 for _, insecure := range []bool{true, false} {
615 TLSClientConfig: &tls.Config{
616 InsecureSkipVerify: insecure,
619 defer tr.CloseIdleConnections()
620 c := &Client{Transport: tr}
621 res, err := c.Get(ts.URL)
622 if (err == nil) != insecure {
623 t.Errorf("insecure=%v: got unexpected err=%v", insecure, err)
632 if !strings.Contains(v, "TLS handshake error") {
633 t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
635 case <-time.After(5 * time.Second):
636 t.Errorf("timeout waiting for logged error")
641 func TestClientErrorWithRequestURI(t *testing.T) {
643 req, _ := NewRequest("GET", "http://localhost:1234/", nil)
644 req.RequestURI = "/this/field/is/illegal/and/should/error/"
645 _, err := DefaultClient.Do(req)
647 t.Fatalf("expected an error")
649 if !strings.Contains(err.Error(), "RequestURI") {
650 t.Errorf("wanted error mentioning RequestURI; got error: %v", err)
654 func newTLSTransport(t *testing.T, ts *httptest.Server) *Transport {
655 certs := x509.NewCertPool()
656 for _, c := range ts.TLS.Certificates {
657 roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
659 t.Fatalf("error parsing server's root cert: %v", err)
661 for _, root := range roots {
666 TLSClientConfig: &tls.Config{RootCAs: certs},
670 func TestClientWithCorrectTLSServerName(t *testing.T) {
673 const serverName = "example.com"
674 ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
675 if r.TLS.ServerName != serverName {
676 t.Errorf("expected client to set ServerName %q, got: %q", serverName, r.TLS.ServerName)
681 trans := newTLSTransport(t, ts)
682 trans.TLSClientConfig.ServerName = serverName
683 c := &Client{Transport: trans}
684 if _, err := c.Get(ts.URL); err != nil {
685 t.Fatalf("expected successful TLS connection, got error: %v", err)
689 func TestClientWithIncorrectTLSServerName(t *testing.T) {
691 ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
693 errc := make(chanWriter, 10) // but only expecting 1
694 ts.Config.ErrorLog = log.New(errc, "", 0)
696 trans := newTLSTransport(t, ts)
697 trans.TLSClientConfig.ServerName = "badserver"
698 c := &Client{Transport: trans}
699 _, err := c.Get(ts.URL)
701 t.Fatalf("expected an error")
703 if !strings.Contains(err.Error(), "127.0.0.1") || !strings.Contains(err.Error(), "badserver") {
704 t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err)
708 if !strings.Contains(v, "TLS handshake error") {
709 t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
711 case <-time.After(5 * time.Second):
712 t.Errorf("timeout waiting for logged error")
716 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
719 // tls.Config.ServerName (non-empty, set to "example.com") takes
720 // precedence over "some-other-host.tld" which previously incorrectly
721 // took precedence. We don't actually connect to (or even resolve)
722 // "some-other-host.tld", though, because of the Transport.Dial hook.
724 // The httptest.Server has a cert with "example.com" as its name.
725 func TestTransportUsesTLSConfigServerName(t *testing.T) {
727 ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
728 w.Write([]byte("Hello"))
732 tr := newTLSTransport(t, ts)
733 tr.TLSClientConfig.ServerName = "example.com" // one of httptest's Server cert names
734 tr.Dial = func(netw, addr string) (net.Conn, error) {
735 return net.Dial(netw, ts.Listener.Addr().String())
737 defer tr.CloseIdleConnections()
738 c := &Client{Transport: tr}
739 res, err := c.Get("https://some-other-host.tld/")
746 func TestResponseSetsTLSConnectionState(t *testing.T) {
748 ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
749 w.Write([]byte("Hello"))
753 tr := newTLSTransport(t, ts)
754 tr.TLSClientConfig.CipherSuites = []uint16{tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA}
755 tr.Dial = func(netw, addr string) (net.Conn, error) {
756 return net.Dial(netw, ts.Listener.Addr().String())
758 defer tr.CloseIdleConnections()
759 c := &Client{Transport: tr}
760 res, err := c.Get("https://example.com/")
764 defer res.Body.Close()
766 t.Fatal("Response didn't set TLS Connection State.")
768 if got, want := res.TLS.CipherSuite, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA; got != want {
769 t.Errorf("TLS Cipher Suite = %d; want %d", got, want)
773 // Check that an HTTPS client can interpret a particular TLS error
774 // to determine that the server is speaking HTTP.
775 // See golang.org/issue/11111.
776 func TestHTTPSClientDetectsHTTPServer(t *testing.T) {
778 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
781 _, err := Get(strings.Replace(ts.URL, "http", "https", 1))
782 if got := err.Error(); !strings.Contains(got, "HTTP response to HTTPS client") {
783 t.Fatalf("error = %q; want error indicating HTTP response to HTTPS request", got)
787 // Verify Response.ContentLength is populated. https://golang.org/issue/4126
788 func TestClientHeadContentLength_h1(t *testing.T) {
789 testClientHeadContentLength(t, h1Mode)
792 func TestClientHeadContentLength_h2(t *testing.T) {
793 testClientHeadContentLength(t, h2Mode)
796 func testClientHeadContentLength(t *testing.T, h2 bool) {
798 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
799 if v := r.FormValue("cl"); v != "" {
800 w.Header().Set("Content-Length", v)
812 for _, tt := range tests {
813 req, _ := NewRequest("HEAD", cst.ts.URL+tt.suffix, nil)
814 res, err := cst.c.Do(req)
818 if res.ContentLength != tt.want {
819 t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want)
821 bs, err := ioutil.ReadAll(res.Body)
826 t.Errorf("Unexpected content: %q", bs)
831 func TestEmptyPasswordAuth(t *testing.T) {
834 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
835 auth := r.Header.Get("Authorization")
836 if strings.HasPrefix(auth, "Basic ") {
838 decoded, err := base64.StdEncoding.DecodeString(encoded)
842 expected := gopher + ":"
845 t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
848 t.Errorf("Invalid auth %q", auth)
853 req, err := NewRequest("GET", ts.URL, nil)
857 req.URL.User = url.User(gopher)
858 resp, err := c.Do(req)
862 defer resp.Body.Close()
865 func TestBasicAuth(t *testing.T) {
867 tr := &recordingTransport{}
868 client := &Client{Transport: tr}
870 url := "http://My%20User:My%20Pass@dummy.faketld/"
871 expected := "My User:My Pass"
874 if tr.req.Method != "GET" {
875 t.Errorf("got method %q, want %q", tr.req.Method, "GET")
877 if tr.req.URL.String() != url {
878 t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
880 if tr.req.Header == nil {
881 t.Fatalf("expected non-nil request Header")
883 auth := tr.req.Header.Get("Authorization")
884 if strings.HasPrefix(auth, "Basic ") {
886 decoded, err := base64.StdEncoding.DecodeString(encoded)
892 t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
895 t.Errorf("Invalid auth %q", auth)
899 func TestBasicAuthHeadersPreserved(t *testing.T) {
901 tr := &recordingTransport{}
902 client := &Client{Transport: tr}
904 // If Authorization header is provided, username in URL should not override it
905 url := "http://My%20User@dummy.faketld/"
906 req, err := NewRequest("GET", url, nil)
910 req.SetBasicAuth("My User", "My Pass")
911 expected := "My User:My Pass"
914 if tr.req.Method != "GET" {
915 t.Errorf("got method %q, want %q", tr.req.Method, "GET")
917 if tr.req.URL.String() != url {
918 t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
920 if tr.req.Header == nil {
921 t.Fatalf("expected non-nil request Header")
923 auth := tr.req.Header.Get("Authorization")
924 if strings.HasPrefix(auth, "Basic ") {
926 decoded, err := base64.StdEncoding.DecodeString(encoded)
932 t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
935 t.Errorf("Invalid auth %q", auth)
940 func TestClientTimeout_h1(t *testing.T) { testClientTimeout(t, h1Mode) }
941 func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) }
943 func testClientTimeout(t *testing.T, h2 bool) {
945 t.Skip("skipping in short mode")
948 sawRoot := make(chan bool, 1)
949 sawSlow := make(chan bool, 1)
950 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
951 if r.URL.Path == "/" {
953 Redirect(w, r, "/slow", StatusFound)
956 if r.URL.Path == "/slow" {
957 w.Write([]byte("Hello"))
960 time.Sleep(2 * time.Second)
965 const timeout = 500 * time.Millisecond
966 cst.c.Timeout = timeout
968 res, err := cst.c.Get(cst.ts.URL)
977 t.Fatal("handler never got / request")
984 t.Fatal("handler never got /slow request")
987 errc := make(chan error, 1)
989 _, err := ioutil.ReadAll(res.Body)
994 const failTime = timeout * 2
998 t.Fatal("expected error from ReadAll")
1000 ne, ok := err.(net.Error)
1002 t.Errorf("error value from ReadAll was %T; expected some net.Error", err)
1003 } else if !ne.Timeout() {
1004 t.Errorf("net.Error.Timeout = false; want true")
1006 if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") {
1007 t.Errorf("error string = %q; missing timeout substring", got)
1009 case <-time.After(failTime):
1010 t.Errorf("timeout after %v waiting for timeout of %v", failTime, timeout)
1014 func TestClientTimeout_Headers_h1(t *testing.T) { testClientTimeout_Headers(t, h1Mode) }
1015 func TestClientTimeout_Headers_h2(t *testing.T) { testClientTimeout_Headers(t, h2Mode) }
1017 // Client.Timeout firing before getting to the body
1018 func testClientTimeout_Headers(t *testing.T, h2 bool) {
1019 if testing.Short() {
1020 t.Skip("skipping in short mode")
1023 donec := make(chan bool)
1024 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
1028 // Note that we use a channel send here and not a close.
1029 // The race detector doesn't know that we're waiting for a timeout
1030 // and thinks that the waitgroup inside httptest.Server is added to concurrently
1031 // with us closing it. If we timed out immediately, we could close the testserver
1032 // before we entered the handler. We're not timing out immediately and there's
1033 // no way we would be done before we entered the handler, but the race detector
1034 // doesn't know this, so synchronize explicitly.
1035 defer func() { donec <- true }()
1037 cst.c.Timeout = 500 * time.Millisecond
1038 _, err := cst.c.Get(cst.ts.URL)
1040 t.Fatal("got response from Get; expected error")
1042 if _, ok := err.(*url.Error); !ok {
1043 t.Fatalf("Got error of type %T; want *url.Error", err)
1045 ne, ok := err.(net.Error)
1047 t.Fatalf("Got error of type %T; want some net.Error", err)
1050 t.Error("net.Error.Timeout = false; want true")
1052 if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") {
1053 t.Errorf("error string = %q; missing timeout substring", got)
1057 func TestClientRedirectEatsBody_h1(t *testing.T) { testClientRedirectEatsBody(t, h1Mode) }
1058 func TestClientRedirectEatsBody_h2(t *testing.T) { testClientRedirectEatsBody(t, h2Mode) }
1059 func testClientRedirectEatsBody(t *testing.T, h2 bool) {
1061 saw := make(chan string, 2)
1062 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
1064 if r.URL.Path == "/" {
1065 Redirect(w, r, "/foo", StatusFound) // which includes a body
1070 res, err := cst.c.Get(cst.ts.URL)
1074 _, err = ioutil.ReadAll(res.Body)
1084 t.Fatal("server didn't see a request")
1089 case second = <-saw:
1091 t.Fatal("server didn't see a second request")
1094 if first != second {
1095 t.Fatal("server saw different client ports before & after the redirect")
1099 // eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
1100 type eofReaderFunc func()
1102 func (f eofReaderFunc) Read(p []byte) (n int, err error) {
1107 func TestReferer(t *testing.T) {
1109 lastReq, newReq string // from -> to URLs
1113 {"http://gopher@test.com", "http://link.com", "http://test.com"},
1114 {"https://gopher@test.com", "https://link.com", "https://test.com"},
1116 // don't send a user and password:
1117 {"http://gopher:go@test.com", "http://link.com", "http://test.com"},
1118 {"https://gopher:go@test.com", "https://link.com", "https://test.com"},
1121 {"http://test.com", "http://link.com", "http://test.com"},
1122 {"https://test.com", "https://link.com", "https://test.com"},
1124 // https to http doesn't send a referer:
1125 {"https://test.com", "http://link.com", ""},
1126 {"https://gopher:go@test.com", "http://link.com", ""},
1128 for _, tt := range tests {
1129 l, err := url.Parse(tt.lastReq)
1133 n, err := url.Parse(tt.newReq)
1137 r := ExportRefererForURL(l, n)
1139 t.Errorf("refererForURL(%q, %q) = %q; want %q", tt.lastReq, tt.newReq, r, tt.want)