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: Make global options pluggable (#3265)
* httpcaddyfile: Make global options pluggable * httpcaddyfile: Add a global options adapt test * httpcaddyfile: Wrap err Co-Authored-By: Dave Henderson <dhenderson@gmail.com> * httpcaddyfile: Revert wrap err Co-authored-by: Dave Henderson <dhenderson@gmail.com>
Francis Lavoie committed
May 11, 2020 at 17:00 UTC
dc9f4f13fc1cc102c713c69ded295b3fcf685e96
4 files changed
+130
-41
caddyconfig/httpcaddyfile/directives.go
+18
@@ -120,6 +120,17 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
120
})
121
}
122
123
+// RegisterGlobalOption registers a unique global option opt with
124
+// an associated unmarshaling (setup) function. When the global
125
+// option opt is encountered in a Caddyfile, setupFunc will be
126
+// called to unmarshal its tokens.
127
+func RegisterGlobalOption(opt string, setupFunc UnmarshalGlobalFunc) {
128
+ if _, ok := registeredGlobalOptions[opt]; ok {
129
+ panic("global option " + opt + " already registered")
130
+ }
131
+ registeredGlobalOptions[opt] = setupFunc
132
+}
133
+
134
// Helper is a type which helps setup a value from
135
// Caddyfile tokens.
136
type Helper struct {
@@ -454,6 +465,13 @@ type (
465
// for you. These are passed to a call to
466
// RegisterHandlerDirective.
467
UnmarshalHandlerFunc func(h Helper) (caddyhttp.MiddlewareHandler, error)
468
+
469
+ // UnmarshalGlobalFunc is a function which can unmarshal Caddyfile
470
+ // tokens into a global option config value using a Helper type.
471
+ // These are passed in a call to RegisterGlobalOption.
472
+ UnmarshalGlobalFunc func(d *caddyfile.Dispenser) (interface{}, error)
473
)
474
475
var registeredDirectives = make(map[string]UnmarshalFunc)
476
+
477
+var registeredGlobalOptions = make(map[string]UnmarshalGlobalFunc)
caddyconfig/httpcaddyfile/httptype.go
+9
-30
@@ -284,39 +284,18 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
284
var val interface{}
285
var err error
286
disp := caddyfile.NewDispenser(segment)
287
- switch dir {
288
- case "debug":
289
- val = true
290
- case "http_port":
291
- val, err = parseOptHTTPPort(disp)
292
- case "https_port":
293
- val, err = parseOptHTTPSPort(disp)
294
- case "default_sni":
295
- val, err = parseOptSingleString(disp)
296
- case "order":
297
- val, err = parseOptOrder(disp)
298
- case "experimental_http3":
299
- val, err = parseOptExperimentalHTTP3(disp)
300
- case "storage":
301
- val, err = parseOptStorage(disp)
302
- case "acme_ca", "acme_dns", "acme_ca_root":
303
- val, err = parseOptSingleString(disp)
304
- case "email":
305
- val, err = parseOptSingleString(disp)
306
- case "admin":
307
- val, err = parseOptAdmin(disp)
308
- case "on_demand_tls":
309
- val, err = parseOptOnDemand(disp)
310
- case "local_certs":
311
- val = true
312
- case "key_type":
313
- val, err = parseOptSingleString(disp)
314
- default:
315
- return nil, fmt.Errorf("unrecognized parameter name: %s", dir)
287
+
288
+ dirFunc, ok := registeredGlobalOptions[dir]
289
+ if !ok {
290
+ tkn := segment[0]
291
+ return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, dir)
292
}
293
+
294
+ val, err = dirFunc(disp)
295
if err != nil {
318
- return nil, fmt.Errorf("%s: %v", dir, err)
296
+ return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
297
}
298
+
299
options[dir] = val
300
}
301
caddyconfig/httpcaddyfile/options.go
+29
-11
@@ -23,7 +23,29 @@ import (
23
"github.com/caddyserver/caddy/v2/modules/caddytls"
24
)
25
26
-func parseOptHTTPPort(d *caddyfile.Dispenser) (int, error) {
26
+func init() {
27
+ RegisterGlobalOption("debug", parseOptTrue)
28
+ RegisterGlobalOption("http_port", parseOptHTTPPort)
29
+ RegisterGlobalOption("https_port", parseOptHTTPSPort)
30
+ RegisterGlobalOption("default_sni", parseOptSingleString)
31
+ RegisterGlobalOption("order", parseOptOrder)
32
+ RegisterGlobalOption("experimental_http3", parseOptTrue)
33
+ RegisterGlobalOption("storage", parseOptStorage)
34
+ RegisterGlobalOption("acme_ca", parseOptSingleString)
35
+ RegisterGlobalOption("acme_dns", parseOptSingleString)
36
+ RegisterGlobalOption("acme_ca_root", parseOptSingleString)
37
+ RegisterGlobalOption("email", parseOptSingleString)
38
+ RegisterGlobalOption("admin", parseOptAdmin)
39
+ RegisterGlobalOption("on_demand_tls", parseOptOnDemand)
40
+ RegisterGlobalOption("local_certs", parseOptTrue)
41
+ RegisterGlobalOption("key_type", parseOptSingleString)
42
+}
43
+
44
+func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
45
+ return true, nil
46
+}
47
+
48
+func parseOptHTTPPort(d *caddyfile.Dispenser) (interface{}, error) {
49
var httpPort int
50
for d.Next() {
51
var httpPortStr string
@@ -39,7 +61,7 @@ func parseOptHTTPPort(d *caddyfile.Dispenser) (int, error) {
61
return httpPort, nil
62
}
63
42
-func parseOptHTTPSPort(d *caddyfile.Dispenser) (int, error) {
64
+func parseOptHTTPSPort(d *caddyfile.Dispenser) (interface{}, error) {
65
var httpsPort int
66
for d.Next() {
67
var httpsPortStr string
@@ -55,11 +77,7 @@ func parseOptHTTPSPort(d *caddyfile.Dispenser) (int, error) {
77
return httpsPort, nil
78
}
79
58
-func parseOptExperimentalHTTP3(d *caddyfile.Dispenser) (bool, error) {
59
- return true, nil
60
-}
61
-
62
-func parseOptOrder(d *caddyfile.Dispenser) ([]string, error) {
80
+func parseOptOrder(d *caddyfile.Dispenser) (interface{}, error) {
81
newOrder := directiveOrder
82
83
for d.Next() {
@@ -135,7 +153,7 @@ func parseOptOrder(d *caddyfile.Dispenser) ([]string, error) {
153
return newOrder, nil
154
}
155
138
-func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) {
156
+func parseOptStorage(d *caddyfile.Dispenser) (interface{}, error) {
157
if !d.Next() { // consume option name
158
return nil, d.ArgErr()
159
}
@@ -162,7 +180,7 @@ func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) {
180
return storage, nil
181
}
182
165
-func parseOptSingleString(d *caddyfile.Dispenser) (string, error) {
183
+func parseOptSingleString(d *caddyfile.Dispenser) (interface{}, error) {
184
d.Next() // consume parameter name
185
if !d.Next() {
186
return "", d.ArgErr()
@@ -174,7 +192,7 @@ func parseOptSingleString(d *caddyfile.Dispenser) (string, error) {
192
return val, nil
193
}
194
177
-func parseOptAdmin(d *caddyfile.Dispenser) (string, error) {
195
+func parseOptAdmin(d *caddyfile.Dispenser) (interface{}, error) {
196
if d.Next() {
197
var listenAddress string
198
if !d.AllArgs(&listenAddress) {
@@ -188,7 +206,7 @@ func parseOptAdmin(d *caddyfile.Dispenser) (string, error) {
206
return "", nil
207
}
208
191
-func parseOptOnDemand(d *caddyfile.Dispenser) (*caddytls.OnDemandConfig, error) {
209
+func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
210
var ond *caddytls.OnDemandConfig
211
for d.Next() {
212
if d.NextArg() {
caddytest/integration/caddyfile_adapt_test.go
+74
@@ -415,3 +415,77 @@ func TestNotBlockMerging(t *testing.T) {
415
}
416
}`)
417
}
418
+
419
+func TestGlobalOptions(t *testing.T) {
420
+ caddytest.AssertAdapt(t, `
421
+ {
422
+ debug
423
+ http_port 8080
424
+ https_port 8443
425
+ default_sni localhost
426
+ order root first
427
+ storage file_system {
428
+ root /data
429
+ }
430
+ acme_ca https://example.com
431
+ acme_ca_root /path/to/ca.crt
432
+ email test@example.com
433
+ admin off
434
+ on_demand_tls {
435
+ ask https://example.com
436
+ interval 30s
437
+ burst 20
438
+ }
439
+ local_certs
440
+ key_type ed25519
441
+ }
442
+
443
+ :80
444
+ `, "caddyfile", `{
445
+ "admin": {
446
+ "disabled": true
447
+ },
448
+ "logging": {
449
+ "logs": {
450
+ "default": {
451
+ "level": "DEBUG"
452
+ }
453
+ }
454
+ },
455
+ "storage": {
456
+ "module": "file_system",
457
+ "root": "/data"
458
+ },
459
+ "apps": {
460
+ "http": {
461
+ "http_port": 8080,
462
+ "https_port": 8443,
463
+ "servers": {
464
+ "srv0": {
465
+ "listen": [
466
+ ":80"
467
+ ]
468
+ }
469
+ }
470
+ },
471
+ "tls": {
472
+ "automation": {
473
+ "policies": [
474
+ {
475
+ "issuer": {
476
+ "module": "internal"
477
+ }
478
+ }
479
+ ],
480
+ "on_demand": {
481
+ "rate_limit": {
482
+ "interval": 30000000000,
483
+ "burst": 20
484
+ },
485
+ "ask": "https://example.com"
486
+ }
487
+ }
488
+ }
489
+ }
490
+}`)
491
+}