URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [net/] [http/] [transport_test.go] - Rev 747
Compare with Previous | Blame | View Log
// Copyright 2011 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 transport.gopackage http_testimport ("bytes""compress/gzip""crypto/rand""fmt""io""io/ioutil". "net/http""net/http/httptest""net/url""strconv""strings""testing""time")// TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close// and then verify that the final 2 responses get errors back.// hostPortHandler writes back the client's "host:port".var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) {if r.FormValue("close") == "true" {w.Header().Set("Connection", "close")}w.Write([]byte(r.RemoteAddr))})// Two subsequent requests and verify their response is the same.// The response from the server is our own IP:portfunc TestTransportKeepAlives(t *testing.T) {ts := httptest.NewServer(hostPortHandler)defer ts.Close()for _, disableKeepAlive := range []bool{false, true} {tr := &Transport{DisableKeepAlives: disableKeepAlive}c := &Client{Transport: tr}fetch := func(n int) string {res, err := c.Get(ts.URL)if err != nil {t.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive, n, err)}body, err := ioutil.ReadAll(res.Body)if err != nil {t.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive, n, err)}return string(body)}body1 := fetch(1)body2 := fetch(2)bodiesDiffer := body1 != body2if bodiesDiffer != disableKeepAlive {t.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",disableKeepAlive, bodiesDiffer, body1, body2)}}}func TestTransportConnectionCloseOnResponse(t *testing.T) {ts := httptest.NewServer(hostPortHandler)defer ts.Close()for _, connectionClose := range []bool{false, true} {tr := &Transport{}c := &Client{Transport: tr}fetch := func(n int) string {req := new(Request)var err errorreq.URL, err = url.Parse(ts.URL + fmt.Sprintf("/?close=%v", connectionClose))if err != nil {t.Fatalf("URL parse error: %v", err)}req.Method = "GET"req.Proto = "HTTP/1.1"req.ProtoMajor = 1req.ProtoMinor = 1res, err := c.Do(req)if err != nil {t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)}body, err := ioutil.ReadAll(res.Body)defer res.Body.Close()if err != nil {t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)}return string(body)}body1 := fetch(1)body2 := fetch(2)bodiesDiffer := body1 != body2if bodiesDiffer != connectionClose {t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",connectionClose, bodiesDiffer, body1, body2)}}}func TestTransportConnectionCloseOnRequest(t *testing.T) {ts := httptest.NewServer(hostPortHandler)defer ts.Close()for _, connectionClose := range []bool{false, true} {tr := &Transport{}c := &Client{Transport: tr}fetch := func(n int) string {req := new(Request)var err errorreq.URL, err = url.Parse(ts.URL)if err != nil {t.Fatalf("URL parse error: %v", err)}req.Method = "GET"req.Proto = "HTTP/1.1"req.ProtoMajor = 1req.ProtoMinor = 1req.Close = connectionCloseres, err := c.Do(req)if err != nil {t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)}body, err := ioutil.ReadAll(res.Body)if err != nil {t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)}return string(body)}body1 := fetch(1)body2 := fetch(2)bodiesDiffer := body1 != body2if bodiesDiffer != connectionClose {t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",connectionClose, bodiesDiffer, body1, body2)}}}func TestTransportIdleCacheKeys(t *testing.T) {ts := httptest.NewServer(hostPortHandler)defer ts.Close()tr := &Transport{DisableKeepAlives: false}c := &Client{Transport: tr}if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)}resp, err := c.Get(ts.URL)if err != nil {t.Error(err)}ioutil.ReadAll(resp.Body)keys := tr.IdleConnKeysForTesting()if e, g := 1, len(keys); e != g {t.Fatalf("After Get expected %d idle conn cache keys; got %d", e, g)}if e := "|http|" + ts.Listener.Addr().String(); keys[0] != e {t.Errorf("Expected idle cache key %q; got %q", e, keys[0])}tr.CloseIdleConnections()if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)}}func TestTransportMaxPerHostIdleConns(t *testing.T) {resch := make(chan string)gotReq := make(chan bool)ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {gotReq <- truemsg := <-resch_, err := w.Write([]byte(msg))if err != nil {t.Fatalf("Write: %v", err)}}))defer ts.Close()maxIdleConns := 2tr := &Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxIdleConns}c := &Client{Transport: tr}// Start 3 outstanding requests and wait for the server to get them.// Their responses will hang until we we write to resch, though.donech := make(chan bool)doReq := func() {resp, err := c.Get(ts.URL)if err != nil {t.Error(err)}_, err = ioutil.ReadAll(resp.Body)if err != nil {t.Fatalf("ReadAll: %v", err)}donech <- true}go doReq()<-gotReqgo doReq()<-gotReqgo doReq()<-gotReqif e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {t.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e, g)}resch <- "res1"<-donechkeys := tr.IdleConnKeysForTesting()if e, g := 1, len(keys); e != g {t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g)}cacheKey := "|http|" + ts.Listener.Addr().String()if keys[0] != cacheKey {t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0])}if e, g := 1, tr.IdleConnCountForTesting(cacheKey); e != g {t.Errorf("after first response, expected %d idle conns; got %d", e, g)}resch <- "res2"<-donechif e, g := 2, tr.IdleConnCountForTesting(cacheKey); e != g {t.Errorf("after second response, expected %d idle conns; got %d", e, g)}resch <- "res3"<-donechif e, g := maxIdleConns, tr.IdleConnCountForTesting(cacheKey); e != g {t.Errorf("after third response, still expected %d idle conns; got %d", e, g)}}func TestTransportServerClosingUnexpectedly(t *testing.T) {ts := httptest.NewServer(hostPortHandler)defer ts.Close()tr := &Transport{}c := &Client{Transport: tr}fetch := func(n, retries int) string {condFatalf := func(format string, arg ...interface{}) {if retries <= 0 {t.Fatalf(format, arg...)}t.Logf("retrying shortly after expected error: "+format, arg...)time.Sleep(time.Second / time.Duration(retries))}for retries >= 0 {retries--res, err := c.Get(ts.URL)if err != nil {condFatalf("error in req #%d, GET: %v", n, err)continue}body, err := ioutil.ReadAll(res.Body)if err != nil {condFatalf("error in req #%d, ReadAll: %v", n, err)continue}res.Body.Close()return string(body)}panic("unreachable")}body1 := fetch(1, 0)body2 := fetch(2, 0)ts.CloseClientConnections() // surprise!// This test has an expected race. Sleeping for 25 ms prevents// it on most fast machines, causing the next fetch() call to// succeed quickly. But if we do get errors, fetch() will retry 5// times with some delays between.time.Sleep(25 * time.Millisecond)body3 := fetch(3, 5)if body1 != body2 {t.Errorf("expected body1 and body2 to be equal")}if body2 == body3 {t.Errorf("expected body2 and body3 to be different")}}// Test for http://golang.org/issue/2616 (appropriate issue number)// This fails pretty reliably with GOMAXPROCS=100 or something high.func TestStressSurpriseServerCloses(t *testing.T) {if testing.Short() {t.Logf("skipping test in short mode")return}ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {w.Header().Set("Content-Length", "5")w.Header().Set("Content-Type", "text/plain")w.Write([]byte("Hello"))w.(Flusher).Flush()conn, buf, _ := w.(Hijacker).Hijack()buf.Flush()conn.Close()}))defer ts.Close()tr := &Transport{DisableKeepAlives: false}c := &Client{Transport: tr}// Do a bunch of traffic from different goroutines. Send to activityc// after each request completes, regardless of whether it failed.const (numClients = 50reqsPerClient = 250)activityc := make(chan bool)for i := 0; i < numClients; i++ {go func() {for i := 0; i < reqsPerClient; i++ {res, err := c.Get(ts.URL)if err == nil {// We expect errors since the server is// hanging up on us after telling us to// send more requests, so we don't// actually care what the error is.// But we want to close the body in cases// where we won the race.res.Body.Close()}activityc <- true}}()}// Make sure all the request come back, one way or another.for i := 0; i < numClients*reqsPerClient; i++ {select {case <-activityc:case <-time.After(5 * time.Second):t.Fatalf("presumed deadlock; no HTTP client activity seen in awhile")}}}// TestTransportHeadResponses verifies that we deal with Content-Lengths// with no bodies properlyfunc TestTransportHeadResponses(t *testing.T) {ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {if r.Method != "HEAD" {panic("expected HEAD; got " + r.Method)}w.Header().Set("Content-Length", "123")w.WriteHeader(200)}))defer ts.Close()tr := &Transport{DisableKeepAlives: false}c := &Client{Transport: tr}for i := 0; i < 2; i++ {res, err := c.Head(ts.URL)if err != nil {t.Errorf("error on loop %d: %v", i, err)}if e, g := "123", res.Header.Get("Content-Length"); e != g {t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)}if e, g := int64(0), res.ContentLength; e != g {t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)}}}// TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding// on responses to HEAD requests.func TestTransportHeadChunkedResponse(t *testing.T) {ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {if r.Method != "HEAD" {panic("expected HEAD; got " + r.Method)}w.Header().Set("Transfer-Encoding", "chunked") // client should ignorew.Header().Set("x-client-ipport", r.RemoteAddr)w.WriteHeader(200)}))defer ts.Close()tr := &Transport{DisableKeepAlives: false}c := &Client{Transport: tr}res1, err := c.Head(ts.URL)if err != nil {t.Fatalf("request 1 error: %v", err)}res2, err := c.Head(ts.URL)if err != nil {t.Fatalf("request 2 error: %v", err)}if v1, v2 := res1.Header.Get("x-client-ipport"), res2.Header.Get("x-client-ipport"); v1 != v2 {t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2)}}var roundTripTests = []struct {accept stringexpectAccept stringcompressed bool}{// Requests with no accept-encoding header use transparent compression{"", "gzip", false},// Requests with other accept-encoding should pass through unmodified{"foo", "foo", false},// Requests with accept-encoding == gzip should be passed through{"gzip", "gzip", true},}// Test that the modification made to the Request by the RoundTripper is cleaned upfunc TestRoundTripGzip(t *testing.T) {const responseBody = "test response body"ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {accept := req.Header.Get("Accept-Encoding")if expect := req.FormValue("expect_accept"); accept != expect {t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",req.FormValue("testnum"), accept, expect)}if accept == "gzip" {rw.Header().Set("Content-Encoding", "gzip")gz, err := gzip.NewWriter(rw)if err != nil {t.Errorf("gzip NewWriter: %v", err)return}gz.Write([]byte(responseBody))gz.Close()} else {rw.Header().Set("Content-Encoding", accept)rw.Write([]byte(responseBody))}}))defer ts.Close()for i, test := range roundTripTests {// Test basic request (no accept-encoding)req, _ := NewRequest("GET", fmt.Sprintf("%s/?testnum=%d&expect_accept=%s", ts.URL, i, test.expectAccept), nil)if test.accept != "" {req.Header.Set("Accept-Encoding", test.accept)}res, err := DefaultTransport.RoundTrip(req)var body []byteif test.compressed {gzip, err := gzip.NewReader(res.Body)if err != nil {t.Errorf("%d. gzip NewReader: %v", i, err)continue}body, err = ioutil.ReadAll(gzip)res.Body.Close()} else {body, err = ioutil.ReadAll(res.Body)}if err != nil {t.Errorf("%d. Error: %q", i, err)continue}if g, e := string(body), responseBody; g != e {t.Errorf("%d. body = %q; want %q", i, g, e)}if g, e := req.Header.Get("Accept-Encoding"), test.accept; g != e {t.Errorf("%d. Accept-Encoding = %q; want %q (it was mutated, in violation of RoundTrip contract)", i, g, e)}if g, e := res.Header.Get("Content-Encoding"), test.accept; g != e {t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e)}}}func TestTransportGzip(t *testing.T) {const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"const nRandBytes = 1024 * 1024ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {if g, e := req.Header.Get("Accept-Encoding"), "gzip"; g != e {t.Errorf("Accept-Encoding = %q, want %q", g, e)}rw.Header().Set("Content-Encoding", "gzip")if req.Method == "HEAD" {return}var w io.Writer = rwvar buf bytes.Bufferif req.FormValue("chunked") == "0" {w = &bufdefer io.Copy(rw, &buf)defer func() {rw.Header().Set("Content-Length", strconv.Itoa(buf.Len()))}()}gz, _ := gzip.NewWriter(w)gz.Write([]byte(testString))if req.FormValue("body") == "large" {io.CopyN(gz, rand.Reader, nRandBytes)}gz.Close()}))defer ts.Close()for _, chunked := range []string{"1", "0"} {c := &Client{Transport: &Transport{}}// First fetch something large, but only read some of it.res, err := c.Get(ts.URL + "/?body=large&chunked=" + chunked)if err != nil {t.Fatalf("large get: %v", err)}buf := make([]byte, len(testString))n, err := io.ReadFull(res.Body, buf)if err != nil {t.Fatalf("partial read of large response: size=%d, %v", n, err)}if e, g := testString, string(buf); e != g {t.Errorf("partial read got %q, expected %q", g, e)}res.Body.Close()// Read on the body, even though it's closedn, err = res.Body.Read(buf)if n != 0 || err == nil {t.Errorf("expected error post-closed large Read; got = %d, %v", n, err)}// Then something small.res, err = c.Get(ts.URL + "/?chunked=" + chunked)if err != nil {t.Fatal(err)}body, err := ioutil.ReadAll(res.Body)if err != nil {t.Fatal(err)}if g, e := string(body), testString; g != e {t.Fatalf("body = %q; want %q", g, e)}if g, e := res.Header.Get("Content-Encoding"), ""; g != e {t.Fatalf("Content-Encoding = %q; want %q", g, e)}// Read on the body after it's been fully read:n, err = res.Body.Read(buf)if n != 0 || err == nil {t.Errorf("expected Read error after exhausted reads; got %d, %v", n, err)}res.Body.Close()n, err = res.Body.Read(buf)if n != 0 || err == nil {t.Errorf("expected Read error after Close; got %d, %v", n, err)}}// And a HEAD request too, because they're always weird.c := &Client{Transport: &Transport{}}res, err := c.Head(ts.URL)if err != nil {t.Fatalf("Head: %v", err)}if res.StatusCode != 200 {t.Errorf("Head status=%d; want=200", res.StatusCode)}}func TestTransportProxy(t *testing.T) {ch := make(chan string, 1)ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {ch <- "real server"}))defer ts.Close()proxy := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {ch <- "proxy for " + r.URL.String()}))defer proxy.Close()pu, err := url.Parse(proxy.URL)if err != nil {t.Fatal(err)}c := &Client{Transport: &Transport{Proxy: ProxyURL(pu)}}c.Head(ts.URL)got := <-chwant := "proxy for " + ts.URL + "/"if got != want {t.Errorf("want %q, got %q", want, got)}}// TestTransportGzipRecursive sends a gzip quine and checks that the// client gets the same value back. This is more cute than anything,// but checks that we don't recurse forever, and checks that// Content-Encoding is removed.func TestTransportGzipRecursive(t *testing.T) {ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {w.Header().Set("Content-Encoding", "gzip")w.Write(rgz)}))defer ts.Close()c := &Client{Transport: &Transport{}}res, err := c.Get(ts.URL)if err != nil {t.Fatal(err)}body, err := ioutil.ReadAll(res.Body)if err != nil {t.Fatal(err)}if !bytes.Equal(body, rgz) {t.Fatalf("Incorrect result from recursive gz:\nhave=%x\nwant=%x",body, rgz)}if g, e := res.Header.Get("Content-Encoding"), ""; g != e {t.Fatalf("Content-Encoding = %q; want %q", g, e)}}type fooProto struct{}func (fooProto) RoundTrip(req *Request) (*Response, error) {res := &Response{Status: "200 OK",StatusCode: 200,Header: make(Header),Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),}return res, nil}func TestTransportAltProto(t *testing.T) {tr := &Transport{}c := &Client{Transport: tr}tr.RegisterProtocol("foo", fooProto{})res, err := c.Get("foo://bar.com/path")if err != nil {t.Fatal(err)}bodyb, err := ioutil.ReadAll(res.Body)if err != nil {t.Fatal(err)}body := string(bodyb)if e := "You wanted foo://bar.com/path"; body != e {t.Errorf("got response %q, want %q", body, e)}}// rgz is a gzip quine that uncompresses to itself.var rgz = []byte{0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,0x69, 0x76, 0x65, 0x00, 0x92, 0xef, 0xe6, 0xe0,0x60, 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2,0xe2, 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17,0x00, 0xe8, 0xff, 0x92, 0xef, 0xe6, 0xe0, 0x60,0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2, 0xe2,0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17, 0x00,0xe8, 0xff, 0x42, 0x12, 0x46, 0x16, 0x06, 0x00,0x05, 0x00, 0xfa, 0xff, 0x42, 0x12, 0x46, 0x16,0x06, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x05,0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,0x42, 0x12, 0x46, 0x16, 0x06, 0x00, 0x05, 0x00,0xfa, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00,0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,0x00, 0x00, 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88,0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00,0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00,0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,0x00, 0xff, 0xff, 0x00, 0x17, 0x00, 0xe8, 0xff,0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00,0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,0x17, 0x00, 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16,0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08,0x00, 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa,0x00, 0x00, 0x00, 0x42, 0x12, 0x46, 0x16, 0x06,0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x00,0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,0x00, 0x00, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,0x00, 0x00,}
