• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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