@marcel91200 / caddy Mirror · read-only / commits / bc2e4065
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: Refactor global options parsing; prevent duplicate keys

Matthew Holt committed Feb 16, 2020 at 15:28 UTC bc2e406572e9af290469b2d4d58974ac99c0b492
1 file changed +63 -36
caddyconfig/httpcaddyfile/httptype.go
+63 -36
@@ -43,49 +43,31 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
43 var warnings []caddyconfig.Warning
44 gc := counter{new(int)}
45
46 + // load all the server blocks and associate them with a "pile"
47 + // of config values; also prohibit duplicate keys because they
48 + // can make a config confusing if more than one server block is
49 + // chosen to handle a request - we actually will make each
50 + // server block's route terminal so that only one will run
51 + sbKeys := make(map[string]struct{})
52 var serverBlocks []serverBlock
47 - for _, sblock := range originalServerBlocks {
53 + for i, sblock := range originalServerBlocks {
54 + for j, k := range sblock.Keys {
55 + if _, ok := sbKeys[k]; ok {
56 + return nil, warnings, fmt.Errorf("duplicate site address not allowed: '%s' in %v (site block %d, key %d)", k, sblock.Keys, i, j)
57 + }
58 + sbKeys[k] = struct{}{}
59 + }
60 serverBlocks = append(serverBlocks, serverBlock{
61 block: sblock,
62 pile: make(map[string][]ConfigValue),
63 })
64 }
65
54 - // global configuration
55 - if len(serverBlocks) > 0 && len(serverBlocks[0].block.Keys) == 0 {
56 - sb := serverBlocks[0]
57 - for _, segment := range sb.block.Segments {
58 - dir := segment.Directive()
59 - var val interface{}
60 - var err error
61 - disp := caddyfile.NewDispenser(segment)
62 - // TODO: make this switch into a map
63 - switch dir {
64 - case "http_port":
65 - val, err = parseOptHTTPPort(disp)
66 - case "https_port":
67 - val, err = parseOptHTTPSPort(disp)
68 - case "order":
69 - val, err = parseOptOrder(disp)
70 - case "experimental_http3":
71 - val, err = parseOptExperimentalHTTP3(disp)
72 - case "storage":
73 - val, err = parseOptStorage(disp)
74 - case "acme_ca", "acme_dns", "acme_ca_root":
75 - val, err = parseOptACME(disp)
76 - case "email":
77 - val, err = parseOptEmail(disp)
78 - case "admin":
79 - val, err = parseOptAdmin(disp)
80 - default:
81 - return nil, warnings, fmt.Errorf("unrecognized parameter name: %s", dir)
82 - }
83 - if err != nil {
84 - return nil, warnings, fmt.Errorf("%s: %v", dir, err)
85 - }
86 - options[dir] = val
87 - }
88 - serverBlocks = serverBlocks[1:]
66 + // apply any global options
67 + var err error
68 + serverBlocks, err = st.evaluateGlobalOptionsBlock(serverBlocks, options)
69 + if err != nil {
70 + return nil, warnings, err
71 }
72
73 for _, sb := range serverBlocks {
@@ -129,6 +111,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
111 }
112 }
113
114 + // evaluate each directive ("segment") in this block
115 for _, segment := range sb.block.Segments {
116 dir := segment.Directive()
117
@@ -288,6 +271,50 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
271 return cfg, warnings, nil
272 }
273
274 +// evaluateGlobalOptionsBlock evaluates the global options block,
275 +// which is expected to be the first server block if it has zero
276 +// keys. It returns the updated list of server blocks with the
277 +// global options block removed, and updates options accordingly.
278 +func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options map[string]interface{}) ([]serverBlock, error) {
279 + if len(serverBlocks) == 0 || len(serverBlocks[0].block.Keys) > 0 {
280 + return serverBlocks, nil
281 + }
282 +
283 + for _, segment := range serverBlocks[0].block.Segments {
284 + dir := segment.Directive()
285 + var val interface{}
286 + var err error
287 + disp := caddyfile.NewDispenser(segment)
288 + // TODO: make this switch into a map
289 + switch dir {
290 + case "http_port":
291 + val, err = parseOptHTTPPort(disp)
292 + case "https_port":
293 + val, err = parseOptHTTPSPort(disp)
294 + case "order":
295 + val, err = parseOptOrder(disp)
296 + case "experimental_http3":
297 + val, err = parseOptExperimentalHTTP3(disp)
298 + case "storage":
299 + val, err = parseOptStorage(disp)
300 + case "acme_ca", "acme_dns", "acme_ca_root":
301 + val, err = parseOptACME(disp)
302 + case "email":
303 + val, err = parseOptEmail(disp)
304 + case "admin":
305 + val, err = parseOptAdmin(disp)
306 + default:
307 + return nil, fmt.Errorf("unrecognized parameter name: %s", dir)
308 + }
309 + if err != nil {
310 + return nil, fmt.Errorf("%s: %v", dir, err)
311 + }
312 + options[dir] = val
313 + }
314 +
315 + return serverBlocks[1:], nil
316 +}
317 +
318 // hostsFromServerBlockKeys returns a list of all the
319 // hostnames found in the keys of the server block sb.
320 // The list may not be in a consistent order.