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