@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 511 lines 10.3 KB
Raw
1 package httpcaddyfile
2
3 import (
4 "encoding/json"
5 "strings"
6 "testing"
7
8 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
9 _ "github.com/caddyserver/caddy/v2/modules/logging"
10 )
11
12 func TestLogDirectiveSyntax(t *testing.T) {
13 for i, tc := range []struct {
14 input string
15 output string
16 expectError bool
17 }{
18 {
19 input: `:8080 {
20 log
21 }
22 `,
23 output: `{"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{}}}}}}`,
24 expectError: false,
25 },
26 {
27 input: `:8080 {
28 log {
29 core mock
30 output file foo.log
31 }
32 }
33 `,
34 output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
35 expectError: false,
36 },
37 {
38 input: `:8080 {
39 log {
40 format filter {
41 wrap console
42 fields {
43 request>remote_ip ip_mask {
44 ipv4 24
45 ipv6 32
46 }
47 }
48 }
49 }
50 }
51 `,
52 output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"encoder":{"fields":{"request\u003eremote_ip":{"filter":"ip_mask","ipv4_cidr":24,"ipv6_cidr":32}},"format":"filter","wrap":{"format":"console"}},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
53 expectError: false,
54 },
55 {
56 input: `:8080 {
57 log name-override {
58 core mock
59 output file foo.log
60 }
61 }
62 `,
63 output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
64 expectError: false,
65 },
66 {
67 input: `:8080 {
68 log {
69 sampling {
70 interval 2s
71 first 3
72 thereafter 4
73 }
74 }
75 }
76 `,
77 output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"sampling":{"interval":2000000000,"first":3,"thereafter":4},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
78 expectError: false,
79 },
80 {
81 input: `:8080 {
82 log {
83 output stdout
84 output file foo.log
85 }
86 }
87 `,
88 expectError: true,
89 },
90 {
91 input: `:8080 {
92 log {
93 format json
94 format console
95 }
96 }
97 `,
98 expectError: true,
99 },
100 {
101 input: `:8080 {
102 log {
103 level INFO
104 level DEBUG
105 }
106 }
107 `,
108 expectError: true,
109 },
110 {
111 input: `:8080 {
112 log {
113 sampling {
114 interval 1s
115 interval 2s
116 }
117 }
118 }
119 `,
120 expectError: true,
121 },
122 } {
123
124 adapter := caddyfile.Adapter{
125 ServerType: ServerType{},
126 }
127
128 out, _, err := adapter.Adapt([]byte(tc.input), nil)
129
130 if err != nil != tc.expectError {
131 t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
132 continue
133 }
134
135 if string(out) != tc.output {
136 t.Errorf("Test %d error output mismatch Expected: %s, got %s", i, tc.output, out)
137 }
138 }
139 }
140
141 func TestRedirDirectiveSyntax(t *testing.T) {
142 for i, tc := range []struct {
143 input string
144 expectError bool
145 }{
146 {
147 input: `:8080 {
148 redir :8081
149 }`,
150 expectError: false,
151 },
152 {
153 input: `:8080 {
154 redir * :8081
155 }`,
156 expectError: false,
157 },
158 {
159 input: `:8080 {
160 redir /api/* :8081 300
161 }`,
162 expectError: false,
163 },
164 {
165 input: `:8080 {
166 redir :8081 300
167 }`,
168 expectError: false,
169 },
170 {
171 input: `:8080 {
172 redir /api/* :8081 399
173 }`,
174 expectError: false,
175 },
176 {
177 input: `:8080 {
178 redir :8081 399
179 }`,
180 expectError: false,
181 },
182 {
183 input: `:8080 {
184 redir /old.html /new.html
185 }`,
186 expectError: false,
187 },
188 {
189 input: `:8080 {
190 redir /old.html /new.html temporary
191 }`,
192 expectError: false,
193 },
194 {
195 input: `:8080 {
196 redir https://example.com{uri} permanent
197 }`,
198 expectError: false,
199 },
200 {
201 input: `:8080 {
202 redir /old.html /new.html permanent
203 }`,
204 expectError: false,
205 },
206 {
207 input: `:8080 {
208 redir /old.html /new.html html
209 }`,
210 expectError: false,
211 },
212 {
213 // this is now allowed so a Location header
214 // can be written and consumed by JS
215 // in the case of XHR requests
216 input: `:8080 {
217 redir * :8081 401
218 }`,
219 expectError: false,
220 },
221 {
222 input: `:8080 {
223 redir * :8081 402
224 }`,
225 expectError: true,
226 },
227 {
228 input: `:8080 {
229 redir * :8081 {http.reverse_proxy.status_code}
230 }`,
231 expectError: false,
232 },
233 {
234 input: `:8080 {
235 redir /old.html /new.html htlm
236 }`,
237 expectError: true,
238 },
239 {
240 input: `:8080 {
241 redir * :8081 200
242 }`,
243 expectError: true,
244 },
245 {
246 input: `:8080 {
247 redir * :8081 temp
248 }`,
249 expectError: true,
250 },
251 {
252 input: `:8080 {
253 redir * :8081 perm
254 }`,
255 expectError: true,
256 },
257 {
258 input: `:8080 {
259 redir * :8081 php
260 }`,
261 expectError: true,
262 },
263 } {
264
265 adapter := caddyfile.Adapter{
266 ServerType: ServerType{},
267 }
268
269 _, _, err := adapter.Adapt([]byte(tc.input), nil)
270
271 if err != nil != tc.expectError {
272 t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
273 continue
274 }
275 }
276 }
277
278 func TestImportErrorLine(t *testing.T) {
279 for i, tc := range []struct {
280 input string
281 errorFunc func(err error) bool
282 }{
283 {
284 input: `(t1) {
285 abort {args[:]}
286 }
287 :8080 {
288 import t1
289 import t1 true
290 }`,
291 errorFunc: func(err error) bool {
292 return err != nil && strings.Contains(err.Error(), "Caddyfile:6 (import t1)")
293 },
294 },
295 {
296 input: `(t1) {
297 abort {args[:]}
298 }
299 :8080 {
300 import t1 true
301 }`,
302 errorFunc: func(err error) bool {
303 return err != nil && strings.Contains(err.Error(), "Caddyfile:5 (import t1)")
304 },
305 },
306 {
307 input: `
308 import testdata/import_variadic_snippet.txt
309 :8080 {
310 import t1 true
311 }`,
312 errorFunc: func(err error) bool {
313 return err == nil
314 },
315 },
316 {
317 input: `
318 import testdata/import_variadic_with_import.txt
319 :8080 {
320 import t1 true
321 import t2 true
322 }`,
323 errorFunc: func(err error) bool {
324 return err == nil
325 },
326 },
327 } {
328 adapter := caddyfile.Adapter{
329 ServerType: ServerType{},
330 }
331
332 _, _, err := adapter.Adapt([]byte(tc.input), nil)
333
334 if !tc.errorFunc(err) {
335 t.Errorf("Test %d error expectation failed, got %s", i, err)
336 continue
337 }
338 }
339 }
340
341 func TestNestedImport(t *testing.T) {
342 for i, tc := range []struct {
343 input string
344 errorFunc func(err error) bool
345 }{
346 {
347 input: `(t1) {
348 respond {args[0]} {args[1]}
349 }
350
351 (t2) {
352 import t1 {args[0]} 202
353 }
354
355 :8080 {
356 handle {
357 import t2 "foobar"
358 }
359 }`,
360 errorFunc: func(err error) bool {
361 return err == nil
362 },
363 },
364 {
365 input: `(t1) {
366 respond {args[:]}
367 }
368
369 (t2) {
370 import t1 {args[0]} {args[1]}
371 }
372
373 :8080 {
374 handle {
375 import t2 "foobar" 202
376 }
377 }`,
378 errorFunc: func(err error) bool {
379 return err == nil
380 },
381 },
382 {
383 input: `(t1) {
384 respond {args[0]} {args[1]}
385 }
386
387 (t2) {
388 import t1 {args[:]}
389 }
390
391 :8080 {
392 handle {
393 import t2 "foobar" 202
394 }
395 }`,
396 errorFunc: func(err error) bool {
397 return err == nil
398 },
399 },
400 } {
401 adapter := caddyfile.Adapter{
402 ServerType: ServerType{},
403 }
404
405 _, _, err := adapter.Adapt([]byte(tc.input), nil)
406
407 if !tc.errorFunc(err) {
408 t.Errorf("Test %d error expectation failed, got %s", i, err)
409 continue
410 }
411 }
412 }
413
414 func TestLogLevelDirectiveSyntax(t *testing.T) {
415 for _, tc := range []struct {
416 directive string
417 wantError bool
418 }{
419 {"log_level DEBUG", false}, {"log_level info", false},
420 {"log_level WARN", false}, {"log_level ERROR", false},
421 {"log_level /warnings WARN", false},
422 {"log_level", true}, {"log_level INFO WARN", true},
423 {"log_level invalid", true}, {"log_level DPANIC", true},
424 {"log_level PANIC", true}, {"log_level FATAL", true},
425 {"log_level WARN {\n foo\n}", true},
426 } {
427 t.Run(tc.directive, func(t *testing.T) {
428 adapter := caddyfile.Adapter{ServerType: ServerType{}}
429 result, _, err := adapter.Adapt([]byte(":8080 {\n"+tc.directive+"\n}"), nil)
430 if tc.wantError {
431 if err == nil {
432 t.Fatal("expected error")
433 }
434 return
435 }
436 if err != nil {
437 t.Fatal(err)
438 }
439 var config struct {
440 Apps struct {
441 HTTP struct {
442 Servers map[string]struct {
443 Routes []struct {
444 Match []map[string]any
445 Handle []struct {
446 Handler string
447 Level string `json:"log_level"`
448 }
449 }
450 }
451 }
452 }
453 }
454 if err := json.Unmarshal(result, &config); err != nil {
455 t.Fatal(err)
456 }
457 routes := config.Apps.HTTP.Servers["srv0"].Routes
458 if len(routes) != 1 || len(routes[0].Handle) != 1 {
459 t.Fatalf("unexpected routes: %s", result)
460 }
461 handler := routes[0].Handle[0]
462 args := strings.Fields(tc.directive)
463 if handler.Handler != "vars" || handler.Level != strings.ToLower(args[len(args)-1]) {
464 t.Fatalf("unexpected handler: %s", result)
465 }
466 if strings.Contains(tc.directive, "/warnings") && len(routes[0].Match) != 1 {
467 t.Fatalf("missing matcher: %s", result)
468 }
469 })
470 }
471 }
472
473 func TestLogLevelDirectiveOrder(t *testing.T) {
474 for _, input := range []string{
475 "log_level /api/private/* ERROR\nlog_level /api/* DEBUG\nlog_level INFO",
476 "log_level INFO\nlog_level /api/* DEBUG\nlog_level /api/private/* ERROR",
477 } {
478 t.Run(input, func(t *testing.T) {
479 adapter := caddyfile.Adapter{ServerType: ServerType{}}
480 result, _, err := adapter.Adapt([]byte(":8080 {\n"+input+"\n}"), nil)
481 if err != nil {
482 t.Fatal(err)
483 }
484 var config struct {
485 Apps struct {
486 HTTP struct {
487 Servers map[string]struct {
488 Routes []struct {
489 Handle []struct {
490 Level string `json:"log_level"`
491 }
492 }
493 }
494 }
495 }
496 }
497 if err := json.Unmarshal(result, &config); err != nil {
498 t.Fatal(err)
499 }
500 var levels []string
501 for _, route := range config.Apps.HTTP.Servers["srv0"].Routes {
502 for _, handler := range route.Handle {
503 levels = append(levels, handler.Level)
504 }
505 }
506 if got := strings.Join(levels, ","); got != "info,debug,error" {
507 t.Fatalf("expected broadest log_level first, got %q: %s", got, result)
508 }
509 })
510 }
511 }