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) }, } } |