aboutsummaryrefslogtreecommitdiffhomepage
path: root/caddyconfig/httpcaddyfile/serveroptions.go
diff options
context:
space:
mode:
authorFrancis Lavoie <[email protected]>2020-11-23 14:46:50 -0500
committerGitHub <[email protected]>2020-11-23 12:46:50 -0700
commit3cfefeb0f71d54f1d9a76a63be7b97d0943c88ef (patch)
treeefc01573221ab12af054df6b1b7832a88adf6fd3 /caddyconfig/httpcaddyfile/serveroptions.go
parent4a641f6c6fb441a8301eeb8cab0c0b61de70ff7f (diff)
downloadcaddy-3cfefeb0f71d54f1d9a76a63be7b97d0943c88ef.tar.gz
caddy-3cfefeb0f71d54f1d9a76a63be7b97d0943c88ef.zip
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 <[email protected]> * httpcaddyfile: Reword listener address "matcher" * Apply suggestions from code review Co-authored-by: Matt Holt <[email protected]> * httpcaddyfile: Deprecate experimental_http3 option (moved to servers) * httpcaddyfile: Remove validation step, no longer needed Co-authored-by: Matt Holt <[email protected]>
Diffstat (limited to 'caddyconfig/httpcaddyfile/serveroptions.go')
-rw-r--r--caddyconfig/httpcaddyfile/serveroptions.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/caddyconfig/httpcaddyfile/serveroptions.go b/caddyconfig/httpcaddyfile/serveroptions.go
new file mode 100644
index 000000000..38fa0f155
--- /dev/null
+++ b/caddyconfig/httpcaddyfile/serveroptions.go
@@ -0,0 +1,235 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package httpcaddyfile
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+ "github.com/dustin/go-humanize"
+)
+
+// serverOptions collects server config overrides parsed from Caddyfile global options
+type serverOptions struct {
+ // If set, will only apply these options to servers that contain a
+ // listener address that matches exactly. If empty, will apply to all
+ // servers that were not already matched by another serverOptions.
+ ListenerAddress string
+
+ // These will all map 1:1 to the caddyhttp.Server struct
+ ListenerWrappersRaw []json.RawMessage
+ ReadTimeout caddy.Duration
+ ReadHeaderTimeout caddy.Duration
+ WriteTimeout caddy.Duration
+ IdleTimeout caddy.Duration
+ MaxHeaderBytes int
+ AllowH2C bool
+ ExperimentalHTTP3 bool
+ StrictSNIHost *bool
+}
+
+func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
+ serverOpts := serverOptions{}
+ for d.Next() {
+ if d.NextArg() {
+ serverOpts.ListenerAddress = d.Val()
+ if d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ }
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
+ switch d.Val() {
+ case "listener_wrappers":
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
+ mod, err := caddy.GetModule("caddy.listeners." + d.Val())
+ if err != nil {
+ return nil, fmt.Errorf("finding listener module '%s': %v", d.Val(), err)
+ }
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
+ if !ok {
+ return nil, fmt.Errorf("listener module '%s' is not a Caddyfile unmarshaler", mod)
+ }
+ err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
+ if err != nil {
+ return nil, err
+ }
+ listenerWrapper, ok := unm.(caddy.ListenerWrapper)
+ if !ok {
+ return nil, fmt.Errorf("module %s is not a listener wrapper", mod)
+ }
+ jsonListenerWrapper := caddyconfig.JSONModuleObject(
+ listenerWrapper,
+ "wrapper",
+ listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
+ nil,
+ )
+ serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
+ }
+
+ case "timeouts":
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
+ switch d.Val() {
+ case "read_body":
+ if !d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ dur, err := caddy.ParseDuration(d.Val())
+ if err != nil {
+ return nil, d.Errf("parsing read_body timeout duration: %v", err)
+ }
+ serverOpts.ReadTimeout = caddy.Duration(dur)
+
+ case "read_header":
+ if !d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ dur, err := caddy.ParseDuration(d.Val())
+ if err != nil {
+ return nil, d.Errf("parsing read_header timeout duration: %v", err)
+ }
+ serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
+
+ case "write":
+ if !d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ dur, err := caddy.ParseDuration(d.Val())
+ if err != nil {
+ return nil, d.Errf("parsing write timeout duration: %v", err)
+ }
+ serverOpts.WriteTimeout = caddy.Duration(dur)
+
+ case "idle":
+ if !d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ dur, err := caddy.ParseDuration(d.Val())
+ if err != nil {
+ return nil, d.Errf("parsing idle timeout duration: %v", err)
+ }
+ serverOpts.IdleTimeout = caddy.Duration(dur)
+
+ default:
+ return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
+ }
+ }
+
+ case "max_header_size":
+ var sizeStr string
+ if !d.AllArgs(&sizeStr) {
+ return nil, d.ArgErr()
+ }
+ size, err := humanize.ParseBytes(sizeStr)
+ if err != nil {
+ return nil, d.Errf("parsing max_header_size: %v", err)
+ }
+ serverOpts.MaxHeaderBytes = int(size)
+
+ case "protocol":
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
+ switch d.Val() {
+ case "allow_h2c":
+ if d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ serverOpts.AllowH2C = true
+
+ case "experimental_http3":
+ if d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ serverOpts.ExperimentalHTTP3 = true
+
+ case "strict_sni_host":
+ if d.NextArg() {
+ return nil, d.ArgErr()
+ }
+ trueBool := true
+ serverOpts.StrictSNIHost = &trueBool
+
+ default:
+ return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
+ }
+ }
+
+ default:
+ return nil, d.Errf("unrecognized servers option '%s'", d.Val())
+ }
+ }
+ }
+ return serverOpts, nil
+}
+
+// applyServerOptions sets the server options on the appropriate servers
+func applyServerOptions(
+ servers map[string]*caddyhttp.Server,
+ options map[string]interface{},
+ warnings *[]caddyconfig.Warning,
+) error {
+ // If experimental HTTP/3 is enabled, enable it on each server.
+ // We already know there won't be a conflict with serverOptions because
+ // we validated earlier that "experimental_http3" cannot be set at the same
+ // time as "servers"
+ if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
+ *warnings = append(*warnings, caddyconfig.Warning{Message: "the 'experimental_http3' global option is deprecated, please use the 'servers > protocol > experimental_http3' option instead"})
+ for _, srv := range servers {
+ srv.ExperimentalHTTP3 = true
+ }
+ }
+
+ serverOpts, ok := options["servers"].([]serverOptions)
+ if !ok {
+ return nil
+ }
+
+ for _, server := range servers {
+ // find the options that apply to this server
+ opts := func() *serverOptions {
+ for _, entry := range serverOpts {
+ if entry.ListenerAddress == "" {
+ return &entry
+ }
+ for _, listener := range server.Listen {
+ if entry.ListenerAddress == listener {
+ return &entry
+ }
+ }
+ }
+ return nil
+ }()
+
+ // if none apply, then move to the next server
+ if opts == nil {
+ continue
+ }
+
+ // set all the options
+ server.ListenerWrappersRaw = opts.ListenerWrappersRaw
+ server.ReadTimeout = opts.ReadTimeout
+ server.ReadHeaderTimeout = opts.ReadHeaderTimeout
+ server.WriteTimeout = opts.WriteTimeout
+ server.IdleTimeout = opts.IdleTimeout
+ server.MaxHeaderBytes = opts.MaxHeaderBytes
+ server.AllowH2C = opts.AllowH2C
+ server.ExperimentalHTTP3 = opts.ExperimentalHTTP3
+ server.StrictSNIHost = opts.StrictSNIHost
+ }
+
+ return nil
+}