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