2009-06-09 10:58:58 -07:00
|
|
|
// Copyright 2009 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.
|
|
|
|
|
|
|
|
|
|
// Tests for client.go
|
|
|
|
|
|
2011-03-10 08:46:57 -08:00
|
|
|
package http_test
|
2009-06-09 10:58:58 -07:00
|
|
|
|
|
|
|
|
import (
|
2012-12-12 11:09:55 -08:00
|
|
|
"bytes"
|
2011-10-21 08:14:38 -07:00
|
|
|
"crypto/tls"
|
2012-08-22 09:15:41 -07:00
|
|
|
"crypto/x509"
|
2013-05-14 15:33:46 -07:00
|
|
|
"encoding/base64"
|
2011-11-01 22:04:37 -04:00
|
|
|
"errors"
|
2011-03-10 08:46:57 -08:00
|
|
|
"fmt"
|
2011-05-24 09:02:01 -07:00
|
|
|
"io"
|
2009-12-15 15:35:38 -08:00
|
|
|
"io/ioutil"
|
2014-02-28 12:12:51 -08:00
|
|
|
"log"
|
2011-06-23 21:10:51 -07:00
|
|
|
"net"
|
2011-11-08 15:41:54 -08:00
|
|
|
. "net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"net/url"
|
2011-04-25 22:41:50 -07:00
|
|
|
"strconv"
|
2009-12-15 15:35:38 -08:00
|
|
|
"strings"
|
2012-01-16 12:57:59 -08:00
|
|
|
"sync"
|
2009-12-15 15:35:38 -08:00
|
|
|
"testing"
|
2014-02-28 12:12:51 -08:00
|
|
|
"time"
|
2009-06-09 10:58:58 -07:00
|
|
|
)
|
|
|
|
|
|
2011-03-10 08:46:57 -08:00
|
|
|
var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
w.Header().Set("Last-Modified", "sometime")
|
|
|
|
|
fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
|
|
|
|
|
})
|
|
|
|
|
|
2011-11-16 17:35:47 -02:00
|
|
|
// pedanticReadAll works like ioutil.ReadAll but additionally
|
|
|
|
|
// verifies that r obeys the documented io.Reader contract.
|
|
|
|
|
func pedanticReadAll(r io.Reader) (b []byte, err error) {
|
|
|
|
|
var bufa [64]byte
|
|
|
|
|
buf := bufa[:]
|
|
|
|
|
for {
|
|
|
|
|
n, err := r.Read(buf)
|
|
|
|
|
if n == 0 && err == nil {
|
|
|
|
|
return nil, fmt.Errorf("Read: n=0 with err=nil")
|
|
|
|
|
}
|
|
|
|
|
b = append(b, buf[:n]...)
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
n, err := r.Read(buf)
|
|
|
|
|
if n != 0 || err != io.EOF {
|
|
|
|
|
return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err)
|
|
|
|
|
}
|
|
|
|
|
return b, nil
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return b, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-28 12:12:51 -08:00
|
|
|
type chanWriter chan string
|
|
|
|
|
|
|
|
|
|
func (w chanWriter) Write(p []byte) (n int, err error) {
|
|
|
|
|
w <- string(p)
|
|
|
|
|
return len(p), nil
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 10:58:58 -07:00
|
|
|
func TestClient(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-03-10 08:46:57 -08:00
|
|
|
ts := httptest.NewServer(robotsTxtHandler)
|
|
|
|
|
defer ts.Close()
|
2009-06-11 15:55:03 -07:00
|
|
|
|
2011-05-13 07:31:24 -07:00
|
|
|
r, err := Get(ts.URL)
|
2009-12-15 15:35:38 -08:00
|
|
|
var b []byte
|
2009-06-09 10:58:58 -07:00
|
|
|
if err == nil {
|
2011-11-16 17:35:47 -02:00
|
|
|
b, err = pedanticReadAll(r.Body)
|
2009-12-15 15:35:38 -08:00
|
|
|
r.Body.Close()
|
2009-06-09 10:58:58 -07:00
|
|
|
}
|
2009-06-22 13:26:13 -07:00
|
|
|
if err != nil {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Error(err)
|
2009-06-24 20:12:50 -07:00
|
|
|
} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
|
2009-06-09 10:58:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
2010-06-08 01:28:40 +02:00
|
|
|
|
2015-12-09 22:02:46 +00:00
|
|
|
func TestClientHead_h1(t *testing.T) { testClientHead(t, h1Mode) }
|
|
|
|
|
func TestClientHead_h2(t *testing.T) { testClientHead(t, h2Mode) }
|
2015-12-08 01:11:30 -07:00
|
|
|
|
|
|
|
|
func testClientHead(t *testing.T, h2 bool) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2015-12-08 01:11:30 -07:00
|
|
|
cst := newClientServerTest(t, h2, robotsTxtHandler)
|
|
|
|
|
defer cst.close()
|
2011-03-10 08:46:57 -08:00
|
|
|
|
2015-12-08 01:11:30 -07:00
|
|
|
r, err := cst.c.Head(cst.ts.URL)
|
2010-06-08 01:28:40 +02:00
|
|
|
if err != nil {
|
2010-06-30 18:57:27 +10:00
|
|
|
t.Fatal(err)
|
2010-06-08 01:28:40 +02:00
|
|
|
}
|
|
|
|
|
if _, ok := r.Header["Last-Modified"]; !ok {
|
|
|
|
|
t.Error("Last-Modified header not found.")
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-23 15:03:30 -08:00
|
|
|
|
|
|
|
|
type recordingTransport struct {
|
|
|
|
|
req *Request
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error) {
|
2011-02-23 15:03:30 -08:00
|
|
|
t.req = req
|
2011-11-01 22:04:37 -04:00
|
|
|
return nil, errors.New("dummy impl")
|
2011-02-23 15:03:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGetRequestFormat(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-02-23 15:03:30 -08:00
|
|
|
tr := &recordingTransport{}
|
2011-03-01 14:07:28 -08:00
|
|
|
client := &Client{Transport: tr}
|
2011-02-23 15:03:30 -08:00
|
|
|
url := "http://dummy.faketld/"
|
|
|
|
|
client.Get(url) // Note: doesn't hit network
|
|
|
|
|
if tr.req.Method != "GET" {
|
2011-03-02 10:21:56 -08:00
|
|
|
t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
|
2011-02-23 15:03:30 -08:00
|
|
|
}
|
|
|
|
|
if tr.req.URL.String() != url {
|
2011-03-02 10:21:56 -08:00
|
|
|
t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Header == nil {
|
|
|
|
|
t.Errorf("expected non-nil request Header")
|
2011-02-23 15:03:30 -08:00
|
|
|
}
|
|
|
|
|
}
|
2011-04-25 22:41:50 -07:00
|
|
|
|
2011-05-31 08:47:03 -07:00
|
|
|
func TestPostRequestFormat(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-05-31 08:47:03 -07:00
|
|
|
tr := &recordingTransport{}
|
|
|
|
|
client := &Client{Transport: tr}
|
|
|
|
|
|
|
|
|
|
url := "http://dummy.faketld/"
|
|
|
|
|
json := `{"key":"value"}`
|
|
|
|
|
b := strings.NewReader(json)
|
|
|
|
|
client.Post(url, "application/json", b) // Note: doesn't hit network
|
|
|
|
|
|
|
|
|
|
if tr.req.Method != "POST" {
|
|
|
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
|
|
|
|
}
|
|
|
|
|
if tr.req.URL.String() != url {
|
|
|
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Header == nil {
|
|
|
|
|
t.Fatalf("expected non-nil request Header")
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Close {
|
|
|
|
|
t.Error("got Close true, want false")
|
|
|
|
|
}
|
|
|
|
|
if g, e := tr.req.ContentLength, int64(len(json)); g != e {
|
|
|
|
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPostFormRequestFormat(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-05-31 08:47:03 -07:00
|
|
|
tr := &recordingTransport{}
|
|
|
|
|
client := &Client{Transport: tr}
|
|
|
|
|
|
2011-08-17 13:36:02 +10:00
|
|
|
urlStr := "http://dummy.faketld/"
|
|
|
|
|
form := make(url.Values)
|
2011-06-08 13:38:20 -07:00
|
|
|
form.Set("foo", "bar")
|
|
|
|
|
form.Add("foo", "bar2")
|
|
|
|
|
form.Set("bar", "baz")
|
2011-08-17 13:36:02 +10:00
|
|
|
client.PostForm(urlStr, form) // Note: doesn't hit network
|
2011-05-31 08:47:03 -07:00
|
|
|
|
|
|
|
|
if tr.req.Method != "POST" {
|
|
|
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "POST")
|
|
|
|
|
}
|
2011-08-17 13:36:02 +10:00
|
|
|
if tr.req.URL.String() != urlStr {
|
|
|
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), urlStr)
|
2011-05-31 08:47:03 -07:00
|
|
|
}
|
|
|
|
|
if tr.req.Header == nil {
|
|
|
|
|
t.Fatalf("expected non-nil request Header")
|
|
|
|
|
}
|
|
|
|
|
if g, e := tr.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; g != e {
|
|
|
|
|
t.Errorf("got Content-Type %q, want %q", g, e)
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Close {
|
|
|
|
|
t.Error("got Close true, want false")
|
|
|
|
|
}
|
2011-10-17 14:51:54 -04:00
|
|
|
// Depending on map iteration, body can be either of these.
|
2011-06-08 13:38:20 -07:00
|
|
|
expectedBody := "foo=bar&foo=bar2&bar=baz"
|
2011-10-17 14:51:54 -04:00
|
|
|
expectedBody1 := "bar=baz&foo=bar&foo=bar2"
|
2011-06-08 13:38:20 -07:00
|
|
|
if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e {
|
2011-05-31 08:47:03 -07:00
|
|
|
t.Errorf("got ContentLength %d, want %d", g, e)
|
|
|
|
|
}
|
2011-06-08 13:38:20 -07:00
|
|
|
bodyb, err := ioutil.ReadAll(tr.req.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("ReadAll on req.Body: %v", err)
|
|
|
|
|
}
|
2011-10-17 14:51:54 -04:00
|
|
|
if g := string(bodyb); g != expectedBody && g != expectedBody1 {
|
|
|
|
|
t.Errorf("got body %q, want %q or %q", g, expectedBody, expectedBody1)
|
2011-06-08 13:38:20 -07:00
|
|
|
}
|
2011-05-31 08:47:03 -07:00
|
|
|
}
|
|
|
|
|
|
2013-04-20 17:20:14 -07:00
|
|
|
func TestClientRedirects(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-04-25 22:41:50 -07:00
|
|
|
var ts *httptest.Server
|
|
|
|
|
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
n, _ := strconv.Atoi(r.FormValue("n"))
|
|
|
|
|
// Test Referer header. (7 is arbitrary position to test at)
|
|
|
|
|
if n == 7 {
|
2011-06-16 13:02:28 -07:00
|
|
|
if g, e := r.Referer(), ts.URL+"/?n=6"; e != g {
|
2011-04-25 22:41:50 -07:00
|
|
|
t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if n < 15 {
|
|
|
|
|
Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, "n=%d", n)
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
c := &Client{}
|
2011-05-13 07:31:24 -07:00
|
|
|
_, err := c.Get(ts.URL)
|
2011-04-25 22:41:50 -07:00
|
|
|
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
2011-05-18 17:17:26 -07:00
|
|
|
t.Errorf("with default client Get, expected error %q, got %q", e, g)
|
2011-04-25 22:41:50 -07:00
|
|
|
}
|
|
|
|
|
|
2011-05-13 08:17:59 -07:00
|
|
|
// HEAD request should also have the ability to follow redirects.
|
|
|
|
|
_, err = c.Head(ts.URL)
|
|
|
|
|
if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
2011-05-18 17:17:26 -07:00
|
|
|
t.Errorf("with default client Head, expected error %q, got %q", e, g)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do should also follow redirects.
|
|
|
|
|
greq, _ := NewRequest("GET", ts.URL, nil)
|
|
|
|
|
_, err = c.Do(greq)
|
|
|
|
|
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
|
|
|
|
t.Errorf("with default client Do, expected error %q, got %q", e, g)
|
2011-05-13 08:17:59 -07:00
|
|
|
}
|
|
|
|
|
|
2015-12-03 18:58:05 +00:00
|
|
|
// Requests with an empty Method should also redirect (Issue 12705)
|
|
|
|
|
greq.Method = ""
|
|
|
|
|
_, err = c.Do(greq)
|
|
|
|
|
if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
|
|
|
|
|
t.Errorf("with default client Do and empty Method, expected error %q, got %q", e, g)
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
var checkErr error
|
2011-04-25 22:41:50 -07:00
|
|
|
var lastVia []*Request
|
2016-01-20 22:53:50 -08:00
|
|
|
var lastReq *Request
|
|
|
|
|
c = &Client{CheckRedirect: func(req *Request, via []*Request) error {
|
|
|
|
|
lastReq = req
|
2011-04-25 22:41:50 -07:00
|
|
|
lastVia = via
|
|
|
|
|
return checkErr
|
|
|
|
|
}}
|
2011-05-13 07:31:24 -07:00
|
|
|
res, err := c.Get(ts.URL)
|
2012-11-13 22:38:25 -08:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Get error: %v", err)
|
|
|
|
|
}
|
2013-02-26 17:12:50 -08:00
|
|
|
res.Body.Close()
|
2011-05-13 07:31:24 -07:00
|
|
|
finalUrl := res.Request.URL.String()
|
2011-04-25 22:41:50 -07:00
|
|
|
if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
|
|
|
|
|
t.Errorf("with custom client, expected error %q, got %q", e, g)
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasSuffix(finalUrl, "/?n=15") {
|
|
|
|
|
t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
|
|
|
|
|
}
|
|
|
|
|
if e, g := 15, len(lastVia); e != g {
|
|
|
|
|
t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 22:53:50 -08:00
|
|
|
// Test that Request.Cancel is propagated between requests (Issue 14053)
|
|
|
|
|
creq, _ := NewRequest("HEAD", ts.URL, nil)
|
|
|
|
|
cancel := make(chan struct{})
|
|
|
|
|
creq.Cancel = cancel
|
|
|
|
|
if _, err := c.Do(creq); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if lastReq == nil {
|
|
|
|
|
t.Fatal("didn't see redirect")
|
|
|
|
|
}
|
|
|
|
|
if lastReq.Cancel != cancel {
|
2016-02-24 11:55:20 +01:00
|
|
|
t.Errorf("expected lastReq to have the cancel channel set on the initial req")
|
2016-01-20 22:53:50 -08:00
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
checkErr = errors.New("no redirects allowed")
|
2011-05-13 07:31:24 -07:00
|
|
|
res, err = c.Get(ts.URL)
|
2012-06-24 10:41:12 -07:00
|
|
|
if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
|
|
|
|
|
t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
|
2011-04-25 22:41:50 -07:00
|
|
|
}
|
2012-07-18 13:48:39 -07:00
|
|
|
if res == nil {
|
2015-07-10 17:17:11 -06:00
|
|
|
t.Fatalf("Expected a non-nil Response on CheckRedirect failure (https://golang.org/issue/3795)")
|
2012-07-18 13:48:39 -07:00
|
|
|
}
|
2013-02-26 17:12:50 -08:00
|
|
|
res.Body.Close()
|
2012-07-18 13:48:39 -07:00
|
|
|
if res.Header.Get("Location") == "" {
|
|
|
|
|
t.Errorf("no Location header in Response")
|
|
|
|
|
}
|
2011-04-25 22:41:50 -07:00
|
|
|
}
|
2011-05-24 09:02:01 -07:00
|
|
|
|
2012-12-12 11:09:55 -08:00
|
|
|
func TestPostRedirects(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2012-12-12 11:09:55 -08:00
|
|
|
var log struct {
|
|
|
|
|
sync.Mutex
|
|
|
|
|
bytes.Buffer
|
|
|
|
|
}
|
|
|
|
|
var ts *httptest.Server
|
|
|
|
|
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
log.Lock()
|
|
|
|
|
fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
|
|
|
|
|
log.Unlock()
|
|
|
|
|
if v := r.URL.Query().Get("code"); v != "" {
|
|
|
|
|
code, _ := strconv.Atoi(v)
|
|
|
|
|
if code/100 == 3 {
|
|
|
|
|
w.Header().Set("Location", ts.URL)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(code)
|
|
|
|
|
}
|
|
|
|
|
}))
|
2013-02-26 17:12:50 -08:00
|
|
|
defer ts.Close()
|
2012-12-12 11:09:55 -08:00
|
|
|
tests := []struct {
|
|
|
|
|
suffix string
|
|
|
|
|
want int // response code
|
|
|
|
|
}{
|
|
|
|
|
{"/", 200},
|
|
|
|
|
{"/?code=301", 301},
|
|
|
|
|
{"/?code=302", 200},
|
|
|
|
|
{"/?code=303", 200},
|
|
|
|
|
{"/?code=404", 404},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if res.StatusCode != tt.want {
|
|
|
|
|
t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
log.Lock()
|
|
|
|
|
got := log.String()
|
|
|
|
|
log.Unlock()
|
|
|
|
|
want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
|
|
|
|
|
if got != want {
|
|
|
|
|
t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-16 12:57:59 -08:00
|
|
|
var expectedCookies = []*Cookie{
|
2012-03-08 10:48:51 -08:00
|
|
|
{Name: "ChocolateChip", Value: "tasty"},
|
|
|
|
|
{Name: "First", Value: "Hit"},
|
|
|
|
|
{Name: "Second", Value: "Hit"},
|
2012-01-16 12:57:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
for _, cookie := range r.Cookies() {
|
|
|
|
|
SetCookie(w, cookie)
|
|
|
|
|
}
|
|
|
|
|
if r.URL.Path == "/" {
|
|
|
|
|
SetCookie(w, expectedCookies[1])
|
|
|
|
|
Redirect(w, r, "/second", StatusMovedPermanently)
|
|
|
|
|
} else {
|
|
|
|
|
SetCookie(w, expectedCookies[2])
|
|
|
|
|
w.Write([]byte("hello"))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2012-05-21 10:57:15 -07:00
|
|
|
func TestClientSendsCookieFromJar(t *testing.T) {
|
2015-04-07 15:50:34 +02:00
|
|
|
defer afterTest(t)
|
2012-05-21 10:57:15 -07:00
|
|
|
tr := &recordingTransport{}
|
|
|
|
|
client := &Client{Transport: tr}
|
|
|
|
|
client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
|
|
|
|
|
us := "http://dummy.faketld/"
|
|
|
|
|
u, _ := url.Parse(us)
|
|
|
|
|
client.Jar.SetCookies(u, expectedCookies)
|
|
|
|
|
|
|
|
|
|
client.Get(us) // Note: doesn't hit network
|
|
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
|
|
|
|
|
client.Head(us) // Note: doesn't hit network
|
|
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
|
|
|
|
|
client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
|
|
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
|
|
|
|
|
client.PostForm(us, url.Values{}) // Note: doesn't hit network
|
|
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
|
|
|
|
|
req, _ := NewRequest("GET", us, nil)
|
|
|
|
|
client.Do(req) // Note: doesn't hit network
|
2012-10-29 17:56:31 +01:00
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
|
|
|
|
|
req, _ = NewRequest("POST", us, nil)
|
|
|
|
|
client.Do(req) // Note: doesn't hit network
|
2012-05-21 10:57:15 -07:00
|
|
|
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-16 12:57:59 -08:00
|
|
|
// Just enough correctness for our redirect tests. Uses the URL.Host as the
|
|
|
|
|
// scope of all cookies.
|
|
|
|
|
type TestJar struct {
|
|
|
|
|
m sync.Mutex
|
|
|
|
|
perURL map[string][]*Cookie
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
|
|
|
|
|
j.m.Lock()
|
|
|
|
|
defer j.m.Unlock()
|
2012-12-12 11:36:44 -08:00
|
|
|
if j.perURL == nil {
|
|
|
|
|
j.perURL = make(map[string][]*Cookie)
|
|
|
|
|
}
|
2012-01-16 12:57:59 -08:00
|
|
|
j.perURL[u.Host] = cookies
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *TestJar) Cookies(u *url.URL) []*Cookie {
|
|
|
|
|
j.m.Lock()
|
|
|
|
|
defer j.m.Unlock()
|
|
|
|
|
return j.perURL[u.Host]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRedirectCookiesJar(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2012-01-16 12:57:59 -08:00
|
|
|
var ts *httptest.Server
|
|
|
|
|
ts = httptest.NewServer(echoCookiesRedirectHandler)
|
|
|
|
|
defer ts.Close()
|
2012-12-12 11:36:44 -08:00
|
|
|
c := &Client{
|
|
|
|
|
Jar: new(TestJar),
|
|
|
|
|
}
|
2012-01-16 12:57:59 -08:00
|
|
|
u, _ := url.Parse(ts.URL)
|
|
|
|
|
c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
|
2012-11-13 22:38:25 -08:00
|
|
|
resp, err := c.Get(ts.URL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Get: %v", err)
|
|
|
|
|
}
|
2013-02-26 17:12:50 -08:00
|
|
|
resp.Body.Close()
|
2012-01-16 12:57:59 -08:00
|
|
|
matchReturnedCookies(t, expectedCookies, resp.Cookies())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
|
|
|
|
|
if len(given) != len(expected) {
|
2013-12-26 13:03:30 -08:00
|
|
|
t.Logf("Received cookies: %v", given)
|
2012-01-16 12:57:59 -08:00
|
|
|
t.Errorf("Expected %d cookies, got %d", len(expected), len(given))
|
|
|
|
|
}
|
|
|
|
|
for _, ec := range expected {
|
|
|
|
|
foundC := false
|
|
|
|
|
for _, c := range given {
|
|
|
|
|
if ec.Name == c.Name && ec.Value == c.Value {
|
|
|
|
|
foundC = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !foundC {
|
|
|
|
|
t.Errorf("Missing cookie %v", ec)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 11:36:44 -08:00
|
|
|
func TestJarCalls(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2012-12-12 11:36:44 -08:00
|
|
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
pathSuffix := r.RequestURI[1:]
|
2012-12-19 16:24:38 -08:00
|
|
|
if r.RequestURI == "/nosetcookie" {
|
2015-06-11 16:49:38 +03:00
|
|
|
return // don't set cookies for this path
|
2012-12-19 16:24:38 -08:00
|
|
|
}
|
2012-12-12 11:36:44 -08:00
|
|
|
SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
|
|
|
|
|
if r.RequestURI == "/" {
|
|
|
|
|
Redirect(w, r, "http://secondhost.fake/secondpath", 302)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
jar := new(RecordingJar)
|
|
|
|
|
c := &Client{
|
|
|
|
|
Jar: jar,
|
|
|
|
|
Transport: &Transport{
|
|
|
|
|
Dial: func(_ string, _ string) (net.Conn, error) {
|
|
|
|
|
return net.Dial("tcp", ts.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_, err := c.Get("http://firsthost.fake/")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2012-12-19 16:24:38 -08:00
|
|
|
_, err = c.Get("http://firsthost.fake/nosetcookie")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2012-12-12 11:36:44 -08:00
|
|
|
got := jar.log.String()
|
|
|
|
|
want := `Cookies("http://firsthost.fake/")
|
|
|
|
|
SetCookie("http://firsthost.fake/", [name=val])
|
|
|
|
|
Cookies("http://secondhost.fake/secondpath")
|
|
|
|
|
SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
|
2012-12-19 16:24:38 -08:00
|
|
|
Cookies("http://firsthost.fake/nosetcookie")
|
2012-12-12 11:36:44 -08:00
|
|
|
`
|
|
|
|
|
if got != want {
|
|
|
|
|
t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RecordingJar keeps a log of calls made to it, without
|
|
|
|
|
// tracking any cookies.
|
|
|
|
|
type RecordingJar struct {
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
log bytes.Buffer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
|
|
|
|
|
j.logf("SetCookie(%q, %v)\n", u, cookies)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
|
|
|
|
|
j.logf("Cookies(%q)\n", u)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *RecordingJar) logf(format string, args ...interface{}) {
|
|
|
|
|
j.mu.Lock()
|
|
|
|
|
defer j.mu.Unlock()
|
|
|
|
|
fmt.Fprintf(&j.log, format, args...)
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-09 22:02:46 +00:00
|
|
|
func TestStreamingGet_h1(t *testing.T) { testStreamingGet(t, h1Mode) }
|
|
|
|
|
func TestStreamingGet_h2(t *testing.T) { testStreamingGet(t, h2Mode) }
|
2015-12-08 02:01:03 -07:00
|
|
|
|
|
|
|
|
func testStreamingGet(t *testing.T, h2 bool) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-05-24 09:02:01 -07:00
|
|
|
say := make(chan string)
|
2015-12-08 02:01:03 -07:00
|
|
|
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
|
2011-05-24 09:02:01 -07:00
|
|
|
w.(Flusher).Flush()
|
|
|
|
|
for str := range say {
|
|
|
|
|
w.Write([]byte(str))
|
|
|
|
|
w.(Flusher).Flush()
|
|
|
|
|
}
|
|
|
|
|
}))
|
2015-12-08 02:01:03 -07:00
|
|
|
defer cst.close()
|
2011-05-24 09:02:01 -07:00
|
|
|
|
2015-12-08 02:01:03 -07:00
|
|
|
c := cst.c
|
|
|
|
|
res, err := c.Get(cst.ts.URL)
|
2011-05-24 09:02:01 -07:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var buf [10]byte
|
|
|
|
|
for _, str := range []string{"i", "am", "also", "known", "as", "comet"} {
|
|
|
|
|
say <- str
|
|
|
|
|
n, err := io.ReadFull(res.Body, buf[0:len(str)])
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("ReadFull on %q: %v", str, err)
|
|
|
|
|
}
|
|
|
|
|
if n != len(str) {
|
|
|
|
|
t.Fatalf("Receiving %q, only read %d bytes", str, n)
|
|
|
|
|
}
|
|
|
|
|
got := string(buf[0:n])
|
|
|
|
|
if got != str {
|
|
|
|
|
t.Fatalf("Expected %q, got %q", str, got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
close(say)
|
|
|
|
|
_, err = io.ReadFull(res.Body, buf[0:1])
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2011-05-24 09:02:01 -07:00
|
|
|
t.Fatalf("at end expected EOF, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-23 21:10:51 -07:00
|
|
|
|
|
|
|
|
type writeCountingConn struct {
|
|
|
|
|
net.Conn
|
|
|
|
|
count *int
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (c *writeCountingConn) Write(p []byte) (int, error) {
|
2011-06-23 21:10:51 -07:00
|
|
|
*c.count++
|
|
|
|
|
return c.Conn.Write(p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestClientWrites verifies that client requests are buffered and we
|
|
|
|
|
// don't send a TCP packet per line of the http request + body.
|
|
|
|
|
func TestClientWrites(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-06-23 21:10:51 -07:00
|
|
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
writes := 0
|
2011-11-01 22:04:37 -04:00
|
|
|
dialer := func(netz string, addr string) (net.Conn, error) {
|
2011-06-23 21:10:51 -07:00
|
|
|
c, err := net.Dial(netz, addr)
|
|
|
|
|
if err == nil {
|
|
|
|
|
c = &writeCountingConn{c, &writes}
|
|
|
|
|
}
|
|
|
|
|
return c, err
|
|
|
|
|
}
|
|
|
|
|
c := &Client{Transport: &Transport{Dial: dialer}}
|
|
|
|
|
|
|
|
|
|
_, err := c.Get(ts.URL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if writes != 1 {
|
|
|
|
|
t.Errorf("Get request did %d Write calls, want 1", writes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writes = 0
|
2011-08-17 13:36:02 +10:00
|
|
|
_, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}})
|
2011-06-23 21:10:51 -07:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if writes != 1 {
|
|
|
|
|
t.Errorf("Post request did %d Write calls, want 1", writes)
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-21 08:14:38 -07:00
|
|
|
|
|
|
|
|
func TestClientInsecureTransport(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2011-10-21 08:14:38 -07:00
|
|
|
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
w.Write([]byte("Hello"))
|
|
|
|
|
}))
|
2014-02-28 12:12:51 -08:00
|
|
|
errc := make(chanWriter, 10) // but only expecting 1
|
|
|
|
|
ts.Config.ErrorLog = log.New(errc, "", 0)
|
2011-10-21 08:14:38 -07:00
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
// TODO(bradfitz): add tests for skipping hostname checks too?
|
|
|
|
|
// would require a new cert for testing, and probably
|
|
|
|
|
// redundant with these tests.
|
|
|
|
|
for _, insecure := range []bool{true, false} {
|
|
|
|
|
tr := &Transport{
|
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
|
InsecureSkipVerify: insecure,
|
|
|
|
|
},
|
|
|
|
|
}
|
2013-02-26 17:12:50 -08:00
|
|
|
defer tr.CloseIdleConnections()
|
2011-10-21 08:14:38 -07:00
|
|
|
c := &Client{Transport: tr}
|
2013-02-26 17:12:50 -08:00
|
|
|
res, err := c.Get(ts.URL)
|
2011-10-21 08:14:38 -07:00
|
|
|
if (err == nil) != insecure {
|
|
|
|
|
t.Errorf("insecure=%v: got unexpected err=%v", insecure, err)
|
|
|
|
|
}
|
2013-02-26 17:12:50 -08:00
|
|
|
if res != nil {
|
|
|
|
|
res.Body.Close()
|
|
|
|
|
}
|
2011-10-21 08:14:38 -07:00
|
|
|
}
|
2014-02-28 12:12:51 -08:00
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case v := <-errc:
|
2014-03-05 14:56:50 -08:00
|
|
|
if !strings.Contains(v, "TLS handshake error") {
|
|
|
|
|
t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
|
2014-02-28 12:12:51 -08:00
|
|
|
}
|
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
|
t.Errorf("timeout waiting for logged error")
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 08:14:38 -07:00
|
|
|
}
|
2012-01-26 14:37:14 -08:00
|
|
|
|
|
|
|
|
func TestClientErrorWithRequestURI(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2012-01-26 14:37:14 -08:00
|
|
|
req, _ := NewRequest("GET", "http://localhost:1234/", nil)
|
|
|
|
|
req.RequestURI = "/this/field/is/illegal/and/should/error/"
|
|
|
|
|
_, err := DefaultClient.Do(req)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected an error")
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(err.Error(), "RequestURI") {
|
|
|
|
|
t.Errorf("wanted error mentioning RequestURI; got error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-22 09:15:41 -07:00
|
|
|
|
|
|
|
|
func newTLSTransport(t *testing.T, ts *httptest.Server) *Transport {
|
|
|
|
|
certs := x509.NewCertPool()
|
|
|
|
|
for _, c := range ts.TLS.Certificates {
|
|
|
|
|
roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("error parsing server's root cert: %v", err)
|
|
|
|
|
}
|
|
|
|
|
for _, root := range roots {
|
|
|
|
|
certs.AddCert(root)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return &Transport{
|
|
|
|
|
TLSClientConfig: &tls.Config{RootCAs: certs},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestClientWithCorrectTLSServerName(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2015-11-10 11:18:50 -08:00
|
|
|
|
|
|
|
|
const serverName = "example.com"
|
2012-08-22 09:15:41 -07:00
|
|
|
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
2015-11-10 11:18:50 -08:00
|
|
|
if r.TLS.ServerName != serverName {
|
|
|
|
|
t.Errorf("expected client to set ServerName %q, got: %q", serverName, r.TLS.ServerName)
|
2012-08-22 09:15:41 -07:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
2015-11-10 11:18:50 -08:00
|
|
|
trans := newTLSTransport(t, ts)
|
|
|
|
|
trans.TLSClientConfig.ServerName = serverName
|
|
|
|
|
c := &Client{Transport: trans}
|
2012-08-22 09:15:41 -07:00
|
|
|
if _, err := c.Get(ts.URL); err != nil {
|
|
|
|
|
t.Fatalf("expected successful TLS connection, got error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestClientWithIncorrectTLSServerName(t *testing.T) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2012-08-22 09:15:41 -07:00
|
|
|
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
|
|
|
|
|
defer ts.Close()
|
2014-02-28 12:12:51 -08:00
|
|
|
errc := make(chanWriter, 10) // but only expecting 1
|
|
|
|
|
ts.Config.ErrorLog = log.New(errc, "", 0)
|
2012-08-22 09:15:41 -07:00
|
|
|
|
|
|
|
|
trans := newTLSTransport(t, ts)
|
|
|
|
|
trans.TLSClientConfig.ServerName = "badserver"
|
|
|
|
|
c := &Client{Transport: trans}
|
|
|
|
|
_, err := c.Get(ts.URL)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected an error")
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(err.Error(), "127.0.0.1") || !strings.Contains(err.Error(), "badserver") {
|
|
|
|
|
t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err)
|
|
|
|
|
}
|
2014-02-28 12:12:51 -08:00
|
|
|
select {
|
|
|
|
|
case v := <-errc:
|
2014-03-04 11:55:35 -08:00
|
|
|
if !strings.Contains(v, "TLS handshake error") {
|
|
|
|
|
t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
|
2014-02-28 12:12:51 -08:00
|
|
|
}
|
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
|
t.Errorf("timeout waiting for logged error")
|
|
|
|
|
}
|
2012-08-22 09:15:41 -07:00
|
|
|
}
|
2012-12-05 22:36:23 -08:00
|
|
|
|
2013-07-22 22:39:09 -07:00
|
|
|
// Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
|
|
|
|
|
// when not empty.
|
|
|
|
|
//
|
|
|
|
|
// tls.Config.ServerName (non-empty, set to "example.com") takes
|
|
|
|
|
// precedence over "some-other-host.tld" which previously incorrectly
|
|
|
|
|
// took precedence. We don't actually connect to (or even resolve)
|
|
|
|
|
// "some-other-host.tld", though, because of the Transport.Dial hook.
|
|
|
|
|
//
|
|
|
|
|
// The httptest.Server has a cert with "example.com" as its name.
|
|
|
|
|
func TestTransportUsesTLSConfigServerName(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
w.Write([]byte("Hello"))
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
tr := newTLSTransport(t, ts)
|
|
|
|
|
tr.TLSClientConfig.ServerName = "example.com" // one of httptest's Server cert names
|
|
|
|
|
tr.Dial = func(netw, addr string) (net.Conn, error) {
|
|
|
|
|
return net.Dial(netw, ts.Listener.Addr().String())
|
|
|
|
|
}
|
|
|
|
|
defer tr.CloseIdleConnections()
|
|
|
|
|
c := &Client{Transport: tr}
|
|
|
|
|
res, err := c.Get("https://some-other-host.tld/")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
res.Body.Close()
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-05 12:25:55 -08:00
|
|
|
func TestResponseSetsTLSConnectionState(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
w.Write([]byte("Hello"))
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
tr := newTLSTransport(t, ts)
|
|
|
|
|
tr.TLSClientConfig.CipherSuites = []uint16{tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA}
|
|
|
|
|
tr.Dial = func(netw, addr string) (net.Conn, error) {
|
|
|
|
|
return net.Dial(netw, ts.Listener.Addr().String())
|
|
|
|
|
}
|
|
|
|
|
defer tr.CloseIdleConnections()
|
|
|
|
|
c := &Client{Transport: tr}
|
|
|
|
|
res, err := c.Get("https://example.com/")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2014-03-05 12:40:13 -08:00
|
|
|
defer res.Body.Close()
|
2014-03-05 12:25:55 -08:00
|
|
|
if res.TLS == nil {
|
|
|
|
|
t.Fatal("Response didn't set TLS Connection State.")
|
|
|
|
|
}
|
2014-03-05 12:40:13 -08:00
|
|
|
if got, want := res.TLS.CipherSuite, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA; got != want {
|
|
|
|
|
t.Errorf("TLS Cipher Suite = %d; want %d", got, want)
|
2014-03-05 12:25:55 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 00:35:42 -07:00
|
|
|
// Check that an HTTPS client can interpret a particular TLS error
|
|
|
|
|
// to determine that the server is speaking HTTP.
|
|
|
|
|
// See golang.org/issue/11111.
|
|
|
|
|
func TestHTTPSClientDetectsHTTPServer(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
|
|
_, err := Get(strings.Replace(ts.URL, "http", "https", 1))
|
|
|
|
|
if got := err.Error(); !strings.Contains(got, "HTTP response to HTTPS client") {
|
|
|
|
|
t.Fatalf("error = %q; want error indicating HTTP response to HTTPS request", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 17:17:11 -06:00
|
|
|
// Verify Response.ContentLength is populated. https://golang.org/issue/4126
|
2015-12-08 02:18:57 -07:00
|
|
|
func TestClientHeadContentLength_h1(t *testing.T) {
|
2015-12-09 22:02:46 +00:00
|
|
|
testClientHeadContentLength(t, h1Mode)
|
2015-12-08 02:18:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestClientHeadContentLength_h2(t *testing.T) {
|
2015-12-09 22:02:46 +00:00
|
|
|
testClientHeadContentLength(t, h2Mode)
|
2015-12-08 02:18:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testClientHeadContentLength(t *testing.T, h2 bool) {
|
2013-03-15 15:09:17 -07:00
|
|
|
defer afterTest(t)
|
2015-12-08 02:18:57 -07:00
|
|
|
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
|
2012-12-05 22:36:23 -08:00
|
|
|
if v := r.FormValue("cl"); v != "" {
|
|
|
|
|
w.Header().Set("Content-Length", v)
|
|
|
|
|
}
|
|
|
|
|
}))
|
2015-12-08 02:18:57 -07:00
|
|
|
defer cst.close()
|
2012-12-05 22:36:23 -08:00
|
|
|
tests := []struct {
|
|
|
|
|
suffix string
|
|
|
|
|
want int64
|
|
|
|
|
}{
|
|
|
|
|
{"/?cl=1234", 1234},
|
|
|
|
|
{"/?cl=0", 0},
|
|
|
|
|
{"", -1},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
2015-12-08 02:18:57 -07:00
|
|
|
req, _ := NewRequest("HEAD", cst.ts.URL+tt.suffix, nil)
|
|
|
|
|
res, err := cst.c.Do(req)
|
2012-12-05 22:36:23 -08:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if res.ContentLength != tt.want {
|
|
|
|
|
t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want)
|
|
|
|
|
}
|
|
|
|
|
bs, err := ioutil.ReadAll(res.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if len(bs) != 0 {
|
|
|
|
|
t.Errorf("Unexpected content: %q", bs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-14 15:33:46 -07:00
|
|
|
|
|
|
|
|
func TestEmptyPasswordAuth(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
gopher := "gopher"
|
|
|
|
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
|
|
|
auth := r.Header.Get("Authorization")
|
|
|
|
|
if strings.HasPrefix(auth, "Basic ") {
|
|
|
|
|
encoded := auth[6:]
|
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
expected := gopher + ":"
|
|
|
|
|
s := string(decoded)
|
|
|
|
|
if expected != s {
|
|
|
|
|
t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Errorf("Invalid auth %q", auth)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
c := &Client{}
|
|
|
|
|
req, err := NewRequest("GET", ts.URL, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
req.URL.User = url.User(gopher)
|
|
|
|
|
resp, err := c.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
}
|
2013-08-07 11:58:59 -07:00
|
|
|
|
|
|
|
|
func TestBasicAuth(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
tr := &recordingTransport{}
|
|
|
|
|
client := &Client{Transport: tr}
|
|
|
|
|
|
|
|
|
|
url := "http://My%20User:My%20Pass@dummy.faketld/"
|
|
|
|
|
expected := "My User:My Pass"
|
|
|
|
|
client.Get(url)
|
|
|
|
|
|
|
|
|
|
if tr.req.Method != "GET" {
|
|
|
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "GET")
|
|
|
|
|
}
|
|
|
|
|
if tr.req.URL.String() != url {
|
|
|
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Header == nil {
|
|
|
|
|
t.Fatalf("expected non-nil request Header")
|
|
|
|
|
}
|
|
|
|
|
auth := tr.req.Header.Get("Authorization")
|
|
|
|
|
if strings.HasPrefix(auth, "Basic ") {
|
|
|
|
|
encoded := auth[6:]
|
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
s := string(decoded)
|
|
|
|
|
if expected != s {
|
|
|
|
|
t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Errorf("Invalid auth %q", auth)
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-02 20:39:20 -08:00
|
|
|
|
2015-06-25 16:52:51 +01:00
|
|
|
func TestBasicAuthHeadersPreserved(t *testing.T) {
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
tr := &recordingTransport{}
|
|
|
|
|
client := &Client{Transport: tr}
|
|
|
|
|
|
|
|
|
|
// If Authorization header is provided, username in URL should not override it
|
|
|
|
|
url := "http://My%20User@dummy.faketld/"
|
|
|
|
|
req, err := NewRequest("GET", url, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth("My User", "My Pass")
|
|
|
|
|
expected := "My User:My Pass"
|
|
|
|
|
client.Do(req)
|
|
|
|
|
|
|
|
|
|
if tr.req.Method != "GET" {
|
|
|
|
|
t.Errorf("got method %q, want %q", tr.req.Method, "GET")
|
|
|
|
|
}
|
|
|
|
|
if tr.req.URL.String() != url {
|
|
|
|
|
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
|
|
|
|
|
}
|
|
|
|
|
if tr.req.Header == nil {
|
|
|
|
|
t.Fatalf("expected non-nil request Header")
|
|
|
|
|
}
|
|
|
|
|
auth := tr.req.Header.Get("Authorization")
|
|
|
|
|
if strings.HasPrefix(auth, "Basic ") {
|
|
|
|
|
encoded := auth[6:]
|
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
s := string(decoded)
|
|
|
|
|
if expected != s {
|
|
|
|
|
t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
t.Errorf("Invalid auth %q", auth)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-14 16:17:21 -08:00
|
|
|
func TestClientTimeout_h1(t *testing.T) { testClientTimeout(t, h1Mode) }
|
net/http: make Client use Request.Cancel for timeouts instead of CancelRequest
In the beginning, there was no way to cancel an HTTP request.
We later added Transport.CancelRequest to cancel an in-flight HTTP
request by breaking its underlying TCP connection, but it was hard to
use correctly and didn't work in all cases. And its error messages
were terrible. Some of those issues were fixed over time, but the most
unfixable problem was that it didn't compose well. All RoundTripper
implementations had to choose to whether to implement CancelRequest
and both decisions had negative consequences.
In Go 1.5 we added Request.Cancel, which composed well, worked in all
phases, had nice error messages, etc. But we forgot to use it in the
implementation of Client.Timeout (a timeout which spans multiple
requests and reading request bodies).
In Go 1.6 (upcoming), we added HTTP/2 support, but now Client.Timeout
didn't work because the http2.Transport didn't have a CancelRequest
method.
Rather than add a CancelRequest method to http2, officially deprecate
it and update the only caller (Client, for Client.Cancel) to use
Request.Cancel instead.
The http2 Client timeout tests are enabled now.
For compatibility, we still use CancelRequest in Client if we don't
recognize the RoundTripper type. But documentation has been updated to
tell people that CancelRequest is deprecated.
Fixes #13540
Change-Id: I15546b90825bb8b54905e17563eca55ea2642075
Reviewed-on: https://go-review.googlesource.com/18260
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-01-04 20:59:05 -08:00
|
|
|
func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) }
|
2015-12-14 16:17:21 -08:00
|
|
|
|
|
|
|
|
func testClientTimeout(t *testing.T, h2 bool) {
|
2014-03-02 20:39:20 -08:00
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping in short mode")
|
|
|
|
|
}
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
sawRoot := make(chan bool, 1)
|
|
|
|
|
sawSlow := make(chan bool, 1)
|
2015-12-14 16:17:21 -08:00
|
|
|
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
|
2014-03-02 20:39:20 -08:00
|
|
|
if r.URL.Path == "/" {
|
|
|
|
|
sawRoot <- true
|
|
|
|
|
Redirect(w, r, "/slow", StatusFound)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if r.URL.Path == "/slow" {
|
|
|
|
|
w.Write([]byte("Hello"))
|
|
|
|
|
w.(Flusher).Flush()
|
|
|
|
|
sawSlow <- true
|
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}))
|
2015-12-14 16:17:21 -08:00
|
|
|
defer cst.close()
|
2014-03-02 20:39:20 -08:00
|
|
|
const timeout = 500 * time.Millisecond
|
2015-12-14 16:17:21 -08:00
|
|
|
cst.c.Timeout = timeout
|
2014-03-02 20:39:20 -08:00
|
|
|
|
2015-12-14 16:17:21 -08:00
|
|
|
res, err := cst.c.Get(cst.ts.URL)
|
2014-03-02 20:39:20 -08:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-sawRoot:
|
|
|
|
|
// good.
|
|
|
|
|
default:
|
|
|
|
|
t.Fatal("handler never got / request")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-sawSlow:
|
|
|
|
|
// good.
|
|
|
|
|
default:
|
|
|
|
|
t.Fatal("handler never got /slow request")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errc := make(chan error, 1)
|
|
|
|
|
go func() {
|
2014-03-10 22:22:51 -04:00
|
|
|
_, err := ioutil.ReadAll(res.Body)
|
2014-03-02 20:39:20 -08:00
|
|
|
errc <- err
|
|
|
|
|
res.Body.Close()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
const failTime = timeout * 2
|
|
|
|
|
select {
|
|
|
|
|
case err := <-errc:
|
|
|
|
|
if err == nil {
|
2014-12-20 15:46:09 +11:00
|
|
|
t.Fatal("expected error from ReadAll")
|
|
|
|
|
}
|
|
|
|
|
ne, ok := err.(net.Error)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("error value from ReadAll was %T; expected some net.Error", err)
|
|
|
|
|
} else if !ne.Timeout() {
|
|
|
|
|
t.Errorf("net.Error.Timeout = false; want true")
|
|
|
|
|
}
|
|
|
|
|
if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") {
|
|
|
|
|
t.Errorf("error string = %q; missing timeout substring", got)
|
2014-03-02 20:39:20 -08:00
|
|
|
}
|
|
|
|
|
case <-time.After(failTime):
|
|
|
|
|
t.Errorf("timeout after %v waiting for timeout of %v", failTime, timeout)
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-03 11:25:57 -08:00
|
|
|
|
2015-12-14 16:39:09 -08:00
|
|
|
func TestClientTimeout_Headers_h1(t *testing.T) { testClientTimeout_Headers(t, h1Mode) }
|
net/http: make Client use Request.Cancel for timeouts instead of CancelRequest
In the beginning, there was no way to cancel an HTTP request.
We later added Transport.CancelRequest to cancel an in-flight HTTP
request by breaking its underlying TCP connection, but it was hard to
use correctly and didn't work in all cases. And its error messages
were terrible. Some of those issues were fixed over time, but the most
unfixable problem was that it didn't compose well. All RoundTripper
implementations had to choose to whether to implement CancelRequest
and both decisions had negative consequences.
In Go 1.5 we added Request.Cancel, which composed well, worked in all
phases, had nice error messages, etc. But we forgot to use it in the
implementation of Client.Timeout (a timeout which spans multiple
requests and reading request bodies).
In Go 1.6 (upcoming), we added HTTP/2 support, but now Client.Timeout
didn't work because the http2.Transport didn't have a CancelRequest
method.
Rather than add a CancelRequest method to http2, officially deprecate
it and update the only caller (Client, for Client.Cancel) to use
Request.Cancel instead.
The http2 Client timeout tests are enabled now.
For compatibility, we still use CancelRequest in Client if we don't
recognize the RoundTripper type. But documentation has been updated to
tell people that CancelRequest is deprecated.
Fixes #13540
Change-Id: I15546b90825bb8b54905e17563eca55ea2642075
Reviewed-on: https://go-review.googlesource.com/18260
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-01-04 20:59:05 -08:00
|
|
|
func TestClientTimeout_Headers_h2(t *testing.T) { testClientTimeout_Headers(t, h2Mode) }
|
2015-12-14 16:39:09 -08:00
|
|
|
|
2014-12-20 15:46:09 +11:00
|
|
|
// Client.Timeout firing before getting to the body
|
2015-12-14 16:39:09 -08:00
|
|
|
func testClientTimeout_Headers(t *testing.T, h2 bool) {
|
2014-12-20 15:46:09 +11:00
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping in short mode")
|
|
|
|
|
}
|
|
|
|
|
defer afterTest(t)
|
|
|
|
|
donec := make(chan bool)
|
2015-12-14 16:39:09 -08:00
|
|
|
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
|
2014-12-20 15:46:09 +11:00
|
|
|
<-donec
|
|
|
|
|
}))
|
2015-12-14 16:39:09 -08:00
|
|
|
defer cst.close()
|
2015-05-11 10:31:24 +01:00
|
|
|
// Note that we use a channel send here and not a close.
|
|
|
|
|
// The race detector doesn't know that we're waiting for a timeout
|
|
|
|
|
// and thinks that the waitgroup inside httptest.Server is added to concurrently
|
|
|
|
|
// with us closing it. If we timed out immediately, we could close the testserver
|
|
|
|
|
// before we entered the handler. We're not timing out immediately and there's
|
|
|
|
|
// no way we would be done before we entered the handler, but the race detector
|
|
|
|
|
// doesn't know this, so synchronize explicitly.
|
|
|
|
|
defer func() { donec <- true }()
|
2014-12-20 15:46:09 +11:00
|
|
|
|
2015-12-14 16:39:09 -08:00
|
|
|
cst.c.Timeout = 500 * time.Millisecond
|
|
|
|
|
_, err := cst.c.Get(cst.ts.URL)
|
2014-12-20 15:46:09 +11:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("got response from Get; expected error")
|
|
|
|
|
}
|
2015-10-10 14:21:42 +11:00
|
|
|
if _, ok := err.(*url.Error); !ok {
|
2014-12-20 15:46:09 +11:00
|
|
|
t.Fatalf("Got error of type %T; want *url.Error", err)
|
|
|
|
|
}
|
2015-10-10 14:21:42 +11:00
|
|
|
ne, ok := err.(net.Error)
|
2014-12-20 15:46:09 +11:00
|
|
|
if !ok {
|
2015-10-10 14:21:42 +11:00
|
|
|
t.Fatalf("Got error of type %T; want some net.Error", err)
|
2014-12-20 15:46:09 +11:00
|
|
|
}
|
|
|
|
|
if !ne.Timeout() {
|
|
|
|
|
t.Error("net.Error.Timeout = false; want true")
|
|
|
|
|
}
|
|
|
|
|
if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") {
|
|
|
|
|
t.Errorf("error string = %q; missing timeout substring", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-09 22:02:46 +00:00
|
|
|
func TestClientRedirectEatsBody_h1(t *testing.T) { testClientRedirectEatsBody(t, h1Mode) }
|
|
|
|
|
func TestClientRedirectEatsBody_h2(t *testing.T) { testClientRedirectEatsBody(t, h2Mode) }
|
2015-12-08 02:48:10 -07:00
|
|
|
func testClientRedirectEatsBody(t *testing.T, h2 bool) {
|
2014-03-03 11:25:57 -08:00
|
|
|
defer afterTest(t)
|
|
|
|
|
saw := make(chan string, 2)
|
2015-12-08 02:48:10 -07:00
|
|
|
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
|
2014-03-03 11:25:57 -08:00
|
|
|
saw <- r.RemoteAddr
|
|
|
|
|
if r.URL.Path == "/" {
|
|
|
|
|
Redirect(w, r, "/foo", StatusFound) // which includes a body
|
|
|
|
|
}
|
|
|
|
|
}))
|
2015-12-08 02:48:10 -07:00
|
|
|
defer cst.close()
|
2014-03-03 11:25:57 -08:00
|
|
|
|
2015-12-08 02:48:10 -07:00
|
|
|
res, err := cst.c.Get(cst.ts.URL)
|
2014-03-03 11:25:57 -08:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
_, err = ioutil.ReadAll(res.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
res.Body.Close()
|
|
|
|
|
|
|
|
|
|
var first string
|
|
|
|
|
select {
|
|
|
|
|
case first = <-saw:
|
|
|
|
|
default:
|
|
|
|
|
t.Fatal("server didn't see a request")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var second string
|
|
|
|
|
select {
|
|
|
|
|
case second = <-saw:
|
|
|
|
|
default:
|
|
|
|
|
t.Fatal("server didn't see a second request")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if first != second {
|
|
|
|
|
t.Fatal("server saw different client ports before & after the redirect")
|
|
|
|
|
}
|
|
|
|
|
}
|
net/http: document, test, define, clean up Request.Trailer
Go's had pretty decent HTTP Trailer support for a long time, but
the docs have been largely non-existent. Fix that.
In the process, re-learn the Trailer code, clean some stuff
up, add some error checks, remove some TODOs, fix a minor bug
or two, and add tests.
LGTM=adg
R=golang-codereviews, adg
CC=dsymonds, golang-codereviews, rsc
https://golang.org/cl/86660043
2014-04-10 17:01:21 -07:00
|
|
|
|
|
|
|
|
// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
|
|
|
|
|
type eofReaderFunc func()
|
|
|
|
|
|
|
|
|
|
func (f eofReaderFunc) Read(p []byte) (n int, err error) {
|
|
|
|
|
f()
|
|
|
|
|
return 0, io.EOF
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-07 07:13:42 -07:00
|
|
|
func TestReferer(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
lastReq, newReq string // from -> to URLs
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
// don't send user:
|
|
|
|
|
{"http://gopher@test.com", "http://link.com", "http://test.com"},
|
|
|
|
|
{"https://gopher@test.com", "https://link.com", "https://test.com"},
|
|
|
|
|
|
|
|
|
|
// don't send a user and password:
|
|
|
|
|
{"http://gopher:go@test.com", "http://link.com", "http://test.com"},
|
|
|
|
|
{"https://gopher:go@test.com", "https://link.com", "https://test.com"},
|
|
|
|
|
|
|
|
|
|
// nothing to do:
|
|
|
|
|
{"http://test.com", "http://link.com", "http://test.com"},
|
|
|
|
|
{"https://test.com", "https://link.com", "https://test.com"},
|
|
|
|
|
|
|
|
|
|
// https to http doesn't send a referer:
|
|
|
|
|
{"https://test.com", "http://link.com", ""},
|
|
|
|
|
{"https://gopher:go@test.com", "http://link.com", ""},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
l, err := url.Parse(tt.lastReq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
n, err := url.Parse(tt.newReq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
r := ExportRefererForURL(l, n)
|
|
|
|
|
if r != tt.want {
|
|
|
|
|
t.Errorf("refererForURL(%q, %q) = %q; want %q", tt.lastReq, tt.newReq, r, tt.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|