1package nghttp2 2 3import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "regexp" 11 "syscall" 12 "testing" 13 "time" 14 15 "golang.org/x/net/http2/hpack" 16 "golang.org/x/net/websocket" 17) 18 19// TestH1H1PlainGET tests whether simple HTTP/1 GET request works. 20func TestH1H1PlainGET(t *testing.T) { 21 st := newServerTester(t, options{}) 22 defer st.Close() 23 24 res, err := st.http1(requestParam{ 25 name: "TestH1H1PlainGET", 26 }) 27 if err != nil { 28 t.Fatalf("Error st.http1() = %v", err) 29 } 30 31 if got, want := res.status, http.StatusOK; got != want { 32 t.Errorf("status = %v; want %v", got, want) 33 } 34} 35 36// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with 37// Connection: close request header field works. 38func TestH1H1PlainGETClose(t *testing.T) { 39 st := newServerTester(t, options{}) 40 defer st.Close() 41 42 res, err := st.http1(requestParam{ 43 name: "TestH1H1PlainGETClose", 44 header: []hpack.HeaderField{ 45 pair("Connection", "close"), 46 }, 47 }) 48 if err != nil { 49 t.Fatalf("Error st.http1() = %v", err) 50 } 51 52 if got, want := res.status, http.StatusOK; got != want { 53 t.Errorf("status = %v; want %v", got, want) 54 } 55} 56 57// TestH1H1InvalidMethod tests that server rejects invalid method with 58// 501 status code 59func TestH1H1InvalidMethod(t *testing.T) { 60 opts := options{ 61 handler: func(w http.ResponseWriter, r *http.Request) { 62 t.Errorf("server should not forward this request") 63 }, 64 } 65 st := newServerTester(t, opts) 66 defer st.Close() 67 68 res, err := st.http1(requestParam{ 69 name: "TestH1H1InvalidMethod", 70 method: "get", 71 }) 72 if err != nil { 73 t.Fatalf("Error st.http1() = %v", err) 74 } 75 76 if got, want := res.status, http.StatusNotImplemented; got != want { 77 t.Errorf("status = %v; want %v", got, want) 78 } 79} 80 81// TestH1H1MultipleRequestCL tests that server rejects request which 82// contains multiple Content-Length header fields. 83func TestH1H1MultipleRequestCL(t *testing.T) { 84 opts := options{ 85 handler: func(w http.ResponseWriter, r *http.Request) { 86 t.Errorf("server should not forward bad request") 87 }, 88 } 89 st := newServerTester(t, opts) 90 defer st.Close() 91 92 if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1MultipleRequestCL\r\nContent-Length: 0\r\nContent-Length: 0\r\n\r\n", 93 st.authority)); err != nil { 94 t.Fatalf("Error io.WriteString() = %v", err) 95 } 96 97 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 98 if err != nil { 99 t.Fatalf("Error http.ReadResponse() = %v", err) 100 } 101 102 defer resp.Body.Close() 103 104 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 105 t.Errorf("status: %v; want %v", got, want) 106 } 107} 108 109// // TestH1H1ConnectFailure tests that server handles the situation that 110// // connection attempt to HTTP/1 backend failed. 111// func TestH1H1ConnectFailure(t *testing.T) { 112// st := newServerTester(t, options{}) 113// defer st.Close() 114 115// // shutdown backend server to simulate backend connect failure 116// st.ts.Close() 117 118// res, err := st.http1(requestParam{ 119// name: "TestH1H1ConnectFailure", 120// }) 121// if err != nil { 122// t.Fatalf("Error st.http1() = %v", err) 123// } 124// want := 503 125// if got := res.status; got != want { 126// t.Errorf("status: %v; want %v", got, want) 127// } 128// } 129 130// TestH1H1AffinityCookie tests that affinity cookie is sent back in 131// cleartext http. 132func TestH1H1AffinityCookie(t *testing.T) { 133 opts := options{ 134 args: []string{"--affinity-cookie"}, 135 } 136 st := newServerTester(t, opts) 137 defer st.Close() 138 139 res, err := st.http1(requestParam{ 140 name: "TestH1H1AffinityCookie", 141 }) 142 if err != nil { 143 t.Fatalf("Error st.http1() = %v", err) 144 } 145 146 if got, want := res.status, http.StatusOK; got != want { 147 t.Errorf("status = %v; want %v", got, want) 148 } 149 150 const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar` 151 validCookie := regexp.MustCompile(pattern) 152 if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { 153 t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) 154 } 155} 156 157// TestH1H1AffinityCookieTLS tests that affinity cookie is sent back 158// in https. 159func TestH1H1AffinityCookieTLS(t *testing.T) { 160 opts := options{ 161 args: []string{"--alpn-h1", "--affinity-cookie"}, 162 tls: true, 163 } 164 st := newServerTester(t, opts) 165 defer st.Close() 166 167 res, err := st.http1(requestParam{ 168 name: "TestH1H1AffinityCookieTLS", 169 }) 170 if err != nil { 171 t.Fatalf("Error st.http1() = %v", err) 172 } 173 174 if got, want := res.status, http.StatusOK; got != want { 175 t.Errorf("status = %v; want %v", got, want) 176 } 177 178 const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure` 179 validCookie := regexp.MustCompile(pattern) 180 if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) { 181 t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern) 182 } 183} 184 185// TestH1H1GracefulShutdown tests graceful shutdown. 186func TestH1H1GracefulShutdown(t *testing.T) { 187 st := newServerTester(t, options{}) 188 defer st.Close() 189 190 res, err := st.http1(requestParam{ 191 name: "TestH1H1GracefulShutdown-1", 192 }) 193 if err != nil { 194 t.Fatalf("Error st.http1() = %v", err) 195 } 196 197 if got, want := res.status, http.StatusOK; got != want { 198 t.Errorf("status: %v; want %v", got, want) 199 } 200 201 if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil { 202 t.Fatalf("Error st.cmd.Process.Signal() = %v", err) 203 } 204 205 time.Sleep(150 * time.Millisecond) 206 207 res, err = st.http1(requestParam{ 208 name: "TestH1H1GracefulShutdown-2", 209 }) 210 if err != nil { 211 t.Fatalf("Error st.http1() = %v", err) 212 } 213 214 if got, want := res.status, http.StatusOK; got != want { 215 t.Errorf("status: %v; want %v", got, want) 216 } 217 218 if got, want := res.connClose, true; got != want { 219 t.Errorf("res.connClose: %v; want %v", got, want) 220 } 221 222 want := io.EOF 223 b := make([]byte, 256) 224 if _, err := st.conn.Read(b); err == nil || err != want { 225 t.Errorf("st.conn.Read(): %v; want %v", err, want) 226 } 227} 228 229// TestH1H1HostRewrite tests that server rewrites Host header field 230func TestH1H1HostRewrite(t *testing.T) { 231 opts := options{ 232 args: []string{"--host-rewrite"}, 233 handler: func(w http.ResponseWriter, r *http.Request) { 234 w.Header().Add("request-host", r.Host) 235 }, 236 } 237 st := newServerTester(t, opts) 238 defer st.Close() 239 240 res, err := st.http1(requestParam{ 241 name: "TestH1H1HostRewrite", 242 }) 243 if err != nil { 244 t.Fatalf("Error st.http1() = %v", err) 245 } 246 if got, want := res.status, http.StatusOK; got != want { 247 t.Errorf("status: %v; want %v", got, want) 248 } 249 if got, want := res.header.Get("request-host"), st.backendHost; got != want { 250 t.Errorf("request-host: %v; want %v", got, want) 251 } 252} 253 254// TestH1H1BadHost tests that server rejects request including bad 255// characters in host header field. 256func TestH1H1BadHost(t *testing.T) { 257 opts := options{ 258 handler: func(w http.ResponseWriter, r *http.Request) { 259 t.Errorf("server should not forward this request") 260 }, 261 } 262 st := newServerTester(t, opts) 263 defer st.Close() 264 265 if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H1HBadHost\r\nHost: foo\"bar\r\n\r\n"); err != nil { 266 t.Fatalf("Error io.WriteString() = %v", err) 267 } 268 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 269 if err != nil { 270 t.Fatalf("Error http.ReadResponse() = %v", err) 271 } 272 273 defer resp.Body.Close() 274 275 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 276 t.Errorf("status: %v; want %v", got, want) 277 } 278} 279 280// TestH1H1BadAuthority tests that server rejects request including 281// bad characters in authority component of requset URI. 282func TestH1H1BadAuthority(t *testing.T) { 283 opts := options{ 284 handler: func(w http.ResponseWriter, r *http.Request) { 285 t.Errorf("server should not forward this request") 286 }, 287 } 288 st := newServerTester(t, opts) 289 defer st.Close() 290 291 if _, err := io.WriteString(st.conn, "GET http://foo\"bar/ HTTP/1.1\r\nTest-Case: TestH1H1HBadAuthority\r\nHost: foobar\r\n\r\n"); err != nil { 292 t.Fatalf("Error io.WriteString() = %v", err) 293 } 294 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 295 if err != nil { 296 t.Fatalf("Error http.ReadResponse() = %v", err) 297 } 298 299 defer resp.Body.Close() 300 301 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 302 t.Errorf("status: %v; want %v", got, want) 303 } 304} 305 306// TestH1H1BadScheme tests that server rejects request including 307// bad characters in scheme component of requset URI. 308func TestH1H1BadScheme(t *testing.T) { 309 opts := options{ 310 handler: func(w http.ResponseWriter, r *http.Request) { 311 t.Errorf("server should not forward this request") 312 }, 313 } 314 st := newServerTester(t, opts) 315 defer st.Close() 316 317 if _, err := io.WriteString(st.conn, "GET http*://example.com/ HTTP/1.1\r\nTest-Case: TestH1H1HBadScheme\r\nHost: example.com\r\n\r\n"); err != nil { 318 t.Fatalf("Error io.WriteString() = %v", err) 319 } 320 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 321 if err != nil { 322 t.Fatalf("Error http.ReadResponse() = %v", err) 323 } 324 325 defer resp.Body.Close() 326 327 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 328 t.Errorf("status: %v; want %v", got, want) 329 } 330} 331 332// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request 333// without Host header field 334func TestH1H1HTTP10(t *testing.T) { 335 opts := options{ 336 handler: func(w http.ResponseWriter, r *http.Request) { 337 w.Header().Add("request-host", r.Host) 338 }, 339 } 340 st := newServerTester(t, opts) 341 defer st.Close() 342 343 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil { 344 t.Fatalf("Error io.WriteString() = %v", err) 345 } 346 347 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 348 if err != nil { 349 t.Fatalf("Error http.ReadResponse() = %v", err) 350 } 351 352 defer resp.Body.Close() 353 354 if got, want := resp.StatusCode, http.StatusOK; got != want { 355 t.Errorf("status: %v; want %v", got, want) 356 } 357 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 358 t.Errorf("request-host: %v; want %v", got, want) 359 } 360} 361 362// TestH1H1HTTP10NoHostRewrite tests that server generates host header 363// field using actual backend server even if --no-http-rewrite is 364// used. 365func TestH1H1HTTP10NoHostRewrite(t *testing.T) { 366 opts := options{ 367 handler: func(w http.ResponseWriter, r *http.Request) { 368 w.Header().Add("request-host", r.Host) 369 }, 370 } 371 st := newServerTester(t, opts) 372 defer st.Close() 373 374 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil { 375 t.Fatalf("Error io.WriteString() = %v", err) 376 } 377 378 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 379 if err != nil { 380 t.Fatalf("Error http.ReadResponse() = %v", err) 381 } 382 383 defer resp.Body.Close() 384 385 if got, want := resp.StatusCode, http.StatusOK; got != want { 386 t.Errorf("status: %v; want %v", got, want) 387 } 388 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 389 t.Errorf("request-host: %v; want %v", got, want) 390 } 391} 392 393// TestH1H1RequestTrailer tests request trailer part is forwarded to 394// backend. 395func TestH1H1RequestTrailer(t *testing.T) { 396 opts := options{ 397 handler: func(w http.ResponseWriter, r *http.Request) { 398 buf := make([]byte, 4096) 399 for { 400 _, err := r.Body.Read(buf) 401 if err == io.EOF { 402 break 403 } 404 if err != nil { 405 t.Fatalf("r.Body.Read() = %v", err) 406 } 407 } 408 if got, want := r.Trailer.Get("foo"), "bar"; got != want { 409 t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) 410 } 411 }, 412 } 413 st := newServerTester(t, opts) 414 defer st.Close() 415 416 res, err := st.http1(requestParam{ 417 name: "TestH1H1RequestTrailer", 418 body: []byte("1"), 419 trailer: []hpack.HeaderField{ 420 pair("foo", "bar"), 421 }, 422 }) 423 if err != nil { 424 t.Fatalf("Error st.http1() = %v", err) 425 } 426 if got, want := res.status, http.StatusOK; got != want { 427 t.Errorf("res.status: %v; want %v", got, want) 428 } 429} 430 431// TestH1H1HeaderFieldBufferPath tests that request with request path 432// larger than configured buffer size is rejected. 433func TestH1H1HeaderFieldBufferPath(t *testing.T) { 434 // The value 100 is chosen so that sum of header fields bytes 435 // does not exceed it. We use > 100 bytes URI to exceed this 436 // limit. 437 opts := options{ 438 args: []string{"--request-header-field-buffer=100"}, 439 handler: func(w http.ResponseWriter, r *http.Request) { 440 t.Fatal("execution path should not be here") 441 }, 442 } 443 st := newServerTester(t, opts) 444 defer st.Close() 445 446 res, err := st.http1(requestParam{ 447 name: "TestH1H1HeaderFieldBufferPath", 448 path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 449 }) 450 if err != nil { 451 t.Fatalf("Error st.http1() = %v", err) 452 } 453 if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { 454 t.Errorf("status: %v; want %v", got, want) 455 } 456} 457 458// TestH1H1HeaderFieldBuffer tests that request with header fields 459// larger than configured buffer size is rejected. 460func TestH1H1HeaderFieldBuffer(t *testing.T) { 461 opts := options{ 462 args: []string{"--request-header-field-buffer=10"}, 463 handler: func(w http.ResponseWriter, r *http.Request) { 464 t.Fatal("execution path should not be here") 465 }, 466 } 467 st := newServerTester(t, opts) 468 defer st.Close() 469 470 res, err := st.http1(requestParam{ 471 name: "TestH1H1HeaderFieldBuffer", 472 }) 473 if err != nil { 474 t.Fatalf("Error st.http1() = %v", err) 475 } 476 if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { 477 t.Errorf("status: %v; want %v", got, want) 478 } 479} 480 481// TestH1H1HeaderFields tests that request with header fields more 482// than configured number is rejected. 483func TestH1H1HeaderFields(t *testing.T) { 484 opts := options{ 485 args: []string{"--max-request-header-fields=1"}, 486 handler: func(w http.ResponseWriter, r *http.Request) { 487 t.Fatal("execution path should not be here") 488 }, 489 } 490 st := newServerTester(t, opts) 491 defer st.Close() 492 493 res, err := st.http1(requestParam{ 494 name: "TestH1H1HeaderFields", 495 header: []hpack.HeaderField{ 496 // Add extra header field to ensure that 497 // header field limit exceeds 498 pair("Connection", "close"), 499 }, 500 }) 501 if err != nil { 502 t.Fatalf("Error st.http1() = %v", err) 503 } 504 if got, want := res.status, http.StatusRequestHeaderFieldsTooLarge; got != want { 505 t.Errorf("status: %v; want %v", got, want) 506 } 507} 508 509// TestH1H1Websocket tests that HTTP Upgrade to WebSocket works. 510func TestH1H1Websocket(t *testing.T) { 511 opts := options{ 512 handler: websocket.Handler(func(ws *websocket.Conn) { 513 if _, err := io.Copy(ws, ws); err != nil { 514 t.Fatalf("Error io.Copy() = %v", err) 515 } 516 }).ServeHTTP, 517 } 518 st := newServerTester(t, opts) 519 defer st.Close() 520 521 content := []byte("hello world") 522 res := st.websocket(requestParam{ 523 name: "TestH1H1Websocket", 524 body: content, 525 }) 526 if got, want := res.body, content; !bytes.Equal(got, want) { 527 t.Errorf("echo: %q; want %q", got, want) 528 } 529} 530 531// TestH1H1ReqPhaseSetHeader tests mruby request phase hook 532// modifies request header fields. 533func TestH1H1ReqPhaseSetHeader(t *testing.T) { 534 opts := options{ 535 args: []string{"--mruby-file=" + testDir + "/req-set-header.rb"}, 536 handler: func(w http.ResponseWriter, r *http.Request) { 537 if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { 538 t.Errorf("User-Agent = %v; want %v", got, want) 539 } 540 }, 541 } 542 st := newServerTester(t, opts) 543 defer st.Close() 544 545 res, err := st.http1(requestParam{ 546 name: "TestH1H1ReqPhaseSetHeader", 547 }) 548 if err != nil { 549 t.Fatalf("Error st.http1() = %v", err) 550 } 551 552 if got, want := res.status, http.StatusOK; got != want { 553 t.Errorf("status = %v; want %v", got, want) 554 } 555} 556 557// TestH1H1ReqPhaseReturn tests mruby request phase hook returns 558// custom response. 559func TestH1H1ReqPhaseReturn(t *testing.T) { 560 opts := options{ 561 args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, 562 handler: func(w http.ResponseWriter, r *http.Request) { 563 t.Fatalf("request should not be forwarded") 564 }, 565 } 566 st := newServerTester(t, opts) 567 defer st.Close() 568 569 res, err := st.http1(requestParam{ 570 name: "TestH1H1ReqPhaseReturn", 571 }) 572 if err != nil { 573 t.Fatalf("Error st.http1() = %v", err) 574 } 575 576 if got, want := res.status, http.StatusNotFound; got != want { 577 t.Errorf("status = %v; want %v", got, want) 578 } 579 580 hdtests := []struct { 581 k, v string 582 }{ 583 {"content-length", "20"}, 584 {"from", "mruby"}, 585 } 586 for _, tt := range hdtests { 587 if got, want := res.header.Get(tt.k), tt.v; got != want { 588 t.Errorf("%v = %v; want %v", tt.k, got, want) 589 } 590 } 591 592 if got, want := string(res.body), "Hello World from req"; got != want { 593 t.Errorf("body = %v; want %v", got, want) 594 } 595} 596 597// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies 598// response header fields. 599func TestH1H1RespPhaseSetHeader(t *testing.T) { 600 opts := options{ 601 args: []string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, 602 } 603 st := newServerTester(t, opts) 604 defer st.Close() 605 606 res, err := st.http1(requestParam{ 607 name: "TestH1H1RespPhaseSetHeader", 608 }) 609 if err != nil { 610 t.Fatalf("Error st.http1() = %v", err) 611 } 612 613 if got, want := res.status, http.StatusOK; got != want { 614 t.Errorf("status = %v; want %v", got, want) 615 } 616 617 if got, want := res.header.Get("alpha"), "bravo"; got != want { 618 t.Errorf("alpha = %v; want %v", got, want) 619 } 620} 621 622// TestH1H1RespPhaseReturn tests mruby response phase hook returns 623// custom response. 624func TestH1H1RespPhaseReturn(t *testing.T) { 625 opts := options{ 626 args: []string{"--mruby-file=" + testDir + "/resp-return.rb"}, 627 } 628 st := newServerTester(t, opts) 629 defer st.Close() 630 631 res, err := st.http1(requestParam{ 632 name: "TestH1H1RespPhaseReturn", 633 }) 634 if err != nil { 635 t.Fatalf("Error st.http1() = %v", err) 636 } 637 638 if got, want := res.status, http.StatusNotFound; got != want { 639 t.Errorf("status = %v; want %v", got, want) 640 } 641 642 hdtests := []struct { 643 k, v string 644 }{ 645 {"content-length", "21"}, 646 {"from", "mruby"}, 647 } 648 for _, tt := range hdtests { 649 if got, want := res.header.Get(tt.k), tt.v; got != want { 650 t.Errorf("%v = %v; want %v", tt.k, got, want) 651 } 652 } 653 654 if got, want := string(res.body), "Hello World from resp"; got != want { 655 t.Errorf("body = %v; want %v", got, want) 656 } 657} 658 659// TestH1H1HTTPSRedirect tests that the request to the backend which 660// requires TLS is redirected to https URI. 661func TestH1H1HTTPSRedirect(t *testing.T) { 662 opts := options{ 663 args: []string{"--redirect-if-not-tls"}, 664 } 665 st := newServerTester(t, opts) 666 defer st.Close() 667 668 res, err := st.http1(requestParam{ 669 name: "TestH1H1HTTPSRedirect", 670 }) 671 if err != nil { 672 t.Fatalf("Error st.http1() = %v", err) 673 } 674 675 if got, want := res.status, http.StatusPermanentRedirect; got != want { 676 t.Errorf("status = %v; want %v", got, want) 677 } 678 if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want { 679 t.Errorf("location: %v; want %v", got, want) 680 } 681} 682 683// TestH1H1HTTPSRedirectPort tests that the request to the backend 684// which requires TLS is redirected to https URI with given port. 685func TestH1H1HTTPSRedirectPort(t *testing.T) { 686 opts := options{ 687 args: []string{ 688 "--redirect-if-not-tls", 689 "--redirect-https-port=8443", 690 }, 691 } 692 st := newServerTester(t, opts) 693 defer st.Close() 694 695 res, err := st.http1(requestParam{ 696 path: "/foo?bar", 697 name: "TestH1H1HTTPSRedirectPort", 698 }) 699 if err != nil { 700 t.Fatalf("Error st.http1() = %v", err) 701 } 702 703 if got, want := res.status, http.StatusPermanentRedirect; got != want { 704 t.Errorf("status = %v; want %v", got, want) 705 } 706 if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want { 707 t.Errorf("location: %v; want %v", got, want) 708 } 709} 710 711// TestH1H1POSTRequests tests that server can handle 2 requests with 712// request body. 713func TestH1H1POSTRequests(t *testing.T) { 714 st := newServerTester(t, options{}) 715 defer st.Close() 716 717 res, err := st.http1(requestParam{ 718 name: "TestH1H1POSTRequestsNo1", 719 body: make([]byte, 1), 720 }) 721 if err != nil { 722 t.Fatalf("Error st.http1() = %v", err) 723 } 724 if got, want := res.status, http.StatusOK; got != want { 725 t.Errorf("res.status: %v; want %v", got, want) 726 } 727 728 res, err = st.http1(requestParam{ 729 name: "TestH1H1POSTRequestsNo2", 730 body: make([]byte, 65536), 731 }) 732 if err != nil { 733 t.Fatalf("Error st.http1() = %v", err) 734 } 735 if got, want := res.status, http.StatusOK; got != want { 736 t.Errorf("res.status: %v; want %v", got, want) 737 } 738} 739 740// // TestH1H2ConnectFailure tests that server handles the situation that 741// // connection attempt to HTTP/2 backend failed. 742// func TestH1H2ConnectFailure(t *testing.T) { 743// opts := options{ 744// args: []string{"--http2-bridge"}, 745// } 746// st := newServerTester(t, opts) 747// defer st.Close() 748 749// // simulate backend connect attempt failure 750// st.ts.Close() 751 752// res, err := st.http1(requestParam{ 753// name: "TestH1H2ConnectFailure", 754// }) 755// if err != nil { 756// t.Fatalf("Error st.http1() = %v", err) 757// } 758// want := 503 759// if got := res.status; got != want { 760// t.Errorf("status: %v; want %v", got, want) 761// } 762// } 763 764// TestH1H2NoHost tests that server rejects request without Host 765// header field for HTTP/2 backend. 766func TestH1H2NoHost(t *testing.T) { 767 opts := options{ 768 args: []string{"--http2-bridge"}, 769 handler: func(w http.ResponseWriter, r *http.Request) { 770 t.Errorf("server should not forward bad request") 771 }, 772 } 773 st := newServerTester(t, opts) 774 defer st.Close() 775 776 // without Host header field, we expect 400 response 777 if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { 778 t.Fatalf("Error io.WriteString() = %v", err) 779 } 780 781 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 782 if err != nil { 783 t.Fatalf("Error http.ReadResponse() = %v", err) 784 } 785 786 defer resp.Body.Close() 787 788 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 789 t.Errorf("status: %v; want %v", got, want) 790 } 791} 792 793// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request 794// without Host header field 795func TestH1H2HTTP10(t *testing.T) { 796 opts := options{ 797 args: []string{"--http2-bridge"}, 798 handler: func(w http.ResponseWriter, r *http.Request) { 799 w.Header().Add("request-host", r.Host) 800 }, 801 } 802 st := newServerTester(t, opts) 803 defer st.Close() 804 805 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { 806 t.Fatalf("Error io.WriteString() = %v", err) 807 } 808 809 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 810 if err != nil { 811 t.Fatalf("Error http.ReadResponse() = %v", err) 812 } 813 814 defer resp.Body.Close() 815 816 if got, want := resp.StatusCode, http.StatusOK; got != want { 817 t.Errorf("status: %v; want %v", got, want) 818 } 819 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 820 t.Errorf("request-host: %v; want %v", got, want) 821 } 822} 823 824// TestH1H2HTTP10NoHostRewrite tests that server generates host header 825// field using actual backend server even if --no-http-rewrite is 826// used. 827func TestH1H2HTTP10NoHostRewrite(t *testing.T) { 828 opts := options{ 829 args: []string{"--http2-bridge"}, 830 handler: func(w http.ResponseWriter, r *http.Request) { 831 w.Header().Add("request-host", r.Host) 832 }, 833 } 834 st := newServerTester(t, opts) 835 defer st.Close() 836 837 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { 838 t.Fatalf("Error io.WriteString() = %v", err) 839 } 840 841 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 842 if err != nil { 843 t.Fatalf("Error http.ReadResponse() = %v", err) 844 } 845 846 defer resp.Body.Close() 847 848 if got, want := resp.StatusCode, http.StatusOK; got != want { 849 t.Errorf("status: %v; want %v", got, want) 850 } 851 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 852 t.Errorf("request-host: %v; want %v", got, want) 853 } 854} 855 856// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled 857// when forwarding to HTTP/2 backend link. go-nghttp2 server 858// concatenates crumbled Cookies automatically, so this test is not 859// much effective now. 860func TestH1H2CrumbleCookie(t *testing.T) { 861 opts := options{ 862 args: []string{"--http2-bridge"}, 863 handler: func(w http.ResponseWriter, r *http.Request) { 864 if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { 865 t.Errorf("Cookie: %v; want %v", got, want) 866 } 867 }, 868 } 869 st := newServerTester(t, opts) 870 defer st.Close() 871 872 res, err := st.http1(requestParam{ 873 name: "TestH1H2CrumbleCookie", 874 header: []hpack.HeaderField{ 875 pair("Cookie", "alpha; bravo; charlie"), 876 }, 877 }) 878 if err != nil { 879 t.Fatalf("Error st.http1() = %v", err) 880 } 881 if got, want := res.status, http.StatusOK; got != want { 882 t.Errorf("status: %v; want %v", got, want) 883 } 884} 885 886// TestH1H2GenerateVia tests that server generates Via header field to and 887// from backend server. 888func TestH1H2GenerateVia(t *testing.T) { 889 opts := options{ 890 args: []string{"--http2-bridge"}, 891 handler: func(w http.ResponseWriter, r *http.Request) { 892 if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { 893 t.Errorf("Via: %v; want %v", got, want) 894 } 895 }, 896 } 897 st := newServerTester(t, opts) 898 defer st.Close() 899 900 res, err := st.http1(requestParam{ 901 name: "TestH1H2GenerateVia", 902 }) 903 if err != nil { 904 t.Fatalf("Error st.http1() = %v", err) 905 } 906 if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { 907 t.Errorf("Via: %v; want %v", got, want) 908 } 909} 910 911// TestH1H2AppendVia tests that server adds value to existing Via 912// header field to and from backend server. 913func TestH1H2AppendVia(t *testing.T) { 914 opts := options{ 915 args: []string{"--http2-bridge"}, 916 handler: func(w http.ResponseWriter, r *http.Request) { 917 if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { 918 t.Errorf("Via: %v; want %v", got, want) 919 } 920 w.Header().Add("Via", "bar") 921 }, 922 } 923 st := newServerTester(t, opts) 924 defer st.Close() 925 926 res, err := st.http1(requestParam{ 927 name: "TestH1H2AppendVia", 928 header: []hpack.HeaderField{ 929 pair("via", "foo"), 930 }, 931 }) 932 if err != nil { 933 t.Fatalf("Error st.http1() = %v", err) 934 } 935 if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { 936 t.Errorf("Via: %v; want %v", got, want) 937 } 938} 939 940// TestH1H2NoVia tests that server does not add value to existing Via 941// header field to and from backend server. 942func TestH1H2NoVia(t *testing.T) { 943 opts := options{ 944 args: []string{"--http2-bridge", "--no-via"}, 945 handler: func(w http.ResponseWriter, r *http.Request) { 946 if got, want := r.Header.Get("Via"), "foo"; got != want { 947 t.Errorf("Via: %v; want %v", got, want) 948 } 949 w.Header().Add("Via", "bar") 950 }, 951 } 952 st := newServerTester(t, opts) 953 defer st.Close() 954 955 res, err := st.http1(requestParam{ 956 name: "TestH1H2NoVia", 957 header: []hpack.HeaderField{ 958 pair("via", "foo"), 959 }, 960 }) 961 if err != nil { 962 t.Fatalf("Error st.http1() = %v", err) 963 } 964 if got, want := res.header.Get("Via"), "bar"; got != want { 965 t.Errorf("Via: %v; want %v", got, want) 966 } 967} 968 969// TestH1H2ReqPhaseReturn tests mruby request phase hook returns 970// custom response. 971func TestH1H2ReqPhaseReturn(t *testing.T) { 972 opts := options{ 973 args: []string{ 974 "--http2-bridge", 975 "--mruby-file=" + testDir + "/req-return.rb", 976 }, 977 handler: func(w http.ResponseWriter, r *http.Request) { 978 t.Fatalf("request should not be forwarded") 979 }, 980 } 981 st := newServerTester(t, opts) 982 defer st.Close() 983 984 res, err := st.http1(requestParam{ 985 name: "TestH1H2ReqPhaseReturn", 986 }) 987 if err != nil { 988 t.Fatalf("Error st.http1() = %v", err) 989 } 990 991 if got, want := res.status, http.StatusNotFound; got != want { 992 t.Errorf("status = %v; want %v", got, want) 993 } 994 995 hdtests := []struct { 996 k, v string 997 }{ 998 {"content-length", "20"}, 999 {"from", "mruby"}, 1000 } 1001 for _, tt := range hdtests { 1002 if got, want := res.header.Get(tt.k), tt.v; got != want { 1003 t.Errorf("%v = %v; want %v", tt.k, got, want) 1004 } 1005 } 1006 1007 if got, want := string(res.body), "Hello World from req"; got != want { 1008 t.Errorf("body = %v; want %v", got, want) 1009 } 1010} 1011 1012// TestH1H2RespPhaseReturn tests mruby response phase hook returns 1013// custom response. 1014func TestH1H2RespPhaseReturn(t *testing.T) { 1015 opts := options{ 1016 args: []string{ 1017 "--http2-bridge", 1018 "--mruby-file=" + testDir + "/resp-return.rb", 1019 }, 1020 } 1021 st := newServerTester(t, opts) 1022 defer st.Close() 1023 1024 res, err := st.http1(requestParam{ 1025 name: "TestH1H2RespPhaseReturn", 1026 }) 1027 if err != nil { 1028 t.Fatalf("Error st.http1() = %v", err) 1029 } 1030 1031 if got, want := res.status, http.StatusNotFound; got != want { 1032 t.Errorf("status = %v; want %v", got, want) 1033 } 1034 1035 hdtests := []struct { 1036 k, v string 1037 }{ 1038 {"content-length", "21"}, 1039 {"from", "mruby"}, 1040 } 1041 for _, tt := range hdtests { 1042 if got, want := res.header.Get(tt.k), tt.v; got != want { 1043 t.Errorf("%v = %v; want %v", tt.k, got, want) 1044 } 1045 } 1046 1047 if got, want := string(res.body), "Hello World from resp"; got != want { 1048 t.Errorf("body = %v; want %v", got, want) 1049 } 1050} 1051 1052// TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2 1053// backend server by stripping other encodings. 1054func TestH1H2TE(t *testing.T) { 1055 opts := options{ 1056 args: []string{"--http2-bridge"}, 1057 handler: func(w http.ResponseWriter, r *http.Request) { 1058 if got, want := r.Header.Get("te"), "trailers"; got != want { 1059 t.Errorf("te: %v; want %v", got, want) 1060 } 1061 }, 1062 } 1063 st := newServerTester(t, opts) 1064 defer st.Close() 1065 1066 res, err := st.http1(requestParam{ 1067 name: "TestH1H2TE", 1068 header: []hpack.HeaderField{ 1069 pair("te", "foo,trailers,bar"), 1070 }, 1071 }) 1072 if err != nil { 1073 t.Fatalf("Error st.http1() = %v", err) 1074 } 1075 if got, want := res.status, http.StatusOK; got != want { 1076 t.Errorf("status: %v; want %v", got, want) 1077 } 1078} 1079 1080// TestH1APIBackendconfig exercise backendconfig API endpoint routine 1081// for successful case. 1082func TestH1APIBackendconfig(t *testing.T) { 1083 opts := options{ 1084 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1085 handler: func(w http.ResponseWriter, r *http.Request) { 1086 t.Fatalf("request should not be forwarded") 1087 }, 1088 connectPort: 3010, 1089 } 1090 st := newServerTester(t, opts) 1091 defer st.Close() 1092 1093 res, err := st.http1(requestParam{ 1094 name: "TestH1APIBackendconfig", 1095 path: "/api/v1beta1/backendconfig", 1096 method: "PUT", 1097 body: []byte(`# comment 1098backend=127.0.0.1,3011 1099 1100`), 1101 }) 1102 if err != nil { 1103 t.Fatalf("Error st.http1() = %v", err) 1104 } 1105 if got, want := res.status, http.StatusOK; got != want { 1106 t.Errorf("res.status: %v; want %v", got, want) 1107 } 1108 1109 var apiResp APIResponse 1110 err = json.Unmarshal(res.body, &apiResp) 1111 if err != nil { 1112 t.Fatalf("Error unmarshaling API response: %v", err) 1113 } 1114 if got, want := apiResp.Status, "Success"; got != want { 1115 t.Errorf("apiResp.Status: %v; want %v", got, want) 1116 } 1117 if got, want := apiResp.Code, 200; got != want { 1118 t.Errorf("apiResp.Status: %v; want %v", got, want) 1119 } 1120} 1121 1122// TestH1APIBackendconfigQuery exercise backendconfig API endpoint 1123// routine with query. 1124func TestH1APIBackendconfigQuery(t *testing.T) { 1125 opts := options{ 1126 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1127 handler: func(w http.ResponseWriter, r *http.Request) { 1128 t.Fatalf("request should not be forwarded") 1129 }, 1130 connectPort: 3010, 1131 } 1132 st := newServerTester(t, opts) 1133 defer st.Close() 1134 1135 res, err := st.http1(requestParam{ 1136 name: "TestH1APIBackendconfigQuery", 1137 path: "/api/v1beta1/backendconfig?foo=bar", 1138 method: "PUT", 1139 body: []byte(`# comment 1140backend=127.0.0.1,3011 1141 1142`), 1143 }) 1144 if err != nil { 1145 t.Fatalf("Error st.http1() = %v", err) 1146 } 1147 if got, want := res.status, http.StatusOK; got != want { 1148 t.Errorf("res.status: %v; want %v", got, want) 1149 } 1150 1151 var apiResp APIResponse 1152 err = json.Unmarshal(res.body, &apiResp) 1153 if err != nil { 1154 t.Fatalf("Error unmarshaling API response: %v", err) 1155 } 1156 if got, want := apiResp.Status, "Success"; got != want { 1157 t.Errorf("apiResp.Status: %v; want %v", got, want) 1158 } 1159 if got, want := apiResp.Code, 200; got != want { 1160 t.Errorf("apiResp.Status: %v; want %v", got, want) 1161 } 1162} 1163 1164// TestH1APIBackendconfigBadMethod exercise backendconfig API endpoint 1165// routine with bad method. 1166func TestH1APIBackendconfigBadMethod(t *testing.T) { 1167 opts := options{ 1168 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1169 handler: func(w http.ResponseWriter, r *http.Request) { 1170 t.Fatalf("request should not be forwarded") 1171 }, 1172 connectPort: 3010, 1173 } 1174 st := newServerTester(t, opts) 1175 defer st.Close() 1176 1177 res, err := st.http1(requestParam{ 1178 name: "TestH1APIBackendconfigBadMethod", 1179 path: "/api/v1beta1/backendconfig", 1180 method: "GET", 1181 body: []byte(`# comment 1182backend=127.0.0.1,3011 1183 1184`), 1185 }) 1186 if err != nil { 1187 t.Fatalf("Error st.http1() = %v", err) 1188 } 1189 if got, want := res.status, http.StatusMethodNotAllowed; got != want { 1190 t.Errorf("res.status: %v; want %v", got, want) 1191 } 1192 1193 var apiResp APIResponse 1194 err = json.Unmarshal(res.body, &apiResp) 1195 if err != nil { 1196 t.Fatalf("Error unmarshaling API response: %v", err) 1197 } 1198 if got, want := apiResp.Status, "Failure"; got != want { 1199 t.Errorf("apiResp.Status: %v; want %v", got, want) 1200 } 1201 if got, want := apiResp.Code, 405; got != want { 1202 t.Errorf("apiResp.Status: %v; want %v", got, want) 1203 } 1204} 1205 1206// TestH1APIConfigrevision tests configrevision API. 1207func TestH1APIConfigrevision(t *testing.T) { 1208 opts := options{ 1209 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1210 handler: func(w http.ResponseWriter, r *http.Request) { 1211 t.Fatalf("request should not be forwarded") 1212 }, 1213 connectPort: 3010, 1214 } 1215 st := newServerTester(t, opts) 1216 defer st.Close() 1217 1218 res, err := st.http1(requestParam{ 1219 name: "TestH1APIConfigrevision", 1220 path: "/api/v1beta1/configrevision", 1221 method: "GET", 1222 }) 1223 if err != nil { 1224 t.Fatalf("Error st.http1() = %v", err) 1225 } 1226 if got, want := res.status, http.StatusOK; got != want { 1227 t.Errorf("res.status: %v; want = %v", got, want) 1228 } 1229 1230 var apiResp APIResponse 1231 d := json.NewDecoder(bytes.NewBuffer(res.body)) 1232 d.UseNumber() 1233 err = d.Decode(&apiResp) 1234 if err != nil { 1235 t.Fatalf("Error unmarshalling API response: %v", err) 1236 } 1237 if got, want := apiResp.Status, "Success"; got != want { 1238 t.Errorf("apiResp.Status: %v; want %v", got, want) 1239 } 1240 if got, want := apiResp.Code, 200; got != want { 1241 t.Errorf("apiResp.Status: %v; want %v", got, want) 1242 } 1243 if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want { 1244 t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want) 1245 } 1246} 1247 1248// TestH1APINotFound exercise backendconfig API endpoint routine when 1249// API endpoint is not found. 1250func TestH1APINotFound(t *testing.T) { 1251 opts := options{ 1252 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1253 handler: func(w http.ResponseWriter, r *http.Request) { 1254 t.Fatalf("request should not be forwarded") 1255 }, 1256 connectPort: 3010, 1257 } 1258 st := newServerTester(t, opts) 1259 defer st.Close() 1260 1261 res, err := st.http1(requestParam{ 1262 name: "TestH1APINotFound", 1263 path: "/api/notfound", 1264 method: "GET", 1265 body: []byte(`# comment 1266backend=127.0.0.1,3011 1267 1268`), 1269 }) 1270 if err != nil { 1271 t.Fatalf("Error st.http1() = %v", err) 1272 } 1273 if got, want := res.status, http.StatusNotFound; got != want { 1274 t.Errorf("res.status: %v; want %v", got, want) 1275 } 1276 1277 var apiResp APIResponse 1278 err = json.Unmarshal(res.body, &apiResp) 1279 if err != nil { 1280 t.Fatalf("Error unmarshaling API response: %v", err) 1281 } 1282 if got, want := apiResp.Status, "Failure"; got != want { 1283 t.Errorf("apiResp.Status: %v; want %v", got, want) 1284 } 1285 if got, want := apiResp.Code, 404; got != want { 1286 t.Errorf("apiResp.Status: %v; want %v", got, want) 1287 } 1288} 1289 1290// TestH1Healthmon tests health monitor endpoint. 1291func TestH1Healthmon(t *testing.T) { 1292 opts := options{ 1293 args: []string{"-f127.0.0.1,3011;healthmon;no-tls"}, 1294 handler: func(w http.ResponseWriter, r *http.Request) { 1295 t.Fatalf("request should not be forwarded") 1296 }, 1297 connectPort: 3011, 1298 } 1299 st := newServerTester(t, opts) 1300 defer st.Close() 1301 1302 res, err := st.http1(requestParam{ 1303 name: "TestH1Healthmon", 1304 path: "/alpha/bravo", 1305 }) 1306 if err != nil { 1307 t.Fatalf("Error st.http1() = %v", err) 1308 } 1309 if got, want := res.status, http.StatusOK; got != want { 1310 t.Errorf("res.status: %v; want %v", got, want) 1311 } 1312} 1313 1314// TestH1ResponseBeforeRequestEnd tests the situation where response 1315// ends before request body finishes. 1316func TestH1ResponseBeforeRequestEnd(t *testing.T) { 1317 opts := options{ 1318 args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, 1319 handler: func(w http.ResponseWriter, r *http.Request) { 1320 t.Fatal("request should not be forwarded") 1321 }, 1322 } 1323 st := newServerTester(t, opts) 1324 defer st.Close() 1325 1326 if _, err := io.WriteString(st.conn, fmt.Sprintf("POST / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1ResponseBeforeRequestEnd\r\nContent-Length: 1000000\r\n\r\n", 1327 st.authority)); err != nil { 1328 t.Fatalf("Error io.WriteString() = %v", err) 1329 } 1330 1331 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 1332 if err != nil { 1333 t.Fatalf("Error http.ReadResponse() = %v", err) 1334 } 1335 1336 defer resp.Body.Close() 1337 1338 if got, want := resp.StatusCode, http.StatusNotFound; got != want { 1339 t.Errorf("status: %v; want %v", got, want) 1340 } 1341} 1342 1343// TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails 1344// if the backend chunked encoded response ends prematurely. 1345func TestH1H1ChunkedEndsPrematurely(t *testing.T) { 1346 opts := options{ 1347 handler: func(w http.ResponseWriter, r *http.Request) { 1348 hj, ok := w.(http.Hijacker) 1349 if !ok { 1350 http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) 1351 return 1352 } 1353 conn, bufrw, err := hj.Hijack() 1354 if err != nil { 1355 http.Error(w, err.Error(), http.StatusInternalServerError) 1356 return 1357 } 1358 defer conn.Close() 1359 if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { 1360 t.Fatalf("Error bufrw.WriteString() = %v", err) 1361 } 1362 bufrw.Flush() 1363 }, 1364 } 1365 st := newServerTester(t, opts) 1366 defer st.Close() 1367 1368 _, err := st.http1(requestParam{ 1369 name: "TestH1H1ChunkedEndsPrematurely", 1370 }) 1371 if err == nil { 1372 t.Fatal("st.http1() should fail") 1373 } 1374} 1375