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// TestH1H1ReqPhaseReturnCONNECTMethod tests that mruby request phase 598// hook resets llhttp HPE_PAUSED_UPGRADE. 599func TestH1H1ReqPhaseReturnCONNECTMethod(t *testing.T) { 600 opts := options{ 601 args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, 602 handler: func(w http.ResponseWriter, r *http.Request) { 603 t.Fatalf("request should not be forwarded") 604 }, 605 } 606 st := newServerTester(t, opts) 607 defer st.Close() 608 609 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 { 610 t.Fatalf("Error io.WriteString() = %v", err) 611 } 612 613 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 614 if err != nil { 615 t.Fatalf("Error http.ReadResponse() = %v", err) 616 } 617 618 defer resp.Body.Close() 619 620 if got, want := resp.StatusCode, http.StatusNotFound; got != want { 621 t.Errorf("status: %v; want %v", got, want) 622 } 623 624 hdCheck := func() { 625 hdtests := []struct { 626 k, v string 627 }{ 628 {"content-length", "20"}, 629 {"from", "mruby"}, 630 } 631 632 for _, tt := range hdtests { 633 if got, want := resp.Header.Get(tt.k), tt.v; got != want { 634 t.Errorf("%v = %v; want %v", tt.k, got, want) 635 } 636 } 637 638 if _, err := io.ReadAll(resp.Body); err != nil { 639 t.Fatalf("Error io.ReadAll() = %v", err) 640 } 641 } 642 643 hdCheck() 644 645 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 { 646 t.Fatalf("Error io.WriteString() = %v", err) 647 } 648 649 resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil) 650 if err != nil { 651 t.Fatalf("Error http.ReadResponse() = %v", err) 652 } 653 654 defer resp.Body.Close() 655 656 if got, want := resp.StatusCode, http.StatusNotFound; got != want { 657 t.Errorf("status: %v; want %v", got, want) 658 } 659 660 hdCheck() 661 662 if _, err := io.ReadAll(resp.Body); err != nil { 663 t.Fatalf("Error io.ReadAll() = %v", err) 664 } 665} 666 667// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies 668// response header fields. 669func TestH1H1RespPhaseSetHeader(t *testing.T) { 670 opts := options{ 671 args: []string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, 672 } 673 st := newServerTester(t, opts) 674 defer st.Close() 675 676 res, err := st.http1(requestParam{ 677 name: "TestH1H1RespPhaseSetHeader", 678 }) 679 if err != nil { 680 t.Fatalf("Error st.http1() = %v", err) 681 } 682 683 if got, want := res.status, http.StatusOK; got != want { 684 t.Errorf("status = %v; want %v", got, want) 685 } 686 687 if got, want := res.header.Get("alpha"), "bravo"; got != want { 688 t.Errorf("alpha = %v; want %v", got, want) 689 } 690} 691 692// TestH1H1RespPhaseReturn tests mruby response phase hook returns 693// custom response. 694func TestH1H1RespPhaseReturn(t *testing.T) { 695 opts := options{ 696 args: []string{"--mruby-file=" + testDir + "/resp-return.rb"}, 697 } 698 st := newServerTester(t, opts) 699 defer st.Close() 700 701 res, err := st.http1(requestParam{ 702 name: "TestH1H1RespPhaseReturn", 703 }) 704 if err != nil { 705 t.Fatalf("Error st.http1() = %v", err) 706 } 707 708 if got, want := res.status, http.StatusNotFound; got != want { 709 t.Errorf("status = %v; want %v", got, want) 710 } 711 712 hdtests := []struct { 713 k, v string 714 }{ 715 {"content-length", "21"}, 716 {"from", "mruby"}, 717 } 718 for _, tt := range hdtests { 719 if got, want := res.header.Get(tt.k), tt.v; got != want { 720 t.Errorf("%v = %v; want %v", tt.k, got, want) 721 } 722 } 723 724 if got, want := string(res.body), "Hello World from resp"; got != want { 725 t.Errorf("body = %v; want %v", got, want) 726 } 727} 728 729// TestH1H1HTTPSRedirect tests that the request to the backend which 730// requires TLS is redirected to https URI. 731func TestH1H1HTTPSRedirect(t *testing.T) { 732 opts := options{ 733 args: []string{"--redirect-if-not-tls"}, 734 } 735 st := newServerTester(t, opts) 736 defer st.Close() 737 738 res, err := st.http1(requestParam{ 739 name: "TestH1H1HTTPSRedirect", 740 }) 741 if err != nil { 742 t.Fatalf("Error st.http1() = %v", err) 743 } 744 745 if got, want := res.status, http.StatusPermanentRedirect; got != want { 746 t.Errorf("status = %v; want %v", got, want) 747 } 748 if got, want := res.header.Get("location"), "https://127.0.0.1/"; got != want { 749 t.Errorf("location: %v; want %v", got, want) 750 } 751} 752 753// TestH1H1HTTPSRedirectPort tests that the request to the backend 754// which requires TLS is redirected to https URI with given port. 755func TestH1H1HTTPSRedirectPort(t *testing.T) { 756 opts := options{ 757 args: []string{ 758 "--redirect-if-not-tls", 759 "--redirect-https-port=8443", 760 }, 761 } 762 st := newServerTester(t, opts) 763 defer st.Close() 764 765 res, err := st.http1(requestParam{ 766 path: "/foo?bar", 767 name: "TestH1H1HTTPSRedirectPort", 768 }) 769 if err != nil { 770 t.Fatalf("Error st.http1() = %v", err) 771 } 772 773 if got, want := res.status, http.StatusPermanentRedirect; got != want { 774 t.Errorf("status = %v; want %v", got, want) 775 } 776 if got, want := res.header.Get("location"), "https://127.0.0.1:8443/foo?bar"; got != want { 777 t.Errorf("location: %v; want %v", got, want) 778 } 779} 780 781// TestH1H1POSTRequests tests that server can handle 2 requests with 782// request body. 783func TestH1H1POSTRequests(t *testing.T) { 784 st := newServerTester(t, options{}) 785 defer st.Close() 786 787 res, err := st.http1(requestParam{ 788 name: "TestH1H1POSTRequestsNo1", 789 body: make([]byte, 1), 790 }) 791 if err != nil { 792 t.Fatalf("Error st.http1() = %v", err) 793 } 794 if got, want := res.status, http.StatusOK; got != want { 795 t.Errorf("res.status: %v; want %v", got, want) 796 } 797 798 res, err = st.http1(requestParam{ 799 name: "TestH1H1POSTRequestsNo2", 800 body: make([]byte, 65536), 801 }) 802 if err != nil { 803 t.Fatalf("Error st.http1() = %v", err) 804 } 805 if got, want := res.status, http.StatusOK; got != want { 806 t.Errorf("res.status: %v; want %v", got, want) 807 } 808} 809 810// TestH1H1CONNECTMethodFailure tests that CONNECT method failure 811// resets llhttp HPE_PAUSED_UPGRADE. 812func TestH1H1CONNECTMethodFailure(t *testing.T) { 813 opts := options{ 814 handler: func(w http.ResponseWriter, r *http.Request) { 815 if r.Header.Get("required-header") == "" { 816 w.WriteHeader(http.StatusNotFound) 817 } 818 }, 819 } 820 st := newServerTester(t, opts) 821 defer st.Close() 822 823 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 { 824 t.Fatalf("Error io.WriteString() = %v", err) 825 } 826 827 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 828 if err != nil { 829 t.Fatalf("Error http.ReadResponse() = %v", err) 830 } 831 832 defer resp.Body.Close() 833 834 if got, want := resp.StatusCode, http.StatusNotFound; got != want { 835 t.Errorf("status: %v; want %v", got, want) 836 } 837 838 if _, err := io.ReadAll(resp.Body); err != nil { 839 t.Fatalf("Error io.ReadAll() = %v", err) 840 } 841 842 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 { 843 t.Fatalf("Error io.WriteString() = %v", err) 844 } 845 846 resp, err = http.ReadResponse(bufio.NewReader(st.conn), nil) 847 if err != nil { 848 t.Fatalf("Error http.ReadResponse() = %v", err) 849 } 850 851 defer resp.Body.Close() 852 853 if got, want := resp.StatusCode, http.StatusOK; got != want { 854 t.Errorf("status: %v; want %v", got, want) 855 } 856} 857 858// // TestH1H2ConnectFailure tests that server handles the situation that 859// // connection attempt to HTTP/2 backend failed. 860// func TestH1H2ConnectFailure(t *testing.T) { 861// opts := options{ 862// args: []string{"--http2-bridge"}, 863// } 864// st := newServerTester(t, opts) 865// defer st.Close() 866 867// // simulate backend connect attempt failure 868// st.ts.Close() 869 870// res, err := st.http1(requestParam{ 871// name: "TestH1H2ConnectFailure", 872// }) 873// if err != nil { 874// t.Fatalf("Error st.http1() = %v", err) 875// } 876// want := 503 877// if got := res.status; got != want { 878// t.Errorf("status: %v; want %v", got, want) 879// } 880// } 881 882// TestH1H2NoHost tests that server rejects request without Host 883// header field for HTTP/2 backend. 884func TestH1H2NoHost(t *testing.T) { 885 opts := options{ 886 args: []string{"--http2-bridge"}, 887 handler: func(w http.ResponseWriter, r *http.Request) { 888 t.Errorf("server should not forward bad request") 889 }, 890 } 891 st := newServerTester(t, opts) 892 defer st.Close() 893 894 // without Host header field, we expect 400 response 895 if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { 896 t.Fatalf("Error io.WriteString() = %v", err) 897 } 898 899 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 900 if err != nil { 901 t.Fatalf("Error http.ReadResponse() = %v", err) 902 } 903 904 defer resp.Body.Close() 905 906 if got, want := resp.StatusCode, http.StatusBadRequest; got != want { 907 t.Errorf("status: %v; want %v", got, want) 908 } 909} 910 911// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request 912// without Host header field 913func TestH1H2HTTP10(t *testing.T) { 914 opts := options{ 915 args: []string{"--http2-bridge"}, 916 handler: func(w http.ResponseWriter, r *http.Request) { 917 w.Header().Add("request-host", r.Host) 918 }, 919 } 920 st := newServerTester(t, opts) 921 defer st.Close() 922 923 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { 924 t.Fatalf("Error io.WriteString() = %v", err) 925 } 926 927 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 928 if err != nil { 929 t.Fatalf("Error http.ReadResponse() = %v", err) 930 } 931 932 defer resp.Body.Close() 933 934 if got, want := resp.StatusCode, http.StatusOK; got != want { 935 t.Errorf("status: %v; want %v", got, want) 936 } 937 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 938 t.Errorf("request-host: %v; want %v", got, want) 939 } 940} 941 942// TestH1H2HTTP10NoHostRewrite tests that server generates host header 943// field using actual backend server even if --no-http-rewrite is 944// used. 945func TestH1H2HTTP10NoHostRewrite(t *testing.T) { 946 opts := options{ 947 args: []string{"--http2-bridge"}, 948 handler: func(w http.ResponseWriter, r *http.Request) { 949 w.Header().Add("request-host", r.Host) 950 }, 951 } 952 st := newServerTester(t, opts) 953 defer st.Close() 954 955 if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { 956 t.Fatalf("Error io.WriteString() = %v", err) 957 } 958 959 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 960 if err != nil { 961 t.Fatalf("Error http.ReadResponse() = %v", err) 962 } 963 964 defer resp.Body.Close() 965 966 if got, want := resp.StatusCode, http.StatusOK; got != want { 967 t.Errorf("status: %v; want %v", got, want) 968 } 969 if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { 970 t.Errorf("request-host: %v; want %v", got, want) 971 } 972} 973 974// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled 975// when forwarding to HTTP/2 backend link. go-nghttp2 server 976// concatenates crumbled Cookies automatically, so this test is not 977// much effective now. 978func TestH1H2CrumbleCookie(t *testing.T) { 979 opts := options{ 980 args: []string{"--http2-bridge"}, 981 handler: func(w http.ResponseWriter, r *http.Request) { 982 if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { 983 t.Errorf("Cookie: %v; want %v", got, want) 984 } 985 }, 986 } 987 st := newServerTester(t, opts) 988 defer st.Close() 989 990 res, err := st.http1(requestParam{ 991 name: "TestH1H2CrumbleCookie", 992 header: []hpack.HeaderField{ 993 pair("Cookie", "alpha; bravo; charlie"), 994 }, 995 }) 996 if err != nil { 997 t.Fatalf("Error st.http1() = %v", err) 998 } 999 if got, want := res.status, http.StatusOK; got != want { 1000 t.Errorf("status: %v; want %v", got, want) 1001 } 1002} 1003 1004// TestH1H2GenerateVia tests that server generates Via header field to and 1005// from backend server. 1006func TestH1H2GenerateVia(t *testing.T) { 1007 opts := options{ 1008 args: []string{"--http2-bridge"}, 1009 handler: func(w http.ResponseWriter, r *http.Request) { 1010 if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { 1011 t.Errorf("Via: %v; want %v", got, want) 1012 } 1013 }, 1014 } 1015 st := newServerTester(t, opts) 1016 defer st.Close() 1017 1018 res, err := st.http1(requestParam{ 1019 name: "TestH1H2GenerateVia", 1020 }) 1021 if err != nil { 1022 t.Fatalf("Error st.http1() = %v", err) 1023 } 1024 if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { 1025 t.Errorf("Via: %v; want %v", got, want) 1026 } 1027} 1028 1029// TestH1H2AppendVia tests that server adds value to existing Via 1030// header field to and from backend server. 1031func TestH1H2AppendVia(t *testing.T) { 1032 opts := options{ 1033 args: []string{"--http2-bridge"}, 1034 handler: func(w http.ResponseWriter, r *http.Request) { 1035 if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { 1036 t.Errorf("Via: %v; want %v", got, want) 1037 } 1038 w.Header().Add("Via", "bar") 1039 }, 1040 } 1041 st := newServerTester(t, opts) 1042 defer st.Close() 1043 1044 res, err := st.http1(requestParam{ 1045 name: "TestH1H2AppendVia", 1046 header: []hpack.HeaderField{ 1047 pair("via", "foo"), 1048 }, 1049 }) 1050 if err != nil { 1051 t.Fatalf("Error st.http1() = %v", err) 1052 } 1053 if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { 1054 t.Errorf("Via: %v; want %v", got, want) 1055 } 1056} 1057 1058// TestH1H2NoVia tests that server does not add value to existing Via 1059// header field to and from backend server. 1060func TestH1H2NoVia(t *testing.T) { 1061 opts := options{ 1062 args: []string{"--http2-bridge", "--no-via"}, 1063 handler: func(w http.ResponseWriter, r *http.Request) { 1064 if got, want := r.Header.Get("Via"), "foo"; got != want { 1065 t.Errorf("Via: %v; want %v", got, want) 1066 } 1067 w.Header().Add("Via", "bar") 1068 }, 1069 } 1070 st := newServerTester(t, opts) 1071 defer st.Close() 1072 1073 res, err := st.http1(requestParam{ 1074 name: "TestH1H2NoVia", 1075 header: []hpack.HeaderField{ 1076 pair("via", "foo"), 1077 }, 1078 }) 1079 if err != nil { 1080 t.Fatalf("Error st.http1() = %v", err) 1081 } 1082 if got, want := res.header.Get("Via"), "bar"; got != want { 1083 t.Errorf("Via: %v; want %v", got, want) 1084 } 1085} 1086 1087// TestH1H2ReqPhaseReturn tests mruby request phase hook returns 1088// custom response. 1089func TestH1H2ReqPhaseReturn(t *testing.T) { 1090 opts := options{ 1091 args: []string{ 1092 "--http2-bridge", 1093 "--mruby-file=" + testDir + "/req-return.rb", 1094 }, 1095 handler: func(w http.ResponseWriter, r *http.Request) { 1096 t.Fatalf("request should not be forwarded") 1097 }, 1098 } 1099 st := newServerTester(t, opts) 1100 defer st.Close() 1101 1102 res, err := st.http1(requestParam{ 1103 name: "TestH1H2ReqPhaseReturn", 1104 }) 1105 if err != nil { 1106 t.Fatalf("Error st.http1() = %v", err) 1107 } 1108 1109 if got, want := res.status, http.StatusNotFound; got != want { 1110 t.Errorf("status = %v; want %v", got, want) 1111 } 1112 1113 hdtests := []struct { 1114 k, v string 1115 }{ 1116 {"content-length", "20"}, 1117 {"from", "mruby"}, 1118 } 1119 for _, tt := range hdtests { 1120 if got, want := res.header.Get(tt.k), tt.v; got != want { 1121 t.Errorf("%v = %v; want %v", tt.k, got, want) 1122 } 1123 } 1124 1125 if got, want := string(res.body), "Hello World from req"; got != want { 1126 t.Errorf("body = %v; want %v", got, want) 1127 } 1128} 1129 1130// TestH1H2RespPhaseReturn tests mruby response phase hook returns 1131// custom response. 1132func TestH1H2RespPhaseReturn(t *testing.T) { 1133 opts := options{ 1134 args: []string{ 1135 "--http2-bridge", 1136 "--mruby-file=" + testDir + "/resp-return.rb", 1137 }, 1138 } 1139 st := newServerTester(t, opts) 1140 defer st.Close() 1141 1142 res, err := st.http1(requestParam{ 1143 name: "TestH1H2RespPhaseReturn", 1144 }) 1145 if err != nil { 1146 t.Fatalf("Error st.http1() = %v", err) 1147 } 1148 1149 if got, want := res.status, http.StatusNotFound; got != want { 1150 t.Errorf("status = %v; want %v", got, want) 1151 } 1152 1153 hdtests := []struct { 1154 k, v string 1155 }{ 1156 {"content-length", "21"}, 1157 {"from", "mruby"}, 1158 } 1159 for _, tt := range hdtests { 1160 if got, want := res.header.Get(tt.k), tt.v; got != want { 1161 t.Errorf("%v = %v; want %v", tt.k, got, want) 1162 } 1163 } 1164 1165 if got, want := string(res.body), "Hello World from resp"; got != want { 1166 t.Errorf("body = %v; want %v", got, want) 1167 } 1168} 1169 1170// TestH1H2TE tests that "te: trailers" header is forwarded to HTTP/2 1171// backend server by stripping other encodings. 1172func TestH1H2TE(t *testing.T) { 1173 opts := options{ 1174 args: []string{"--http2-bridge"}, 1175 handler: func(w http.ResponseWriter, r *http.Request) { 1176 if got, want := r.Header.Get("te"), "trailers"; got != want { 1177 t.Errorf("te: %v; want %v", got, want) 1178 } 1179 }, 1180 } 1181 st := newServerTester(t, opts) 1182 defer st.Close() 1183 1184 res, err := st.http1(requestParam{ 1185 name: "TestH1H2TE", 1186 header: []hpack.HeaderField{ 1187 pair("te", "foo,trailers,bar"), 1188 }, 1189 }) 1190 if err != nil { 1191 t.Fatalf("Error st.http1() = %v", err) 1192 } 1193 if got, want := res.status, http.StatusOK; got != want { 1194 t.Errorf("status: %v; want %v", got, want) 1195 } 1196} 1197 1198// TestH1APIBackendconfig exercise backendconfig API endpoint routine 1199// for successful case. 1200func TestH1APIBackendconfig(t *testing.T) { 1201 opts := options{ 1202 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1203 handler: func(w http.ResponseWriter, r *http.Request) { 1204 t.Fatalf("request should not be forwarded") 1205 }, 1206 connectPort: 3010, 1207 } 1208 st := newServerTester(t, opts) 1209 defer st.Close() 1210 1211 res, err := st.http1(requestParam{ 1212 name: "TestH1APIBackendconfig", 1213 path: "/api/v1beta1/backendconfig", 1214 method: "PUT", 1215 body: []byte(`# comment 1216backend=127.0.0.1,3011 1217 1218`), 1219 }) 1220 if err != nil { 1221 t.Fatalf("Error st.http1() = %v", err) 1222 } 1223 if got, want := res.status, http.StatusOK; got != want { 1224 t.Errorf("res.status: %v; want %v", got, want) 1225 } 1226 1227 var apiResp APIResponse 1228 err = json.Unmarshal(res.body, &apiResp) 1229 if err != nil { 1230 t.Fatalf("Error unmarshaling API response: %v", err) 1231 } 1232 if got, want := apiResp.Status, "Success"; got != want { 1233 t.Errorf("apiResp.Status: %v; want %v", got, want) 1234 } 1235 if got, want := apiResp.Code, 200; got != want { 1236 t.Errorf("apiResp.Status: %v; want %v", got, want) 1237 } 1238} 1239 1240// TestH1APIBackendconfigQuery exercise backendconfig API endpoint 1241// routine with query. 1242func TestH1APIBackendconfigQuery(t *testing.T) { 1243 opts := options{ 1244 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1245 handler: func(w http.ResponseWriter, r *http.Request) { 1246 t.Fatalf("request should not be forwarded") 1247 }, 1248 connectPort: 3010, 1249 } 1250 st := newServerTester(t, opts) 1251 defer st.Close() 1252 1253 res, err := st.http1(requestParam{ 1254 name: "TestH1APIBackendconfigQuery", 1255 path: "/api/v1beta1/backendconfig?foo=bar", 1256 method: "PUT", 1257 body: []byte(`# comment 1258backend=127.0.0.1,3011 1259 1260`), 1261 }) 1262 if err != nil { 1263 t.Fatalf("Error st.http1() = %v", err) 1264 } 1265 if got, want := res.status, http.StatusOK; got != want { 1266 t.Errorf("res.status: %v; want %v", got, want) 1267 } 1268 1269 var apiResp APIResponse 1270 err = json.Unmarshal(res.body, &apiResp) 1271 if err != nil { 1272 t.Fatalf("Error unmarshaling API response: %v", err) 1273 } 1274 if got, want := apiResp.Status, "Success"; got != want { 1275 t.Errorf("apiResp.Status: %v; want %v", got, want) 1276 } 1277 if got, want := apiResp.Code, 200; got != want { 1278 t.Errorf("apiResp.Status: %v; want %v", got, want) 1279 } 1280} 1281 1282// TestH1APIBackendconfigBadMethod exercise backendconfig API endpoint 1283// routine with bad method. 1284func TestH1APIBackendconfigBadMethod(t *testing.T) { 1285 opts := options{ 1286 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1287 handler: func(w http.ResponseWriter, r *http.Request) { 1288 t.Fatalf("request should not be forwarded") 1289 }, 1290 connectPort: 3010, 1291 } 1292 st := newServerTester(t, opts) 1293 defer st.Close() 1294 1295 res, err := st.http1(requestParam{ 1296 name: "TestH1APIBackendconfigBadMethod", 1297 path: "/api/v1beta1/backendconfig", 1298 method: "GET", 1299 body: []byte(`# comment 1300backend=127.0.0.1,3011 1301 1302`), 1303 }) 1304 if err != nil { 1305 t.Fatalf("Error st.http1() = %v", err) 1306 } 1307 if got, want := res.status, http.StatusMethodNotAllowed; got != want { 1308 t.Errorf("res.status: %v; want %v", got, want) 1309 } 1310 1311 var apiResp APIResponse 1312 err = json.Unmarshal(res.body, &apiResp) 1313 if err != nil { 1314 t.Fatalf("Error unmarshaling API response: %v", err) 1315 } 1316 if got, want := apiResp.Status, "Failure"; got != want { 1317 t.Errorf("apiResp.Status: %v; want %v", got, want) 1318 } 1319 if got, want := apiResp.Code, 405; got != want { 1320 t.Errorf("apiResp.Status: %v; want %v", got, want) 1321 } 1322} 1323 1324// TestH1APIConfigrevision tests configrevision API. 1325func TestH1APIConfigrevision(t *testing.T) { 1326 opts := options{ 1327 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1328 handler: func(w http.ResponseWriter, r *http.Request) { 1329 t.Fatalf("request should not be forwarded") 1330 }, 1331 connectPort: 3010, 1332 } 1333 st := newServerTester(t, opts) 1334 defer st.Close() 1335 1336 res, err := st.http1(requestParam{ 1337 name: "TestH1APIConfigrevision", 1338 path: "/api/v1beta1/configrevision", 1339 method: "GET", 1340 }) 1341 if err != nil { 1342 t.Fatalf("Error st.http1() = %v", err) 1343 } 1344 if got, want := res.status, http.StatusOK; got != want { 1345 t.Errorf("res.status: %v; want = %v", got, want) 1346 } 1347 1348 var apiResp APIResponse 1349 d := json.NewDecoder(bytes.NewBuffer(res.body)) 1350 d.UseNumber() 1351 err = d.Decode(&apiResp) 1352 if err != nil { 1353 t.Fatalf("Error unmarshalling API response: %v", err) 1354 } 1355 if got, want := apiResp.Status, "Success"; got != want { 1356 t.Errorf("apiResp.Status: %v; want %v", got, want) 1357 } 1358 if got, want := apiResp.Code, 200; got != want { 1359 t.Errorf("apiResp.Status: %v; want %v", got, want) 1360 } 1361 if got, want := apiResp.Data["configRevision"], json.Number("0"); got != want { 1362 t.Errorf(`apiResp.Data["configRevision"]: %v %t; want %v`, got, got, want) 1363 } 1364} 1365 1366// TestH1APINotFound exercise backendconfig API endpoint routine when 1367// API endpoint is not found. 1368func TestH1APINotFound(t *testing.T) { 1369 opts := options{ 1370 args: []string{"-f127.0.0.1,3010;api;no-tls"}, 1371 handler: func(w http.ResponseWriter, r *http.Request) { 1372 t.Fatalf("request should not be forwarded") 1373 }, 1374 connectPort: 3010, 1375 } 1376 st := newServerTester(t, opts) 1377 defer st.Close() 1378 1379 res, err := st.http1(requestParam{ 1380 name: "TestH1APINotFound", 1381 path: "/api/notfound", 1382 method: "GET", 1383 body: []byte(`# comment 1384backend=127.0.0.1,3011 1385 1386`), 1387 }) 1388 if err != nil { 1389 t.Fatalf("Error st.http1() = %v", err) 1390 } 1391 if got, want := res.status, http.StatusNotFound; got != want { 1392 t.Errorf("res.status: %v; want %v", got, want) 1393 } 1394 1395 var apiResp APIResponse 1396 err = json.Unmarshal(res.body, &apiResp) 1397 if err != nil { 1398 t.Fatalf("Error unmarshaling API response: %v", err) 1399 } 1400 if got, want := apiResp.Status, "Failure"; got != want { 1401 t.Errorf("apiResp.Status: %v; want %v", got, want) 1402 } 1403 if got, want := apiResp.Code, 404; got != want { 1404 t.Errorf("apiResp.Status: %v; want %v", got, want) 1405 } 1406} 1407 1408// TestH1Healthmon tests health monitor endpoint. 1409func TestH1Healthmon(t *testing.T) { 1410 opts := options{ 1411 args: []string{"-f127.0.0.1,3011;healthmon;no-tls"}, 1412 handler: func(w http.ResponseWriter, r *http.Request) { 1413 t.Fatalf("request should not be forwarded") 1414 }, 1415 connectPort: 3011, 1416 } 1417 st := newServerTester(t, opts) 1418 defer st.Close() 1419 1420 res, err := st.http1(requestParam{ 1421 name: "TestH1Healthmon", 1422 path: "/alpha/bravo", 1423 }) 1424 if err != nil { 1425 t.Fatalf("Error st.http1() = %v", err) 1426 } 1427 if got, want := res.status, http.StatusOK; got != want { 1428 t.Errorf("res.status: %v; want %v", got, want) 1429 } 1430} 1431 1432// TestH1ResponseBeforeRequestEnd tests the situation where response 1433// ends before request body finishes. 1434func TestH1ResponseBeforeRequestEnd(t *testing.T) { 1435 opts := options{ 1436 args: []string{"--mruby-file=" + testDir + "/req-return.rb"}, 1437 handler: func(w http.ResponseWriter, r *http.Request) { 1438 t.Fatal("request should not be forwarded") 1439 }, 1440 } 1441 st := newServerTester(t, opts) 1442 defer st.Close() 1443 1444 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", 1445 st.authority)); err != nil { 1446 t.Fatalf("Error io.WriteString() = %v", err) 1447 } 1448 1449 resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) 1450 if err != nil { 1451 t.Fatalf("Error http.ReadResponse() = %v", err) 1452 } 1453 1454 defer resp.Body.Close() 1455 1456 if got, want := resp.StatusCode, http.StatusNotFound; got != want { 1457 t.Errorf("status: %v; want %v", got, want) 1458 } 1459} 1460 1461// TestH1H1ChunkedEndsPrematurely tests that an HTTP/1.1 request fails 1462// if the backend chunked encoded response ends prematurely. 1463func TestH1H1ChunkedEndsPrematurely(t *testing.T) { 1464 opts := options{ 1465 handler: func(w http.ResponseWriter, r *http.Request) { 1466 hj, ok := w.(http.Hijacker) 1467 if !ok { 1468 http.Error(w, "Could not hijack the connection", http.StatusInternalServerError) 1469 return 1470 } 1471 conn, bufrw, err := hj.Hijack() 1472 if err != nil { 1473 http.Error(w, err.Error(), http.StatusInternalServerError) 1474 return 1475 } 1476 defer conn.Close() 1477 if _, err := bufrw.WriteString("HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"); err != nil { 1478 t.Fatalf("Error bufrw.WriteString() = %v", err) 1479 } 1480 bufrw.Flush() 1481 }, 1482 } 1483 st := newServerTester(t, opts) 1484 defer st.Close() 1485 1486 _, err := st.http1(requestParam{ 1487 name: "TestH1H1ChunkedEndsPrematurely", 1488 }) 1489 if err == nil { 1490 t.Fatal("st.http1() should fail") 1491 } 1492} 1493