Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 18:15 UTC
httpcaddyfile: Add optional status code argument to `handle_errors` directive (#5965)
Co-authored-by: Aziz Rmadi <azizrmadi@Azizs-MacBook-Air.local>
Aziz Rmadi committed
Jan 16, 2024 at 00:24 UTC
4181c79a8130a59c40c76437e15265452422ccb1
8 files changed
+942
-2
caddyconfig/httpcaddyfile/builtins.go
+58
-1
@@ -844,10 +844,67 @@ func parseHandle(h Helper) (caddyhttp.MiddlewareHandler, error) {
844
}
845
846
func parseHandleErrors(h Helper) ([]ConfigValue, error) {
847
- subroute, err := ParseSegmentAsSubroute(h)
847
+ h.Next()
848
+ args := h.RemainingArgs()
849
+ expression := ""
850
+ if len(args) > 0 {
851
+ expression = ""
852
+ codes := []string{}
853
+ for _, val := range args {
854
+ if len(val) != 3 {
855
+ return nil, h.Errf("bad status value '%s'", val)
856
+ }
857
+ if strings.HasSuffix(val, "xx") {
858
+ val = val[:1]
859
+ _, err := strconv.Atoi(val)
860
+ if err != nil {
861
+ return nil, h.Errf("bad status value '%s': %v", val, err)
862
+ }
863
+ if expression != "" {
864
+ expression += " || "
865
+ }
866
+ expression += fmt.Sprintf("{http.error.status_code} >= %s00 && {http.error.status_code} <= %s99", val, val)
867
+ continue
868
+ }
869
+ _, err := strconv.Atoi(val)
870
+ if err != nil {
871
+ return nil, h.Errf("bad status value '%s': %v", val, err)
872
+ }
873
+ codes = append(codes, val)
874
+ }
875
+ if len(codes) > 0 {
876
+ if expression != "" {
877
+ expression += " || "
878
+ }
879
+ expression += "{http.error.status_code} in [" + strings.Join(codes, ", ") + "]"
880
+ }
881
+ // Reset cursor position to get ready for ParseSegmentAsSubroute
882
+ h.Reset()
883
+ h.Next()
884
+ h.RemainingArgs()
885
+ h.Prev()
886
+ } else {
887
+ // If no arguments present reset the cursor position to get ready for ParseSegmentAsSubroute
888
+ h.Prev()
889
+ }
890
+
891
+ handler, err := ParseSegmentAsSubroute(h)
892
if err != nil {
893
return nil, err
894
}
895
+ subroute, ok := handler.(*caddyhttp.Subroute)
896
+ if !ok {
897
+ return nil, h.Errf("segment was not parsed as a subroute")
898
+ }
899
+
900
+ if expression != "" {
901
+ statusMatcher := caddy.ModuleMap{
902
+ "expression": h.JSON(caddyhttp.MatchExpression{Expr: expression}),
903
+ }
904
+ for i := range subroute.Routes {
905
+ subroute.Routes[i].MatcherSetsRaw = []caddy.ModuleMap{statusMatcher}
906
+ }
907
+ }
908
return []ConfigValue{
909
{
910
Class: "error_route",
caddyconfig/httpcaddyfile/httptype.go
+10
-1
@@ -774,10 +774,19 @@ func (st *ServerType) serversFromPairings(
774
if srv.Errors == nil {
775
srv.Errors = new(caddyhttp.HTTPErrorConfig)
776
}
777
+ sort.SliceStable(errorSubrouteVals, func(i, j int) bool {
778
+ sri, srj := errorSubrouteVals[i].Value.(*caddyhttp.Subroute), errorSubrouteVals[j].Value.(*caddyhttp.Subroute)
779
+ if len(sri.Routes[0].MatcherSetsRaw) == 0 && len(srj.Routes[0].MatcherSetsRaw) != 0 {
780
+ return false
781
+ }
782
+ return true
783
+ })
784
+ errorsSubroute := &caddyhttp.Subroute{}
785
for _, val := range errorSubrouteVals {
786
sr := val.Value.(*caddyhttp.Subroute)
779
- srv.Errors.Routes = appendSubrouteToRouteList(srv.Errors.Routes, sr, matcherSetsEnc, p, warnings)
787
+ errorsSubroute.Routes = append(errorsSubroute.Routes, sr.Routes...)
788
}
789
+ srv.Errors.Routes = appendSubrouteToRouteList(srv.Errors.Routes, errorsSubroute, matcherSetsEnc, p, warnings)
790
}
791
792
// add log associations
caddytest/integration/caddyfile_adapt/error_multi_site_blocks.txt
new
+245
@@ -0,0 +1,245 @@
1
+foo.localhost {
2
+ root * /srv
3
+ error /private* "Unauthorized" 410
4
+ error /fivehundred* "Internal Server Error" 500
5
+
6
+ handle_errors 5xx {
7
+ respond "Error In range [500 .. 599]"
8
+ }
9
+ handle_errors 410 {
10
+ respond "404 or 410 error"
11
+ }
12
+}
13
+
14
+bar.localhost {
15
+ root * /srv
16
+ error /private* "Unauthorized" 410
17
+ error /fivehundred* "Internal Server Error" 500
18
+
19
+ handle_errors 5xx {
20
+ respond "Error In range [500 .. 599] from second site"
21
+ }
22
+ handle_errors 410 {
23
+ respond "404 or 410 error from second site"
24
+ }
25
+}
26
+----------
27
+{
28
+ "apps": {
29
+ "http": {
30
+ "servers": {
31
+ "srv0": {
32
+ "listen": [
33
+ ":443"
34
+ ],
35
+ "routes": [
36
+ {
37
+ "match": [
38
+ {
39
+ "host": [
40
+ "foo.localhost"
41
+ ]
42
+ }
43
+ ],
44
+ "handle": [
45
+ {
46
+ "handler": "subroute",
47
+ "routes": [
48
+ {
49
+ "handle": [
50
+ {
51
+ "handler": "vars",
52
+ "root": "/srv"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "handle": [
58
+ {
59
+ "error": "Internal Server Error",
60
+ "handler": "error",
61
+ "status_code": 500
62
+ }
63
+ ],
64
+ "match": [
65
+ {
66
+ "path": [
67
+ "/fivehundred*"
68
+ ]
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ "handle": [
74
+ {
75
+ "error": "Unauthorized",
76
+ "handler": "error",
77
+ "status_code": 410
78
+ }
79
+ ],
80
+ "match": [
81
+ {
82
+ "path": [
83
+ "/private*"
84
+ ]
85
+ }
86
+ ]
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ "terminal": true
92
+ },
93
+ {
94
+ "match": [
95
+ {
96
+ "host": [
97
+ "bar.localhost"
98
+ ]
99
+ }
100
+ ],
101
+ "handle": [
102
+ {
103
+ "handler": "subroute",
104
+ "routes": [
105
+ {
106
+ "handle": [
107
+ {
108
+ "handler": "vars",
109
+ "root": "/srv"
110
+ }
111
+ ]
112
+ },
113
+ {
114
+ "handle": [
115
+ {
116
+ "error": "Internal Server Error",
117
+ "handler": "error",
118
+ "status_code": 500
119
+ }
120
+ ],
121
+ "match": [
122
+ {
123
+ "path": [
124
+ "/fivehundred*"
125
+ ]
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "handle": [
131
+ {
132
+ "error": "Unauthorized",
133
+ "handler": "error",
134
+ "status_code": 410
135
+ }
136
+ ],
137
+ "match": [
138
+ {
139
+ "path": [
140
+ "/private*"
141
+ ]
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ }
147
+ ],
148
+ "terminal": true
149
+ }
150
+ ],
151
+ "errors": {
152
+ "routes": [
153
+ {
154
+ "match": [
155
+ {
156
+ "host": [
157
+ "foo.localhost"
158
+ ]
159
+ }
160
+ ],
161
+ "handle": [
162
+ {
163
+ "handler": "subroute",
164
+ "routes": [
165
+ {
166
+ "handle": [
167
+ {
168
+ "body": "404 or 410 error",
169
+ "handler": "static_response"
170
+ }
171
+ ],
172
+ "match": [
173
+ {
174
+ "expression": "{http.error.status_code} in [410]"
175
+ }
176
+ ]
177
+ },
178
+ {
179
+ "handle": [
180
+ {
181
+ "body": "Error In range [500 .. 599]",
182
+ "handler": "static_response"
183
+ }
184
+ ],
185
+ "match": [
186
+ {
187
+ "expression": "{http.error.status_code} \u003e= 500 \u0026\u0026 {http.error.status_code} \u003c= 599"
188
+ }
189
+ ]
190
+ }
191
+ ]
192
+ }
193
+ ],
194
+ "terminal": true
195
+ },
196
+ {
197
+ "match": [
198
+ {
199
+ "host": [
200
+ "bar.localhost"
201
+ ]
202
+ }
203
+ ],
204
+ "handle": [
205
+ {
206
+ "handler": "subroute",
207
+ "routes": [
208
+ {
209
+ "handle": [
210
+ {
211
+ "body": "404 or 410 error from second site",
212
+ "handler": "static_response"
213
+ }
214
+ ],
215
+ "match": [
216
+ {
217
+ "expression": "{http.error.status_code} in [410]"
218
+ }
219
+ ]
220
+ },
221
+ {
222
+ "handle": [
223
+ {
224
+ "body": "Error In range [500 .. 599] from second site",
225
+ "handler": "static_response"
226
+ }
227
+ ],
228
+ "match": [
229
+ {
230
+ "expression": "{http.error.status_code} \u003e= 500 \u0026\u0026 {http.error.status_code} \u003c= 599"
231
+ }
232
+ ]
233
+ }
234
+ ]
235
+ }
236
+ ],
237
+ "terminal": true
238
+ }
239
+ ]
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+}
caddytest/integration/caddyfile_adapt/error_range_codes.txt
new
+120
@@ -0,0 +1,120 @@
1
+{
2
+ http_port 3010
3
+}
4
+localhost:3010 {
5
+ root * /srv
6
+ error /private* "Unauthorized" 410
7
+ error /hidden* "Not found" 404
8
+
9
+ handle_errors 4xx {
10
+ respond "Error in the [400 .. 499] range"
11
+ }
12
+}
13
+----------
14
+{
15
+ "apps": {
16
+ "http": {
17
+ "http_port": 3010,
18
+ "servers": {
19
+ "srv0": {
20
+ "listen": [
21
+ ":3010"
22
+ ],
23
+ "routes": [
24
+ {
25
+ "match": [
26
+ {
27
+ "host": [
28
+ "localhost"
29
+ ]
30
+ }
31
+ ],
32
+ "handle": [
33
+ {
34
+ "handler": "subroute",
35
+ "routes": [
36
+ {
37
+ "handle": [
38
+ {
39
+ "handler": "vars",
40
+ "root": "/srv"
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "handle": [
46
+ {
47
+ "error": "Unauthorized",
48
+ "handler": "error",
49
+ "status_code": 410
50
+ }
51
+ ],
52
+ "match": [
53
+ {
54
+ "path": [
55
+ "/private*"
56
+ ]
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "handle": [
62
+ {
63
+ "error": "Not found",
64
+ "handler": "error",
65
+ "status_code": 404
66
+ }
67
+ ],
68
+ "match": [
69
+ {
70
+ "path": [
71
+ "/hidden*"
72
+ ]
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "terminal": true
80
+ }
81
+ ],
82
+ "errors": {
83
+ "routes": [
84
+ {
85
+ "match": [
86
+ {
87
+ "host": [
88
+ "localhost"
89
+ ]
90
+ }
91
+ ],
92
+ "handle": [
93
+ {
94
+ "handler": "subroute",
95
+ "routes": [
96
+ {
97
+ "handle": [
98
+ {
99
+ "body": "Error in the [400 .. 499] range",
100
+ "handler": "static_response"
101
+ }
102
+ ],
103
+ "match": [
104
+ {
105
+ "expression": "{http.error.status_code} \u003e= 400 \u0026\u0026 {http.error.status_code} \u003c= 499"
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ }
111
+ ],
112
+ "terminal": true
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+}
caddytest/integration/caddyfile_adapt/error_range_simple_codes.txt
new
+153
@@ -0,0 +1,153 @@
1
+{
2
+ http_port 2099
3
+}
4
+localhost:2099 {
5
+ root * /srv
6
+ error /private* "Unauthorized" 410
7
+ error /threehundred* "Moved Permanently" 301
8
+ error /internalerr* "Internal Server Error" 500
9
+
10
+ handle_errors 500 3xx {
11
+ respond "Error code is equal to 500 or in the [300..399] range"
12
+ }
13
+ handle_errors 4xx {
14
+ respond "Error in the [400 .. 499] range"
15
+ }
16
+}
17
+----------
18
+{
19
+ "apps": {
20
+ "http": {
21
+ "http_port": 2099,
22
+ "servers": {
23
+ "srv0": {
24
+ "listen": [
25
+ ":2099"
26
+ ],
27
+ "routes": [
28
+ {
29
+ "match": [
30
+ {
31
+ "host": [
32
+ "localhost"
33
+ ]
34
+ }
35
+ ],
36
+ "handle": [
37
+ {
38
+ "handler": "subroute",
39
+ "routes": [
40
+ {
41
+ "handle": [
42
+ {
43
+ "handler": "vars",
44
+ "root": "/srv"
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "handle": [
50
+ {
51
+ "error": "Moved Permanently",
52
+ "handler": "error",
53
+ "status_code": 301
54
+ }
55
+ ],
56
+ "match": [
57
+ {
58
+ "path": [
59
+ "/threehundred*"
60
+ ]
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "handle": [
66
+ {
67
+ "error": "Internal Server Error",
68
+ "handler": "error",
69
+ "status_code": 500
70
+ }
71
+ ],
72
+ "match": [
73
+ {
74
+ "path": [
75
+ "/internalerr*"
76
+ ]
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "handle": [
82
+ {
83
+ "error": "Unauthorized",
84
+ "handler": "error",
85
+ "status_code": 410
86
+ }
87
+ ],
88
+ "match": [
89
+ {
90
+ "path": [
91
+ "/private*"
92
+ ]
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+ ],
99
+ "terminal": true
100
+ }
101
+ ],
102
+ "errors": {
103
+ "routes": [
104
+ {
105
+ "match": [
106
+ {
107
+ "host": [
108
+ "localhost"
109
+ ]
110
+ }
111
+ ],
112
+ "handle": [
113
+ {
114
+ "handler": "subroute",
115
+ "routes": [
116
+ {
117
+ "handle": [
118
+ {
119
+ "body": "Error in the [400 .. 499] range",
120
+ "handler": "static_response"
121
+ }
122
+ ],
123
+ "match": [
124
+ {
125
+ "expression": "{http.error.status_code} \u003e= 400 \u0026\u0026 {http.error.status_code} \u003c= 499"
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "handle": [
131
+ {
132
+ "body": "Error code is equal to 500 or in the [300..399] range",
133
+ "handler": "static_response"
134
+ }
135
+ ],
136
+ "match": [
137
+ {
138
+ "expression": "{http.error.status_code} \u003e= 300 \u0026\u0026 {http.error.status_code} \u003c= 399 || {http.error.status_code} in [500]"
139
+ }
140
+ ]
141
+ }
142
+ ]
143
+ }
144
+ ],
145
+ "terminal": true
146
+ }
147
+ ]
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+}
caddytest/integration/caddyfile_adapt/error_simple_codes.txt
new
+120
@@ -0,0 +1,120 @@
1
+{
2
+ http_port 3010
3
+}
4
+localhost:3010 {
5
+ root * /srv
6
+ error /private* "Unauthorized" 410
7
+ error /hidden* "Not found" 404
8
+
9
+ handle_errors 404 410 {
10
+ respond "404 or 410 error"
11
+ }
12
+}
13
+----------
14
+{
15
+ "apps": {
16
+ "http": {
17
+ "http_port": 3010,
18
+ "servers": {
19
+ "srv0": {
20
+ "listen": [
21
+ ":3010"
22
+ ],
23
+ "routes": [
24
+ {
25
+ "match": [
26
+ {
27
+ "host": [
28
+ "localhost"
29
+ ]
30
+ }
31
+ ],
32
+ "handle": [
33
+ {
34
+ "handler": "subroute",
35
+ "routes": [
36
+ {
37
+ "handle": [
38
+ {
39
+ "handler": "vars",
40
+ "root": "/srv"
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "handle": [
46
+ {
47
+ "error": "Unauthorized",
48
+ "handler": "error",
49
+ "status_code": 410
50
+ }
51
+ ],
52
+ "match": [
53
+ {
54
+ "path": [
55
+ "/private*"
56
+ ]
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "handle": [
62
+ {
63
+ "error": "Not found",
64
+ "handler": "error",
65
+ "status_code": 404
66
+ }
67
+ ],
68
+ "match": [
69
+ {
70
+ "path": [
71
+ "/hidden*"
72
+ ]
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "terminal": true
80
+ }
81
+ ],
82
+ "errors": {
83
+ "routes": [
84
+ {
85
+ "match": [
86
+ {
87
+ "host": [
88
+ "localhost"
89
+ ]
90
+ }
91
+ ],
92
+ "handle": [
93
+ {
94
+ "handler": "subroute",
95
+ "routes": [
96
+ {
97
+ "handle": [
98
+ {
99
+ "body": "404 or 410 error",
100
+ "handler": "static_response"
101
+ }
102
+ ],
103
+ "match": [
104
+ {
105
+ "expression": "{http.error.status_code} in [404, 410]"
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ }
111
+ ],
112
+ "terminal": true
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+}
caddytest/integration/caddyfile_adapt/error_sort.txt
new
+148
@@ -0,0 +1,148 @@
1
+{
2
+ http_port 2099
3
+}
4
+localhost:2099 {
5
+ root * /srv
6
+ error /private* "Unauthorized" 410
7
+ error /hidden* "Not found" 404
8
+ error /internalerr* "Internal Server Error" 500
9
+
10
+ handle_errors {
11
+ respond "Fallback route: code outside the [400..499] range"
12
+ }
13
+ handle_errors 4xx {
14
+ respond "Error in the [400 .. 499] range"
15
+ }
16
+}
17
+----------
18
+{
19
+ "apps": {
20
+ "http": {
21
+ "http_port": 2099,
22
+ "servers": {
23
+ "srv0": {
24
+ "listen": [
25
+ ":2099"
26
+ ],
27
+ "routes": [
28
+ {
29
+ "match": [
30
+ {
31
+ "host": [
32
+ "localhost"
33
+ ]
34
+ }
35
+ ],
36
+ "handle": [
37
+ {
38
+ "handler": "subroute",
39
+ "routes": [
40
+ {
41
+ "handle": [
42
+ {
43
+ "handler": "vars",
44
+ "root": "/srv"
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "handle": [
50
+ {
51
+ "error": "Internal Server Error",
52
+ "handler": "error",
53
+ "status_code": 500
54
+ }
55
+ ],
56
+ "match": [
57
+ {
58
+ "path": [
59
+ "/internalerr*"
60
+ ]
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "handle": [
66
+ {
67
+ "error": "Unauthorized",
68
+ "handler": "error",
69
+ "status_code": 410
70
+ }
71
+ ],
72
+ "match": [
73
+ {
74
+ "path": [
75
+ "/private*"
76
+ ]
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "handle": [
82
+ {
83
+ "error": "Not found",
84
+ "handler": "error",
85
+ "status_code": 404
86
+ }
87
+ ],
88
+ "match": [
89
+ {
90
+ "path": [
91
+ "/hidden*"
92
+ ]
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+ ],
99
+ "terminal": true
100
+ }
101
+ ],
102
+ "errors": {
103
+ "routes": [
104
+ {
105
+ "match": [
106
+ {
107
+ "host": [
108
+ "localhost"
109
+ ]
110
+ }
111
+ ],
112
+ "handle": [
113
+ {
114
+ "handler": "subroute",
115
+ "routes": [
116
+ {
117
+ "handle": [
118
+ {
119
+ "body": "Error in the [400 .. 499] range",
120
+ "handler": "static_response"
121
+ }
122
+ ],
123
+ "match": [
124
+ {
125
+ "expression": "{http.error.status_code} \u003e= 400 \u0026\u0026 {http.error.status_code} \u003c= 499"
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "handle": [
131
+ {
132
+ "body": "Fallback route: code outside the [400..499] range",
133
+ "handler": "static_response"
134
+ }
135
+ ]
136
+ }
137
+ ]
138
+ }
139
+ ],
140
+ "terminal": true
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+}
caddytest/integration/caddyfile_test.go
+88
@@ -496,3 +496,91 @@ func TestUriReplace(t *testing.T) {
496
497
tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
498
}
499
+func TestHandleErrorSimpleCodes(t *testing.T) {
500
+ tester := caddytest.NewTester(t)
501
+ tester.InitServer(`{
502
+ admin localhost:2999
503
+ http_port 9080
504
+ }
505
+ localhost:9080 {
506
+ root * /srv
507
+ error /private* "Unauthorized" 410
508
+ error /hidden* "Not found" 404
509
+
510
+ handle_errors 404 410 {
511
+ respond "404 or 410 error"
512
+ }
513
+ }`, "caddyfile")
514
+ // act and assert
515
+ tester.AssertGetResponse("http://localhost:9080/private", 410, "404 or 410 error")
516
+ tester.AssertGetResponse("http://localhost:9080/hidden", 404, "404 or 410 error")
517
+}
518
+
519
+func TestHandleErrorRange(t *testing.T) {
520
+ tester := caddytest.NewTester(t)
521
+ tester.InitServer(`{
522
+ admin localhost:2999
523
+ http_port 9080
524
+ }
525
+ localhost:9080 {
526
+ root * /srv
527
+ error /private* "Unauthorized" 410
528
+ error /hidden* "Not found" 404
529
+
530
+ handle_errors 4xx {
531
+ respond "Error in the [400 .. 499] range"
532
+ }
533
+ }`, "caddyfile")
534
+ // act and assert
535
+ tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
536
+ tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
537
+}
538
+
539
+func TestHandleErrorSort(t *testing.T) {
540
+ tester := caddytest.NewTester(t)
541
+ tester.InitServer(`{
542
+ admin localhost:2999
543
+ http_port 9080
544
+ }
545
+ localhost:9080 {
546
+ root * /srv
547
+ error /private* "Unauthorized" 410
548
+ error /hidden* "Not found" 404
549
+ error /internalerr* "Internal Server Error" 500
550
+
551
+ handle_errors {
552
+ respond "Fallback route: code outside the [400..499] range"
553
+ }
554
+ handle_errors 4xx {
555
+ respond "Error in the [400 .. 499] range"
556
+ }
557
+ }`, "caddyfile")
558
+ // act and assert
559
+ tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Fallback route: code outside the [400..499] range")
560
+ tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
561
+}
562
+
563
+func TestHandleErrorRangeAndCodes(t *testing.T) {
564
+ tester := caddytest.NewTester(t)
565
+ tester.InitServer(`{
566
+ admin localhost:2999
567
+ http_port 9080
568
+ }
569
+ localhost:9080 {
570
+ root * /srv
571
+ error /private* "Unauthorized" 410
572
+ error /threehundred* "Moved Permanently" 301
573
+ error /internalerr* "Internal Server Error" 500
574
+
575
+ handle_errors 500 3xx {
576
+ respond "Error code is equal to 500 or in the [300..399] range"
577
+ }
578
+ handle_errors 4xx {
579
+ respond "Error in the [400 .. 499] range"
580
+ }
581
+ }`, "caddyfile")
582
+ // act and assert
583
+ tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
584
+ tester.AssertGetResponse("http://localhost:9080/threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
585
+ tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
586
+}