summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2019-12-10 13:36:46 -0700
committerGitHub <noreply@github.com>2019-12-10 13:36:46 -0700
commit3c90e370a49cafe7f58c7195187822ddc86ced4a (patch)
treeaadac21fcc1d55b37e65762022f8f30f565c2d8d /modules
parenta8533e563045f686b4c5af8d293903ab5c238244 (diff)
v2: Module documentation; refactor LoadModule(); new caddy struct tags (#2924)
This commit goes a long way toward making automated documentation of Caddy config and Caddy modules possible. It's a broad, sweeping change, but mostly internal. It allows us to automatically generate docs for all Caddy modules (including future third-party ones) and make them viewable on a web page; it also doubles as godoc comments. As such, this commit makes significant progress in migrating the docs from our temporary wiki page toward our new website which is still under construction. With this change, all host modules will use ctx.LoadModule() and pass in both the struct pointer and the field name as a string. This allows the reflect package to read the struct tag from that field so that it can get the necessary information like the module namespace and the inline key. This has the nice side-effect of unifying the code and documentation. It also simplifies module loading, and handles several variations on field types for raw module fields (i.e. variations on json.RawMessage, such as arrays and maps). I also renamed ModuleInfo.Name -> ModuleInfo.ID, to make it clear that the ID is the "full name" which includes both the module namespace and the name. This clarity is helpful when describing module hierarchy. As of this change, Caddy modules are no longer an experimental design. I think the architecture is good enough to go forward.
Diffstat (limited to 'modules')
-rw-r--r--modules/caddyhttp/caddyauth/basicauth.go11
-rw-r--r--modules/caddyhttp/caddyauth/caddyauth.go21
-rw-r--r--modules/caddyhttp/caddyauth/caddyfile.go4
-rw-r--r--modules/caddyhttp/caddyauth/hashes.go8
-rw-r--r--modules/caddyhttp/caddyhttp.go35
-rw-r--r--modules/caddyhttp/encode/brotli/brotli.go4
-rw-r--r--modules/caddyhttp/encode/caddyfile.go13
-rw-r--r--modules/caddyhttp/encode/encode.go27
-rw-r--r--modules/caddyhttp/encode/gzip/gzip.go4
-rw-r--r--modules/caddyhttp/encode/zstd/zstd.go4
-rw-r--r--modules/caddyhttp/fileserver/caddyfile.go5
-rw-r--r--modules/caddyhttp/fileserver/command.go6
-rw-r--r--modules/caddyhttp/fileserver/matcher.go4
-rw-r--r--modules/caddyhttp/fileserver/staticfiles.go4
-rw-r--r--modules/caddyhttp/headers/headers.go4
-rw-r--r--modules/caddyhttp/httpcache/httpcache.go4
-rw-r--r--modules/caddyhttp/markdown/markdown.go4
-rw-r--r--modules/caddyhttp/matchers.go61
-rw-r--r--modules/caddyhttp/requestbody/requestbody.go4
-rw-r--r--modules/caddyhttp/reverseproxy/caddyfile.go12
-rw-r--r--modules/caddyhttp/reverseproxy/circuitbreaker.go4
-rw-r--r--modules/caddyhttp/reverseproxy/command.go6
-rw-r--r--modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go17
-rw-r--r--modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go4
-rw-r--r--modules/caddyhttp/reverseproxy/httptransport.go5
-rw-r--r--modules/caddyhttp/reverseproxy/ntlm.go8
-rw-r--r--modules/caddyhttp/reverseproxy/reverseproxy.go50
-rw-r--r--modules/caddyhttp/reverseproxy/selectionpolicies.go32
-rw-r--r--modules/caddyhttp/rewrite/rewrite.go4
-rw-r--r--modules/caddyhttp/routes.go143
-rw-r--r--modules/caddyhttp/server.go112
-rw-r--r--modules/caddyhttp/starlarkmw/internal/lib/module.go4
-rw-r--r--modules/caddyhttp/starlarkmw/starlarkmw.go6
-rw-r--r--modules/caddyhttp/staticerror.go4
-rw-r--r--modules/caddyhttp/staticresp.go4
-rw-r--r--modules/caddyhttp/subroute.go4
-rw-r--r--modules/caddyhttp/templates/templates.go4
-rw-r--r--modules/caddyhttp/vars.go8
-rw-r--r--modules/caddytls/acmemanager.go68
-rw-r--r--modules/caddytls/certselection.go4
-rw-r--r--modules/caddytls/connpolicy.go51
-rw-r--r--modules/caddytls/distributedstek/distributedstek.go17
-rw-r--r--modules/caddytls/fileloader.go21
-rw-r--r--modules/caddytls/folderloader.go4
-rw-r--r--modules/caddytls/matchers.go4
-rw-r--r--modules/caddytls/pemloader.go17
-rw-r--r--modules/caddytls/sessiontickets.go24
-rw-r--r--modules/caddytls/standardstek/stek.go4
-rw-r--r--modules/caddytls/tls.go201
-rw-r--r--modules/filestorage/filestorage.go4
-rw-r--r--modules/logging/encoders.go21
-rw-r--r--modules/logging/filewriter.go40
-rw-r--r--modules/logging/filterencoder.go41
-rw-r--r--modules/logging/filters.go11
54 files changed, 771 insertions, 419 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) },
}
}
diff --git a/modules/caddytls/acmemanager.go b/modules/caddytls/acmemanager.go
index 9f31215..31c954f 100644
--- a/modules/caddytls/acmemanager.go
+++ b/modules/caddytls/acmemanager.go
@@ -40,16 +40,50 @@ func init() {
// after you have configured this struct
// to your liking.
type ACMEManagerMaker struct {
- CA string `json:"ca,omitempty"`
- Email string `json:"email,omitempty"`
- RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
- KeyType string `json:"key_type,omitempty"`
- ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
- MustStaple bool `json:"must_staple,omitempty"`
- Challenges *ChallengesConfig `json:"challenges,omitempty"`
- OnDemand bool `json:"on_demand,omitempty"`
- Storage json.RawMessage `json:"storage,omitempty"`
- TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`
+ // The URL to the CA's ACME directory endpoint.
+ CA string `json:"ca,omitempty"`
+
+ // Your email address, so the CA can contact you if necessary.
+ // Not required, but strongly recommended to provide one so
+ // you can be reached if there is a problem. Your email is
+ // not sent to any Caddy mothership or used for any purpose
+ // other than ACME transactions.
+ Email string `json:"email,omitempty"`
+
+ // How long before a certificate's expiration to try renewing it.
+ // Should usually be about 1/3 of certificate lifetime, but long
+ // enough to give yourself time to troubleshoot problems before
+ // expiration. Default: 30d
+ RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
+
+ // The type of key to generate for the certificate.
+ // Supported values: `rsa2048`, `rsa4096`, `p256`, `p384`.
+ KeyType string `json:"key_type,omitempty"`
+
+ // Time to wait before timing out an ACME operation.
+ ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
+
+ // If true, certificates will be requested with MustStaple. Not all
+ // CAs support this, and there are potentially serious consequences
+ // of enabling this feature without proper threat modeling.
+ MustStaple bool `json:"must_staple,omitempty"`
+
+ // Configures the various ACME challenge types.
+ Challenges *ChallengesConfig `json:"challenges,omitempty"`
+
+ // If true, certificates will be managed "on demand", that is, during
+ // TLS handshakes or when needed, as opposed to at startup or config
+ // load.
+ OnDemand bool `json:"on_demand,omitempty"`
+
+ // Optionally configure a separate storage module associated with this
+ // manager, instead of using Caddy's global/default-configured storage.
+ Storage json.RawMessage `json:"storage,omitempty"`
+
+ // An array of files of CA certificates to accept when connecting to the
+ // ACME CA. Generally, you should only use this if the ACME CA endpoint
+ // is internal or for development/testing purposes.
+ TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`
storage certmagic.Storage
rootPool *x509.CertPool
@@ -58,8 +92,8 @@ type ACMEManagerMaker struct {
// CaddyModule returns the Caddy module information.
func (ACMEManagerMaker) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.management.acme",
- New: func() caddy.Module { return new(ACMEManagerMaker) },
+ ID: "tls.management.acme",
+ New: func() caddy.Module { return new(ACMEManagerMaker) },
}
}
@@ -73,26 +107,24 @@ func (m ACMEManagerMaker) NewManager(interactive bool) (certmagic.Manager, error
func (m *ACMEManagerMaker) Provision(ctx caddy.Context) error {
// DNS providers
if m.Challenges != nil && m.Challenges.DNSRaw != nil {
- val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNSRaw)
+ val, err := ctx.LoadModule(m.Challenges, "DNSRaw")
if err != nil {
- return fmt.Errorf("loading DNS provider module: %s", err)
+ return fmt.Errorf("loading DNS provider module: %v", err)
}
m.Challenges.DNS = val.(challenge.Provider)
- m.Challenges.DNSRaw = nil // allow GC to deallocate
}
// policy-specific storage implementation
if m.Storage != nil {
- val, err := ctx.LoadModuleInline("module", "caddy.storage", m.Storage)
+ val, err := ctx.LoadModule(m, "Storage")
if err != nil {
- return fmt.Errorf("loading TLS storage module: %s", err)
+ return fmt.Errorf("loading TLS storage module: %v", err)
}
cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
if err != nil {
return fmt.Errorf("creating TLS storage configuration: %v", err)
}
m.storage = cmStorage
- m.Storage = nil // allow GC to deallocate
}
// add any custom CAs to trust store
diff --git a/modules/caddytls/certselection.go b/modules/caddytls/certselection.go
index b56185a..eb01605 100644
--- a/modules/caddytls/certselection.go
+++ b/modules/caddytls/certselection.go
@@ -28,8 +28,8 @@ type Policy struct {
// CaddyModule returns the Caddy module information.
func (Policy) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.certificate_selection.custom",
- New: func() caddy.Module { return new(Policy) },
+ ID: "tls.certificate_selection.custom",
+ New: func() caddy.Module { return new(Policy) },
}
}
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index c82337d..6ce6b9e 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -39,23 +39,21 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) (*tls.Config, error) {
// set up each of the connection policies
for i, pol := range cp {
// matchers
- for modName, rawMsg := range pol.Matchers {
- val, err := ctx.LoadModule("tls.handshake_match."+modName, rawMsg)
- if err != nil {
- return nil, fmt.Errorf("loading handshake matcher module '%s': %s", modName, err)
- }
- cp[i].matchers = append(cp[i].matchers, val.(ConnectionMatcher))
+ mods, err := ctx.LoadModule(pol, "MatchersRaw")
+ if err != nil {
+ return nil, fmt.Errorf("loading handshake matchers: %v", err)
+ }
+ for _, modIface := range mods.(map[string]interface{}) {
+ cp[i].matchers = append(cp[i].matchers, modIface.(ConnectionMatcher))
}
- cp[i].Matchers = nil // allow GC to deallocate
// certificate selector
if pol.CertSelection != nil {
- val, err := ctx.LoadModuleInline("policy", "tls.certificate_selection", pol.CertSelection)
+ val, err := ctx.LoadModule(pol, "CertSelection")
if err != nil {
return nil, fmt.Errorf("loading certificate selection module: %s", err)
}
cp[i].certSelector = val.(certmagic.CertificateSelector)
- cp[i].CertSelection = nil // allow GC to deallocate
}
}
@@ -109,14 +107,33 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) (*tls.Config, error) {
// ConnectionPolicy specifies the logic for handling a TLS handshake.
type ConnectionPolicy struct {
- Matchers map[string]json.RawMessage `json:"match,omitempty"`
- CertSelection json.RawMessage `json:"certificate_selection,omitempty"`
-
- CipherSuites []string `json:"cipher_suites,omitempty"`
- Curves []string `json:"curves,omitempty"`
- ALPN []string `json:"alpn,omitempty"`
- ProtocolMin string `json:"protocol_min,omitempty"`
- ProtocolMax string `json:"protocol_max,omitempty"`
+ // How to match this policy with a TLS ClientHello. If
+ // this policy is the first to match, it will be used.
+ MatchersRaw caddy.ModuleMap `json:"match,omitempty" caddy:"namespace=tls.handshake_match"`
+
+ // How to choose a certificate if more than one matched
+ // the given ServerName (SNI) value.
+ CertSelection json.RawMessage `json:"certificate_selection,omitempty" caddy:"namespace=tls.certificate_selection inline_key=policy"`
+
+ // The list of cipher suites to support. Caddy's
+ // defaults are modern and secure.
+ CipherSuites []string `json:"cipher_suites,omitempty"`
+
+ // The list of elliptic curves to support. Caddy's
+ // defaults are modern and secure.
+ Curves []string `json:"curves,omitempty"`
+
+ // Protocols to use for Application-Layer Protocol
+ // Negotiation (ALPN) during the handshake.
+ ALPN []string `json:"alpn,omitempty"`
+
+ // Minimum TLS protocol version to allow. Default: `tls1.2`
+ ProtocolMin string `json:"protocol_min,omitempty"`
+
+ // Maximum TLS protocol version to allow. Default: `tls1.3`
+ ProtocolMax string `json:"protocol_max,omitempty"`
+
+ // Enables and configures TLS client authentication.
ClientAuthentication *ClientAuthentication `json:"client_authentication,omitempty"`
matchers []ConnectionMatcher
diff --git a/modules/caddytls/distributedstek/distributedstek.go b/modules/caddytls/distributedstek/distributedstek.go
index a0c4cd2..cef3733 100644
--- a/modules/caddytls/distributedstek/distributedstek.go
+++ b/modules/caddytls/distributedstek/distributedstek.go
@@ -39,9 +39,15 @@ func init() {
caddy.RegisterModule(Provider{})
}
-// Provider implements a distributed STEK provider.
+// Provider implements a distributed STEK provider. This
+// module will obtain STEKs from a storage module instead
+// of generating STEKs internally. This allows STEKs to be
+// coordinated, improving TLS session resumption in a cluster.
type Provider struct {
- Storage json.RawMessage `json:"storage,omitempty"`
+ // The storage module wherein to store and obtain session
+ // ticket keys. If unset, Caddy's default/global-configured
+ // storage module will be used.
+ Storage json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
storage certmagic.Storage
stekConfig *caddytls.SessionTicketService
@@ -51,8 +57,8 @@ type Provider struct {
// CaddyModule returns the Caddy module information.
func (Provider) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.stek.distributed",
- New: func() caddy.Module { return new(Provider) },
+ ID: "tls.stek.distributed",
+ New: func() caddy.Module { return new(Provider) },
}
}
@@ -60,7 +66,7 @@ func (Provider) CaddyModule() caddy.ModuleInfo {
func (s *Provider) Provision(ctx caddy.Context) error {
// unpack the storage module to use, if different from the default
if s.Storage != nil {
- val, err := ctx.LoadModuleInline("module", "caddy.storage", s.Storage)
+ val, err := ctx.LoadModule(s, "Storage")
if err != nil {
return fmt.Errorf("loading TLS storage module: %s", err)
}
@@ -69,7 +75,6 @@ func (s *Provider) Provision(ctx caddy.Context) error {
return fmt.Errorf("creating TLS storage configuration: %v", err)
}
s.storage = cmStorage
- s.Storage = nil // allow GC to deallocate
}
// otherwise, use default storage
diff --git a/modules/caddytls/fileloader.go b/modules/caddytls/fileloader.go
index b2cc132..6d6ff99 100644
--- a/modules/caddytls/fileloader.go
+++ b/modules/caddytls/fileloader.go
@@ -32,18 +32,27 @@ type FileLoader []CertKeyFilePair
// CaddyModule returns the Caddy module information.
func (FileLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.certificates.load_files",
- New: func() caddy.Module { return new(FileLoader) },
+ ID: "tls.certificates.load_files",
+ New: func() caddy.Module { return new(FileLoader) },
}
}
// CertKeyFilePair pairs certificate and key file names along with their
// encoding format so that they can be loaded from disk.
type CertKeyFilePair struct {
- Certificate string `json:"certificate"`
- Key string `json:"key"`
- Format string `json:"format,omitempty"` // "pem" is default
- Tags []string `json:"tags,omitempty"`
+ // Path to the certificate (public key) file.
+ Certificate string `json:"certificate"`
+
+ // Path to the private key file.
+ Key string `json:"key"`
+
+ // The format of the cert and key. Can be "pem". Default: "pem"
+ Format string `json:"format,omitempty"`
+
+ // Arbitrary values to associate with this certificate.
+ // Can be useful when you want to select a particular
+ // certificate when there may be multiple valid candidates.
+ Tags []string `json:"tags,omitempty"`
}
// LoadCertificates returns the certificates to be loaded by fl.
diff --git a/modules/caddytls/folderloader.go b/modules/caddytls/folderloader.go
index da1dff0..f1a742d 100644
--- a/modules/caddytls/folderloader.go
+++ b/modules/caddytls/folderloader.go
@@ -39,8 +39,8 @@ type FolderLoader []string
// CaddyModule returns the Caddy module information.
func (FolderLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.certificates.load_folders",
- New: func() caddy.Module { return new(FolderLoader) },
+ ID: "tls.certificates.load_folders",
+ New: func() caddy.Module { return new(FolderLoader) },
}
}
diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go
index 47fb296..9e2dfc5 100644
--- a/modules/caddytls/matchers.go
+++ b/modules/caddytls/matchers.go
@@ -30,8 +30,8 @@ type MatchServerName []string
// CaddyModule returns the Caddy module information.
func (MatchServerName) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.handshake_match.sni",
- New: func() caddy.Module { return new(MatchServerName) },
+ ID: "tls.handshake_match.sni",
+ New: func() caddy.Module { return new(MatchServerName) },
}
}
diff --git a/modules/caddytls/pemloader.go b/modules/caddytls/pemloader.go
index 30a491c..46d06a8 100644
--- a/modules/caddytls/pemloader.go
+++ b/modules/caddytls/pemloader.go
@@ -33,16 +33,23 @@ type PEMLoader []CertKeyPEMPair
// CaddyModule returns the Caddy module information.
func (PEMLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.certificates.load_pem",
- New: func() caddy.Module { return PEMLoader{} },
+ ID: "tls.certificates.load_pem",
+ New: func() caddy.Module { return PEMLoader{} },
}
}
// CertKeyPEMPair pairs certificate and key PEM blocks.
type CertKeyPEMPair struct {
- CertificatePEM string `json:"certificate"`
- KeyPEM string `json:"key"`
- Tags []string `json:"tags,omitempty"`
+ // The certificate (public key) in PEM format.
+ CertificatePEM string `json:"certificate"`
+
+ // The private key in PEM format.
+ KeyPEM string `json:"key"`
+
+ // Arbitrary values to associate with this certificate.
+ // Can be useful when you want to select a particular
+ // certificate when there may be multiple valid candidates.
+ Tags []string `json:"tags,omitempty"`
}
// LoadCertificates returns the certificates contained in pl.
diff --git a/modules/caddytls/sessiontickets.go b/modules/caddytls/sessiontickets.go
index 6ca921d..258c135 100644
--- a/modules/caddytls/sessiontickets.go
+++ b/modules/caddytls/sessiontickets.go
@@ -28,11 +28,22 @@ import (
// SessionTicketService configures and manages TLS session tickets.
type SessionTicketService struct {
- KeySource json.RawMessage `json:"key_source,omitempty"`
- RotationInterval caddy.Duration `json:"rotation_interval,omitempty"`
- MaxKeys int `json:"max_keys,omitempty"`
- DisableRotation bool `json:"disable_rotation,omitempty"`
- Disabled bool `json:"disabled,omitempty"`
+ // KeySource is the method by which Caddy produces or obtains
+ // TLS session ticket keys (STEKs). By default, Caddy generates
+ // them internally using a secure pseudorandom source.
+ KeySource json.RawMessage `json:"key_source,omitempty" caddy:"namespace=tls.stek inline_key=provider"`
+
+ // How often Caddy rotates STEKs. Default: 12h.
+ RotationInterval caddy.Duration `json:"rotation_interval,omitempty"`
+
+ // The maximum number of keys to keep in rotation. Default: 4.
+ MaxKeys int `json:"max_keys,omitempty"`
+
+ // Disables STEK rotation.
+ DisableRotation bool `json:"disable_rotation,omitempty"`
+
+ // Disables TLS session resumption by tickets.
+ Disabled bool `json:"disabled,omitempty"`
keySource STEKProvider
configs map[*tls.Config]struct{}
@@ -57,12 +68,11 @@ func (s *SessionTicketService) provision(ctx caddy.Context) error {
}
// load the STEK module, which will provide keys
- val, err := ctx.LoadModuleInline("provider", "tls.stek", s.KeySource)
+ val, err := ctx.LoadModule(s, "KeySource")
if err != nil {
return fmt.Errorf("loading TLS session ticket ephemeral keys provider module: %s", err)
}
s.keySource = val.(STEKProvider)
- s.KeySource = nil // allow GC to deallocate
// if session tickets or just rotation are
// disabled, no need to start service
diff --git a/modules/caddytls/standardstek/stek.go b/modules/caddytls/standardstek/stek.go
index 6d10c76..eb609ca 100644
--- a/modules/caddytls/standardstek/stek.go
+++ b/modules/caddytls/standardstek/stek.go
@@ -35,8 +35,8 @@ type standardSTEKProvider struct {
// CaddyModule returns the Caddy module information.
func (standardSTEKProvider) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls.stek.standard",
- New: func() caddy.Module { return new(standardSTEKProvider) },
+ ID: "tls.stek.standard",
+ New: func() caddy.Module { return new(standardSTEKProvider) },
}
}
diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go
index 5dfe063..1b155b0 100644
--- a/modules/caddytls/tls.go
+++ b/modules/caddytls/tls.go
@@ -30,15 +30,30 @@ import (
func init() {
caddy.RegisterModule(TLS{})
+ caddy.RegisterModule(AutomateLoader{})
}
-// TLS represents a process-wide TLS configuration.
+// TLS provides TLS facilities including certificate
+// loading and management, client auth, and more.
type TLS struct {
- Certificates map[string]json.RawMessage `json:"certificates,omitempty"`
- Automation *AutomationConfig `json:"automation,omitempty"`
- SessionTickets *SessionTicketService `json:"session_tickets,omitempty"`
+ // Caches certificates in memory for quick use during
+ // TLS handshakes. Each key is the name of a certificate
+ // loader module. All loaded certificates get pooled
+ // into the same cache and may be used to complete TLS
+ // handshakes for the relevant server names (SNI).
+ // Certificates loaded manually (anything other than
+ // "automate") are not automatically managed and will
+ // have to be refreshed manually before they expire.
+ CertificatesRaw caddy.ModuleMap `json:"certificates,omitempty" caddy:"namespace=tls.certificates"`
+
+ // Configures the automation of certificate management.
+ Automation *AutomationConfig `json:"automation,omitempty"`
+
+ // Configures session ticket ephemeral keys (STEKs).
+ SessionTickets *SessionTicketService `json:"session_tickets,omitempty"`
certificateLoaders []CertificateLoader
+ automateNames []string
certCache *certmagic.Cache
ctx caddy.Context
storageCleanTicker *time.Ticker
@@ -49,8 +64,8 @@ type TLS struct {
// CaddyModule returns the Caddy module information.
func (TLS) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "tls",
- New: func() caddy.Module { return new(TLS) },
+ ID: "tls",
+ New: func() caddy.Module { return new(TLS) },
}
}
@@ -74,25 +89,32 @@ func (t *TLS) Provision(ctx caddy.Context) error {
// automation/management policies
if t.Automation != nil {
for i, ap := range t.Automation.Policies {
- val, err := ctx.LoadModuleInline("module", "tls.management", ap.ManagementRaw)
+ val, err := ctx.LoadModule(&ap, "ManagementRaw")
if err != nil {
return fmt.Errorf("loading TLS automation management module: %s", err)
}
t.Automation.Policies[i].Management = val.(ManagerMaker)
- t.Automation.Policies[i].ManagementRaw = nil // allow GC to deallocate
}
}
// certificate loaders
- for modName, rawMsg := range t.Certificates {
- if modName == automateKey {
- continue // special case; these will be loaded in later
- }
- val, err := ctx.LoadModule("tls.certificates."+modName, rawMsg)
- if err != nil {
- return fmt.Errorf("loading certificate module '%s': %s", modName, err)
+ val, err := ctx.LoadModule(t, "CertificatesRaw")
+ if err != nil {
+ return fmt.Errorf("loading TLS automation management module: %s", err)
+ }
+ for modName, modIface := range val.(map[string]interface{}) {
+ if modName == "automate" {
+ // special case; these will be loaded in later
+ // using our automation facilities, which we
+ // want to avoid during provisioning
+ var ok bool
+ t.automateNames, ok = modIface.([]string)
+ if !ok {
+ return fmt.Errorf("loading certificates with 'automate' requires []string, got: %#v", modIface)
+ }
+ continue
}
- t.certificateLoaders = append(t.certificateLoaders, val.(CertificateLoader))
+ t.certificateLoaders = append(t.certificateLoaders, modIface.(CertificateLoader))
}
// session ticket ephemeral keys (STEK) service and provider
@@ -115,7 +137,8 @@ func (t *TLS) Provision(ctx caddy.Context) error {
// load manual/static (unmanaged) certificates - we do this in
// provision so that other apps (such as http) can know which
- // certificates have been manually loaded
+ // certificates have been manually loaded, and also so that
+ // commands like validate can be a better test
magic := certmagic.New(t.certCache, certmagic.Config{
Storage: ctx.Storage(),
})
@@ -137,19 +160,12 @@ func (t *TLS) Provision(ctx caddy.Context) error {
// Start activates the TLS module.
func (t *TLS) Start() error {
- // load automated (managed) certificates
- if automatedRawMsg, ok := t.Certificates[automateKey]; ok {
- var names []string
- err := json.Unmarshal(automatedRawMsg, &names)
- if err != nil {
- return fmt.Errorf("automate: decoding names: %v", err)
- }
- err = t.Manage(names)
- if err != nil {
- return fmt.Errorf("automate: managing %v: %v", names, err)
- }
+ // now that we are running, and all manual certificates have
+ // been loaded, time to load the automated/managed certificates
+ err := t.Manage(t.automateNames)
+ if err != nil {
+ return fmt.Errorf("automate: managing %v: %v", t.automateNames, err)
}
- t.Certificates = nil // allow GC to deallocate
t.keepStorageClean()
@@ -311,18 +327,48 @@ type Certificate struct {
// AutomationConfig designates configuration for the
// construction and use of ACME clients.
type AutomationConfig struct {
- Policies []AutomationPolicy `json:"policies,omitempty"`
- OnDemand *OnDemandConfig `json:"on_demand,omitempty"`
- OCSPCheckInterval caddy.Duration `json:"ocsp_interval,omitempty"`
- RenewCheckInterval caddy.Duration `json:"renew_interval,omitempty"`
+ // The list of automation policies. The first matching
+ // policy will be applied for a given certificate/name.
+ Policies []AutomationPolicy `json:"policies,omitempty"`
+
+ // On-Demand TLS defers certificate operations to the
+ // moment they are needed, e.g. during a TLS handshake.
+ // Useful when you don't know all the hostnames up front.
+ // Caddy was the first web server to deploy this technology.
+ OnDemand *OnDemandConfig `json:"on_demand,omitempty"`
+
+ // Caddy staples OCSP (and caches the response) for all
+ // qualifying certificates by default. This setting
+ // changes how often it scans responses for freshness,
+ // and updates them if they are getting stale.
+ OCSPCheckInterval caddy.Duration `json:"ocsp_interval,omitempty"`
+
+ // Every so often, Caddy will scan all loaded, managed
+ // certificates for expiration. Certificates which are
+ // about 2/3 into their valid lifetime are due for
+ // renewal. This setting changes how frequently the scan
+ // is performed. If your certificate lifetimes are very
+ // short (less than ~1 week), you should customize this.
+ RenewCheckInterval caddy.Duration `json:"renew_interval,omitempty"`
}
// AutomationPolicy designates the policy for automating the
-// management of managed TLS certificates.
+// management (obtaining, renewal, and revocation) of managed
+// TLS certificates.
type AutomationPolicy struct {
- Hosts []string `json:"hosts,omitempty"`
- ManagementRaw json.RawMessage `json:"management,omitempty"`
- ManageSync bool `json:"manage_sync,omitempty"`
+ // Which hostnames this policy applies to.
+ Hosts []string `json:"hosts,omitempty"`
+
+ // How to manage certificates.
+ ManagementRaw json.RawMessage `json:"management,omitempty" caddy:"namespace=tls.management inline_key=module"`
+
+ // If true, certificate management will be conducted
+ // in the foreground; this will block config reloads
+ // and return errors if there were problems with
+ // obtaining or renewing certificates. This is often
+ // not desirable, especially when serving sites out
+ // of your control. Default: false
+ ManageSync bool `json:"manage_sync,omitempty"`
Management ManagerMaker `json:"-"`
}
@@ -345,36 +391,84 @@ func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
// ChallengesConfig configures the ACME challenges.
type ChallengesConfig struct {
- HTTP *HTTPChallengeConfig `json:"http,omitempty"`
+ // HTTP configures the ACME HTTP challenge. This
+ // challenge is enabled and used automatically
+ // and by default.
+ HTTP *HTTPChallengeConfig `json:"http,omitempty"`
+
+ // TLSALPN configures the ACME TLS-ALPN challenge.
+ // This challenge is enabled and used automatically
+ // and by default.
TLSALPN *TLSALPNChallengeConfig `json:"tls-alpn,omitempty"`
- DNSRaw json.RawMessage `json:"dns,omitempty"`
+
+ // Configures the ACME DNS challenge. Because this
+ // challenge typically requires credentials for
+ // interfacing with a DNS provider, this challenge is
+ // not enabled by default. This is the only challenge
+ // type which does not require a direct connection
+ // to Caddy from an external server.
+ DNSRaw json.RawMessage `json:"dns,omitempty" caddy:"namespace=tls.dns inline_key=provider"`
DNS challenge.Provider `json:"-"`
}
// HTTPChallengeConfig configures the ACME HTTP challenge.
type HTTPChallengeConfig struct {
- Disabled bool `json:"disabled,omitempty"`
- AlternatePort int `json:"alternate_port,omitempty"`
+ // If true, the HTTP challenge will be disabled.
+ Disabled bool `json:"disabled,omitempty"`
+
+ // An alternate port on which to service this
+ // challenge. Note that the HTTP challenge port is
+ // hard-coded into the spec and cannot be changed,
+ // so you would have to forward packets from the
+ // standard HTTP challenge port to this one.
+ AlternatePort int `json:"alternate_port,omitempty"`
}
// TLSALPNChallengeConfig configures the ACME TLS-ALPN challenge.
type TLSALPNChallengeConfig struct {
- Disabled bool `json:"disabled,omitempty"`
- AlternatePort int `json:"alternate_port,omitempty"`
+ // If true, the TLS-ALPN challenge will be disabled.
+ Disabled bool `json:"disabled,omitempty"`
+
+ // An alternate port on which to service this
+ // challenge. Note that the TLS-ALPN challenge port
+ // is hard-coded into the spec and cannot be changed,
+ // so you would have to forward packets from the
+ // standard TLS-ALPN challenge port to this one.
+ AlternatePort int `json:"alternate_port,omitempty"`
}
// OnDemandConfig configures on-demand TLS, for obtaining
-// needed certificates at handshake-time.
+// needed certificates at handshake-time. Because this
+// feature can easily be abused, you should set up rate
+// limits and/or an internal endpoint that Caddy can
+// "ask" if it should be allowed to manage certificates
+// for a given hostname.
type OnDemandConfig struct {
+ // An optional rate limit to throttle the
+ // issuance of certificates from handshakes.
RateLimit *RateLimit `json:"rate_limit,omitempty"`
- Ask string `json:"ask,omitempty"`
+
+ // If Caddy needs to obtain or renew a certificate
+ // during a TLS handshake, it will perform a quick
+ // HTTP request to this URL to check if it should be
+ // allowed to try to get a certificate for the name
+ // in the "domain" query string parameter, like so:
+ // `?domain=example.com`. The endpoint must return a
+ // 200 OK status if a certificate is allowed;
+ // anything else will cause it to be denied.
+ // Redirects are not followed.
+ Ask string `json:"ask,omitempty"`
}
// RateLimit specifies an interval with optional burst size.
type RateLimit struct {
+ // A duration value. A certificate may be obtained 'burst'
+ // times during this interval.
Interval caddy.Duration `json:"interval,omitempty"`
- Burst int `json:"burst,omitempty"`
+
+ // How many times during an interval a certificate can be obtained.
+ Burst int `json:"burst,omitempty"`
}
// ManagerMaker makes a certificate manager.
@@ -382,6 +476,21 @@ type ManagerMaker interface {
NewManager(interactive bool) (certmagic.Manager, error)
}
+// AutomateLoader is a no-op certificate loader module
+// that is treated as a special case: it uses this app's
+// automation features to load certificates for the
+// list of hostnames, rather than loading certificates
+// manually.
+type AutomateLoader []string
+
+// CaddyModule returns the Caddy module information.
+func (AutomateLoader) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ ID: "tls.certificates.automate",
+ New: func() caddy.Module { return new(AutomateLoader) },
+ }
+}
+
// These perpetual values are used for on-demand TLS.
var (
onDemandRateLimiter = certmagic.NewRateLimiter(0, 0)
diff --git a/modules/filestorage/filestorage.go b/modules/filestorage/filestorage.go
index 6217a07..55607ba 100644
--- a/modules/filestorage/filestorage.go
+++ b/modules/filestorage/filestorage.go
@@ -32,8 +32,8 @@ type FileStorage struct {
// CaddyModule returns the Caddy module information.
func (FileStorage) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.storage.file_system",
- New: func() caddy.Module { return new(FileStorage) },
+ ID: "caddy.storage.file_system",
+ New: func() caddy.Module { return new(FileStorage) },
}
}
diff --git a/modules/logging/encoders.go b/modules/logging/encoders.go
index c3c8aba..28cda55 100644
--- a/modules/logging/encoders.go
+++ b/modules/logging/encoders.go
@@ -43,8 +43,8 @@ type ConsoleEncoder struct {
// CaddyModule returns the Caddy module information.
func (ConsoleEncoder) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.console",
- New: func() caddy.Module { return new(ConsoleEncoder) },
+ ID: "caddy.logging.encoders.console",
+ New: func() caddy.Module { return new(ConsoleEncoder) },
}
}
@@ -63,8 +63,8 @@ type JSONEncoder struct {
// CaddyModule returns the Caddy module information.
func (JSONEncoder) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.json",
- New: func() caddy.Module { return new(JSONEncoder) },
+ ID: "caddy.logging.encoders.json",
+ New: func() caddy.Module { return new(JSONEncoder) },
}
}
@@ -84,8 +84,8 @@ type LogfmtEncoder struct {
// CaddyModule returns the Caddy module information.
func (LogfmtEncoder) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.logfmt",
- New: func() caddy.Module { return new(LogfmtEncoder) },
+ ID: "caddy.logging.encoders.logfmt",
+ New: func() caddy.Module { return new(LogfmtEncoder) },
}
}
@@ -102,25 +102,24 @@ func (lfe *LogfmtEncoder) Provision(_ caddy.Context) error {
type StringEncoder struct {
zapcore.Encoder
FieldName string `json:"field,omitempty"`
- FallbackRaw json.RawMessage `json:"fallback,omitempty"`
+ FallbackRaw json.RawMessage `json:"fallback,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
}
// CaddyModule returns the Caddy module information.
func (StringEncoder) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.string",
- New: func() caddy.Module { return new(StringEncoder) },
+ ID: "caddy.logging.encoders.string",
+ New: func() caddy.Module { return new(StringEncoder) },
}
}
// Provision sets up the encoder.
func (se *StringEncoder) Provision(ctx caddy.Context) error {
if se.FallbackRaw != nil {
- val, err := ctx.LoadModuleInline("format", "caddy.logging.encoders", se.FallbackRaw)
+ val, err := ctx.LoadModule(se, "FallbackRaw")
if err != nil {
return fmt.Errorf("loading fallback encoder module: %v", err)
}
- se.FallbackRaw = nil // allow GC to deallocate
se.Encoder = val.(zapcore.Encoder)
}
if se.Encoder == nil {
diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go
index 6957b6a..cc60c64 100644
--- a/modules/logging/filewriter.go
+++ b/modules/logging/filewriter.go
@@ -28,22 +28,42 @@ func init() {
caddy.RegisterModule(FileWriter{})
}
-// FileWriter can write logs to files.
+// FileWriter can write logs to files. By default, log files
+// are rotated ("rolled") when they get large, and old log
+// files get deleted, to ensure that the process does not
+// exhaust disk space.
type FileWriter struct {
- Filename string `json:"filename,omitempty"`
- Roll *bool `json:"roll,omitempty"`
- RollSizeMB int `json:"roll_size_mb,omitempty"`
- RollCompress *bool `json:"roll_gzip,omitempty"`
- RollLocalTime bool `json:"roll_local_time,omitempty"`
- RollKeep int `json:"roll_keep,omitempty"`
- RollKeepDays int `json:"roll_keep_days,omitempty"`
+ // Filename is the name of the file to write.
+ Filename string `json:"filename,omitempty"`
+
+ // Roll toggles log rolling or rotation, which is
+ // enabled by default.
+ Roll *bool `json:"roll,omitempty"`
+
+ // When a log file reaches approximately this size,
+ // it will be rotated.
+ RollSizeMB int `json:"roll_size_mb,omitempty"`
+
+ // Whether to compress rolled files. Default: true
+ RollCompress *bool `json:"roll_gzip,omitempty"`
+
+ // Whether to use local timestamps in rolled filenames.
+ // Default: false
+ RollLocalTime bool `json:"roll_local_time,omitempty"`
+
+ // The maximum number of rolled log files to keep.
+ // Default: 10
+ RollKeep int `json:"roll_keep,omitempty"`
+
+ // How many days to keep rolled log files. Default: 90
+ RollKeepDays int `json:"roll_keep_days,omitempty"`
}
// CaddyModule returns the Caddy module information.
func (FileWriter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.writers.file",
- New: func() caddy.Module { return new(FileWriter) },
+ ID: "caddy.logging.writers.file",
+ New: func() caddy.Module { return new(FileWriter) },
}
}
diff --git a/modules/logging/filterencoder.go b/modules/logging/filterencoder.go
index eff0279..6680019 100644
--- a/modules/logging/filterencoder.go
+++ b/modules/logging/filterencoder.go
@@ -29,13 +29,17 @@ func init() {
caddy.RegisterModule(FilterEncoder{})
}
-// FilterEncoder wraps an underlying encoder. It does
-// not do any encoding itself, but it can manipulate
-// (filter) fields before they are actually encoded.
-// A wrapped encoder is required.
+// FilterEncoder can filter (manipulate) fields on
+// log entries before they are actually encoded by
+// an underlying encoder.
type FilterEncoder struct {
- WrappedRaw json.RawMessage `json:"wrap,omitempty"`
- FieldsRaw map[string]json.RawMessage `json:"fields,omitempty"`
+ // The underlying encoder that actually
+ // encodes the log entries. Required.
+ WrappedRaw json.RawMessage `json:"wrap,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
+
+ // A map of field names to their filters. Note that this
+ // is not a module map; the keys are field names.
+ FieldsRaw map[string]json.RawMessage `json:"fields,omitempty" caddy:"namespace=caddy.logging.encoders.filter inline_key=filter"`
wrapped zapcore.Encoder
Fields map[string]LogFieldFilter `json:"-"`
@@ -47,8 +51,8 @@ type FilterEncoder struct {
// CaddyModule returns the Caddy module information.
func (FilterEncoder) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.filter",
- New: func() caddy.Module { return new(FilterEncoder) },
+ ID: "caddy.logging.encoders.filter",
+ New: func() caddy.Module { return new(FilterEncoder) },
}
}
@@ -59,28 +63,23 @@ func (fe *FilterEncoder) Provision(ctx caddy.Context) error {
}
// set up wrapped encoder (required)
- val, err := ctx.LoadModuleInline("format", "caddy.logging.encoders", fe.WrappedRaw)
+ val, err := ctx.LoadModule(fe, "WrappedRaw")
if err != nil {
return fmt.Errorf("loading fallback encoder module: %v", err)
}
- fe.WrappedRaw = nil // allow GC to deallocate
fe.wrapped = val.(zapcore.Encoder)
// set up each field filter
if fe.Fields == nil {
fe.Fields = make(map[string]LogFieldFilter)
}
- for field, filterRaw := range fe.FieldsRaw {
- if filterRaw == nil {
- continue
- }
- val, err := ctx.LoadModuleInline("filter", "caddy.logging.encoders.filter", filterRaw)
- if err != nil {
- return fmt.Errorf("loading log filter module: %v", err)
- }
- fe.Fields[field] = val.(LogFieldFilter)
- }
- fe.FieldsRaw = nil // allow GC to deallocate
+ vals, err := ctx.LoadModule(fe, "FieldsRaw")
+ if err != nil {
+ return fmt.Errorf("loading log filter modules: %v", err)
+ }
+ for fieldName, modIface := range vals.(map[string]interface{}) {
+ fe.Fields[fieldName] = modIface.(LogFieldFilter)
+ }
return nil
}
diff --git a/modules/logging/filters.go b/modules/logging/filters.go
index b44e084..52db2fe 100644
--- a/modules/logging/filters.go
+++ b/modules/logging/filters.go
@@ -41,8 +41,8 @@ type DeleteFilter struct{}
// CaddyModule returns the Caddy module information.
func (DeleteFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.filter.delete",
- New: func() caddy.Module { return new(DeleteFilter) },
+ ID: "caddy.logging.encoders.filter.delete",
+ New: func() caddy.Module { return new(DeleteFilter) },
}
}
@@ -55,15 +55,18 @@ func (DeleteFilter) Filter(in zapcore.Field) zapcore.Field {
// IPMaskFilter is a Caddy log field filter that
// masks IP addresses.
type IPMaskFilter struct {
+ // The IPv4 range in CIDR notation.
IPv4CIDR int `json:"ipv4_cidr,omitempty"`
+
+ // The IPv6 range in CIDR notation.
IPv6CIDR int `json:"ipv6_cidr,omitempty"`
}
// CaddyModule returns the Caddy module information.
func (IPMaskFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
- Name: "caddy.logging.encoders.filter.ip_mask",
- New: func() caddy.Module { return new(IPMaskFilter) },
+ ID: "caddy.logging.encoders.filter.ip_mask",
+ New: func() caddy.Module { return new(IPMaskFilter) },
}
}