@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 947 lines 19.2 KB
Raw
1 package integration
2
3 import (
4 "net/http"
5 "net/url"
6 "testing"
7
8 "github.com/caddyserver/caddy/v2/caddytest"
9 )
10
11 func TestRespond(t *testing.T) {
12 // arrange
13 tester := caddytest.NewTester(t)
14 tester.InitServer(`
15 {
16 admin localhost:2999
17 http_port 9080
18 https_port 9443
19 grace_period 1ns
20 }
21
22 localhost:9080 {
23 respond /version 200 {
24 body "hello from localhost"
25 }
26 }
27 `, "caddyfile")
28
29 // act and assert
30 tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost")
31 }
32
33 func TestRedirect(t *testing.T) {
34 // arrange
35 tester := caddytest.NewTester(t)
36 tester.InitServer(`
37 {
38 admin localhost:2999
39 http_port 9080
40 https_port 9443
41 grace_period 1ns
42 }
43
44 localhost:9080 {
45
46 redir / http://localhost:9080/hello 301
47
48 respond /hello 200 {
49 body "hello from localhost"
50 }
51 }
52 `, "caddyfile")
53
54 // act and assert
55 tester.AssertRedirect("http://localhost:9080/", "http://localhost:9080/hello", 301)
56
57 // follow redirect
58 tester.AssertGetResponse("http://localhost:9080/", 200, "hello from localhost")
59 }
60
61 func TestDuplicateHosts(t *testing.T) {
62 // act and assert
63 caddytest.AssertLoadError(t,
64 `
65 localhost:9080 {
66 }
67
68 localhost:9080 {
69 }
70 `,
71 "caddyfile",
72 "ambiguous site definition")
73 }
74
75 func TestReadCookie(t *testing.T) {
76 localhost, _ := url.Parse("http://localhost")
77 cookie := http.Cookie{
78 Name: "clientname",
79 Value: "caddytest",
80 }
81
82 // arrange
83 tester := caddytest.NewTester(t)
84 tester.Client.Jar.SetCookies(localhost, []*http.Cookie{&cookie})
85 tester.InitServer(`
86 {
87 skip_install_trust
88 admin localhost:2999
89 http_port 9080
90 https_port 9443
91 grace_period 1ns
92 }
93
94 localhost:9080 {
95 templates {
96 root testdata
97 }
98 file_server {
99 root testdata
100 }
101 }
102 `, "caddyfile")
103
104 // act and assert
105 tester.AssertGetResponse("http://localhost:9080/cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
106 }
107
108 func TestReplIndex(t *testing.T) {
109 tester := caddytest.NewTester(t)
110 tester.InitServer(`
111 {
112 skip_install_trust
113 admin localhost:2999
114 http_port 9080
115 https_port 9443
116 grace_period 1ns
117 }
118
119 localhost:9080 {
120 templates {
121 root testdata
122 }
123 file_server {
124 root testdata
125 index "index.{host}.html"
126 }
127 }
128 `, "caddyfile")
129
130 // act and assert
131 tester.AssertGetResponse("http://localhost:9080/", 200, "")
132 }
133
134 func TestInvalidPrefix(t *testing.T) {
135 type testCase struct {
136 config, expectedError string
137 }
138
139 failureCases := []testCase{
140 {
141 config: `wss://localhost`,
142 expectedError: `the scheme wss:// is only supported in browsers; use https:// instead`,
143 },
144 {
145 config: `ws://localhost`,
146 expectedError: `the scheme ws:// is only supported in browsers; use http:// instead`,
147 },
148 {
149 config: `someInvalidPrefix://localhost`,
150 expectedError: "unsupported URL scheme someinvalidprefix://",
151 },
152 {
153 config: `h2c://localhost`,
154 expectedError: `unsupported URL scheme h2c://`,
155 },
156 {
157 config: `localhost, wss://localhost`,
158 expectedError: `the scheme wss:// is only supported in browsers; use https:// instead`,
159 },
160 {
161 config: `localhost {
162 reverse_proxy ws://localhost"
163 }`,
164 expectedError: `the scheme ws:// is only supported in browsers; use http:// instead`,
165 },
166 {
167 config: `localhost {
168 reverse_proxy someInvalidPrefix://localhost"
169 }`,
170 expectedError: `unsupported URL scheme someinvalidprefix://`,
171 },
172 }
173
174 for _, failureCase := range failureCases {
175 caddytest.AssertLoadError(t, failureCase.config, "caddyfile", failureCase.expectedError)
176 }
177 }
178
179 func TestValidPrefix(t *testing.T) {
180 type testCase struct {
181 rawConfig, expectedResponse string
182 }
183
184 successCases := []testCase{
185 {
186 "localhost",
187 `{
188 "apps": {
189 "http": {
190 "servers": {
191 "srv0": {
192 "listen": [
193 ":443"
194 ],
195 "routes": [
196 {
197 "match": [
198 {
199 "host": [
200 "localhost"
201 ]
202 }
203 ],
204 "terminal": true
205 }
206 ]
207 }
208 }
209 }
210 }
211 }`,
212 },
213 {
214 "https://localhost",
215 `{
216 "apps": {
217 "http": {
218 "servers": {
219 "srv0": {
220 "listen": [
221 ":443"
222 ],
223 "routes": [
224 {
225 "match": [
226 {
227 "host": [
228 "localhost"
229 ]
230 }
231 ],
232 "terminal": true
233 }
234 ]
235 }
236 }
237 }
238 }
239 }`,
240 },
241 {
242 "http://localhost",
243 `{
244 "apps": {
245 "http": {
246 "servers": {
247 "srv0": {
248 "listen": [
249 ":80"
250 ],
251 "routes": [
252 {
253 "match": [
254 {
255 "host": [
256 "localhost"
257 ]
258 }
259 ],
260 "terminal": true
261 }
262 ]
263 }
264 }
265 }
266 }
267 }`,
268 },
269 {
270 `localhost {
271 reverse_proxy http://localhost:3000
272 }`,
273 `{
274 "apps": {
275 "http": {
276 "servers": {
277 "srv0": {
278 "listen": [
279 ":443"
280 ],
281 "routes": [
282 {
283 "match": [
284 {
285 "host": [
286 "localhost"
287 ]
288 }
289 ],
290 "handle": [
291 {
292 "handler": "subroute",
293 "routes": [
294 {
295 "handle": [
296 {
297 "handler": "reverse_proxy",
298 "upstreams": [
299 {
300 "dial": "localhost:3000"
301 }
302 ]
303 }
304 ]
305 }
306 ]
307 }
308 ],
309 "terminal": true
310 }
311 ]
312 }
313 }
314 }
315 }
316 }`,
317 },
318 {
319 `localhost {
320 reverse_proxy https://localhost:3000
321 }`,
322 `{
323 "apps": {
324 "http": {
325 "servers": {
326 "srv0": {
327 "listen": [
328 ":443"
329 ],
330 "routes": [
331 {
332 "match": [
333 {
334 "host": [
335 "localhost"
336 ]
337 }
338 ],
339 "handle": [
340 {
341 "handler": "subroute",
342 "routes": [
343 {
344 "handle": [
345 {
346 "handler": "reverse_proxy",
347 "transport": {
348 "protocol": "http",
349 "tls": {}
350 },
351 "upstreams": [
352 {
353 "dial": "localhost:3000"
354 }
355 ]
356 }
357 ]
358 }
359 ]
360 }
361 ],
362 "terminal": true
363 }
364 ]
365 }
366 }
367 }
368 }
369 }`,
370 },
371 {
372 `localhost {
373 reverse_proxy h2c://localhost:3000
374 }`,
375 `{
376 "apps": {
377 "http": {
378 "servers": {
379 "srv0": {
380 "listen": [
381 ":443"
382 ],
383 "routes": [
384 {
385 "match": [
386 {
387 "host": [
388 "localhost"
389 ]
390 }
391 ],
392 "handle": [
393 {
394 "handler": "subroute",
395 "routes": [
396 {
397 "handle": [
398 {
399 "handler": "reverse_proxy",
400 "transport": {
401 "protocol": "http",
402 "versions": [
403 "h2c",
404 "2"
405 ]
406 },
407 "upstreams": [
408 {
409 "dial": "localhost:3000"
410 }
411 ]
412 }
413 ]
414 }
415 ]
416 }
417 ],
418 "terminal": true
419 }
420 ]
421 }
422 }
423 }
424 }
425 }`,
426 },
427 {
428 `localhost {
429 reverse_proxy localhost:3000
430 }`,
431 `{
432 "apps": {
433 "http": {
434 "servers": {
435 "srv0": {
436 "listen": [
437 ":443"
438 ],
439 "routes": [
440 {
441 "match": [
442 {
443 "host": [
444 "localhost"
445 ]
446 }
447 ],
448 "handle": [
449 {
450 "handler": "subroute",
451 "routes": [
452 {
453 "handle": [
454 {
455 "handler": "reverse_proxy",
456 "upstreams": [
457 {
458 "dial": "localhost:3000"
459 }
460 ]
461 }
462 ]
463 }
464 ]
465 }
466 ],
467 "terminal": true
468 }
469 ]
470 }
471 }
472 }
473 }
474 }`,
475 },
476 }
477
478 for _, successCase := range successCases {
479 caddytest.AssertAdapt(t, successCase.rawConfig, "caddyfile", successCase.expectedResponse)
480 }
481 }
482
483 func TestUriReplace(t *testing.T) {
484 tester := caddytest.NewTester(t)
485
486 tester.InitServer(`
487 {
488 admin localhost:2999
489 http_port 9080
490 }
491 :9080
492 uri replace "\}" %7D
493 uri replace "\{" %7B
494
495 respond "{query}"`, "caddyfile")
496
497 tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
498 }
499
500 func TestUriOps(t *testing.T) {
501 tester := caddytest.NewTester(t)
502
503 tester.InitServer(`
504 {
505 admin localhost:2999
506 http_port 9080
507 }
508 :9080
509 uri query +foo bar
510 uri query -baz
511 uri query taz test
512 uri query key=value example
513 uri query changethis>changed
514
515 respond "{query}"`, "caddyfile")
516
517 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
518 }
519
520 // Tests the `http.request.local.port` placeholder.
521 // We don't test the very similar `http.request.local.host` placeholder,
522 // because depending on the host the test is running on, localhost might
523 // refer to 127.0.0.1 or ::1.
524 // TODO: Test each http version separately (especially http/3)
525 func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
526 tester := caddytest.NewTester(t)
527
528 tester.InitServer(`
529 {
530 admin localhost:2999
531 http_port 9080
532 }
533 :9080
534 respond "{http.request.local.port}"`, "caddyfile")
535
536 tester.AssertGetResponse("http://localhost:9080/", 200, "9080")
537 }
538
539 func TestSetThenAddQueryParams(t *testing.T) {
540 tester := caddytest.NewTester(t)
541
542 tester.InitServer(`
543 {
544 admin localhost:2999
545 http_port 9080
546 }
547 :9080
548 uri query foo bar
549 uri query +foo baz
550
551 respond "{query}"`, "caddyfile")
552
553 tester.AssertGetResponse("http://localhost:9080/endpoint", 200, "foo=bar&foo=baz")
554 }
555
556 func TestSetThenDeleteParams(t *testing.T) {
557 tester := caddytest.NewTester(t)
558
559 tester.InitServer(`
560 {
561 admin localhost:2999
562 http_port 9080
563 }
564 :9080
565 uri query bar foo{query.foo}
566 uri query -foo
567
568 respond "{query}"`, "caddyfile")
569
570 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=foobar")
571 }
572
573 func TestRenameAndOtherOps(t *testing.T) {
574 tester := caddytest.NewTester(t)
575
576 tester.InitServer(`
577 {
578 admin localhost:2999
579 http_port 9080
580 }
581 :9080
582 uri query foo>bar
583 uri query bar taz
584 uri query +bar baz
585
586 respond "{query}"`, "caddyfile")
587
588 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
589 }
590
591 func TestReplaceOps(t *testing.T) {
592 tester := caddytest.NewTester(t)
593
594 tester.InitServer(`
595 {
596 admin localhost:2999
597 http_port 9080
598 }
599 :9080
600 uri query foo bar baz
601 respond "{query}"`, "caddyfile")
602
603 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
604 }
605
606 func TestReplaceWithReplacementPlaceholder(t *testing.T) {
607 tester := caddytest.NewTester(t)
608 tester.InitServer(`
609 {
610 admin localhost:2999
611 http_port 9080
612 }
613 :9080
614 uri query foo bar {query.placeholder}
615 respond "{query}"`, "caddyfile")
616
617 tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
618 }
619
620 func TestReplaceWithKeyPlaceholder(t *testing.T) {
621 tester := caddytest.NewTester(t)
622 tester.InitServer(`
623 {
624 admin localhost:2999
625 http_port 9080
626 }
627 :9080
628 uri query {query.placeholder} bar baz
629 respond "{query}"`, "caddyfile")
630
631 tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
632 }
633
634 func TestPartialReplacement(t *testing.T) {
635 tester := caddytest.NewTester(t)
636 tester.InitServer(`
637 {
638 admin localhost:2999
639 http_port 9080
640 }
641 :9080
642 uri query foo ar az
643 respond "{query}"`, "caddyfile")
644
645 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
646 }
647
648 func TestNonExistingSearch(t *testing.T) {
649 tester := caddytest.NewTester(t)
650 tester.InitServer(`
651 {
652 admin localhost:2999
653 http_port 9080
654 }
655 :9080
656 uri query foo var baz
657 respond "{query}"`, "caddyfile")
658
659 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
660 }
661
662 func TestReplaceAllOps(t *testing.T) {
663 tester := caddytest.NewTester(t)
664
665 tester.InitServer(`
666 {
667 admin localhost:2999
668 http_port 9080
669 }
670 :9080
671 uri query * bar baz
672 respond "{query}"`, "caddyfile")
673
674 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
675 }
676
677 func TestUriOpsBlock(t *testing.T) {
678 tester := caddytest.NewTester(t)
679
680 tester.InitServer(`
681 {
682 admin localhost:2999
683 http_port 9080
684 }
685 :9080
686 uri query {
687 +foo bar
688 -baz
689 taz test
690 }
691 respond "{query}"`, "caddyfile")
692
693 tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
694 }
695
696 func TestHandleErrorSimpleCodes(t *testing.T) {
697 tester := caddytest.NewTester(t)
698 tester.InitServer(`{
699 admin localhost:2999
700 http_port 9080
701 }
702 localhost:9080 {
703 root * /srv
704 error /private* "Unauthorized" 410
705 error /hidden* "Not found" 404
706
707 handle_errors 404 410 {
708 respond "404 or 410 error"
709 }
710 }`, "caddyfile")
711 // act and assert
712 tester.AssertGetResponse("http://localhost:9080/private", 410, "404 or 410 error")
713 tester.AssertGetResponse("http://localhost:9080/hidden", 404, "404 or 410 error")
714 }
715
716 func TestHandleErrorRange(t *testing.T) {
717 tester := caddytest.NewTester(t)
718 tester.InitServer(`{
719 admin localhost:2999
720 http_port 9080
721 }
722 localhost:9080 {
723 root * /srv
724 error /private* "Unauthorized" 410
725 error /hidden* "Not found" 404
726
727 handle_errors 4xx {
728 respond "Error in the [400 .. 499] range"
729 }
730 }`, "caddyfile")
731 // act and assert
732 tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
733 tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
734 }
735
736 func TestHandleErrorSort(t *testing.T) {
737 tester := caddytest.NewTester(t)
738 tester.InitServer(`{
739 admin localhost:2999
740 http_port 9080
741 }
742 localhost:9080 {
743 root * /srv
744 error /private* "Unauthorized" 410
745 error /hidden* "Not found" 404
746 error /internalerr* "Internal Server Error" 500
747
748 handle_errors {
749 respond "Fallback route: code outside the [400..499] range"
750 }
751 handle_errors 4xx {
752 respond "Error in the [400 .. 499] range"
753 }
754 }`, "caddyfile")
755 // act and assert
756 tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Fallback route: code outside the [400..499] range")
757 tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
758 }
759
760 func TestHandleErrorRangeAndCodes(t *testing.T) {
761 tester := caddytest.NewTester(t)
762 tester.InitServer(`{
763 admin localhost:2999
764 http_port 9080
765 }
766 localhost:9080 {
767 root * /srv
768 error /private* "Unauthorized" 410
769 error /threehundred* "Moved Permanently" 301
770 error /internalerr* "Internal Server Error" 500
771
772 handle_errors 500 3xx {
773 respond "Error code is equal to 500 or in the [300..399] range"
774 }
775 handle_errors 4xx {
776 respond "Error in the [400 .. 499] range"
777 }
778 }`, "caddyfile")
779 // act and assert
780 tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
781 tester.AssertGetResponse("http://localhost:9080/threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
782 tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
783 }
784
785 func TestHandleErrorOverlappingCodesKeepOrder(t *testing.T) {
786 tester := caddytest.NewTester(t)
787 tester.InitServer(`{
788 admin localhost:2999
789 http_port 9080
790 }
791 localhost:9080 {
792 root * /srv
793 error /private* "Unauthorized" 410
794 error /hidden* "Not found" 404
795 error /internalerr* "Internal Server Error" 500
796
797 handle_errors 404 {
798 respond "Not found page"
799 }
800 handle_errors 4xx {
801 respond "Error in the [400 .. 499] range"
802 }
803 handle_errors {
804 respond "First fallback"
805 }
806 handle_errors {
807 respond "Second fallback"
808 }
809 }`, "caddyfile")
810 // act and assert
811 tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Not found page")
812 tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
813 tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "First fallback")
814 }
815
816 func TestHandleErrorSubHandlers(t *testing.T) {
817 tester := caddytest.NewTester(t)
818 tester.InitServer(`{
819 admin localhost:2999
820 http_port 9080
821 }
822 localhost:9080 {
823 root * /srv
824 file_server
825 error /*/internalerr* "Internal Server Error" 500
826
827 handle_errors 404 {
828 handle /en/* {
829 respond "not found" 404
830 }
831 handle /es/* {
832 respond "no encontrado" 404
833 }
834 handle {
835 respond "default not found"
836 }
837 }
838 handle_errors {
839 handle {
840 respond "Default error"
841 }
842 handle /en/* {
843 respond "English error"
844 }
845 }
846 }
847 `, "caddyfile")
848 // act and assert
849 tester.AssertGetResponse("http://localhost:9080/en/notfound", 404, "not found")
850 tester.AssertGetResponse("http://localhost:9080/es/notfound", 404, "no encontrado")
851 tester.AssertGetResponse("http://localhost:9080/notfound", 404, "default not found")
852 tester.AssertGetResponse("http://localhost:9080/es/internalerr", 500, "Default error")
853 tester.AssertGetResponse("http://localhost:9080/en/internalerr", 500, "English error")
854 }
855
856 func TestHeaderDefaults(t *testing.T) {
857 tester := caddytest.NewTester(t)
858 tester.InitServer(`{
859 admin localhost:2999
860 http_port 9080
861 }
862 localhost:9080 {
863 header Cache-Control "no-store"
864 header {
865 ?Cache-Control "max-age=60"
866 ?X-Frame-Options "DENY"
867 }
868 header {
869 match status 404
870 ?X-Robots-Tag "noindex"
871 }
872 header {
873 ?X-Dup "first"
874 ?X-Dup "second"
875 }
876 header {
877 match header !X-Absent
878 ?X-Absent "fallback"
879 }
880 respond /missing 404
881 respond "ok"
882 }
883 `, "caddyfile")
884
885 resp, _ := tester.AssertGetResponse("http://localhost:9080/", 200, "ok")
886 if got := resp.Header.Get("Cache-Control"); got != "no-store" {
887 t.Errorf("Cache-Control = %q, want %q", got, "no-store")
888 }
889 if got := resp.Header.Get("X-Frame-Options"); got != "DENY" {
890 t.Errorf("X-Frame-Options = %q, want %q", got, "DENY")
891 }
892 if got := resp.Header.Get("X-Robots-Tag"); got != "" {
893 t.Errorf("X-Robots-Tag on a 200 = %q, want none", got)
894 }
895 if got := resp.Header.Get("X-Dup"); got != "second" {
896 t.Errorf("X-Dup = %q, want %q", got, "second")
897 }
898 if got := resp.Header.Get("X-Absent"); got != "fallback" {
899 t.Errorf("X-Absent = %q, want %q", got, "fallback")
900 }
901
902 resp, _ = tester.AssertGetResponse("http://localhost:9080/missing", 404, "")
903 if got := resp.Header.Get("X-Robots-Tag"); got != "noindex" {
904 t.Errorf("X-Robots-Tag on a 404 = %q, want %q", got, "noindex")
905 }
906 }
907
908 func TestHeaderDefaultMatcherSameField(t *testing.T) {
909 caddytest.AssertLoadError(t, `{
910 admin localhost:2999
911 http_port 9080
912 }
913 localhost:9080 {
914 header {
915 match header X-Frame-Options SAMEORIGIN
916 ?x-frame-options "DENY"
917 }
918 }
919 `, "caddyfile", "cannot match on response header X-Frame-Options and also set a default for it")
920 }
921
922 func TestInvalidSiteAddressesAsDirectives(t *testing.T) {
923 type testCase struct {
924 config, expectedError string
925 }
926
927 failureCases := []testCase{
928 {
929 config: `
930 handle {
931 file_server
932 }`,
933 expectedError: `Caddyfile:2: parsed 'handle' as a site address, but it is a known directive; directives must appear in a site block`,
934 },
935 {
936 config: `
937 reverse_proxy localhost:9000 localhost:9001 {
938 file_server
939 }`,
940 expectedError: `Caddyfile:2: parsed 'reverse_proxy' as a site address, but it is a known directive; directives must appear in a site block`,
941 },
942 }
943
944 for _, failureCase := range failureCases {
945 caddytest.AssertLoadError(t, failureCase.config, "caddyfile", failureCase.expectedError)
946 }
947 }