@marcel91200 / caddy Mirror · read-only / commits / 3cfefeb0
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

httpcaddyfile: Configure servers via global options (#3836)

* httpcaddyfile: First pass at implementing server options * httpcaddyfile: Add listener wrapper support * httpcaddyfile: Sort sbaddrs to make adapt output more deterministic * httpcaddyfile: Add server options adapt tests * httpcaddyfile: Windows line endings lol * caddytest: More windows line endings lol (sorry Matt) * Update caddyconfig/httpcaddyfile/serveroptions.go Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * httpcaddyfile: Reword listener address "matcher" * Apply suggestions from code review Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * httpcaddyfile: Deprecate experimental_http3 option (moved to servers) * httpcaddyfile: Remove validation step, no longer needed Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

Francis Lavoie committed Nov 23, 2020 at 14:46 UTC 3cfefeb0f71d54f1d9a76a63be7b97d0943c88ef
15 files changed +1084 -658
caddyconfig/httpcaddyfile/addresses.go
+8
@@ -18,6 +18,7 @@ import (
18 "fmt"
19 "net"
20 "reflect"
21 + "sort"
22 "strconv"
23 "strings"
24 "unicode"
@@ -163,6 +164,13 @@ func (st *ServerType) consolidateAddrMappings(addrToServerBlocks map[string][]se
164
165 sbaddrs = append(sbaddrs, a)
166 }
167 +
168 + // sort them by their first address (we know there will always be at least one)
169 + // to avoid problems with non-deterministic ordering (makes tests flaky)
170 + sort.Slice(sbaddrs, func(i, j int) bool {
171 + return sbaddrs[i].addresses[0] < sbaddrs[j].addresses[0]
172 + })
173 +
174 return sbaddrs
175 }
176
caddyconfig/httpcaddyfile/httptype.go
+42 -13
@@ -218,13 +218,6 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
218 return nil, warnings, err
219 }
220
221 - // if experimental HTTP/3 is enabled, enable it on each server
222 - if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
223 - for _, srv := range httpApp.Servers {
224 - srv.ExperimentalHTTP3 = true
225 - }
226 - }
227 -
221 // extract any custom logs, and enforce configured levels
222 var customLogs []namedCustomLog
223 var hasDefaultLog bool
@@ -311,23 +304,54 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
304 }
305
306 for _, segment := range serverBlocks[0].block.Segments {
314 - dir := segment.Directive()
307 + opt := segment.Directive()
308 var val interface{}
309 var err error
310 disp := caddyfile.NewDispenser(segment)
311
319 - dirFunc, ok := registeredGlobalOptions[dir]
312 + optFunc, ok := registeredGlobalOptions[opt]
313 if !ok {
314 tkn := segment[0]
322 - return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, dir)
315 + return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, opt)
316 }
317
325 - val, err = dirFunc(disp)
318 + val, err = optFunc(disp)
319 if err != nil {
327 - return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
320 + return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", opt, err)
321 + }
322 +
323 + // As a special case, fold multiple "servers" options together
324 + // in an array instead of overwriting a possible existing value
325 + if opt == "servers" {
326 + existingOpts, ok := options[opt].([]serverOptions)
327 + if !ok {
328 + existingOpts = []serverOptions{}
329 + }
330 + serverOpts, ok := val.(serverOptions)
331 + if !ok {
332 + return nil, fmt.Errorf("unexpected type from 'servers' global options")
333 + }
334 + options[opt] = append(existingOpts, serverOpts)
335 + continue
336 }
337
330 - options[dir] = val
338 + options[opt] = val
339 + }
340 +
341 + // If we got "servers" options, we'll sort them by their listener address
342 + if serverOpts, ok := options["servers"].([]serverOptions); ok {
343 + sort.Slice(serverOpts, func(i, j int) bool {
344 + return len(serverOpts[i].ListenerAddress) > len(serverOpts[j].ListenerAddress)
345 + })
346 +
347 + // Reject the config if there are duplicate listener address
348 + seen := make(map[string]bool)
349 + for _, entry := range serverOpts {
350 + if _, alreadySeen := seen[entry.ListenerAddress]; alreadySeen {
351 + return nil, fmt.Errorf("cannot have 'servers' global options with duplicate listener addresses: %s", entry.ListenerAddress)
352 + }
353 + seen[entry.ListenerAddress] = true
354 + }
355 }
356
357 return serverBlocks[1:], nil
@@ -602,6 +626,11 @@ func (st *ServerType) serversFromPairings(
626 servers[fmt.Sprintf("srv%d", i)] = srv
627 }
628
629 + err := applyServerOptions(servers, options, warnings)
630 + if err != nil {
631 + return nil, err
632 + }
633 +
634 return servers, nil
635 }
636
caddyconfig/httpcaddyfile/options.go
+5
@@ -43,6 +43,7 @@ func init() {
43 RegisterGlobalOption("local_certs", parseOptTrue)
44 RegisterGlobalOption("key_type", parseOptSingleString)
45 RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
46 + RegisterGlobalOption("servers", parseServerOptions)
47 }
48
49 func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
@@ -361,3 +362,7 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser) (interface{}, error) {
362 }
363 return val, nil
364 }
365 +
366 +func parseServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
367 + return unmarshalCaddyfileServerOptions(d)
368 +}
caddyconfig/httpcaddyfile/serveroptions.go new
+235
@@ -0,0 +1,235 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package httpcaddyfile
16 +
17 +import (
18 + "encoding/json"
19 + "fmt"
20 +
21 + "github.com/caddyserver/caddy/v2"
22 + "github.com/caddyserver/caddy/v2/caddyconfig"
23 + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
24 + "github.com/caddyserver/caddy/v2/modules/caddyhttp"
25 + "github.com/dustin/go-humanize"
26 +)
27 +
28 +// serverOptions collects server config overrides parsed from Caddyfile global options
29 +type serverOptions struct {
30 + // If set, will only apply these options to servers that contain a
31 + // listener address that matches exactly. If empty, will apply to all
32 + // servers that were not already matched by another serverOptions.
33 + ListenerAddress string
34 +
35 + // These will all map 1:1 to the caddyhttp.Server struct
36 + ListenerWrappersRaw []json.RawMessage
37 + ReadTimeout caddy.Duration
38 + ReadHeaderTimeout caddy.Duration
39 + WriteTimeout caddy.Duration
40 + IdleTimeout caddy.Duration
41 + MaxHeaderBytes int
42 + AllowH2C bool
43 + ExperimentalHTTP3 bool
44 + StrictSNIHost *bool
45 +}
46 +
47 +func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
48 + serverOpts := serverOptions{}
49 + for d.Next() {
50 + if d.NextArg() {
51 + serverOpts.ListenerAddress = d.Val()
52 + if d.NextArg() {
53 + return nil, d.ArgErr()
54 + }
55 + }
56 + for nesting := d.Nesting(); d.NextBlock(nesting); {
57 + switch d.Val() {
58 + case "listener_wrappers":
59 + for nesting := d.Nesting(); d.NextBlock(nesting); {
60 + mod, err := caddy.GetModule("caddy.listeners." + d.Val())
61 + if err != nil {
62 + return nil, fmt.Errorf("finding listener module '%s': %v", d.Val(), err)
63 + }
64 + unm, ok := mod.New().(caddyfile.Unmarshaler)
65 + if !ok {
66 + return nil, fmt.Errorf("listener module '%s' is not a Caddyfile unmarshaler", mod)
67 + }
68 + err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
69 + if err != nil {
70 + return nil, err
71 + }
72 + listenerWrapper, ok := unm.(caddy.ListenerWrapper)
73 + if !ok {
74 + return nil, fmt.Errorf("module %s is not a listener wrapper", mod)
75 + }
76 + jsonListenerWrapper := caddyconfig.JSONModuleObject(
77 + listenerWrapper,
78 + "wrapper",
79 + listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
80 + nil,
81 + )
82 + serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
83 + }
84 +
85 + case "timeouts":
86 + for nesting := d.Nesting(); d.NextBlock(nesting); {
87 + switch d.Val() {
88 + case "read_body":
89 + if !d.NextArg() {
90 + return nil, d.ArgErr()
91 + }
92 + dur, err := caddy.ParseDuration(d.Val())
93 + if err != nil {
94 + return nil, d.Errf("parsing read_body timeout duration: %v", err)
95 + }
96 + serverOpts.ReadTimeout = caddy.Duration(dur)
97 +
98 + case "read_header":
99 + if !d.NextArg() {
100 + return nil, d.ArgErr()
101 + }
102 + dur, err := caddy.ParseDuration(d.Val())
103 + if err != nil {
104 + return nil, d.Errf("parsing read_header timeout duration: %v", err)
105 + }
106 + serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
107 +
108 + case "write":
109 + if !d.NextArg() {
110 + return nil, d.ArgErr()
111 + }
112 + dur, err := caddy.ParseDuration(d.Val())
113 + if err != nil {
114 + return nil, d.Errf("parsing write timeout duration: %v", err)
115 + }
116 + serverOpts.WriteTimeout = caddy.Duration(dur)
117 +
118 + case "idle":
119 + if !d.NextArg() {
120 + return nil, d.ArgErr()
121 + }
122 + dur, err := caddy.ParseDuration(d.Val())
123 + if err != nil {
124 + return nil, d.Errf("parsing idle timeout duration: %v", err)
125 + }
126 + serverOpts.IdleTimeout = caddy.Duration(dur)
127 +
128 + default:
129 + return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
130 + }
131 + }
132 +
133 + case "max_header_size":
134 + var sizeStr string
135 + if !d.AllArgs(&sizeStr) {
136 + return nil, d.ArgErr()
137 + }
138 + size, err := humanize.ParseBytes(sizeStr)
139 + if err != nil {
140 + return nil, d.Errf("parsing max_header_size: %v", err)
141 + }
142 + serverOpts.MaxHeaderBytes = int(size)
143 +
144 + case "protocol":
145 + for nesting := d.Nesting(); d.NextBlock(nesting); {
146 + switch d.Val() {
147 + case "allow_h2c":
148 + if d.NextArg() {
149 + return nil, d.ArgErr()
150 + }
151 + serverOpts.AllowH2C = true
152 +
153 + case "experimental_http3":
154 + if d.NextArg() {
155 + return nil, d.ArgErr()
156 + }
157 + serverOpts.ExperimentalHTTP3 = true
158 +
159 + case "strict_sni_host":
160 + if d.NextArg() {
161 + return nil, d.ArgErr()
162 + }
163 + trueBool := true
164 + serverOpts.StrictSNIHost = &trueBool
165 +
166 + default:
167 + return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
168 + }
169 + }
170 +
171 + default:
172 + return nil, d.Errf("unrecognized servers option '%s'", d.Val())
173 + }
174 + }
175 + }
176 + return serverOpts, nil
177 +}
178 +
179 +// applyServerOptions sets the server options on the appropriate servers
180 +func applyServerOptions(
181 + servers map[string]*caddyhttp.Server,
182 + options map[string]interface{},
183 + warnings *[]caddyconfig.Warning,
184 +) error {
185 + // If experimental HTTP/3 is enabled, enable it on each server.
186 + // We already know there won't be a conflict with serverOptions because
187 + // we validated earlier that "experimental_http3" cannot be set at the same
188 + // time as "servers"
189 + if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
190 + *warnings = append(*warnings, caddyconfig.Warning{Message: "the 'experimental_http3' global option is deprecated, please use the 'servers > protocol > experimental_http3' option instead"})
191 + for _, srv := range servers {
192 + srv.ExperimentalHTTP3 = true
193 + }
194 + }
195 +
196 + serverOpts, ok := options["servers"].([]serverOptions)
197 + if !ok {
198 + return nil
199 + }
200 +
201 + for _, server := range servers {
202 + // find the options that apply to this server
203 + opts := func() *serverOptions {
204 + for _, entry := range serverOpts {
205 + if entry.ListenerAddress == "" {
206 + return &entry
207 + }
208 + for _, listener := range server.Listen {
209 + if entry.ListenerAddress == listener {
210 + return &entry
211 + }
212 + }
213 + }
214 + return nil
215 + }()
216 +
217 + // if none apply, then move to the next server
218 + if opts == nil {
219 + continue
220 + }
221 +
222 + // set all the options
223 + server.ListenerWrappersRaw = opts.ListenerWrappersRaw
224 + server.ReadTimeout = opts.ReadTimeout
225 + server.ReadHeaderTimeout = opts.ReadHeaderTimeout
226 + server.WriteTimeout = opts.WriteTimeout
227 + server.IdleTimeout = opts.IdleTimeout
228 + server.MaxHeaderBytes = opts.MaxHeaderBytes
229 + server.AllowH2C = opts.AllowH2C
230 + server.ExperimentalHTTP3 = opts.ExperimentalHTTP3
231 + server.StrictSNIHost = opts.StrictSNIHost
232 + }
233 +
234 + return nil
235 +}
caddytest/integration/caddyfile_adapt/global_server_options_multi.txt new
+83
@@ -0,0 +1,83 @@
1 +{
2 + servers {
3 + timeouts {
4 + idle 90s
5 + }
6 + }
7 + servers :80 {
8 + timeouts {
9 + idle 60s
10 + }
11 + }
12 + servers :443 {
13 + timeouts {
14 + idle 30s
15 + }
16 + }
17 +}
18 +
19 +foo.com {
20 +}
21 +
22 +http://bar.com {
23 +}
24 +
25 +:8080 {
26 +}
27 +
28 +----------
29 +{
30 + "apps": {
31 + "http": {
32 + "servers": {
33 + "srv0": {
34 + "listen": [
35 + ":443"
36 + ],
37 + "idle_timeout": 30000000000,
38 + "routes": [
39 + {
40 + "match": [
41 + {
42 + "host": [
43 + "foo.com"
44 + ]
45 + }
46 + ],
47 + "terminal": true
48 + }
49 + ]
50 + },
51 + "srv1": {
52 + "listen": [
53 + ":80"
54 + ],
55 + "idle_timeout": 60000000000,
56 + "routes": [
57 + {
58 + "match": [
59 + {
60 + "host": [
61 + "bar.com"
62 + ]
63 + }
64 + ],
65 + "terminal": true
66 + }
67 + ],
68 + "automatic_https": {
69 + "skip": [
70 + "bar.com"
71 + ]
72 + }
73 + },
74 + "srv2": {
75 + "listen": [
76 + ":8080"
77 + ],
78 + "idle_timeout": 90000000000
79 + }
80 + }
81 + }
82 + }
83 +}
caddytest/integration/caddyfile_adapt/global_server_options_single.txt new
+62
@@ -0,0 +1,62 @@
1 +{
2 + servers {
3 + listener_wrappers {
4 + tls
5 + }
6 + timeouts {
7 + read_body 30s
8 + read_header 30s
9 + write 30s
10 + idle 30s
11 + }
12 + max_header_size 100MB
13 + protocol {
14 + allow_h2c
15 + experimental_http3
16 + strict_sni_host
17 + }
18 + }
19 +}
20 +
21 +foo.com {
22 +}
23 +
24 +----------
25 +{
26 + "apps": {
27 + "http": {
28 + "servers": {
29 + "srv0": {
30 + "listen": [
31 + ":443"
32 + ],
33 + "listener_wrappers": [
34 + {
35 + "wrapper": "tls"
36 + }
37 + ],
38 + "read_timeout": 30000000000,
39 + "read_header_timeout": 30000000000,
40 + "write_timeout": 30000000000,
41 + "idle_timeout": 30000000000,
42 + "max_header_bytes": 100000000,
43 + "routes": [
44 + {
45 + "match": [
46 + {
47 + "host": [
48 + "foo.com"
49 + ]
50 + }
51 + ],
52 + "terminal": true
53 + }
54 + ],
55 + "strict_sni_host": true,
56 + "experimental_http3": true,
57 + "allow_h2c": true
58 + }
59 + }
60 + }
61 + }
62 +}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/handle_path.txt
+51 -51
@@ -1,52 +1,52 @@
1 -:80
2 -handle_path /api/v1/* {
3 - respond "API v1"
4 -}
5 -----------
6 -{
7 - "apps": {
8 - "http": {
9 - "servers": {
10 - "srv0": {
11 - "listen": [
12 - ":80"
13 - ],
14 - "routes": [
15 - {
16 - "match": [
17 - {
18 - "path": [
19 - "/api/v1/*"
20 - ]
21 - }
22 - ],
23 - "handle": [
24 - {
25 - "handler": "subroute",
26 - "routes": [
27 - {
28 - "handle": [
29 - {
30 - "handler": "rewrite",
31 - "strip_path_prefix": "/api/v1"
32 - }
33 - ]
34 - },
35 - {
36 - "handle": [
37 - {
38 - "body": "API v1",
39 - "handler": "static_response"
40 - }
41 - ]
42 - }
43 - ]
44 - }
45 - ]
46 - }
47 - ]
48 - }
49 - }
50 - }
51 - }
1 +:80
2 +handle_path /api/v1/* {
3 + respond "API v1"
4 +}
5 +----------
6 +{
7 + "apps": {
8 + "http": {
9 + "servers": {
10 + "srv0": {
11 + "listen": [
12 + ":80"
13 + ],
14 + "routes": [
15 + {
16 + "match": [
17 + {
18 + "path": [
19 + "/api/v1/*"
20 + ]
21 + }
22 + ],
23 + "handle": [
24 + {
25 + "handler": "subroute",
26 + "routes": [
27 + {
28 + "handle": [
29 + {
30 + "handler": "rewrite",
31 + "strip_path_prefix": "/api/v1"
32 + }
33 + ]
34 + },
35 + {
36 + "handle": [
37 + {
38 + "body": "API v1",
39 + "handler": "static_response"
40 + }
41 + ]
42 + }
43 + ]
44 + }
45 + ]
46 + }
47 + ]
48 + }
49 + }
50 + }
51 + }
52 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/handle_path_sorting.txt
+104 -104
@@ -1,105 +1,105 @@
1 -:80 {
2 - handle /api/* {
3 - respond "api"
4 - }
5 -
6 - handle_path /static/* {
7 - respond "static"
8 - }
9 -
10 - handle {
11 - respond "handle"
12 - }
13 -}
14 -----------
15 -{
16 - "apps": {
17 - "http": {
18 - "servers": {
19 - "srv0": {
20 - "listen": [
21 - ":80"
22 - ],
23 - "routes": [
24 - {
25 - "group": "group3",
26 - "match": [
27 - {
28 - "path": [
29 - "/static/*"
30 - ]
31 - }
32 - ],
33 - "handle": [
34 - {
35 - "handler": "subroute",
36 - "routes": [
37 - {
38 - "handle": [
39 - {
40 - "handler": "rewrite",
41 - "strip_path_prefix": "/static"
42 - }
43 - ]
44 - },
45 - {
46 - "handle": [
47 - {
48 - "body": "static",
49 - "handler": "static_response"
50 - }
51 - ]
52 - }
53 - ]
54 - }
55 - ]
56 - },
57 - {
58 - "group": "group3",
59 - "match": [
60 - {
61 - "path": [
62 - "/api/*"
63 - ]
64 - }
65 - ],
66 - "handle": [
67 - {
68 - "handler": "subroute",
69 - "routes": [
70 - {
71 - "handle": [
72 - {
73 - "body": "api",
74 - "handler": "static_response"
75 - }
76 - ]
77 - }
78 - ]
79 - }
80 - ]
81 - },
82 - {
83 - "group": "group3",
84 - "handle": [
85 - {
86 - "handler": "subroute",
87 - "routes": [
88 - {
89 - "handle": [
90 - {
91 - "body": "handle",
92 - "handler": "static_response"
93 - }
94 - ]
95 - }
96 - ]
97 - }
98 - ]
99 - }
100 - ]
101 - }
102 - }
103 - }
104 - }
1 +:80 {
2 + handle /api/* {
3 + respond "api"
4 + }
5 +
6 + handle_path /static/* {
7 + respond "static"
8 + }
9 +
10 + handle {
11 + respond "handle"
12 + }
13 +}
14 +----------
15 +{
16 + "apps": {
17 + "http": {
18 + "servers": {
19 + "srv0": {
20 + "listen": [
21 + ":80"
22 + ],
23 + "routes": [
24 + {
25 + "group": "group3",
26 + "match": [
27 + {
28 + "path": [
29 + "/static/*"
30 + ]
31 + }
32 + ],
33 + "handle": [
34 + {
35 + "handler": "subroute",
36 + "routes": [
37 + {
38 + "handle": [
39 + {
40 + "handler": "rewrite",
41 + "strip_path_prefix": "/static"
42 + }
43 + ]
44 + },
45 + {
46 + "handle": [
47 + {
48 + "body": "static",
49 + "handler": "static_response"
50 + }
51 + ]
52 + }
53 + ]
54 + }
55 + ]
56 + },
57 + {
58 + "group": "group3",
59 + "match": [
60 + {
61 + "path": [
62 + "/api/*"
63 + ]
64 + }
65 + ],
66 + "handle": [
67 + {
68 + "handler": "subroute",
69 + "routes": [
70 + {
71 + "handle": [
72 + {
73 + "body": "api",
74 + "handler": "static_response"
75 + }
76 + ]
77 + }
78 + ]
79 + }
80 + ]
81 + },
82 + {
83 + "group": "group3",
84 + "handle": [
85 + {
86 + "handler": "subroute",
87 + "routes": [
88 + {
89 + "handle": [
90 + {
91 + "body": "handle",
92 + "handler": "static_response"
93 + }
94 + ]
95 + }
96 + ]
97 + }
98 + ]
99 + }
100 + ]
101 + }
102 + }
103 + }
104 + }
105 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/import_args_file.txt
+48 -48
@@ -1,49 +1,49 @@
1 -example.com
2 -
3 -import testdata/import_respond.txt Groot Rocket
4 -import testdata/import_respond.txt you "the confused man"
5 -----------
6 -{
7 - "apps": {
8 - "http": {
9 - "servers": {
10 - "srv0": {
11 - "listen": [
12 - ":443"
13 - ],
14 - "routes": [
15 - {
16 - "match": [
17 - {
18 - "host": [
19 - "example.com"
20 - ]
21 - }
22 - ],
23 - "handle": [
24 - {
25 - "handler": "subroute",
26 - "routes": [
27 - {
28 - "handle": [
29 - {
30 - "body": "'I am Groot', hears Rocket",
31 - "handler": "static_response"
32 - },
33 - {
34 - "body": "'I am you', hears the confused man",
35 - "handler": "static_response"
36 - }
37 - ]
38 - }
39 - ]
40 - }
41 - ],
42 - "terminal": true
43 - }
44 - ]
45 - }
46 - }
47 - }
48 - }
1 +example.com
2 +
3 +import testdata/import_respond.txt Groot Rocket
4 +import testdata/import_respond.txt you "the confused man"
5 +----------
6 +{
7 + "apps": {
8 + "http": {
9 + "servers": {
10 + "srv0": {
11 + "listen": [
12 + ":443"
13 + ],
14 + "routes": [
15 + {
16 + "match": [
17 + {
18 + "host": [
19 + "example.com"
20 + ]
21 + }
22 + ],
23 + "handle": [
24 + {
25 + "handler": "subroute",
26 + "routes": [
27 + {
28 + "handle": [
29 + {
30 + "body": "'I am Groot', hears Rocket",
31 + "handler": "static_response"
32 + },
33 + {
34 + "body": "'I am you', hears the confused man",
35 + "handler": "static_response"
36 + }
37 + ]
38 + }
39 + ]
40 + }
41 + ],
42 + "terminal": true
43 + }
44 + ]
45 + }
46 + }
47 + }
48 + }
49 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/import_args_snippet.txt
+82 -82
@@ -1,83 +1,83 @@
1 -(logging) {
2 - log {
3 - output file /var/log/caddy/{args.0}.access.log
4 - }
5 -}
6 -
7 -a.example.com {
8 - import logging a.example.com
9 -}
10 -
11 -b.example.com {
12 - import logging b.example.com
13 -}
14 -----------
15 -{
16 - "logging": {
17 - "logs": {
18 - "default": {
19 - "exclude": [
20 - "http.log.access.log0",
21 - "http.log.access.log1"
22 - ]
23 - },
24 - "log0": {
25 - "writer": {
26 - "filename": "/var/log/caddy/a.example.com.access.log",
27 - "output": "file"
28 - },
29 - "include": [
30 - "http.log.access.log0"
31 - ]
32 - },
33 - "log1": {
34 - "writer": {
35 - "filename": "/var/log/caddy/b.example.com.access.log",
36 - "output": "file"
37 - },
38 - "include": [
39 - "http.log.access.log1"
40 - ]
41 - }
42 - }
43 - },
44 - "apps": {
45 - "http": {
46 - "servers": {
47 - "srv0": {
48 - "listen": [
49 - ":443"
50 - ],
51 - "routes": [
52 - {
53 - "match": [
54 - {
55 - "host": [
56 - "a.example.com"
57 - ]
58 - }
59 - ],
60 - "terminal": true
61 - },
62 - {
63 - "match": [
64 - {
65 - "host": [
66 - "b.example.com"
67 - ]
68 - }
69 - ],
70 - "terminal": true
71 - }
72 - ],
73 - "logs": {
74 - "logger_names": {
75 - "a.example.com": "log0",
76 - "b.example.com": "log1"
77 - }
78 - }
79 - }
80 - }
81 - }
82 - }
1 +(logging) {
2 + log {
3 + output file /var/log/caddy/{args.0}.access.log
4 + }
5 +}
6 +
7 +a.example.com {
8 + import logging a.example.com
9 +}
10 +
11 +b.example.com {
12 + import logging b.example.com
13 +}
14 +----------
15 +{
16 + "logging": {
17 + "logs": {
18 + "default": {
19 + "exclude": [
20 + "http.log.access.log0",
21 + "http.log.access.log1"
22 + ]
23 + },
24 + "log0": {
25 + "writer": {
26 + "filename": "/var/log/caddy/a.example.com.access.log",
27 + "output": "file"
28 + },
29 + "include": [
30 + "http.log.access.log0"
31 + ]
32 + },
33 + "log1": {
34 + "writer": {
35 + "filename": "/var/log/caddy/b.example.com.access.log",
36 + "output": "file"
37 + },
38 + "include": [
39 + "http.log.access.log1"
40 + ]
41 + }
42 + }
43 + },
44 + "apps": {
45 + "http": {
46 + "servers": {
47 + "srv0": {
48 + "listen": [
49 + ":443"
50 + ],
51 + "routes": [
52 + {
53 + "match": [
54 + {
55 + "host": [
56 + "a.example.com"
57 + ]
58 + }
59 + ],
60 + "terminal": true
61 + },
62 + {
63 + "match": [
64 + {
65 + "host": [
66 + "b.example.com"
67 + ]
68 + }
69 + ],
70 + "terminal": true
71 + }
72 + ],
73 + "logs": {
74 + "logger_names": {
75 + "a.example.com": "log0",
76 + "b.example.com": "log1"
77 + }
78 + }
79 + }
80 + }
81 + }
82 + }
83 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/log_filters.txt
+68 -68
@@ -1,69 +1,69 @@
1 -:80
2 -
3 -log {
4 - output stdout
5 - format filter {
6 - wrap console
7 - fields {
8 - request>headers>Authorization delete
9 - request>headers>Server delete
10 - request>remote_addr ip_mask {
11 - ipv4 24
12 - ipv6 32
13 - }
14 - }
15 - }
16 -}
17 -----------
18 -{
19 - "logging": {
20 - "logs": {
21 - "default": {
22 - "exclude": [
23 - "http.log.access.log0"
24 - ]
25 - },
26 - "log0": {
27 - "writer": {
28 - "output": "stdout"
29 - },
30 - "encoder": {
31 - "fields": {
32 - "request\u003eheaders\u003eAuthorization": {
33 - "filter": "delete"
34 - },
35 - "request\u003eheaders\u003eServer": {
36 - "filter": "delete"
37 - },
38 - "request\u003eremote_addr": {
39 - "filter": "ip_mask",
40 - "ipv4_cidr": 24,
41 - "ipv6_cidr": 32
42 - }
43 - },
44 - "format": "filter",
45 - "wrap": {
46 - "format": "console"
47 - }
48 - },
49 - "include": [
50 - "http.log.access.log0"
51 - ]
52 - }
53 - }
54 - },
55 - "apps": {
56 - "http": {
57 - "servers": {
58 - "srv0": {
59 - "listen": [
60 - ":80"
61 - ],
62 - "logs": {
63 - "default_logger_name": "log0"
64 - }
65 - }
66 - }
67 - }
68 - }
1 +:80
2 +
3 +log {
4 + output stdout
5 + format filter {
6 + wrap console
7 + fields {
8 + request>headers>Authorization delete
9 + request>headers>Server delete
10 + request>remote_addr ip_mask {
11 + ipv4 24
12 + ipv6 32
13 + }
14 + }
15 + }
16 +}
17 +----------
18 +{
19 + "logging": {
20 + "logs": {
21 + "default": {
22 + "exclude": [
23 + "http.log.access.log0"
24 + ]
25 + },
26 + "log0": {
27 + "writer": {
28 + "output": "stdout"
29 + },
30 + "encoder": {
31 + "fields": {
32 + "request\u003eheaders\u003eAuthorization": {
33 + "filter": "delete"
34 + },
35 + "request\u003eheaders\u003eServer": {
36 + "filter": "delete"
37 + },
38 + "request\u003eremote_addr": {
39 + "filter": "ip_mask",
40 + "ipv4_cidr": 24,
41 + "ipv6_cidr": 32
42 + }
43 + },
44 + "format": "filter",
45 + "wrap": {
46 + "format": "console"
47 + }
48 + },
49 + "include": [
50 + "http.log.access.log0"
51 + ]
52 + }
53 + }
54 + },
55 + "apps": {
56 + "http": {
57 + "servers": {
58 + "srv0": {
59 + "listen": [
60 + ":80"
61 + ],
62 + "logs": {
63 + "default_logger_name": "log0"
64 + }
65 + }
66 + }
67 + }
68 + }
69 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/php_fastcgi_expanded_form.txt
+131 -131
@@ -1,132 +1,132 @@
1 -:8886
2 -
3 -route {
4 - # Add trailing slash for directory requests
5 - @canonicalPath {
6 - file {
7 - try_files {path}/index.php
8 - }
9 - not path */
10 - }
11 - redir @canonicalPath {path}/ 308
12 -
13 - # If the requested file does not exist, try index files
14 - @indexFiles {
15 - file {
16 - try_files {path} {path}/index.php index.php
17 - split_path .php
18 - }
19 - }
20 - rewrite @indexFiles {http.matchers.file.relative}
21 -
22 - # Proxy PHP files to the FastCGI responder
23 - @phpFiles {
24 - path *.php
25 - }
26 - reverse_proxy @phpFiles 127.0.0.1:9000 {
27 - transport fastcgi {
28 - split .php
29 - }
30 - }
31 -}
32 -----------
33 -{
34 - "apps": {
35 - "http": {
36 - "servers": {
37 - "srv0": {
38 - "listen": [
39 - ":8886"
40 - ],
41 - "routes": [
42 - {
43 - "handle": [
44 - {
45 - "handler": "subroute",
46 - "routes": [
47 - {
48 - "handle": [
49 - {
50 - "handler": "static_response",
51 - "headers": {
52 - "Location": [
53 - "{http.request.uri.path}/"
54 - ]
55 - },
56 - "status_code": 308
57 - }
58 - ],
59 - "match": [
60 - {
61 - "file": {
62 - "try_files": [
63 - "{http.request.uri.path}/index.php"
64 - ]
65 - },
66 - "not": [
67 - {
68 - "path": [
69 - "*/"
70 - ]
71 - }
72 - ]
73 - }
74 - ]
75 - },
76 - {
77 - "handle": [
78 - {
79 - "handler": "rewrite",
80 - "uri": "{http.matchers.file.relative}"
81 - }
82 - ],
83 - "match": [
84 - {
85 - "file": {
86 - "split_path": [
87 - ".php"
88 - ],
89 - "try_files": [
90 - "{http.request.uri.path}",
91 - "{http.request.uri.path}/index.php",
92 - "index.php"
93 - ]
94 - }
95 - }
96 - ]
97 - },
98 - {
99 - "handle": [
100 - {
101 - "handler": "reverse_proxy",
102 - "transport": {
103 - "protocol": "fastcgi",
104 - "split_path": [
105 - ".php"
106 - ]
107 - },
108 - "upstreams": [
109 - {
110 - "dial": "127.0.0.1:9000"
111 - }
112 - ]
113 - }
114 - ],
115 - "match": [
116 - {
117 - "path": [
118 - "*.php"
119 - ]
120 - }
121 - ]
122 - }
123 - ]
124 - }
125 - ]
126 - }
127 - ]
128 - }
129 - }
130 - }
131 - }
1 +:8886
2 +
3 +route {
4 + # Add trailing slash for directory requests
5 + @canonicalPath {
6 + file {
7 + try_files {path}/index.php
8 + }
9 + not path */
10 + }
11 + redir @canonicalPath {path}/ 308
12 +
13 + # If the requested file does not exist, try index files
14 + @indexFiles {
15 + file {
16 + try_files {path} {path}/index.php index.php
17 + split_path .php
18 + }
19 + }
20 + rewrite @indexFiles {http.matchers.file.relative}
21 +
22 + # Proxy PHP files to the FastCGI responder
23 + @phpFiles {
24 + path *.php
25 + }
26 + reverse_proxy @phpFiles 127.0.0.1:9000 {
27 + transport fastcgi {
28 + split .php
29 + }
30 + }
31 +}
32 +----------
33 +{
34 + "apps": {
35 + "http": {
36 + "servers": {
37 + "srv0": {
38 + "listen": [
39 + ":8886"
40 + ],
41 + "routes": [
42 + {
43 + "handle": [
44 + {
45 + "handler": "subroute",
46 + "routes": [
47 + {
48 + "handle": [
49 + {
50 + "handler": "static_response",
51 + "headers": {
52 + "Location": [
53 + "{http.request.uri.path}/"
54 + ]
55 + },
56 + "status_code": 308
57 + }
58 + ],
59 + "match": [
60 + {
61 + "file": {
62 + "try_files": [
63 + "{http.request.uri.path}/index.php"
64 + ]
65 + },
66 + "not": [
67 + {
68 + "path": [
69 + "*/"
70 + ]
71 + }
72 + ]
73 + }
74 + ]
75 + },
76 + {
77 + "handle": [
78 + {
79 + "handler": "rewrite",
80 + "uri": "{http.matchers.file.relative}"
81 + }
82 + ],
83 + "match": [
84 + {
85 + "file": {
86 + "split_path": [
87 + ".php"
88 + ],
89 + "try_files": [
90 + "{http.request.uri.path}",
91 + "{http.request.uri.path}/index.php",
92 + "index.php"
93 + ]
94 + }
95 + }
96 + ]
97 + },
98 + {
99 + "handle": [
100 + {
101 + "handler": "reverse_proxy",
102 + "transport": {
103 + "protocol": "fastcgi",
104 + "split_path": [
105 + ".php"
106 + ]
107 + },
108 + "upstreams": [
109 + {
110 + "dial": "127.0.0.1:9000"
111 + }
112 + ]
113 + }
114 + ],
115 + "match": [
116 + {
117 + "path": [
118 + "*.php"
119 + ]
120 + }
121 + ]
122 + }
123 + ]
124 + }
125 + ]
126 + }
127 + ]
128 + }
129 + }
130 + }
131 + }
132 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.txt
+111 -111
@@ -1,112 +1,112 @@
1 -:8884
2 -
3 -@api host example.com
4 -php_fastcgi @api localhost:9000
5 -----------
6 -{
7 - "apps": {
8 - "http": {
9 - "servers": {
10 - "srv0": {
11 - "listen": [
12 - ":8884"
13 - ],
14 - "routes": [
15 - {
16 - "match": [
17 - {
18 - "host": [
19 - "example.com"
20 - ]
21 - }
22 - ],
23 - "handle": [
24 - {
25 - "handler": "subroute",
26 - "routes": [
27 - {
28 - "handle": [
29 - {
30 - "handler": "static_response",
31 - "headers": {
32 - "Location": [
33 - "{http.request.uri.path}/"
34 - ]
35 - },
36 - "status_code": 308
37 - }
38 - ],
39 - "match": [
40 - {
41 - "file": {
42 - "try_files": [
43 - "{http.request.uri.path}/index.php"
44 - ]
45 - },
46 - "not": [
47 - {
48 - "path": [
49 - "*/"
50 - ]
51 - }
52 - ]
53 - }
54 - ]
55 - },
56 - {
57 - "handle": [
58 - {
59 - "handler": "rewrite",
60 - "uri": "{http.matchers.file.relative}"
61 - }
62 - ],
63 - "match": [
64 - {
65 - "file": {
66 - "split_path": [
67 - ".php"
68 - ],
69 - "try_files": [
70 - "{http.request.uri.path}",
71 - "{http.request.uri.path}/index.php",
72 - "index.php"
73 - ]
74 - }
75 - }
76 - ]
77 - },
78 - {
79 - "handle": [
80 - {
81 - "handler": "reverse_proxy",
82 - "transport": {
83 - "protocol": "fastcgi",
84 - "split_path": [
85 - ".php"
86 - ]
87 - },
88 - "upstreams": [
89 - {
90 - "dial": "localhost:9000"
91 - }
92 - ]
93 - }
94 - ],
95 - "match": [
96 - {
97 - "path": [
98 - "*.php"
99 - ]
100 - }
101 - ]
102 - }
103 - ]
104 - }
105 - ]
106 - }
107 - ]
108 - }
109 - }
110 - }
111 - }
1 +:8884
2 +
3 +@api host example.com
4 +php_fastcgi @api localhost:9000
5 +----------
6 +{
7 + "apps": {
8 + "http": {
9 + "servers": {
10 + "srv0": {
11 + "listen": [
12 + ":8884"
13 + ],
14 + "routes": [
15 + {
16 + "match": [
17 + {
18 + "host": [
19 + "example.com"
20 + ]
21 + }
22 + ],
23 + "handle": [
24 + {
25 + "handler": "subroute",
26 + "routes": [
27 + {
28 + "handle": [
29 + {
30 + "handler": "static_response",
31 + "headers": {
32 + "Location": [
33 + "{http.request.uri.path}/"
34 + ]
35 + },
36 + "status_code": 308
37 + }
38 + ],
39 + "match": [
40 + {
41 + "file": {
42 + "try_files": [
43 + "{http.request.uri.path}/index.php"
44 + ]
45 + },
46 + "not": [
47 + {
48 + "path": [
49 + "*/"
50 + ]
51 + }
52 + ]
53 + }
54 + ]
55 + },
56 + {
57 + "handle": [
58 + {
59 + "handler": "rewrite",
60 + "uri": "{http.matchers.file.relative}"
61 + }
62 + ],
63 + "match": [
64 + {
65 + "file": {
66 + "split_path": [
67 + ".php"
68 + ],
69 + "try_files": [
70 + "{http.request.uri.path}",
71 + "{http.request.uri.path}/index.php",
72 + "index.php"
73 + ]
74 + }
75 + }
76 + ]
77 + },
78 + {
79 + "handle": [
80 + {
81 + "handler": "reverse_proxy",
82 + "transport": {
83 + "protocol": "fastcgi",
84 + "split_path": [
85 + ".php"
86 + ]
87 + },
88 + "upstreams": [
89 + {
90 + "dial": "localhost:9000"
91 + }
92 + ]
93 + }
94 + ],
95 + "match": [
96 + {
97 + "path": [
98 + "*.php"
99 + ]
100 + }
101 + ]
102 + }
103 + ]
104 + }
105 + ]
106 + }
107 + ]
108 + }
109 + }
110 + }
111 + }
112 }
\ No newline at end of file
caddytest/integration/caddyfile_adapt/sort_directives_with_any_matcher_first.txt
+50 -50
@@ -1,51 +1,51 @@
1 -:80
2 -
3 -respond 200
4 -
5 -@untrusted not remote_ip 10.1.1.0/24
6 -respond @untrusted 401
7 -----------
8 -{
9 - "apps": {
10 - "http": {
11 - "servers": {
12 - "srv0": {
13 - "listen": [
14 - ":80"
15 - ],
16 - "routes": [
17 - {
18 - "match": [
19 - {
20 - "not": [
21 - {
22 - "remote_ip": {
23 - "ranges": [
24 - "10.1.1.0/24"
25 - ]
26 - }
27 - }
28 - ]
29 - }
30 - ],
31 - "handle": [
32 - {
33 - "handler": "static_response",
34 - "status_code": 401
35 - }
36 - ]
37 - },
38 - {
39 - "handle": [
40 - {
41 - "handler": "static_response",
42 - "status_code": 200
43 - }
44 - ]
45 - }
46 - ]
47 - }
48 - }
49 - }
50 - }
1 +:80
2 +
3 +respond 200
4 +
5 +@untrusted not remote_ip 10.1.1.0/24
6 +respond @untrusted 401
7 +----------
8 +{
9 + "apps": {
10 + "http": {
11 + "servers": {
12 + "srv0": {
13 + "listen": [
14 + ":80"
15 + ],
16 + "routes": [
17 + {
18 + "match": [
19 + {
20 + "not": [
21 + {
22 + "remote_ip": {
23 + "ranges": [
24 + "10.1.1.0/24"
25 + ]
26 + }
27 + }
28 + ]
29 + }
30 + ],
31 + "handle": [
32 + {
33 + "handler": "static_response",
34 + "status_code": 401
35 + }
36 + ]
37 + },
38 + {
39 + "handle": [
40 + {
41 + "handler": "static_response",
42 + "status_code": 200
43 + }
44 + ]
45 + }
46 + ]
47 + }
48 + }
49 + }
50 + }
51 }
\ No newline at end of file
modules/caddyhttp/caddyhttp.go
+4
@@ -23,6 +23,7 @@ import (
23 "strconv"
24
25 "github.com/caddyserver/caddy/v2"
26 + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
27 )
28
29 func init() {
@@ -231,6 +232,8 @@ func (tlsPlaceholderWrapper) CaddyModule() caddy.ModuleInfo {
232
233 func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
234
235 +func (tlsPlaceholderWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return nil }
236 +
237 const (
238 // DefaultHTTPPort is the default port for HTTP.
239 DefaultHTTPPort = 80
@@ -241,3 +244,4 @@ const (
244
245 // Interface guard
246 var _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
247 +var _ caddyfile.Unmarshaler = (*tlsPlaceholderWrapper)(nil)