diff options
Diffstat (limited to 'modules/caddyhttp')
38 files changed, 400 insertions, 258 deletions
diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index 6412d36..8aa44f1 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -28,7 +28,7 @@ func init() {  // HTTPBasicAuth facilitates HTTP basic authentication.  type HTTPBasicAuth struct { -	HashRaw     json.RawMessage `json:"hash,omitempty"` +	HashRaw     json.RawMessage `json:"hash,omitempty" caddy:"namespace=http.authentication.hashes inline_key=algorithm"`  	AccountList []Account       `json:"accounts,omitempty"`  	Realm       string          `json:"realm,omitempty"` @@ -39,8 +39,8 @@ type HTTPBasicAuth struct {  // CaddyModule returns the Caddy module information.  func (HTTPBasicAuth) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.authentication.providers.http_basic", -		New:  func() caddy.Module { return new(HTTPBasicAuth) }, +		ID:  "http.authentication.providers.http_basic", +		New: func() caddy.Module { return new(HTTPBasicAuth) },  	}  } @@ -51,12 +51,11 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error {  	}  	// load password hasher -	hashIface, err := ctx.LoadModuleInline("algorithm", "http.handlers.authentication.hashes", hba.HashRaw) +	hasherIface, err := ctx.LoadModule(hba, "HashRaw")  	if err != nil {  		return fmt.Errorf("loading password hasher module: %v", err)  	} -	hba.Hash = hashIface.(Comparer) -	hba.HashRaw = nil // allow GC to deallocate +	hba.Hash = hasherIface.(Comparer)  	if hba.Hash == nil {  		return fmt.Errorf("hash is required") diff --git a/modules/caddyhttp/caddyauth/caddyauth.go b/modules/caddyhttp/caddyauth/caddyauth.go index 48d4fba..c79d080 100644 --- a/modules/caddyhttp/caddyauth/caddyauth.go +++ b/modules/caddyhttp/caddyauth/caddyauth.go @@ -15,7 +15,6 @@  package caddyauth  import ( -	"encoding/json"  	"fmt"  	"log"  	"net/http" @@ -30,7 +29,7 @@ func init() {  // Authentication is a middleware which provides user authentication.  type Authentication struct { -	ProvidersRaw map[string]json.RawMessage `json:"providers,omitempty"` +	ProvidersRaw caddy.ModuleMap `json:"providers,omitempty" caddy:"namespace=http.authentication.providers"`  	Providers map[string]Authenticator `json:"-"`  } @@ -38,23 +37,21 @@ type Authentication struct {  // CaddyModule returns the Caddy module information.  func (Authentication) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.authentication", -		New:  func() caddy.Module { return new(Authentication) }, +		ID:  "http.handlers.authentication", +		New: func() caddy.Module { return new(Authentication) },  	}  }  // Provision sets up a.  func (a *Authentication) Provision(ctx caddy.Context) error {  	a.Providers = make(map[string]Authenticator) -	for modName, rawMsg := range a.ProvidersRaw { -		val, err := ctx.LoadModule("http.handlers.authentication.providers."+modName, rawMsg) -		if err != nil { -			return fmt.Errorf("loading authentication provider module '%s': %v", modName, err) -		} -		a.Providers[modName] = val.(Authenticator) +	mods, err := ctx.LoadModule(a, "ProvidersRaw") +	if err != nil { +		return fmt.Errorf("loading authentication providers: %v", err) +	} +	for modName, modIface := range mods.(map[string]interface{}) { +		a.Providers[modName] = modIface.(Authenticator)  	} -	a.ProvidersRaw = nil // allow GC to deallocate -  	return nil  } diff --git a/modules/caddyhttp/caddyauth/caddyfile.go b/modules/caddyhttp/caddyauth/caddyfile.go index 3600324..8a33e6f 100644 --- a/modules/caddyhttp/caddyauth/caddyfile.go +++ b/modules/caddyhttp/caddyauth/caddyfile.go @@ -16,8 +16,8 @@ package caddyauth  import (  	"encoding/base64" -	"encoding/json" +	"github.com/caddyserver/caddy/v2"  	"github.com/caddyserver/caddy/v2/caddyconfig"  	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"  	"github.com/caddyserver/caddy/v2/modules/caddyhttp" @@ -97,7 +97,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)  	}  	return Authentication{ -		ProvidersRaw: map[string]json.RawMessage{ +		ProvidersRaw: caddy.ModuleMap{  			"http_basic": caddyconfig.JSON(ba, nil),  		},  	}, nil diff --git a/modules/caddyhttp/caddyauth/hashes.go b/modules/caddyhttp/caddyauth/hashes.go index 13010db..3ca5116 100644 --- a/modules/caddyhttp/caddyauth/hashes.go +++ b/modules/caddyhttp/caddyauth/hashes.go @@ -33,8 +33,8 @@ type BcryptHash struct{}  // CaddyModule returns the Caddy module information.  func (BcryptHash) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.authentication.hashes.bcrypt", -		New:  func() caddy.Module { return new(BcryptHash) }, +		ID:  "http.authentication.hashes.bcrypt", +		New: func() caddy.Module { return new(BcryptHash) },  	}  } @@ -61,8 +61,8 @@ type ScryptHash struct {  // CaddyModule returns the Caddy module information.  func (ScryptHash) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.authentication.hashes.scrypt", -		New:  func() caddy.Module { return new(ScryptHash) }, +		ID:  "http.authentication.hashes.scrypt", +		New: func() caddy.Module { return new(ScryptHash) },  	}  } diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index 064a963..756a6c3 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -44,12 +44,27 @@ func init() {  	}  } -// App is the HTTP app for Caddy. +// App is a robust, flexible HTTP server for Caddy.  type App struct { -	HTTPPort    int                `json:"http_port,omitempty"` -	HTTPSPort   int                `json:"https_port,omitempty"` -	GracePeriod caddy.Duration     `json:"grace_period,omitempty"` -	Servers     map[string]*Server `json:"servers,omitempty"` +	// HTTPPort specifies the port to use for HTTP (as opposed to HTTPS), +	// which is used when setting up HTTP->HTTPS redirects or ACME HTTP +	// challenge solvers. Default: 80. +	HTTPPort int `json:"http_port,omitempty"` + +	// HTTPSPort specifies the port to use for HTTPS, which is used when +	// solving the ACME TLS-ALPN challenges, or whenever HTTPS is needed +	// but no specific port number is given. Default: 443. +	HTTPSPort int `json:"https_port,omitempty"` + +	// GracePeriod is how long to wait for active connections when shutting +	// down the server. Once the grace period is over, connections will +	// be forcefully closed. +	GracePeriod caddy.Duration `json:"grace_period,omitempty"` + +	// Servers is the list of servers, keyed by arbitrary names chosen +	// at your discretion for your own convenience; the keys do not +	// affect functionality. +	Servers map[string]*Server `json:"servers,omitempty"`  	servers     []*http.Server  	h3servers   []*http3.Server @@ -62,8 +77,8 @@ type App struct {  // CaddyModule returns the Caddy module information.  func (App) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http", -		New:  func() caddy.Module { return new(App) }, +		ID:  "http", +		New: func() caddy.Module { return new(App) },  	}  } @@ -561,8 +576,10 @@ var emptyHandler HandlerFunc = func(http.ResponseWriter, *http.Request) error {  // WeakString is a type that unmarshals any JSON value  // as a string literal, with the following exceptions: -// 1) actual string values are decoded as strings; and -// 2) null is decoded as empty string; +// +// 1. actual string values are decoded as strings; and +// 2. null is decoded as empty string; +//  // and provides methods for getting the value as various  // primitive types. However, using this type removes any  // type safety as far as deserializing JSON is concerned. diff --git a/modules/caddyhttp/encode/brotli/brotli.go b/modules/caddyhttp/encode/brotli/brotli.go index cf055aa..52bb205 100644 --- a/modules/caddyhttp/encode/brotli/brotli.go +++ b/modules/caddyhttp/encode/brotli/brotli.go @@ -37,8 +37,8 @@ type Brotli struct {  // CaddyModule returns the Caddy module information.  func (Brotli) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.encoders.brotli", -		New:  func() caddy.Module { return new(Brotli) }, +		ID:  "http.encoders.brotli", +		New: func() caddy.Module { return new(Brotli) },  	}  } diff --git a/modules/caddyhttp/encode/caddyfile.go b/modules/caddyhttp/encode/caddyfile.go index 4764e9b..dd12de2 100644 --- a/modules/caddyhttp/encode/caddyfile.go +++ b/modules/caddyhttp/encode/caddyfile.go @@ -15,7 +15,6 @@  package encode  import ( -	"encoding/json"  	"fmt"  	"github.com/caddyserver/caddy/v2" @@ -52,14 +51,14 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  		for _, arg := range d.RemainingArgs() {  			mod, err := caddy.GetModule("http.encoders." + arg)  			if err != nil { -				return fmt.Errorf("finding encoder module '%s': %v", mod.Name, err) +				return fmt.Errorf("finding encoder module '%s': %v", mod, err)  			}  			encoding, ok := mod.New().(Encoding)  			if !ok { -				return fmt.Errorf("module %s is not an HTTP encoding", mod.Name) +				return fmt.Errorf("module %s is not an HTTP encoding", mod)  			}  			if enc.EncodingsRaw == nil { -				enc.EncodingsRaw = make(map[string]json.RawMessage) +				enc.EncodingsRaw = make(caddy.ModuleMap)  			}  			enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)  		} @@ -72,7 +71,7 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  			}  			unm, ok := mod.New().(caddyfile.Unmarshaler)  			if !ok { -				return fmt.Errorf("encoder module '%s' is not a Caddyfile unmarshaler", mod.Name) +				return fmt.Errorf("encoder module '%s' is not a Caddyfile unmarshaler", mod)  			}  			err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())  			if err != nil { @@ -80,10 +79,10 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  			}  			encoding, ok := unm.(Encoding)  			if !ok { -				return fmt.Errorf("module %s is not an HTTP encoding", mod.Name) +				return fmt.Errorf("module %s is not an HTTP encoding", mod)  			}  			if enc.EncodingsRaw == nil { -				enc.EncodingsRaw = make(map[string]json.RawMessage) +				enc.EncodingsRaw = make(caddy.ModuleMap)  			}  			enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil)  		} diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go index 3716fc6..c68f507 100644 --- a/modules/caddyhttp/encode/encode.go +++ b/modules/caddyhttp/encode/encode.go @@ -21,7 +21,6 @@ package encode  import (  	"bytes" -	"encoding/json"  	"fmt"  	"io"  	"net/http" @@ -40,9 +39,9 @@ func init() {  // Encode is a middleware which can encode responses.  type Encode struct { -	EncodingsRaw map[string]json.RawMessage `json:"encodings,omitempty"` -	Prefer       []string                   `json:"prefer,omitempty"` -	MinLength    int                        `json:"minimum_length,omitempty"` +	EncodingsRaw caddy.ModuleMap `json:"encodings,omitempty" caddy:"namespace=http.encoders"` +	Prefer       []string        `json:"prefer,omitempty"` +	MinLength    int             `json:"minimum_length,omitempty"`  	writerPools map[string]*sync.Pool // TODO: these pools do not get reused through config reloads...  } @@ -50,25 +49,23 @@ type Encode struct {  // CaddyModule returns the Caddy module information.  func (Encode) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.encode", -		New:  func() caddy.Module { return new(Encode) }, +		ID:  "http.handlers.encode", +		New: func() caddy.Module { return new(Encode) },  	}  }  // Provision provisions enc.  func (enc *Encode) Provision(ctx caddy.Context) error { -	for modName, rawMsg := range enc.EncodingsRaw { -		val, err := ctx.LoadModule("http.encoders."+modName, rawMsg) -		if err != nil { -			return fmt.Errorf("loading encoder module '%s': %v", modName, err) -		} -		encoding := val.(Encoding) -		err = enc.addEncoding(encoding) +	mods, err := ctx.LoadModule(enc, "EncodingsRaw") +	if err != nil { +		return fmt.Errorf("loading encoder modules: %v", err) +	} +	for modName, modIface := range mods.(map[string]interface{}) { +		err = enc.addEncoding(modIface.(Encoding))  		if err != nil { -			return err +			return fmt.Errorf("adding encoding %s: %v", modName, err)  		}  	} -	enc.EncodingsRaw = nil // allow GC to deallocate  	if enc.MinLength == 0 {  		enc.MinLength = defaultMinLength diff --git a/modules/caddyhttp/encode/gzip/gzip.go b/modules/caddyhttp/encode/gzip/gzip.go index d6d67f7..590f708 100644 --- a/modules/caddyhttp/encode/gzip/gzip.go +++ b/modules/caddyhttp/encode/gzip/gzip.go @@ -37,8 +37,8 @@ type Gzip struct {  // CaddyModule returns the Caddy module information.  func (Gzip) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.encoders.gzip", -		New:  func() caddy.Module { return new(Gzip) }, +		ID:  "http.encoders.gzip", +		New: func() caddy.Module { return new(Gzip) },  	}  } diff --git a/modules/caddyhttp/encode/zstd/zstd.go b/modules/caddyhttp/encode/zstd/zstd.go index f2b4e85..5182fc4 100644 --- a/modules/caddyhttp/encode/zstd/zstd.go +++ b/modules/caddyhttp/encode/zstd/zstd.go @@ -31,8 +31,8 @@ type Zstd struct{}  // CaddyModule returns the Caddy module information.  func (Zstd) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.encoders.zstd", -		New:  func() caddy.Module { return new(Zstd) }, +		ID:  "http.encoders.zstd", +		New: func() caddy.Module { return new(Zstd) },  	}  } diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go index 46bc5b7..fb931a1 100644 --- a/modules/caddyhttp/fileserver/caddyfile.go +++ b/modules/caddyhttp/fileserver/caddyfile.go @@ -15,8 +15,7 @@  package fileserver  import ( -	"encoding/json" - +	"github.com/caddyserver/caddy/v2"  	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"  	"github.com/caddyserver/caddy/v2/modules/caddyhttp"  	"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" @@ -122,7 +121,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)  		URI: "{http.matchers.file.relative}{http.request.uri.query_string}",  	} -	matcherSet := map[string]json.RawMessage{ +	matcherSet := caddy.ModuleMap{  		"file": h.JSON(MatchFile{  			TryFiles: try,  		}, nil), diff --git a/modules/caddyhttp/fileserver/command.go b/modules/caddyhttp/fileserver/command.go index b861a99..e7a0ee3 100644 --- a/modules/caddyhttp/fileserver/command.go +++ b/modules/caddyhttp/fileserver/command.go @@ -75,8 +75,8 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {  		},  	}  	if domain != "" { -		route.MatcherSetsRaw = []map[string]json.RawMessage{ -			map[string]json.RawMessage{ +		route.MatcherSetsRaw = []caddy.ModuleMap{ +			caddy.ModuleMap{  				"host": caddyconfig.JSON(caddyhttp.MatchHost{domain}, nil),  			},  		} @@ -100,7 +100,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {  	cfg := &caddy.Config{  		Admin: &caddy.AdminConfig{Disabled: true}, -		AppsRaw: map[string]json.RawMessage{ +		AppsRaw: caddy.ModuleMap{  			"http": caddyconfig.JSON(httpApp, nil),  		},  	} diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index 4a7f657..13cb60a 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -54,8 +54,8 @@ type MatchFile struct {  // CaddyModule returns the Caddy module information.  func (MatchFile) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.file", -		New:  func() caddy.Module { return new(MatchFile) }, +		ID:  "http.matchers.file", +		New: func() caddy.Module { return new(MatchFile) },  	}  } diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index 732894d..a9e6e1c 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -53,8 +53,8 @@ type FileServer struct {  // CaddyModule returns the Caddy module information.  func (FileServer) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.file_server", -		New:  func() caddy.Module { return new(FileServer) }, +		ID:  "http.handlers.file_server", +		New: func() caddy.Module { return new(FileServer) },  	}  } diff --git a/modules/caddyhttp/headers/headers.go b/modules/caddyhttp/headers/headers.go index 813b9fe..f53e859 100644 --- a/modules/caddyhttp/headers/headers.go +++ b/modules/caddyhttp/headers/headers.go @@ -37,8 +37,8 @@ type Handler struct {  // CaddyModule returns the Caddy module information.  func (Handler) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.headers", -		New:  func() caddy.Module { return new(Handler) }, +		ID:  "http.handlers.headers", +		New: func() caddy.Module { return new(Handler) },  	}  } diff --git a/modules/caddyhttp/httpcache/httpcache.go b/modules/caddyhttp/httpcache/httpcache.go index b5bc044..81f5816 100644 --- a/modules/caddyhttp/httpcache/httpcache.go +++ b/modules/caddyhttp/httpcache/httpcache.go @@ -43,8 +43,8 @@ type Cache struct {  // CaddyModule returns the Caddy module information.  func (Cache) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.cache", -		New:  func() caddy.Module { return new(Cache) }, +		ID:  "http.handlers.cache", +		New: func() caddy.Module { return new(Cache) },  	}  } diff --git a/modules/caddyhttp/markdown/markdown.go b/modules/caddyhttp/markdown/markdown.go index 5ff18b8..acaa0c3 100644 --- a/modules/caddyhttp/markdown/markdown.go +++ b/modules/caddyhttp/markdown/markdown.go @@ -38,8 +38,8 @@ type Markdown struct {  // CaddyModule returns the Caddy module information.  func (Markdown) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.markdown", -		New:  func() caddy.Module { return new(Markdown) }, +		ID:  "http.handlers.markdown", +		New: func() caddy.Module { return new(Markdown) },  	}  } diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 82fb04a..ea715c5 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -66,7 +66,7 @@ type (  	// MatchNegate matches requests by negating its matchers' results.  	MatchNegate struct { -		MatchersRaw map[string]json.RawMessage `json:"-"` +		MatchersRaw caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"`  		Matchers MatcherSet `json:"-"`  	} @@ -95,8 +95,8 @@ func init() {  // CaddyModule returns the Caddy module information.  func (MatchHost) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.host", -		New:  func() caddy.Module { return new(MatchHost) }, +		ID:  "http.matchers.host", +		New: func() caddy.Module { return new(MatchHost) },  	}  } @@ -149,8 +149,8 @@ outer:  // CaddyModule returns the Caddy module information.  func (MatchPath) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.path", -		New:  func() caddy.Module { return new(MatchPath) }, +		ID:  "http.matchers.path", +		New: func() caddy.Module { return new(MatchPath) },  	}  } @@ -208,8 +208,8 @@ func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  // CaddyModule returns the Caddy module information.  func (MatchPathRE) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.path_regexp", -		New:  func() caddy.Module { return new(MatchPathRE) }, +		ID:  "http.matchers.path_regexp", +		New: func() caddy.Module { return new(MatchPathRE) },  	}  } @@ -222,8 +222,8 @@ func (m MatchPathRE) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchMethod) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.method", -		New:  func() caddy.Module { return new(MatchMethod) }, +		ID:  "http.matchers.method", +		New: func() caddy.Module { return new(MatchMethod) },  	}  } @@ -248,8 +248,8 @@ func (m MatchMethod) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchQuery) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.query", -		New:  func() caddy.Module { return new(MatchQuery) }, +		ID:  "http.matchers.query", +		New: func() caddy.Module { return new(MatchQuery) },  	}  } @@ -291,8 +291,8 @@ func (m MatchQuery) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchHeader) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.header", -		New:  func() caddy.Module { return new(MatchHeader) }, +		ID:  "http.matchers.header", +		New: func() caddy.Module { return new(MatchHeader) },  	}  } @@ -349,8 +349,8 @@ func (m MatchHeader) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchHeaderRE) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.header_regexp", -		New:  func() caddy.Module { return new(MatchHeaderRE) }, +		ID:  "http.matchers.header_regexp", +		New: func() caddy.Module { return new(MatchHeaderRE) },  	}  } @@ -406,8 +406,8 @@ func (m MatchHeaderRE) Validate() error {  // CaddyModule returns the Caddy module information.  func (MatchProtocol) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.protocol", -		New:  func() caddy.Module { return new(MatchProtocol) }, +		ID:  "http.matchers.protocol", +		New: func() caddy.Module { return new(MatchProtocol) },  	}  } @@ -439,8 +439,8 @@ func (m *MatchProtocol) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  // CaddyModule returns the Caddy module information.  func (MatchNegate) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.not", -		New:  func() caddy.Module { return new(MatchNegate) }, +		ID:  "http.matchers.not", +		New: func() caddy.Module { return new(MatchNegate) },  	}  } @@ -486,7 +486,7 @@ func (m *MatchNegate) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  	// we should now be functional, but we also need  	// to be able to marshal as JSON, otherwise config  	// adaptation won't work properly -	m.MatchersRaw = make(map[string]json.RawMessage) +	m.MatchersRaw = make(caddy.ModuleMap)  	for name, matchers := range matcherMap {  		jsonBytes, err := json.Marshal(matchers)  		if err != nil { @@ -500,14 +500,13 @@ func (m *MatchNegate) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  // Provision loads the matcher modules to be negated.  func (m *MatchNegate) Provision(ctx caddy.Context) error { -	for modName, rawMsg := range m.MatchersRaw { -		val, err := ctx.LoadModule("http.matchers."+modName, rawMsg) -		if err != nil { -			return fmt.Errorf("loading matcher module '%s': %v", modName, err) -		} -		m.Matchers = append(m.Matchers, val.(RequestMatcher)) +	mods, err := ctx.LoadModule(m, "MatchersRaw") +	if err != nil { +		return fmt.Errorf("loading matchers: %v", err) +	} +	for _, modIface := range mods.(map[string]interface{}) { +		m.Matchers = append(m.Matchers, modIface.(RequestMatcher))  	} -	m.MatchersRaw = nil // allow GC to deallocate  	return nil  } @@ -520,8 +519,8 @@ func (m MatchNegate) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.remote_ip", -		New:  func() caddy.Module { return new(MatchRemoteIP) }, +		ID:  "http.matchers.remote_ip", +		New: func() caddy.Module { return new(MatchRemoteIP) },  	}  } @@ -597,8 +596,8 @@ func (m MatchRemoteIP) Match(r *http.Request) bool {  // CaddyModule returns the Caddy module information.  func (MatchStarlarkExpr) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.starlark_expr", // TODO: Rename to 'starlark'? -		New:  func() caddy.Module { return new(MatchStarlarkExpr) }, +		ID:  "http.matchers.starlark_expr", // TODO: Rename to 'starlark'? +		New: func() caddy.Module { return new(MatchStarlarkExpr) },  	}  } diff --git a/modules/caddyhttp/requestbody/requestbody.go b/modules/caddyhttp/requestbody/requestbody.go index 9b16250..dd3f256 100644 --- a/modules/caddyhttp/requestbody/requestbody.go +++ b/modules/caddyhttp/requestbody/requestbody.go @@ -33,8 +33,8 @@ type RequestBody struct {  // CaddyModule returns the Caddy module information.  func (RequestBody) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.request_body", // TODO: better name for this? -		New:  func() caddy.Module { return new(RequestBody) }, +		ID:  "http.handlers.request_body", // TODO: better name for this? +		New: func() caddy.Module { return new(RequestBody) },  	}  } diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index c8cf26e..9dba769 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -108,11 +108,11 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  				name := d.Val()  				mod, err := caddy.GetModule("http.handlers.reverse_proxy.selection_policies." + name)  				if err != nil { -					return d.Errf("getting load balancing policy module '%s': %v", mod.Name, err) +					return d.Errf("getting load balancing policy module '%s': %v", mod, err)  				}  				unm, ok := mod.New().(caddyfile.Unmarshaler)  				if !ok { -					return d.Errf("load balancing policy module '%s' is not a Caddyfile unmarshaler", mod.Name) +					return d.Errf("load balancing policy module '%s' is not a Caddyfile unmarshaler", mod)  				}  				err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())  				if err != nil { @@ -120,7 +120,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  				}  				sel, ok := unm.(Selector)  				if !ok { -					return d.Errf("module %s is not a Selector", mod.Name) +					return d.Errf("module %s is not a Selector", mod)  				}  				if h.LoadBalancing == nil {  					h.LoadBalancing = new(LoadBalancing) @@ -391,11 +391,11 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  				name := d.Val()  				mod, err := caddy.GetModule("http.handlers.reverse_proxy.transport." + name)  				if err != nil { -					return d.Errf("getting transport module '%s': %v", mod.Name, err) +					return d.Errf("getting transport module '%s': %v", mod, err)  				}  				unm, ok := mod.New().(caddyfile.Unmarshaler)  				if !ok { -					return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod.Name) +					return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod)  				}  				err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())  				if err != nil { @@ -403,7 +403,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {  				}  				rt, ok := unm.(http.RoundTripper)  				if !ok { -					return d.Errf("module %s is not a RoundTripper", mod.Name) +					return d.Errf("module %s is not a RoundTripper", mod)  				}  				h.TransportRaw = caddyconfig.JSONModuleObject(rt, "protocol", name, nil) diff --git a/modules/caddyhttp/reverseproxy/circuitbreaker.go b/modules/caddyhttp/reverseproxy/circuitbreaker.go index de2a6f9..474f1c6 100644 --- a/modules/caddyhttp/reverseproxy/circuitbreaker.go +++ b/modules/caddyhttp/reverseproxy/circuitbreaker.go @@ -41,8 +41,8 @@ type localCircuitBreaker struct {  // CaddyModule returns the Caddy module information.  func (localCircuitBreaker) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.circuit_breakers.local", -		New:  func() caddy.Module { return new(localCircuitBreaker) }, +		ID:  "http.reverse_proxy.circuit_breakers.local", +		New: func() caddy.Module { return new(localCircuitBreaker) },  	}  } diff --git a/modules/caddyhttp/reverseproxy/command.go b/modules/caddyhttp/reverseproxy/command.go index 0ddb8f2..e16c4f5 100644 --- a/modules/caddyhttp/reverseproxy/command.go +++ b/modules/caddyhttp/reverseproxy/command.go @@ -113,8 +113,8 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {  	}  	urlHost := fromURL.Hostname()  	if urlHost != "" { -		route.MatcherSetsRaw = []map[string]json.RawMessage{ -			map[string]json.RawMessage{ +		route.MatcherSetsRaw = []caddy.ModuleMap{ +			caddy.ModuleMap{  				"host": caddyconfig.JSON(caddyhttp.MatchHost{urlHost}, nil),  			},  		} @@ -138,7 +138,7 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {  	cfg := &caddy.Config{  		Admin: &caddy.AdminConfig{Disabled: true}, -		AppsRaw: map[string]json.RawMessage{ +		AppsRaw: caddy.ModuleMap{  			"http": caddyconfig.JSON(httpApp, nil),  		},  	} diff --git a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go index ed97342..8e723b2 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -18,6 +18,7 @@ import (  	"encoding/json"  	"net/http" +	"github.com/caddyserver/caddy/v2"  	"github.com/caddyserver/caddy/v2/caddyconfig"  	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"  	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" @@ -121,12 +122,12 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error  	}  	// route to redirect to canonical path if index PHP file -	redirMatcherSet := map[string]json.RawMessage{ +	redirMatcherSet := caddy.ModuleMap{  		"file": h.JSON(fileserver.MatchFile{  			TryFiles: []string{"{http.request.uri.path}/index.php"},  		}, nil),  		"not": h.JSON(caddyhttp.MatchNegate{ -			MatchersRaw: map[string]json.RawMessage{ +			MatchersRaw: caddy.ModuleMap{  				"path": h.JSON(caddyhttp.MatchPath{"*/"}, nil),  			},  		}, nil), @@ -136,12 +137,12 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error  		Headers:    http.Header{"Location": []string{"{http.request.uri.path}/"}},  	}  	redirRoute := caddyhttp.Route{ -		MatcherSetsRaw: []map[string]json.RawMessage{redirMatcherSet}, +		MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},  		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},  	}  	// route to rewrite to PHP index file -	rewriteMatcherSet := map[string]json.RawMessage{ +	rewriteMatcherSet := caddy.ModuleMap{  		"file": h.JSON(fileserver.MatchFile{  			TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php"},  		}, nil), @@ -151,13 +152,13 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error  		Rehandle: true,  	}  	rewriteRoute := caddyhttp.Route{ -		MatcherSetsRaw: []map[string]json.RawMessage{rewriteMatcherSet}, +		MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet},  		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},  	}  	// route to actually reverse proxy requests to PHP files;  	// match only requests that are for PHP files -	rpMatcherSet := map[string]json.RawMessage{ +	rpMatcherSet := caddy.ModuleMap{  		"path": h.JSON([]string{"*.php"}, nil),  	} @@ -193,7 +194,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error  	// create the final reverse proxy route which is  	// conditional on matching PHP files  	rpRoute := caddyhttp.Route{ -		MatcherSetsRaw: []map[string]json.RawMessage{rpMatcherSet}, +		MatcherSetsRaw: []caddy.ModuleMap{rpMatcherSet},  		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rpHandler, "handler", "reverse_proxy", nil)},  	} @@ -207,7 +208,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error  			{  				Class: "route",  				Value: caddyhttp.Route{ -					MatcherSetsRaw: []map[string]json.RawMessage{userMatcherSet}, +					MatcherSetsRaw: []caddy.ModuleMap{userMatcherSet},  					HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)},  				},  			}, diff --git a/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go b/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go index 21aeb17..aff9a6e 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go @@ -73,8 +73,8 @@ type Transport struct {  // CaddyModule returns the Caddy module information.  func (Transport) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.transport.fastcgi", -		New:  func() caddy.Module { return new(Transport) }, +		ID:  "http.reverse_proxy.transport.fastcgi", +		New: func() caddy.Module { return new(Transport) },  	}  } diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go index 38a904e..1dd1d14 100644 --- a/modules/caddyhttp/reverseproxy/httptransport.go +++ b/modules/caddyhttp/reverseproxy/httptransport.go @@ -40,6 +40,7 @@ type HTTPTransport struct {  	// TODO: It's possible that other transports (like fastcgi) might be  	// able to borrow/use at least some of these config fields; if so,  	// maybe move them into a type called CommonTransport and embed it? +  	TLS                   *TLSConfig     `json:"tls,omitempty"`  	KeepAlive             *KeepAlive     `json:"keep_alive,omitempty"`  	Compression           *bool          `json:"compression,omitempty"` @@ -59,8 +60,8 @@ type HTTPTransport struct {  // CaddyModule returns the Caddy module information.  func (HTTPTransport) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.transport.http", -		New:  func() caddy.Module { return new(HTTPTransport) }, +		ID:  "http.reverse_proxy.transport.http", +		New: func() caddy.Module { return new(HTTPTransport) },  	}  } diff --git a/modules/caddyhttp/reverseproxy/ntlm.go b/modules/caddyhttp/reverseproxy/ntlm.go index e2d46b4..ea2bb85 100644 --- a/modules/caddyhttp/reverseproxy/ntlm.go +++ b/modules/caddyhttp/reverseproxy/ntlm.go @@ -30,12 +30,12 @@ func init() {  	caddy.RegisterModule(NTLMTransport{})  } -// NTLMTransport proxies HTTP+NTLM authentication is being used. +// NTLMTransport proxies HTTP with NTLM authentication.  // It basically wraps HTTPTransport so that it is compatible with  // NTLM's HTTP-hostile requirements. Specifically, it will use  // HTTPTransport's single, default *http.Transport for all requests  // (unless the client's connection is already mapped to a different -// transport) until a request comes in with Authorization header +// transport) until a request comes in with an Authorization header  // that has "NTLM" or "Negotiate"; when that happens, NTLMTransport  // maps the client's connection (by its address, req.RemoteAddr)  // to a new transport that is used only by that downstream conn. @@ -56,8 +56,8 @@ type NTLMTransport struct {  // CaddyModule returns the Caddy module information.  func (NTLMTransport) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.transport.http_ntlm", -		New:  func() caddy.Module { return new(NTLMTransport) }, +		ID:  "http.reverse_proxy.transport.http_ntlm", +		New: func() caddy.Module { return new(NTLMTransport) },  	}  } diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 87895e2..132f222 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -42,8 +42,8 @@ func init() {  // Handler implements a highly configurable and production-ready reverse proxy.  type Handler struct { -	TransportRaw   json.RawMessage  `json:"transport,omitempty"` -	CBRaw          json.RawMessage  `json:"circuit_breaker,omitempty"` +	TransportRaw   json.RawMessage  `json:"transport,omitempty" caddy:"namespace=http.reverse_proxy.transport inline_key=protocol"` +	CBRaw          json.RawMessage  `json:"circuit_breaker,omitempty" caddy:"namespace=http.reverse_proxy.circuit_breakers inline_key=type"`  	LoadBalancing  *LoadBalancing   `json:"load_balancing,omitempty"`  	HealthChecks   *HealthChecks    `json:"health_checks,omitempty"`  	Upstreams      UpstreamPool     `json:"upstreams,omitempty"` @@ -60,8 +60,8 @@ type Handler struct {  // CaddyModule returns the Caddy module information.  func (Handler) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy", -		New:  func() caddy.Module { return new(Handler) }, +		ID:  "http.handlers.reverse_proxy", +		New: func() caddy.Module { return new(Handler) },  	}  } @@ -71,30 +71,25 @@ func (h *Handler) Provision(ctx caddy.Context) error {  	// start by loading modules  	if h.TransportRaw != nil { -		val, err := ctx.LoadModuleInline("protocol", "http.handlers.reverse_proxy.transport", h.TransportRaw) +		mod, err := ctx.LoadModule(h, "TransportRaw")  		if err != nil { -			return fmt.Errorf("loading transport module: %s", err) +			return fmt.Errorf("loading transport: %v", err)  		} -		h.Transport = val.(http.RoundTripper) -		h.TransportRaw = nil // allow GC to deallocate +		h.Transport = mod.(http.RoundTripper)  	}  	if h.LoadBalancing != nil && h.LoadBalancing.SelectionPolicyRaw != nil { -		val, err := ctx.LoadModuleInline("policy", -			"http.handlers.reverse_proxy.selection_policies", -			h.LoadBalancing.SelectionPolicyRaw) +		mod, err := ctx.LoadModule(h.LoadBalancing, "SelectionPolicyRaw")  		if err != nil { -			return fmt.Errorf("loading load balancing selection module: %s", err) +			return fmt.Errorf("loading load balancing selection policy: %s", err)  		} -		h.LoadBalancing.SelectionPolicy = val.(Selector) -		h.LoadBalancing.SelectionPolicyRaw = nil // allow GC to deallocate +		h.LoadBalancing.SelectionPolicy = mod.(Selector)  	}  	if h.CBRaw != nil { -		val, err := ctx.LoadModuleInline("type", "http.handlers.reverse_proxy.circuit_breakers", h.CBRaw) +		mod, err := ctx.LoadModule(h, "CBRaw")  		if err != nil { -			return fmt.Errorf("loading circuit breaker module: %s", err) +			return fmt.Errorf("loading circuit breaker: %s", err)  		} -		h.CB = val.(CircuitBreaker) -		h.CBRaw = nil // allow GC to deallocate +		h.CB = mod.(CircuitBreaker)  	}  	// set up transport @@ -128,12 +123,14 @@ func (h *Handler) Provision(ctx caddy.Context) error {  		// defaulting to a sane wait period between attempts  		h.LoadBalancing.TryInterval = caddy.Duration(250 * time.Millisecond)  	} -	lbMatcherSets, err := h.LoadBalancing.RetryMatchRaw.Setup(ctx) +	lbMatcherSets, err := ctx.LoadModule(h.LoadBalancing, "RetryMatchRaw") +	if err != nil { +		return err +	} +	err = h.LoadBalancing.RetryMatch.FromInterface(lbMatcherSets)  	if err != nil {  		return err  	} -	h.LoadBalancing.RetryMatch = lbMatcherSets -	h.LoadBalancing.RetryMatchRaw = nil // allow GC to deallocate  	// if active health checks are enabled, configure them and start a worker  	if h.HealthChecks != nil && @@ -407,7 +404,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia  	// do the round-trip  	start := time.Now()  	res, err := h.Transport.RoundTrip(req) -	latency := time.Since(start) +	duration := time.Since(start)  	if err != nil {  		return err  	} @@ -415,12 +412,13 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia  	h.logger.Debug("upstream roundtrip",  		zap.Object("request", caddyhttp.LoggableHTTPRequest{Request: req}),  		zap.Object("headers", caddyhttp.LoggableHTTPHeader(res.Header)), +		zap.Duration("duration", duration),  		zap.Int("status", res.StatusCode),  	)  	// update circuit breaker on current conditions  	if di.Upstream.cb != nil { -		di.Upstream.cb.RecordMetric(res.StatusCode, latency) +		di.Upstream.cb.RecordMetric(res.StatusCode, duration)  	}  	// perform passive health checks (if enabled) @@ -434,7 +432,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia  		// strike if the roundtrip took too long  		if h.HealthChecks.Passive.UnhealthyLatency > 0 && -			latency >= time.Duration(h.HealthChecks.Passive.UnhealthyLatency) { +			duration >= time.Duration(h.HealthChecks.Passive.UnhealthyLatency) {  			h.countFailure(di.Upstream)  		}  	} @@ -651,10 +649,10 @@ func removeConnectionHeaders(h http.Header) {  // LoadBalancing has parameters related to load balancing.  type LoadBalancing struct { -	SelectionPolicyRaw json.RawMessage          `json:"selection_policy,omitempty"` +	SelectionPolicyRaw json.RawMessage          `json:"selection_policy,omitempty" caddy:"namespace=http.reverse_proxy.selection_policies inline_key=policy"`  	TryDuration        caddy.Duration           `json:"try_duration,omitempty"`  	TryInterval        caddy.Duration           `json:"try_interval,omitempty"` -	RetryMatchRaw      caddyhttp.RawMatcherSets `json:"retry_match,omitempty"` +	RetryMatchRaw      caddyhttp.RawMatcherSets `json:"retry_match,omitempty" caddy:"namespace=http.matchers"`  	SelectionPolicy Selector              `json:"-"`  	RetryMatch      caddyhttp.MatcherSets `json:"-"` diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies.go b/modules/caddyhttp/reverseproxy/selectionpolicies.go index a21e44c..937ae37 100644 --- a/modules/caddyhttp/reverseproxy/selectionpolicies.go +++ b/modules/caddyhttp/reverseproxy/selectionpolicies.go @@ -48,8 +48,8 @@ type RandomSelection struct{}  // CaddyModule returns the Caddy module information.  func (RandomSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.random", -		New:  func() caddy.Module { return new(RandomSelection) }, +		ID:  "http.reverse_proxy.selection_policies.random", +		New: func() caddy.Module { return new(RandomSelection) },  	}  } @@ -84,8 +84,8 @@ type RandomChoiceSelection struct {  // CaddyModule returns the Caddy module information.  func (RandomChoiceSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.random_choose", -		New:  func() caddy.Module { return new(RandomChoiceSelection) }, +		ID:  "http.reverse_proxy.selection_policies.random_choose", +		New: func() caddy.Module { return new(RandomChoiceSelection) },  	}  } @@ -154,8 +154,8 @@ type LeastConnSelection struct{}  // CaddyModule returns the Caddy module information.  func (LeastConnSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.least_conn", -		New:  func() caddy.Module { return new(LeastConnSelection) }, +		ID:  "http.reverse_proxy.selection_policies.least_conn", +		New: func() caddy.Module { return new(LeastConnSelection) },  	}  } @@ -199,8 +199,8 @@ type RoundRobinSelection struct {  // CaddyModule returns the Caddy module information.  func (RoundRobinSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.round_robin", -		New:  func() caddy.Module { return new(RoundRobinSelection) }, +		ID:  "http.reverse_proxy.selection_policies.round_robin", +		New: func() caddy.Module { return new(RoundRobinSelection) },  	}  } @@ -227,8 +227,8 @@ type FirstSelection struct{}  // CaddyModule returns the Caddy module information.  func (FirstSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.first", -		New:  func() caddy.Module { return new(FirstSelection) }, +		ID:  "http.reverse_proxy.selection_policies.first", +		New: func() caddy.Module { return new(FirstSelection) },  	}  } @@ -249,8 +249,8 @@ type IPHashSelection struct{}  // CaddyModule returns the Caddy module information.  func (IPHashSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.ip_hash", -		New:  func() caddy.Module { return new(IPHashSelection) }, +		ID:  "http.reverse_proxy.selection_policies.ip_hash", +		New: func() caddy.Module { return new(IPHashSelection) },  	}  } @@ -270,8 +270,8 @@ type URIHashSelection struct{}  // CaddyModule returns the Caddy module information.  func (URIHashSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.uri_hash", -		New:  func() caddy.Module { return new(URIHashSelection) }, +		ID:  "http.reverse_proxy.selection_policies.uri_hash", +		New: func() caddy.Module { return new(URIHashSelection) },  	}  } @@ -289,8 +289,8 @@ type HeaderHashSelection struct {  // CaddyModule returns the Caddy module information.  func (HeaderHashSelection) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.reverse_proxy.selection_policies.header", -		New:  func() caddy.Module { return new(HeaderHashSelection) }, +		ID:  "http.reverse_proxy.selection_policies.header", +		New: func() caddy.Module { return new(HeaderHashSelection) },  	}  } diff --git a/modules/caddyhttp/rewrite/rewrite.go b/modules/caddyhttp/rewrite/rewrite.go index 5a84a33..f610658 100644 --- a/modules/caddyhttp/rewrite/rewrite.go +++ b/modules/caddyhttp/rewrite/rewrite.go @@ -47,8 +47,8 @@ type Rewrite struct {  // CaddyModule returns the Caddy module information.  func (Rewrite) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.rewrite", -		New:  func() caddy.Module { return new(Rewrite) }, +		ID:  "http.handlers.rewrite", +		New: func() caddy.Module { return new(Rewrite) },  	}  } diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go index 550b14e..0dce990 100644 --- a/modules/caddyhttp/routes.go +++ b/modules/caddyhttp/routes.go @@ -22,14 +22,73 @@ import (  	"github.com/caddyserver/caddy/v2"  ) -// Route represents a set of matching rules, -// middlewares, and a responder for handling HTTP -// requests. +// Route consists of a set of rules for matching HTTP requests, +// a list of handlers to execute, and optional flow control +// parameters which customize the handling of HTTP requests +// in a highly flexible and performant manner.  type Route struct { -	Group          string            `json:"group,omitempty"` -	MatcherSetsRaw RawMatcherSets    `json:"match,omitempty"` -	HandlersRaw    []json.RawMessage `json:"handle,omitempty"` -	Terminal       bool              `json:"terminal,omitempty"` +	// Group is an optional name for a group to which this +	// route belongs. If a route belongs to a group, only +	// the first matching route in the group will be used. +	Group string `json:"group,omitempty"` + +	// The matcher sets which will be used to qualify this +	// route for a request. Essentially the "if" statement +	// of this route. Each matcher set is OR'ed, but matchers +	// within a set are AND'ed together. +	MatcherSetsRaw RawMatcherSets `json:"match,omitempty" caddy:"namespace=http.matchers"` + +	// The list of handlers for this route. Upon matching a request, they are chained +	// together in a middleware fashion: requests flow from the first handler to the last +	// (top of the list to the bottom), with the possibility that any handler could stop +	// the chain and/or return an error. Responses flow back through the chain (bottom of +	// the list to the top) as they are written out to the client. +	// +	// Not all handlers call the next handler in the chain. For example, the reverse_proxy +	// handler always sends a request upstream or returns an error. Thus, configuring +	// handlers after reverse_proxy in the same route is illogical, since they would never +	// be executed. You will want to put handlers which originate the response at the very +	// end of your route(s). The documentation for a module should state whether it invokes +	// the next handler, but sometimes it is common sense. +	// +	// Some handlers manipulate the response. Remember that requests flow down the list, and +	// responses flow up the list. +	// +	// For example, if you wanted to use both `templates` and `encode` handlers, you would +	// need to put `templates` after `encode` in your route, because responses flow up. +	// Thus, `templates` will be able to parse and execute the plain-text response as a +	// template, and then return it up to the `encode` handler which will then compress it +	// into a binary format. +	// +	// If `templates` came before `encode`, then `encode` would write a compressed, +	// binary-encoded response to `templates` which would not be able to parse the response +	// properly. +	// +	// The correct order, then, is this: +	// +	//     [ +	//         {"handler": "encode"}, +	//         {"handler": "templates"}, +	//         {"handler": "file_server"} +	//     ] +	// +	// The request flows ⬇️ DOWN (`encode` -> `templates` -> `file_server`). +	// +	// 1. First, `encode` will choose how to `encode` the response and wrap the response. +	// 2. Then, `templates` will wrap the response with a buffer. +	// 3. Finally, `file_server` will originate the content from a file. +	// +	// The response flows ⬆️ UP (`file_server` -> `templates` -> `encode`): +	// +	// 1. First, `file_server` will write the file to the response. +	// 2. That write will be buffered and then executed by `templates`. +	// 3. Lastly, the write from `templates` will flow into `encode` which will compress the stream. +	// +	// If you think of routes in this way, it will be easy and even fun to solve the puzzle of writing correct routes. +	HandlersRaw []json.RawMessage `json:"handle,omitempty" caddy:"namespace=http.handlers inline_key=handler"` + +	// If true, no more routes will be executed after this one, even if they matched. +	Terminal bool `json:"terminal,omitempty"`  	// decoded values  	MatcherSets MatcherSets         `json:"-"` @@ -54,22 +113,23 @@ type RouteList []Route  func (routes RouteList) Provision(ctx caddy.Context) error {  	for i, route := range routes {  		// matchers -		matcherSets, err := route.MatcherSetsRaw.Setup(ctx) +		matchersIface, err := ctx.LoadModule(&route, "MatcherSetsRaw") +		if err != nil { +			return fmt.Errorf("loadng matchers in route %d: %v", i, err) +		} +		err = routes[i].MatcherSets.FromInterface(matchersIface)  		if err != nil { -			return err +			return fmt.Errorf("route %d: %v", i, err)  		} -		routes[i].MatcherSets = matcherSets -		routes[i].MatcherSetsRaw = nil // allow GC to deallocate  		// handlers -		for j, rawMsg := range route.HandlersRaw { -			mh, err := ctx.LoadModuleInline("handler", "http.handlers", rawMsg) -			if err != nil { -				return fmt.Errorf("loading handler module in position %d: %v", j, err) -			} -			routes[i].Handlers = append(routes[i].Handlers, mh.(MiddlewareHandler)) +		handlersIface, err := ctx.LoadModule(&route, "HandlersRaw") +		if err != nil { +			return fmt.Errorf("loading handler modules in route %d: %v", i, err) +		} +		for _, handler := range handlersIface.([]interface{}) { +			routes[i].Handlers = append(routes[i].Handlers, handler.(MiddlewareHandler))  		} -		routes[i].HandlersRaw = nil // allow GC to deallocate  	}  	return nil  } @@ -171,28 +231,7 @@ func (mset MatcherSet) Match(r *http.Request) bool {  // RawMatcherSets is a group of matcher sets  // in their raw, JSON form. -type RawMatcherSets []map[string]json.RawMessage - -// Setup sets up all matcher sets by loading each matcher module -// and returning the group of provisioned matcher sets. -func (rm RawMatcherSets) Setup(ctx caddy.Context) (MatcherSets, error) { -	if rm == nil { -		return nil, nil -	} -	var ms MatcherSets -	for _, matcherSet := range rm { -		var matchers MatcherSet -		for modName, rawMsg := range matcherSet { -			val, err := ctx.LoadModule("http.matchers."+modName, rawMsg) -			if err != nil { -				return nil, fmt.Errorf("loading matcher module '%s': %v", modName, err) -			} -			matchers = append(matchers, val.(RequestMatcher)) -		} -		ms = append(ms, matchers) -	} -	return ms, nil -} +type RawMatcherSets []caddy.ModuleMap  // MatcherSets is a group of matcher sets capable  // of checking whether a request matches any of @@ -202,11 +241,27 @@ type MatcherSets []MatcherSet  // AnyMatch returns true if req matches any of the  // matcher sets in mss or if there are no matchers,  // in which case the request always matches. -func (mss MatcherSets) AnyMatch(req *http.Request) bool { -	for _, ms := range mss { -		if ms.Match(req) { +func (ms MatcherSets) AnyMatch(req *http.Request) bool { +	for _, m := range ms { +		if m.Match(req) {  			return true  		}  	} -	return len(mss) == 0 +	return len(ms) == 0 +} + +// FromInterface fills ms from an interface{} value obtained from LoadModule. +func (ms *MatcherSets) FromInterface(matcherSets interface{}) error { +	for _, matcherSetIfaces := range matcherSets.([]map[string]interface{}) { +		var matcherSet MatcherSet +		for _, matcher := range matcherSetIfaces { +			reqMatcher, ok := matcher.(RequestMatcher) +			if !ok { +				return fmt.Errorf("decoded module is not a RequestMatcher: %#v", matcher) +			} +			matcherSet = append(matcherSet, reqMatcher) +		} +		*ms = append(*ms, matcherSet) +	} +	return nil  } diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index fef887d..c34444e 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -31,23 +31,101 @@ import (  	"go.uber.org/zap/zapcore"  ) -// Server is an HTTP server. +// Server describes an HTTP server.  type Server struct { -	Listen            []string                    `json:"listen,omitempty"` -	ReadTimeout       caddy.Duration              `json:"read_timeout,omitempty"` -	ReadHeaderTimeout caddy.Duration              `json:"read_header_timeout,omitempty"` -	WriteTimeout      caddy.Duration              `json:"write_timeout,omitempty"` -	IdleTimeout       caddy.Duration              `json:"idle_timeout,omitempty"` -	MaxHeaderBytes    int                         `json:"max_header_bytes,omitempty"` -	Routes            RouteList                   `json:"routes,omitempty"` -	Errors            *HTTPErrorConfig            `json:"errors,omitempty"` -	TLSConnPolicies   caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"` -	AutoHTTPS         *AutoHTTPSConfig            `json:"automatic_https,omitempty"` -	MaxRehandles      *int                        `json:"max_rehandles,omitempty"` -	StrictSNIHost     *bool                       `json:"strict_sni_host,omitempty"` -	Logs              *ServerLogConfig            `json:"logs,omitempty"` - -	// This field is not subject to compatibility promises +	// Socket interfaces to which to bind listeners. Caddy network +	// addresses have the following form: +	// +	//     network/address +	// +	// The network part is anything that [Go's `net` package](https://golang.org/pkg/net/) +	// recognizes, and is optional. The default network is `tcp`. If +	// a network is specified, a single forward slash `/` is used to +	// separate the network and address portions. +	// +	// The address part may be any of these forms: +	// +	//    - `host` +	//    - `host:port` +	//    - `:port` +	//    - `/path/to/unix/socket` +	// +	// The host may be any hostname, resolvable domain name, or IP address. +	// The port may be a single value (`:8080`) or a range (`:8080-8085`). +	// A port range will be multiplied into singular addresses. Not all +	// config parameters accept port ranges, but Listen does. +	// +	// Valid examples: +	// +	//     :8080 +	//     127.0.0.1:8080 +	//     localhost:8080 +	//     localhost:8080-8085 +	//     tcp/localhost:8080 +	//     tcp/localhost:8080-8085 +	//     udp/localhost:9005 +	//     unix//path/to/socket +	// +	Listen []string `json:"listen,omitempty"` + +	// How long to allow a read from a client's upload. Setting this +	// to a short, non-zero value can mitigate slowloris attacks, but +	// may also affect legitimately slow clients. +	ReadTimeout caddy.Duration `json:"read_timeout,omitempty"` + +	// ReadHeaderTimeout is like ReadTimeout but for request headers. +	ReadHeaderTimeout caddy.Duration `json:"read_header_timeout,omitempty"` + +	// WriteTimeout is how long to allow a write to a client. Note +	// that setting this to a small value when serving large files +	// may negatively affect legitimately slow clients. +	WriteTimeout caddy.Duration `json:"write_timeout,omitempty"` + +	// IdleTimeout is the maximum time to wait for the next request +	// when keep-alives are enabled. If zero, ReadTimeout is used. +	// If both are zero, there is no timeout. +	IdleTimeout caddy.Duration `json:"idle_timeout,omitempty"` + +	// MaxHeaderBytes is the maximum size to parse from a client's +	// HTTP request headers. +	MaxHeaderBytes int `json:"max_header_bytes,omitempty"` + +	// Routes describes how this server will handle requests. +	// When a request comes in, each route's matchers will +	// be evaluated against the request, and matching routes +	// will be compiled into a middleware chain in the order +	// in which they appear in the list. +	Routes RouteList `json:"routes,omitempty"` + +	// Errors is how this server will handle errors returned from +	// any of the handlers in the primary routes. +	Errors *HTTPErrorConfig `json:"errors,omitempty"` + +	// How to handle TLS connections. +	TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"` + +	// AutoHTTPS configures or disables automatic HTTPS within this server. +	// HTTPS is enabled automatically and by default when qualifying names +	// are present in a Host matcher. +	AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"` + +	// MaxRehandles is the maximum number of times to allow a +	// request to be rehandled, to prevent accidental infinite +	// loops. Default: 1. +	MaxRehandles *int `json:"max_rehandles,omitempty"` + +	// If true, will require that a request's Host header match +	// the value of the ServerName sent by the client's TLS +	// ClientHello; often a necessary safeguard when using TLS +	// client authentication. +	StrictSNIHost *bool `json:"strict_sni_host,omitempty"` + +	// Logs customizes how access logs are handled in this server. +	Logs *ServerLogConfig `json:"logs,omitempty"` + +	// Enable experimental HTTP/3 support. Note that HTTP/3 is not a +	// finished standard and has extremely limited client support. +	// This field is not subject to compatibility promises.  	ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`  	tlsApp       *caddytls.TLS @@ -296,6 +374,8 @@ func (s *Server) hasTLSClientAuth() bool {  // AutoHTTPSConfig is used to disable automatic HTTPS  // or certain aspects of it for a specific server. +// HTTPS is enabled automatically and by default when +// qualifying hostnames are available from the config.  type AutoHTTPSConfig struct {  	// If true, automatic HTTPS will be entirely disabled.  	Disabled bool `json:"disable,omitempty"` diff --git a/modules/caddyhttp/starlarkmw/internal/lib/module.go b/modules/caddyhttp/starlarkmw/internal/lib/module.go index a7164cd..a75aedf 100644 --- a/modules/caddyhttp/starlarkmw/internal/lib/module.go +++ b/modules/caddyhttp/starlarkmw/internal/lib/module.go @@ -64,7 +64,7 @@ func (r *LoadMiddleware) Run(thread *starlark.Thread, fn *starlark.Builtin, args  		name = fmt.Sprintf("http.handlers.%s", name)  	} -	inst, err := r.Ctx.LoadModule(name, js) +	inst, err := r.Ctx.LoadModuleByID(name, js)  	if err != nil {  		return starlark.None, err  	} @@ -112,7 +112,7 @@ func (r *LoadResponder) Run(thread *starlark.Thread, fn *starlark.Builtin, args  		name = fmt.Sprintf("http.handlers.%s", name)  	} -	inst, err := r.Ctx.LoadModule(name, js) +	inst, err := r.Ctx.LoadModuleByID(name, js)  	if err != nil {  		return starlark.None, err  	} diff --git a/modules/caddyhttp/starlarkmw/starlarkmw.go b/modules/caddyhttp/starlarkmw/starlarkmw.go index 007ddb4..47e335d 100644 --- a/modules/caddyhttp/starlarkmw/starlarkmw.go +++ b/modules/caddyhttp/starlarkmw/starlarkmw.go @@ -7,8 +7,8 @@ import (  	"github.com/caddyserver/caddy/v2"  	"github.com/caddyserver/caddy/v2/modules/caddyhttp" -	caddyscript "github.com/caddyserver/caddy/v2/pkg/caddyscript/lib"  	"github.com/caddyserver/caddy/v2/modules/caddyhttp/starlarkmw/internal/lib" +	caddyscript "github.com/caddyserver/caddy/v2/pkg/caddyscript/lib"  	"github.com/starlight-go/starlight/convert"  	"go.starlark.net/starlark"  ) @@ -34,8 +34,8 @@ type StarlarkMW struct {  // CaddyModule returns the Caddy module information.  func (StarlarkMW) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.starlark", -		New:  func() caddy.Module { return new(StarlarkMW) }, +		ID:  "http.handlers.starlark", +		New: func() caddy.Module { return new(StarlarkMW) },  	}  } diff --git a/modules/caddyhttp/staticerror.go b/modules/caddyhttp/staticerror.go index 3a45366..fd1490d 100644 --- a/modules/caddyhttp/staticerror.go +++ b/modules/caddyhttp/staticerror.go @@ -35,8 +35,8 @@ type StaticError struct {  // CaddyModule returns the Caddy module information.  func (StaticError) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.error", -		New:  func() caddy.Module { return new(StaticError) }, +		ID:  "http.handlers.error", +		New: func() caddy.Module { return new(StaticError) },  	}  } diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go index 732a3fb..44b045e 100644 --- a/modules/caddyhttp/staticresp.go +++ b/modules/caddyhttp/staticresp.go @@ -38,8 +38,8 @@ type StaticResponse struct {  // CaddyModule returns the Caddy module information.  func (StaticResponse) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.static_response", -		New:  func() caddy.Module { return new(StaticResponse) }, +		ID:  "http.handlers.static_response", +		New: func() caddy.Module { return new(StaticResponse) },  	}  } diff --git a/modules/caddyhttp/subroute.go b/modules/caddyhttp/subroute.go index 57fb80a..a60eaf7 100644 --- a/modules/caddyhttp/subroute.go +++ b/modules/caddyhttp/subroute.go @@ -44,8 +44,8 @@ type Subroute struct {  // CaddyModule returns the Caddy module information.  func (Subroute) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.subroute", -		New:  func() caddy.Module { return new(Subroute) }, +		ID:  "http.handlers.subroute", +		New: func() caddy.Module { return new(Subroute) },  	}  } diff --git a/modules/caddyhttp/templates/templates.go b/modules/caddyhttp/templates/templates.go index e9c1da8..ac37e9d 100644 --- a/modules/caddyhttp/templates/templates.go +++ b/modules/caddyhttp/templates/templates.go @@ -39,8 +39,8 @@ type Templates struct {  // CaddyModule returns the Caddy module information.  func (Templates) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.templates", -		New:  func() caddy.Module { return new(Templates) }, +		ID:  "http.handlers.templates", +		New: func() caddy.Module { return new(Templates) },  	}  } diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go index 3fb8fa3..791203b 100644 --- a/modules/caddyhttp/vars.go +++ b/modules/caddyhttp/vars.go @@ -32,8 +32,8 @@ type VarsMiddleware map[string]string  // CaddyModule returns the Caddy module information.  func (VarsMiddleware) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.handlers.vars", -		New:  func() caddy.Module { return new(VarsMiddleware) }, +		ID:  "http.handlers.vars", +		New: func() caddy.Module { return new(VarsMiddleware) },  	}  } @@ -55,8 +55,8 @@ type VarsMatcher map[string]string  // CaddyModule returns the Caddy module information.  func (VarsMatcher) CaddyModule() caddy.ModuleInfo {  	return caddy.ModuleInfo{ -		Name: "http.matchers.vars", -		New:  func() caddy.Module { return new(VarsMatcher) }, +		ID:  "http.matchers.vars", +		New: func() caddy.Module { return new(VarsMatcher) },  	}  }  | 
