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 }