github.com/fjballest/golang@v0.0.0-20151209143359-e4c5fe594ca8/src/net/http/clientserver_test.go (about)

     1  // Copyright 2015 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.
     4  
     5  // Tests that use both the client & server, in both HTTP/1 and HTTP/2 mode.
     6  
     7  package http_test
     8  
     9  import (
    10  	"bytes"
    11  	"compress/gzip"
    12  	"crypto/tls"
    13  	"fmt"
    14  	"io"
    15  	"io/ioutil"
    16  	"log"
    17  	. "net/http"
    18  	"net/http/httptest"
    19  	"os"
    20  	"reflect"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  )
    25  
    26  type clientServerTest struct {
    27  	t  *testing.T
    28  	h2 bool
    29  	h  Handler
    30  	ts *httptest.Server
    31  	tr *Transport
    32  	c  *Client
    33  }
    34  
    35  func (t *clientServerTest) close() {
    36  	t.tr.CloseIdleConnections()
    37  	t.ts.Close()
    38  }
    39  
    40  func newClientServerTest(t *testing.T, h2 bool, h Handler) *clientServerTest {
    41  	cst := &clientServerTest{
    42  		t:  t,
    43  		h2: h2,
    44  		h:  h,
    45  		tr: &Transport{},
    46  	}
    47  	cst.c = &Client{Transport: cst.tr}
    48  	if !h2 {
    49  		cst.ts = httptest.NewServer(h)
    50  		return cst
    51  	}
    52  	cst.ts = httptest.NewUnstartedServer(h)
    53  	ExportHttp2ConfigureServer(cst.ts.Config, nil)
    54  	cst.ts.TLS = cst.ts.Config.TLSConfig
    55  	cst.ts.StartTLS()
    56  
    57  	cst.tr.TLSClientConfig = &tls.Config{
    58  		InsecureSkipVerify: true,
    59  	}
    60  	if err := ExportHttp2ConfigureTransport(cst.tr); err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	return cst
    64  }
    65  
    66  // Testing the newClientServerTest helper itself.
    67  func TestNewClientServerTest(t *testing.T) {
    68  	var got struct {
    69  		sync.Mutex
    70  		log []string
    71  	}
    72  	h := HandlerFunc(func(w ResponseWriter, r *Request) {
    73  		got.Lock()
    74  		defer got.Unlock()
    75  		got.log = append(got.log, r.Proto)
    76  	})
    77  	for _, v := range [2]bool{false, true} {
    78  		cst := newClientServerTest(t, v, h)
    79  		if _, err := cst.c.Head(cst.ts.URL); err != nil {
    80  			t.Fatal(err)
    81  		}
    82  		cst.close()
    83  	}
    84  	got.Lock() // no need to unlock
    85  	if want := []string{"HTTP/1.1", "HTTP/2.0"}; !reflect.DeepEqual(got.log, want) {
    86  		t.Errorf("got %q; want %q", got.log, want)
    87  	}
    88  }
    89  
    90  func TestChunkedResponseHeaders_h1(t *testing.T) { testChunkedResponseHeaders(t, false) }
    91  func TestChunkedResponseHeaders_h2(t *testing.T) { testChunkedResponseHeaders(t, true) }
    92  
    93  func testChunkedResponseHeaders(t *testing.T, h2 bool) {
    94  	defer afterTest(t)
    95  	log.SetOutput(ioutil.Discard) // is noisy otherwise
    96  	defer log.SetOutput(os.Stderr)
    97  	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
    98  		w.Header().Set("Content-Length", "intentional gibberish") // we check that this is deleted
    99  		w.(Flusher).Flush()
   100  		fmt.Fprintf(w, "I am a chunked response.")
   101  	}))
   102  	defer cst.close()
   103  
   104  	res, err := cst.c.Get(cst.ts.URL)
   105  	if err != nil {
   106  		t.Fatalf("Get error: %v", err)
   107  	}
   108  	defer res.Body.Close()
   109  	if g, e := res.ContentLength, int64(-1); g != e {
   110  		t.Errorf("expected ContentLength of %d; got %d", e, g)
   111  	}
   112  	wantTE := []string{"chunked"}
   113  	if h2 {
   114  		wantTE = nil
   115  	}
   116  	if !reflect.DeepEqual(res.TransferEncoding, wantTE) {
   117  		t.Errorf("TransferEncoding = %v; want %v", res.TransferEncoding, wantTE)
   118  	}
   119  	if got, haveCL := res.Header["Content-Length"]; haveCL {
   120  		t.Errorf("Unexpected Content-Length: %q", got)
   121  	}
   122  }
   123  
   124  type reqFunc func(c *Client, url string) (*Response, error)
   125  
   126  // h12Compare is a test that compares HTTP/1 and HTTP/2 behavior
   127  // against each other.
   128  type h12Compare struct {
   129  	Handler       func(ResponseWriter, *Request)    // required
   130  	ReqFunc       reqFunc                           // optional
   131  	CheckResponse func(proto string, res *Response) // optional
   132  }
   133  
   134  func (tt h12Compare) reqFunc() reqFunc {
   135  	if tt.ReqFunc == nil {
   136  		return (*Client).Get
   137  	}
   138  	return tt.ReqFunc
   139  }
   140  
   141  func (tt h12Compare) run(t *testing.T) {
   142  	cst1 := newClientServerTest(t, false, HandlerFunc(tt.Handler))
   143  	defer cst1.close()
   144  	cst2 := newClientServerTest(t, true, HandlerFunc(tt.Handler))
   145  	defer cst2.close()
   146  
   147  	res1, err := tt.reqFunc()(cst1.c, cst1.ts.URL)
   148  	if err != nil {
   149  		t.Errorf("HTTP/1 request: %v", err)
   150  		return
   151  	}
   152  	res2, err := tt.reqFunc()(cst2.c, cst2.ts.URL)
   153  	if err != nil {
   154  		t.Errorf("HTTP/2 request: %v", err)
   155  		return
   156  	}
   157  	tt.normalizeRes(t, res1, "HTTP/1.1")
   158  	tt.normalizeRes(t, res2, "HTTP/2.0")
   159  	res1body, res2body := res1.Body, res2.Body
   160  
   161  	eres1 := mostlyCopy(res1)
   162  	eres2 := mostlyCopy(res2)
   163  	if !reflect.DeepEqual(eres1, eres2) {
   164  		t.Errorf("Response headers to handler differed:\nhttp/1 (%v):\n\t%#v\nhttp/2 (%v):\n\t%#v",
   165  			cst1.ts.URL, eres1, cst2.ts.URL, eres2)
   166  	}
   167  	if !reflect.DeepEqual(res1body, res2body) {
   168  		t.Errorf("Response bodies to handler differed.\nhttp1: %v\nhttp2: %v\n", res1body, res2body)
   169  	}
   170  	if fn := tt.CheckResponse; fn != nil {
   171  		res1.Body, res2.Body = res1body, res2body
   172  		fn("HTTP/1.1", res1)
   173  		fn("HTTP/2.0", res2)
   174  	}
   175  }
   176  
   177  func mostlyCopy(r *Response) *Response {
   178  	c := *r
   179  	c.Body = nil
   180  	c.TransferEncoding = nil
   181  	c.TLS = nil
   182  	c.Request = nil
   183  	return &c
   184  }
   185  
   186  type slurpResult struct {
   187  	io.ReadCloser
   188  	body []byte
   189  	err  error
   190  }
   191  
   192  func (sr slurpResult) String() string { return fmt.Sprintf("body %q; err %v", sr.body, sr.err) }
   193  
   194  func (tt h12Compare) normalizeRes(t *testing.T, res *Response, wantProto string) {
   195  	if res.Proto == wantProto {
   196  		res.Proto, res.ProtoMajor, res.ProtoMinor = "", 0, 0
   197  	} else {
   198  		t.Errorf("got %q response; want %q", res.Proto, wantProto)
   199  	}
   200  	slurp, err := ioutil.ReadAll(res.Body)
   201  	res.Body.Close()
   202  	res.Body = slurpResult{
   203  		ReadCloser: ioutil.NopCloser(bytes.NewReader(slurp)),
   204  		body:       slurp,
   205  		err:        err,
   206  	}
   207  	for i, v := range res.Header["Date"] {
   208  		res.Header["Date"][i] = strings.Repeat("x", len(v))
   209  	}
   210  	if res.Request == nil {
   211  		t.Errorf("for %s, no request", wantProto)
   212  	}
   213  	if (res.TLS != nil) != (wantProto == "HTTP/2.0") {
   214  		t.Errorf("TLS set = %v; want %v", res.TLS != nil, res.TLS == nil)
   215  	}
   216  }
   217  
   218  // Issue 13532
   219  func TestH12_HeadContentLengthNoBody(t *testing.T) {
   220  	h12Compare{
   221  		ReqFunc: (*Client).Head,
   222  		Handler: func(w ResponseWriter, r *Request) {
   223  		},
   224  	}.run(t)
   225  }
   226  
   227  func TestH12_HeadContentLengthSmallBody(t *testing.T) {
   228  	h12Compare{
   229  		ReqFunc: (*Client).Head,
   230  		Handler: func(w ResponseWriter, r *Request) {
   231  			io.WriteString(w, "small")
   232  		},
   233  	}.run(t)
   234  }
   235  
   236  func TestH12_HeadContentLengthLargeBody(t *testing.T) {
   237  	h12Compare{
   238  		ReqFunc: (*Client).Head,
   239  		Handler: func(w ResponseWriter, r *Request) {
   240  			chunk := strings.Repeat("x", 512<<10)
   241  			for i := 0; i < 10; i++ {
   242  				io.WriteString(w, chunk)
   243  			}
   244  		},
   245  	}.run(t)
   246  }
   247  
   248  func TestH12_200NoBody(t *testing.T) {
   249  	h12Compare{Handler: func(w ResponseWriter, r *Request) {}}.run(t)
   250  }
   251  
   252  func TestH2_204NoBody(t *testing.T) { testH12_noBody(t, 204) }
   253  func TestH2_304NoBody(t *testing.T) { testH12_noBody(t, 304) }
   254  func TestH2_404NoBody(t *testing.T) { testH12_noBody(t, 404) }
   255  
   256  func testH12_noBody(t *testing.T, status int) {
   257  	h12Compare{Handler: func(w ResponseWriter, r *Request) {
   258  		w.WriteHeader(status)
   259  	}}.run(t)
   260  }
   261  
   262  func TestH12_SmallBody(t *testing.T) {
   263  	h12Compare{Handler: func(w ResponseWriter, r *Request) {
   264  		io.WriteString(w, "small body")
   265  	}}.run(t)
   266  }
   267  
   268  func TestH12_ExplicitContentLength(t *testing.T) {
   269  	h12Compare{Handler: func(w ResponseWriter, r *Request) {
   270  		w.Header().Set("Content-Length", "3")
   271  		io.WriteString(w, "foo")
   272  	}}.run(t)
   273  }
   274  
   275  func TestH12_FlushBeforeBody(t *testing.T) {
   276  	h12Compare{Handler: func(w ResponseWriter, r *Request) {
   277  		w.(Flusher).Flush()
   278  		io.WriteString(w, "foo")
   279  	}}.run(t)
   280  }
   281  
   282  func TestH12_FlushMidBody(t *testing.T) {
   283  	h12Compare{Handler: func(w ResponseWriter, r *Request) {
   284  		io.WriteString(w, "foo")
   285  		w.(Flusher).Flush()
   286  		io.WriteString(w, "bar")
   287  	}}.run(t)
   288  }
   289  
   290  func TestH12_Head_ExplicitLen(t *testing.T) {
   291  	h12Compare{
   292  		ReqFunc: (*Client).Head,
   293  		Handler: func(w ResponseWriter, r *Request) {
   294  			if r.Method != "HEAD" {
   295  				t.Errorf("unexpected method %q", r.Method)
   296  			}
   297  			w.Header().Set("Content-Length", "1235")
   298  		},
   299  	}.run(t)
   300  }
   301  
   302  func TestH12_Head_ImplicitLen(t *testing.T) {
   303  	h12Compare{
   304  		ReqFunc: (*Client).Head,
   305  		Handler: func(w ResponseWriter, r *Request) {
   306  			if r.Method != "HEAD" {
   307  				t.Errorf("unexpected method %q", r.Method)
   308  			}
   309  			io.WriteString(w, "foo")
   310  		},
   311  	}.run(t)
   312  }
   313  
   314  func TestH12_HandlerWritesTooLittle(t *testing.T) {
   315  	h12Compare{
   316  		Handler: func(w ResponseWriter, r *Request) {
   317  			w.Header().Set("Content-Length", "3")
   318  			io.WriteString(w, "12") // one byte short
   319  		},
   320  		CheckResponse: func(proto string, res *Response) {
   321  			sr, ok := res.Body.(slurpResult)
   322  			if !ok {
   323  				t.Errorf("%s body is %T; want slurpResult", proto, res.Body)
   324  				return
   325  			}
   326  			if sr.err != io.ErrUnexpectedEOF {
   327  				t.Errorf("%s read error = %v; want io.ErrUnexpectedEOF", proto, sr.err)
   328  			}
   329  			if string(sr.body) != "12" {
   330  				t.Errorf("%s body = %q; want %q", proto, sr.body, "12")
   331  			}
   332  		},
   333  	}.run(t)
   334  }
   335  
   336  // Tests that the HTTP/1 and HTTP/2 servers prevent handlers from
   337  // writing more than they declared.  This test does not test whether
   338  // the transport deals with too much data, though, since the server
   339  // doesn't make it possible to send bogus data. For those tests, see
   340  // transport_test.go (for HTTP/1) or x/net/http2/transport_test.go
   341  // (for HTTP/2).
   342  func TestH12_HandlerWritesTooMuch(t *testing.T) {
   343  	h12Compare{
   344  		Handler: func(w ResponseWriter, r *Request) {
   345  			w.Header().Set("Content-Length", "3")
   346  			w.(Flusher).Flush()
   347  			io.WriteString(w, "123")
   348  			w.(Flusher).Flush()
   349  			n, err := io.WriteString(w, "x") // too many
   350  			if n > 0 || err == nil {
   351  				t.Errorf("for proto %q, final write = %v, %v; want 0, some error", r.Proto, n, err)
   352  			}
   353  		},
   354  	}.run(t)
   355  }
   356  
   357  // TODO: TestH12_Trailers
   358  
   359  // Verify that both our HTTP/1 and HTTP/2 request and auto-decompress gzip.
   360  // Some hosts send gzip even if you don't ask for it; see golang.org/issue/13298
   361  func TestH12_AutoGzip(t *testing.T) {
   362  	h12Compare{
   363  		Handler: func(w ResponseWriter, r *Request) {
   364  			if ae := r.Header.Get("Accept-Encoding"); ae != "gzip" {
   365  				t.Errorf("%s Accept-Encoding = %q; want gzip", r.Proto, ae)
   366  			}
   367  			w.Header().Set("Content-Encoding", "gzip")
   368  			gz := gzip.NewWriter(w)
   369  			io.WriteString(gz, "I am some gzipped content. Go go go go go go go go go go go go should compress well.")
   370  			gz.Close()
   371  		},
   372  	}.run(t)
   373  }
   374  
   375  // Test304Responses verifies that 304s don't declare that they're
   376  // chunking in their response headers and aren't allowed to produce
   377  // output.
   378  func Test304Responses_h1(t *testing.T) { test304Responses(t, false) }
   379  func Test304Responses_h2(t *testing.T) { test304Responses(t, true) }
   380  
   381  func test304Responses(t *testing.T, h2 bool) {
   382  	defer afterTest(t)
   383  	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
   384  		w.WriteHeader(StatusNotModified)
   385  		_, err := w.Write([]byte("illegal body"))
   386  		if err != ErrBodyNotAllowed {
   387  			t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err)
   388  		}
   389  	}))
   390  	defer cst.close()
   391  	res, err := cst.c.Get(cst.ts.URL)
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  	if len(res.TransferEncoding) > 0 {
   396  		t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding)
   397  	}
   398  	body, err := ioutil.ReadAll(res.Body)
   399  	if err != nil {
   400  		t.Error(err)
   401  	}
   402  	if len(body) > 0 {
   403  		t.Errorf("got unexpected body %q", string(body))
   404  	}
   405  }
   406  
   407  func TestH12_ServerEmptyContentLength(t *testing.T) {
   408  	h12Compare{
   409  		Handler: func(w ResponseWriter, r *Request) {
   410  			w.Header()["Content-Type"] = []string{""}
   411  			io.WriteString(w, "<html><body>hi</body></html>")
   412  		},
   413  	}.run(t)
   414  }