@marcel91200 / caddy Mirror · read-only / commits / c8557dc0
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 14, 05:15 UTC

caddyfile: Introduce basic linting and fmt check (#3923)

* caddyfile: Introduce basic linting and fmt check This will help encourage people to keep their Caddyfiles tidy. * Remove unrelated tests I am not sure that testing the output of warnings here is quite the right idea; these tests are just for syntax and parsing success.

Matt Holt committed Jan 4, 2021 at 11:11 UTC c8557dc00bd93ce8ecf0bb724856a320e129c71b
7 files changed +40 -54
caddyconfig/caddyfile/adapter.go
+11 -4
@@ -15,6 +15,7 @@
15 package caddyfile
16
17 import (
18 + "bytes"
19 "encoding/json"
20 "fmt"
21
@@ -51,11 +52,17 @@ func (a Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []c
52 return nil, warnings, err
53 }
54
54 - marshalFunc := json.Marshal
55 - if options["pretty"] == "true" {
56 - marshalFunc = caddyconfig.JSONIndent
55 + // lint check: see if input was properly formatted; sometimes messy files files parse
56 + // successfully but result in logical errors because the Caddyfile is a bad format
57 + // TODO: also perform this check on imported files
58 + if !bytes.Equal(Format(body), body) {
59 + warnings = append(warnings, caddyconfig.Warning{
60 + File: filename,
61 + Message: "file is not formatted with 'caddy fmt'",
62 + })
63 }
58 - result, err := marshalFunc(cfg)
64 +
65 + result, err := json.Marshal(cfg)
66
67 return result, warnings, err
68 }
caddyconfig/configadapters.go
-6
@@ -93,12 +93,6 @@ func JSONModuleObject(val interface{}, fieldName, fieldVal string, warnings *[]W
93 return result
94 }
95
96 -// JSONIndent is used to JSON-marshal the final resulting Caddy
97 -// configuration in a consistent, human-readable way.
98 -func JSONIndent(val interface{}) ([]byte, error) {
99 - return json.MarshalIndent(val, "", "\t")
100 -}
101 -
96 // RegisterAdapter registers a config adapter with the given name.
97 // This should usually be done at init-time. It panics if the
98 // adapter cannot be registered successfully.
caddyconfig/httpcaddyfile/builtins_test.go
+1 -10
@@ -10,7 +10,6 @@ import (
10 func TestLogDirectiveSyntax(t *testing.T) {
11 for i, tc := range []struct {
12 input string
13 - expectWarn bool
13 expectError bool
14 }{
15 {
@@ -18,7 +17,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
17 log
18 }
19 `,
21 - expectWarn: false,
20 expectError: false,
21 },
22 {
@@ -28,7 +26,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
26 }
27 }
28 `,
31 - expectWarn: false,
29 expectError: false,
30 },
31 {
@@ -38,7 +35,6 @@ func TestLogDirectiveSyntax(t *testing.T) {
35 }
36 }
37 `,
41 - expectWarn: false,
38 expectError: true,
39 },
40 } {
@@ -47,12 +43,7 @@ func TestLogDirectiveSyntax(t *testing.T) {
43 ServerType: ServerType{},
44 }
45
50 - _, warnings, err := adapter.Adapt([]byte(tc.input), nil)
51 -
52 - if len(warnings) > 0 != tc.expectWarn {
53 - t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
54 - continue
55 - }
46 + _, _, err := adapter.Adapt([]byte(tc.input), nil)
47
48 if err != nil != tc.expectError {
49 t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
caddyconfig/httpcaddyfile/httptype_test.go
+2 -28
@@ -9,7 +9,6 @@ import (
9 func TestMatcherSyntax(t *testing.T) {
10 for i, tc := range []struct {
11 input string
12 - expectWarn bool
12 expectError bool
13 }{
14 {
@@ -18,7 +17,6 @@ func TestMatcherSyntax(t *testing.T) {
17 query showdebug=1
18 }
19 `,
21 - expectWarn: false,
20 expectError: false,
21 },
22 {
@@ -27,7 +25,6 @@ func TestMatcherSyntax(t *testing.T) {
25 query bad format
26 }
27 `,
30 - expectWarn: false,
28 expectError: true,
29 },
30 {
@@ -38,7 +35,6 @@ func TestMatcherSyntax(t *testing.T) {
35 }
36 }
37 `,
41 - expectWarn: false,
38 expectError: false,
39 },
40 {
@@ -47,14 +43,12 @@ func TestMatcherSyntax(t *testing.T) {
43 not path /somepath*
44 }
45 `,
50 - expectWarn: false,
46 expectError: false,
47 },
48 {
49 input: `http://localhost
50 @debug not path /somepath*
51 `,
57 - expectWarn: false,
52 expectError: false,
53 },
54 {
@@ -63,7 +57,6 @@ func TestMatcherSyntax(t *testing.T) {
57 }
58 http://localhost
59 `,
66 - expectWarn: false,
60 expectError: true,
61 },
62 } {
@@ -72,12 +65,7 @@ func TestMatcherSyntax(t *testing.T) {
65 ServerType: ServerType{},
66 }
67
75 - _, warnings, err := adapter.Adapt([]byte(tc.input), nil)
76 -
77 - if len(warnings) > 0 != tc.expectWarn {
78 - t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
79 - continue
80 - }
68 + _, _, err := adapter.Adapt([]byte(tc.input), nil)
69
70 if err != nil != tc.expectError {
71 t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
@@ -119,7 +107,6 @@ func TestSpecificity(t *testing.T) {
107 func TestGlobalOptions(t *testing.T) {
108 for i, tc := range []struct {
109 input string
122 - expectWarn bool
110 expectError bool
111 }{
112 {
@@ -129,7 +116,6 @@ func TestGlobalOptions(t *testing.T) {
116 }
117 :80
118 `,
132 - expectWarn: false,
119 expectError: false,
120 },
121 {
@@ -139,7 +125,6 @@ func TestGlobalOptions(t *testing.T) {
125 }
126 :80
127 `,
142 - expectWarn: false,
128 expectError: false,
129 },
130 {
@@ -149,7 +134,6 @@ func TestGlobalOptions(t *testing.T) {
134 }
135 :80
136 `,
152 - expectWarn: false,
137 expectError: false,
138 },
139 {
@@ -161,7 +145,6 @@ func TestGlobalOptions(t *testing.T) {
145 }
146 :80
147 `,
164 - expectWarn: false,
148 expectError: true,
149 },
150 {
@@ -174,7 +157,6 @@ func TestGlobalOptions(t *testing.T) {
157 }
158 :80
159 `,
177 - expectWarn: false,
160 expectError: false,
161 },
162 {
@@ -187,7 +169,6 @@ func TestGlobalOptions(t *testing.T) {
169 }
170 :80
171 `,
190 - expectWarn: false,
172 expectError: false,
173 },
174 {
@@ -200,7 +181,6 @@ func TestGlobalOptions(t *testing.T) {
181 }
182 :80
183 `,
203 - expectWarn: false,
184 expectError: true,
185 },
186 {
@@ -213,7 +193,6 @@ func TestGlobalOptions(t *testing.T) {
193 }
194 :80
195 `,
216 - expectWarn: false,
196 expectError: true,
197 },
198 } {
@@ -222,12 +201,7 @@ func TestGlobalOptions(t *testing.T) {
201 ServerType: ServerType{},
202 }
203
225 - _, warnings, err := adapter.Adapt([]byte(tc.input), nil)
226 -
227 - if len(warnings) > 0 != tc.expectWarn {
228 - t.Errorf("Test %d warning expectation failed Expected: %v, got %v", i, tc.expectWarn, warnings)
229 - continue
230 - }
204 + _, _, err := adapter.Adapt([]byte(tc.input), nil)
205
206 if err != nil != tc.expectError {
207 t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
caddytest/caddytest.go
+8 -1
@@ -336,7 +336,6 @@ func CompareAdapt(t *testing.T, rawConfig string, adapterName string, expectedRe
336 }
337
338 options := make(map[string]interface{})
339 - options["pretty"] = "true"
339
340 result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
341 if err != nil {
@@ -344,6 +343,14 @@ func CompareAdapt(t *testing.T, rawConfig string, adapterName string, expectedRe
343 return false
344 }
345
346 + // prettify results to keep tests human-manageable
347 + var prettyBuf bytes.Buffer
348 + err = json.Indent(&prettyBuf, result, "", "\t")
349 + if err != nil {
350 + return false
351 + }
352 + result = prettyBuf.Bytes()
353 +
354 if len(warnings) > 0 {
355 for _, w := range warnings {
356 t.Logf("warning: directive: %s : %s", w.Directive, w.Message)
cmd/commandfuncs.go
+10 -5
@@ -463,17 +463,22 @@ func cmdAdaptConfig(fl Flags) (int, error) {
463 fmt.Errorf("reading input file: %v", err)
464 }
465
466 - opts := make(map[string]interface{})
467 - if adaptCmdPrettyFlag {
468 - opts["pretty"] = "true"
469 - }
470 - opts["filename"] = adaptCmdInputFlag
466 + opts := map[string]interface{}{"filename": adaptCmdInputFlag}
467
468 adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
469 if err != nil {
470 return caddy.ExitCodeFailedStartup, err
471 }
472
473 + if adaptCmdPrettyFlag {
474 + var prettyBuf bytes.Buffer
475 + err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
476 + if err != nil {
477 + return caddy.ExitCodeFailedStartup, err
478 + }
479 + adaptedConfig = prettyBuf.Bytes()
480 + }
481 +
482 // print warnings to stderr
483 for _, warn := range warnings {
484 msg := warn.Message
modules/caddyhttp/reverseproxy/caddyfile.go
+8
@@ -15,6 +15,7 @@
15 package reverseproxy
16
17 import (
18 + "log"
19 "net"
20 "net/http"
21 "net/url"
@@ -497,6 +498,13 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
498 case 1:
499 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
500 case 2:
501 + // some lint checks, I guess
502 + if strings.EqualFold(args[0], "host") && (args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
503 + log.Printf("[WARNING] Unnecessary header_up ('Host' field): the reverse proxy's default behavior is to pass headers to the upstream")
504 + }
505 + if strings.EqualFold(args[0], "x-forwarded-proto") && (args[1] == "{scheme}" || args[1] == "{http.request.scheme}") {
506 + log.Printf("[WARNING] Unnecessary header_up ('X-Forwarded-Proto' field): the reverse proxy's default behavior is to pass headers to the upstream")
507 + }
508 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], "")
509 case 3:
510 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], args[2])