diff options
26 files changed, 388 insertions, 99 deletions
diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index 8aa44f1..74aa407 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -28,9 +28,14 @@ func init() { // HTTPBasicAuth facilitates HTTP basic authentication. type HTTPBasicAuth struct { - HashRaw json.RawMessage `json:"hash,omitempty" caddy:"namespace=http.authentication.hashes inline_key=algorithm"` - AccountList []Account `json:"accounts,omitempty"` - Realm string `json:"realm,omitempty"` + // The algorithm with which the passwords are hashed. Default: bcrypt + HashRaw json.RawMessage `json:"hash,omitempty" caddy:"namespace=http.authentication.hashes inline_key=algorithm"` + + // The list of accounts to authenticate. + AccountList []Account `json:"accounts,omitempty"` + + // The name of the realm. Default: restricted + Realm string `json:"realm,omitempty"` Accounts map[string]Account `json:"-"` Hash Comparer `json:"-"` @@ -125,9 +130,15 @@ type Comparer interface { // Account contains a username, password, and salt (if applicable). type Account struct { + // A user's username. Username string `json:"username"` + + // The user's hashed password, base64-encoded. Password []byte `json:"password"` - Salt []byte `json:"salt,omitempty"` // for algorithms where external salt is needed + + // The user's password salt, base64-encoded; for + // algorithms where external salt is needed. + Salt []byte `json:"salt,omitempty"` } // Interface guards diff --git a/modules/caddyhttp/caddyauth/caddyauth.go b/modules/caddyhttp/caddyauth/caddyauth.go index c814caf..aefeec5 100644 --- a/modules/caddyhttp/caddyauth/caddyauth.go +++ b/modules/caddyhttp/caddyauth/caddyauth.go @@ -28,7 +28,10 @@ func init() { } // Authentication is a middleware which provides user authentication. +// Rejects requests with HTTP 401 if the request is not authenticated. type Authentication struct { + // A set of authentication providers. If none are specified, + // all requests will always be unauthenticated. ProvidersRaw caddy.ModuleMap `json:"providers,omitempty" caddy:"namespace=http.authentication.providers"` Providers map[string]Authenticator `json:"-"` diff --git a/modules/caddyhttp/caddyauth/hashes.go b/modules/caddyhttp/caddyauth/hashes.go index 3ca5116..5a3173e 100644 --- a/modules/caddyhttp/caddyauth/hashes.go +++ b/modules/caddyhttp/caddyauth/hashes.go @@ -52,9 +52,17 @@ func (BcryptHash) Compare(hashed, plaintext, _ []byte) (bool, error) { // ScryptHash implements the scrypt KDF as a hash. type ScryptHash struct { - N int `json:"N,omitempty"` - R int `json:"r,omitempty"` - P int `json:"p,omitempty"` + // scrypt's N parameter. If unset or 0, a safe default is used. + N int `json:"N,omitempty"` + + // scrypt's r parameter. If unset or 0, a safe default is used. + R int `json:"r,omitempty"` + + // scrypt's p parameter. If unset or 0, a safe default is used. + P int `json:"p,omitempty"` + + // scrypt's key length parameter (in bytes). If unset or 0, a + // safe default is used. KeyLength int `json:"key_length,omitempty"` } diff --git a/modules/caddyhttp/encode/brotli/brotli.go b/modules/caddyhttp/encode/brotli/brotli.go index 52bb205..fababd3 100644 --- a/modules/caddyhttp/encode/brotli/brotli.go +++ b/modules/caddyhttp/encode/brotli/brotli.go @@ -29,7 +29,9 @@ func init() { } // Brotli can create brotli encoders. Note that brotli -// is not known for great encoding performance. +// is not known for great encoding performance, and +// its use during requests is discouraged; instead, +// pre-compress the content instead. type Brotli struct { Quality *int `json:"quality,omitempty"` } diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go index c68f507..c3a1c23 100644 --- a/modules/caddyhttp/encode/encode.go +++ b/modules/caddyhttp/encode/encode.go @@ -39,9 +39,15 @@ func init() { // Encode is a middleware which can encode responses. type Encode struct { + // Selection of compression algorithms to choose from. The best one + // will be chosen based on the client's Accept-Encoding header. EncodingsRaw caddy.ModuleMap `json:"encodings,omitempty" caddy:"namespace=http.encoders"` - Prefer []string `json:"prefer,omitempty"` - MinLength int `json:"minimum_length,omitempty"` + + // If the client has no strong preference, choose this encoding. TODO: Not yet implemented + // Prefer []string `json:"prefer,omitempty"` + + // Only encode responses that are at least this many bytes long. + MinLength int `json:"minimum_length,omitempty"` writerPools map[string]*sync.Pool // TODO: these pools do not get reused through config reloads... } @@ -66,11 +72,9 @@ func (enc *Encode) Provision(ctx caddy.Context) error { return fmt.Errorf("adding encoding %s: %v", modName, err) } } - if enc.MinLength == 0 { enc.MinLength = defaultMinLength } - return nil } diff --git a/modules/caddyhttp/fileserver/browse.go b/modules/caddyhttp/fileserver/browse.go index 48f2cc7..aa8372e 100644 --- a/modules/caddyhttp/fileserver/browse.go +++ b/modules/caddyhttp/fileserver/browse.go @@ -29,6 +29,7 @@ import ( // Browse configures directory browsing. type Browse struct { + // Use this template file instead of the default browse template. TemplateFile string `json:"template_file,omitempty"` template *template.Template diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index 13cb60a..5ca97f2 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -46,7 +46,13 @@ type MatchFile struct { // placeholders. TryFiles []string `json:"try_files,omitempty"` - // How to choose a file in TryFiles. + // How to choose a file in TryFiles. Can be: + // + // - first_exist + // - smallest_size + // - largest_size + // - most_recently_modified + // // Default is first_exist. TryPolicy string `json:"try_policy,omitempty"` } @@ -64,7 +70,7 @@ func (MatchFile) CaddyModule() caddy.ModuleInfo { // file { // root <path> // try_files <files...> -// try_policy first_exist|smallest_size|largest_size|most_recent_modified +// try_policy first_exist|smallest_size|largest_size|most_recently_modified // } // func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { @@ -107,7 +113,7 @@ func (m MatchFile) Validate() error { tryPolicyFirstExist, tryPolicyLargestSize, tryPolicySmallestSize, - tryPolicyMostRecentMod: + tryPolicyMostRecentlyMod: default: return fmt.Errorf("unknown try policy %s", m.TryPolicy) } @@ -187,7 +193,7 @@ func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) { } return smallestSuffix, smallestFilename, true - case tryPolicyMostRecentMod: + case tryPolicyMostRecentlyMod: var recentDate time.Time var recentFilename string var recentSuffix string @@ -238,10 +244,10 @@ func strictFileExists(file string) bool { } const ( - tryPolicyFirstExist = "first_exist" - tryPolicyLargestSize = "largest_size" - tryPolicySmallestSize = "smallest_size" - tryPolicyMostRecentMod = "most_recent_modified" + tryPolicyFirstExist = "first_exist" + tryPolicyLargestSize = "largest_size" + tryPolicySmallestSize = "smallest_size" + tryPolicyMostRecentlyMod = "most_recently_modified" ) // Interface guards diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index a9e6e1c..f2722ba 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -42,12 +42,29 @@ func init() { // FileServer implements a static file server responder for Caddy. type FileServer struct { - Root string `json:"root,omitempty"` // default is current directory - Hide []string `json:"hide,omitempty"` - IndexNames []string `json:"index_names,omitempty"` - Browse *Browse `json:"browse,omitempty"` - CanonicalURIs *bool `json:"canonical_uris,omitempty"` - PassThru bool `json:"pass_thru,omitempty"` // if 404, call next handler instead + // The path to the root of the site. Default is `{http.vars.root}` if set, + // or current working directory otherwise. + Root string `json:"root,omitempty"` + + // A list of files or folders to hide; the file server will pretend as if + // they don't exist. Accepts globular patterns like "*.hidden" or "/foo/*/bar". + Hide []string `json:"hide,omitempty"` + + // The names of files to try as index files if a folder is requested. + IndexNames []string `json:"index_names,omitempty"` + + // Enables file listings if a directory was requested and no index + // file is present. + Browse *Browse `json:"browse,omitempty"` + + // Use redirects to enforce trailing slashes for directories, or to + // remove trailing slash from URIs for files. Default is true. + CanonicalURIs *bool `json:"canonical_uris,omitempty"` + + // If pass-thru mode is enabled and a requested file is not found, + // it will invoke the next handler in the chain instead of returning + // a 404 error. By default, this is false (disabled). + PassThru bool `json:"pass_thru,omitempty"` } // CaddyModule returns the Caddy module information. diff --git a/modules/caddyhttp/headers/headers.go b/modules/caddyhttp/headers/headers.go index f53e859..ad6c08b 100644 --- a/modules/caddyhttp/headers/headers.go +++ b/modules/caddyhttp/headers/headers.go @@ -28,7 +28,18 @@ func init() { caddy.RegisterModule(Handler{}) } -// Handler is a middleware which can mutate HTTP headers. +// Handler is a middleware which modifies request and response headers. +// +// Changes to headers are applied immediately, except for the response +// headers when Deferred is true or when Required is set. In those cases, +// the changes are applied when the headers are written to the response. +// Note that deferred changes do not take effect if an error occurs later +// in the middleware chain. +// +// Properties in this module accept placeholders. +// +// Response header operations can be conditioned upon response status code +// and/or other header values. type Handler struct { Request *HeaderOps `json:"request,omitempty"` Response *RespHeaderOps `json:"response,omitempty"` @@ -99,12 +110,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt return next.ServeHTTP(w, r) } -// HeaderOps defines some operations to -// perform on HTTP headers. +// HeaderOps defines manipulations for HTTP headers. type HeaderOps struct { - Add http.Header `json:"add,omitempty"` - Set http.Header `json:"set,omitempty"` - Delete []string `json:"delete,omitempty"` + // Adds HTTP headers; does not replace any existing header fields. + Add http.Header `json:"add,omitempty"` + + // Sets HTTP headers; replaces existing header fields. + Set http.Header `json:"set,omitempty"` + + // Names of HTTP header fields to delete. + Delete []string `json:"delete,omitempty"` + + // Performs substring replacements of HTTP headers in-situ. Replace map[string][]Replacement `json:"replace,omitempty"` } @@ -135,22 +152,33 @@ func (ops HeaderOps) validate() error { } // Replacement describes a string replacement, -// either a simple and fast sugbstring search +// either a simple and fast substring search // or a slower but more powerful regex search. type Replacement struct { - Search string `json:"search,omitempty"` + // The substring to search for. + Search string `json:"search,omitempty"` + + // The regular expression to search with. SearchRegexp string `json:"search_regexp,omitempty"` - Replace string `json:"replace,omitempty"` + + // The string with which to replace matches. + Replace string `json:"replace,omitempty"` re *regexp.Regexp } -// RespHeaderOps is like HeaderOps, but -// optionally deferred until response time. +// RespHeaderOps defines manipulations for response headers. type RespHeaderOps struct { *HeaderOps - Require *caddyhttp.ResponseMatcher `json:"require,omitempty"` - Deferred bool `json:"deferred,omitempty"` + + // If set, header operations will be deferred until + // they are written out and only performed if the + // response matches these criteria. + Require *caddyhttp.ResponseMatcher `json:"require,omitempty"` + + // If true, header operations will be deferred until + // they are written out. Superceded if Require is set. + Deferred bool `json:"deferred,omitempty"` } // ApplyTo applies ops to hdr using repl. diff --git a/modules/caddyhttp/httpcache/httpcache.go b/modules/caddyhttp/httpcache/httpcache.go index 81f5816..f8bdde8 100644 --- a/modules/caddyhttp/httpcache/httpcache.go +++ b/modules/caddyhttp/httpcache/httpcache.go @@ -33,11 +33,33 @@ func init() { } // Cache implements a simple distributed cache. +// +// NOTE: This module is a work-in-progress. It is +// not finished and is NOT ready for production use. +// [We need your help to finish it! Please volunteer +// in this issue.](https://github.com/caddyserver/caddy/issues/2820) +// Until it is finished, this module is subject to +// breaking changes. +// +// Caches only GET and HEAD requests. Honors the Cache-Control: no-cache header. +// +// Still TODO: +// +// - Eviction policies and API +// - Use single cache per-process +// - Preserve cache through config reloads +// - More control over what gets cached type Cache struct { - Self string `json:"self,omitempty"` - Peers []string `json:"peers,omitempty"` - MaxSize int64 `json:"max_size,omitempty"` - group *groupcache.Group + // The network address of this cache instance; required. + Self string `json:"self,omitempty"` + + // A list of network addresses of cache instances in the group. + Peers []string `json:"peers,omitempty"` + + // Maximum size of the cache, in bytes. Default is 512 MB. + MaxSize int64 `json:"max_size,omitempty"` + + group *groupcache.Group } // CaddyModule returns the Caddy module information. diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 6d1728d..00f273e 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -695,8 +695,8 @@ func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return nil } -// ResponseMatcher is a type which can determine if a given response -// status code and its headers match some criteria. +// ResponseMatcher is a type which can determine if an +// HTTP response matches some criteria. type ResponseMatcher struct { // If set, one of these status codes would be required. // A one-digit status can be used to represent all codes diff --git a/modules/caddyhttp/requestbody/requestbody.go b/modules/caddyhttp/requestbody/requestbody.go index dd3f256..dcebd8c 100644 --- a/modules/caddyhttp/requestbody/requestbody.go +++ b/modules/caddyhttp/requestbody/requestbody.go @@ -27,6 +27,7 @@ func init() { // RequestBody is a middleware for manipulating the request body. type RequestBody struct { + // The maximum number of bytes to allow reading from the body by a later handler. MaxSize int64 `json:"max_size,omitempty"` } diff --git a/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go b/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go index aff9a6e..f3f979d 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go @@ -57,7 +57,7 @@ type Transport struct { // that 404's if the fastcgi path info is not found. SplitPath string `json:"split_path,omitempty"` - // Extra environment variables + // Extra environment variables. EnvVars map[string]string `json:"env,omitempty"` // The duration used to set a deadline when connecting to an upstream. diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index a64d845..76ee945 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -31,9 +31,16 @@ import ( "go.uber.org/zap" ) -// HealthChecks holds configuration related to health checking. +// HealthChecks configures active and passive health checks. type HealthChecks struct { - Active *ActiveHealthChecks `json:"active,omitempty"` + // Active health checks run in the background on a timer. To + // minimally enable active health checks, set either path or + // port (or both). + Active *ActiveHealthChecks `json:"active,omitempty"` + + // Passive health checks monitor proxied requests for errors or timeouts. + // To minimally enable passive health checks, specify at least an empty + // config object. Passive *PassiveHealthChecks `json:"passive,omitempty"` } @@ -41,14 +48,33 @@ type HealthChecks struct { // health checks (that is, health checks which occur in a // background goroutine independently). type ActiveHealthChecks struct { - Path string `json:"path,omitempty"` - Port int `json:"port,omitempty"` - Headers http.Header `json:"headers,omitempty"` - Interval caddy.Duration `json:"interval,omitempty"` - Timeout caddy.Duration `json:"timeout,omitempty"` - MaxSize int64 `json:"max_size,omitempty"` - ExpectStatus int `json:"expect_status,omitempty"` - ExpectBody string `json:"expect_body,omitempty"` + // The URI path to use for health checks. + Path string `json:"path,omitempty"` + + // The port to use (if different from the upstream's dial + // address) for health checks. + Port int `json:"port,omitempty"` + + // HTTP headers to set on health check requests. + Headers http.Header `json:"headers,omitempty"` + + // How frequently to perform active health checks (default 30s). + Interval caddy.Duration `json:"interval,omitempty"` + + // How long to wait for a response from a backend before + // considering it unhealthy (default 5s). + Timeout caddy.Duration `json:"timeout,omitempty"` + + // The maximum response body to download from the backend + // during a health check. + MaxSize int64 `json:"max_size,omitempty"` + + // The HTTP status code to expect from a healthy backend. + ExpectStatus int `json:"expect_status,omitempty"` + + // A regular expression against which to match the response + // body of a healthy backend. + ExpectBody string `json:"expect_body,omitempty"` stopChan chan struct{} httpClient *http.Client @@ -60,11 +86,27 @@ type ActiveHealthChecks struct { // health checks (that is, health checks which occur during // the normal flow of request proxying). type PassiveHealthChecks struct { - MaxFails int `json:"max_fails,omitempty"` - FailDuration caddy.Duration `json:"fail_duration,omitempty"` - UnhealthyRequestCount int `json:"unhealthy_request_count,omitempty"` - UnhealthyStatus []int `json:"unhealthy_status,omitempty"` - UnhealthyLatency caddy.Duration `json:"unhealthy_latency,omitempty"` + // How long to remember a failed request to a backend. A duration > 0 + // enables passive health checking. Default is 0. + FailDuration caddy.Duration `json:"fail_duration,omitempty"` + + // The number of failed requests within the FailDuration window to + // consider a backend as "down". Must be >= 1; default is 1. Requires + // that FailDuration be > 0. + MaxFails int `json:"max_fails,omitempty"` + + // Limits the number of simultaneous requests to a backend by + // marking the backend as "down" if it has this many concurrent + // requests or more. + UnhealthyRequestCount int `json:"unhealthy_request_count,omitempty"` + + // Count the request as failed if the response comes back with + // one of these status codes. + UnhealthyStatus []int `json:"unhealthy_status,omitempty"` + + // Count the request as failed if the response takes at least this + // long to receive. + UnhealthyLatency caddy.Duration `json:"unhealthy_latency,omitempty"` logger *zap.Logger } diff --git a/modules/caddyhttp/reverseproxy/hosts.go b/modules/caddyhttp/reverseproxy/hosts.go index 8bad7c2..e7c61fb 100644 --- a/modules/caddyhttp/reverseproxy/hosts.go +++ b/modules/caddyhttp/reverseproxy/hosts.go @@ -61,8 +61,22 @@ type UpstreamPool []*Upstream type Upstream struct { Host `json:"-"` - Dial string `json:"dial,omitempty"` - MaxRequests int `json:"max_requests,omitempty"` + // The [network address](/docs/json/apps/http/#servers/listen) + // to dial to connect to the upstream. Must represent precisely + // one socket (i.e. no port ranges). A valid network address + // either has a host and port, or is a unix socket address. + // + // Placeholders may be used to make the upstream dynamic, but be + // aware of the health check implications of this: a single + // upstream that represents numerous (perhaps arbitrary) backends + // can be considered down if one or enough of the arbitrary + // backends is down. Also be aware of open proxy vulnerabilities. + Dial string `json:"dial,omitempty"` + + // The maximum number of simultaneous requests to allow to + // this upstream. If set, overrides the global passive health + // check UnhealthyRequestCount value. + MaxRequests int `json:"max_requests,omitempty"` // TODO: This could be really useful, to bind requests // with certain properties to specific backends diff --git a/modules/caddyhttp/reverseproxy/ntlm.go b/modules/caddyhttp/reverseproxy/ntlm.go index ea2bb85..be4330f 100644 --- a/modules/caddyhttp/reverseproxy/ntlm.go +++ b/modules/caddyhttp/reverseproxy/ntlm.go @@ -46,6 +46,10 @@ func init() { // // This transport also forces HTTP/1.1 and Keep-Alives in order // for NTLM to succeed. +// +// It is basically the same thing as +// [nginx's paid ntlm directive](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ntlm) +// (but is free in Caddy!). type NTLMTransport struct { *HTTPTransport diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index d353dc1..24389b2 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -41,15 +41,56 @@ func init() { } // Handler implements a highly configurable and production-ready reverse proxy. +// Upon proxying, this module sets the following placeholders (which can be used +// both within and after this handler): +// +// {http.reverse_proxy.upstream.address} +// The full address to the upstream as given in the config +// {http.reverse_proxy.upstream.hostport} +// The host:port of the upstream +// {http.reverse_proxy.upstream.host} +// The host of the upstream +// {http.reverse_proxy.upstream.port} +// The port of the upstream +// {http.reverse_proxy.upstream.requests} +// The approximate current number of requests to the upstream +// {http.reverse_proxy.upstream.max_requests} +// The maximum approximate number of requests allowed to the upstream +// {http.reverse_proxy.upstream.fails} +// The number of recent failed requests to the upstream +// type Handler struct { - 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"` - FlushInterval caddy.Duration `json:"flush_interval,omitempty"` - Headers *headers.Handler `json:"headers,omitempty"` - BufferRequests bool `json:"buffer_requests,omitempty"` + // Configures the method of transport for the proxy. A transport + // is what performs the actual "round trip" to the backend. + // The default transport is plaintext HTTP. + TransportRaw json.RawMessage `json:"transport,omitempty" caddy:"namespace=http.reverse_proxy.transport inline_key=protocol"` + + // A circuit breaker may be used to relieve pressure on a backend + // that is beginning to exhibit symptoms of stress or latency. + // By default, there is no circuit breaker. + CBRaw json.RawMessage `json:"circuit_breaker,omitempty" caddy:"namespace=http.reverse_proxy.circuit_breakers inline_key=type"` + + // Load balancing distributes load/requests between backends. + LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"` + + // Health checks update the status of backends, whether they are + // up or down. Down backends will not be proxied to. + HealthChecks *HealthChecks `json:"health_checks,omitempty"` + + // Upstreams is the list of backends to proxy to. + Upstreams UpstreamPool `json:"upstreams,omitempty"` + + // TODO: figure out good defaults and write docs for this + // (see https://github.com/caddyserver/caddy/issues/1460) + FlushInterval caddy.Duration `json:"flush_interval,omitempty"` + + // Headers manipulates headers between Caddy and the backend. + Headers *headers.Handler `json:"headers,omitempty"` + + // If true, the entire request body will be read and buffered + // in memory before being proxied to the backend. This should + // be avoided if at all possible for performance reasons. + BufferRequests bool `json:"buffer_requests,omitempty"` Transport http.RoundTripper `json:"-"` CB CircuitBreaker `json:"-"` @@ -140,7 +181,7 @@ func (h *Handler) Provision(ctx caddy.Context) error { timeout := time.Duration(h.HealthChecks.Active.Timeout) if timeout == 0 { - timeout = 10 * time.Second + timeout = 5 * time.Second } h.HealthChecks.Active.stopChan = make(chan struct{}) @@ -649,10 +690,30 @@ func removeConnectionHeaders(h http.Header) { // LoadBalancing has parameters related to load balancing. type LoadBalancing struct { - 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" caddy:"namespace=http.matchers"` + // A selection policy is how to choose an available backend. + // The default policy is random selection. + SelectionPolicyRaw json.RawMessage `json:"selection_policy,omitempty" caddy:"namespace=http.reverse_proxy.selection_policies inline_key=policy"` + + // How long to try selecting available backends for each request + // if the next available host is down. By default, this retry is + // disabled. Clients will wait for up to this long while the load + // balancer tries to find an available upstream host. + TryDuration caddy.Duration `json:"try_duration,omitempty"` + + // How long to wait between selecting the next host from the pool. Default + // is 250ms. Only relevant when a request to an upstream host fails. Be + // aware that setting this to 0 with a non-zero try_duration can cause the + // CPU to spin if all backends are down and latency is very low. + TryInterval caddy.Duration `json:"try_interval,omitempty"` + + // A list of matcher sets that restricts with which requests retries are + // allowed. A request must match any of the given matcher sets in order + // to be retried if the connection to the upstream succeeded but the + // subsequent round-trip failed. If the connection to the upstream failed, + // a retry is always allowed. If unspecified, only GET requests will be + // allowed to be retried. Note that a retry is done with the next available + // host according to the load balancing policy. + RetryMatchRaw caddyhttp.RawMatcherSets `json:"retry_match,omitempty" caddy:"namespace=http.matchers"` SelectionPolicy Selector `json:"-"` RetryMatch caddyhttp.MatcherSets `json:"-"` diff --git a/modules/caddyhttp/rewrite/rewrite.go b/modules/caddyhttp/rewrite/rewrite.go index 3a644ef..03c54b1 100644 --- a/modules/caddyhttp/rewrite/rewrite.go +++ b/modules/caddyhttp/rewrite/rewrite.go @@ -31,16 +31,39 @@ func init() { } // Rewrite is a middleware which can rewrite HTTP requests. +// +// The Rehandle and HTTPRedirect properties are mutually exclusive +// (you cannot both rehandle and issue a redirect). +// +// These rewrite properties are applied to a request in this order: +// Method, URI, StripPathPrefix, StripPathSuffix, URISubstring. +// +// TODO: This module is still a WIP and may experience breaking changes. type Rewrite struct { + // Changes the request's HTTP verb. Method string `json:"method,omitempty"` - URI string `json:"uri,omitempty"` - StripPathPrefix string `json:"strip_path_prefix,omitempty"` - StripPathSuffix string `json:"strip_path_suffix,omitempty"` - URISubstring []replacer `json:"uri_substring,omitempty"` + // Changes the request's URI (path, query string, and fragment if present). + // Only components of the URI that are specified will be changed. + URI string `json:"uri,omitempty"` + + // Strips the given prefix from the beginning of the URI path. + StripPathPrefix string `json:"strip_path_prefix,omitempty"` + // Strips the given suffix from the end of the URI path. + StripPathSuffix string `json:"strip_path_suffix,omitempty"` + + // Performs substring replacements on the URI. + URISubstring []replacer `json:"uri_substring,omitempty"` + + // If set to a 3xx HTTP status code and if the URI was rewritten (changed), + // the handler will issue a simple HTTP redirect to the new URI using the + // given status code. HTTPRedirect caddyhttp.WeakString `json:"http_redirect,omitempty"` - Rehandle bool `json:"rehandle,omitempty"` + + // If true, the request will sent for rehandling after rewriting + // only if anything about the request was changed. + Rehandle bool `json:"rehandle,omitempty"` logger *zap.Logger } diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index 83b90e6..60ce07e 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -97,8 +97,12 @@ type Server struct { // 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 is how this server will handle errors returned from any + // of the handlers in the primary routes. If the primary handler + // chain returns an error, the error along with its recommended + // status code are bubbled back up to the HTTP server which + // executes a separate error route, specified using this property. + // The error routes work exactly like the normal routes. Errors *HTTPErrorConfig `json:"errors,omitempty"` // How to handle TLS connections. @@ -418,6 +422,20 @@ func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool { // HTTPErrorConfig determines how to handle errors // from the HTTP handlers. type HTTPErrorConfig struct { + // The routes to evaluate after the primary handler + // chain returns an error. In an error route, extra + // placeholders are available: + // + // {http.error.status_code} + // The recommended HTTP status code + // {http.error.status_text} + // The status text associated with the recommended status code + // {http.error.message} + // The error message + // {http.error.trace} + // The origin of the error + // {http.error.id} + // A short, human-conveyable ID for the error Routes RouteList `json:"routes,omitempty"` } diff --git a/modules/caddyhttp/starlarkmw/example/caddy.json b/modules/caddyhttp/starlarkmw/example/caddy.json index 66f9f2c..9479d17 100644 --- a/modules/caddyhttp/starlarkmw/example/caddy.json +++ b/modules/caddyhttp/starlarkmw/example/caddy.json @@ -8,7 +8,7 @@ { "handle": { "handler": "starlark", - "script": "def setup(r):\n\t# create some middlewares specific to this request\n\ttemplates = loadModule('http.handlers.templates', {'include_root': './includes'})\n\tmidChain = execute([templates])\n\ndef serveHTTP (rw, r):\n\trw.Write('Hello world, from Starlark!')\n" + "script": "def setup(r):\n\t# create some middlewares specific to this request\n\ttemplates = loadModule('http.handlers.templates', {'file_root': './includes'})\n\tmidChain = execute([templates])\n\ndef serveHTTP (rw, r):\n\trw.Write('Hello world, from Starlark!')\n" } } ] diff --git a/modules/caddyhttp/staticerror.go b/modules/caddyhttp/staticerror.go index fd1490d..0ddebfe 100644 --- a/modules/caddyhttp/staticerror.go +++ b/modules/caddyhttp/staticerror.go @@ -27,8 +27,18 @@ func init() { } // StaticError implements a simple handler that returns an error. +// This handler returns an error value, but does not write a response. +// This is useful when you want the server to act as if an error +// occurred; for example, to invoke your custom error handling logic. +// +// Since this handler does not write a response, the error information +// is for use by the server to know how to handle the error. type StaticError struct { - Error string `json:"error,omitempty"` + // The recommended HTTP status code. Can be either an integer or a + // string if placeholders are needed. Optional. Default is 500. + Error string `json:"error,omitempty"` + + // The error message. Optional. Default is no error message. StatusCode WeakString `json:"status_code,omitempty"` } diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go index 44b045e..cc294d5 100644 --- a/modules/caddyhttp/staticresp.go +++ b/modules/caddyhttp/staticresp.go @@ -29,10 +29,19 @@ func init() { // StaticResponse implements a simple responder for static responses. type StaticResponse struct { - StatusCode WeakString `json:"status_code,omitempty"` - Headers http.Header `json:"headers,omitempty"` - Body string `json:"body,omitempty"` - Close bool `json:"close,omitempty"` + // The HTTP status code to respond with. Can be an integer or, + // if needing to use a placeholder, a string. + StatusCode WeakString `json:"status_code,omitempty"` + + // Header fields to set on the response. + Headers http.Header `json:"headers,omitempty"` + + // The response body. + Body string `json:"body,omitempty"` + + // If true, the server will close the client's connection + // after writing the response. + Close bool `json:"close,omitempty"` } // CaddyModule returns the Caddy module information. diff --git a/modules/caddyhttp/subroute.go b/modules/caddyhttp/subroute.go index a60eaf7..13453a5 100644 --- a/modules/caddyhttp/subroute.go +++ b/modules/caddyhttp/subroute.go @@ -37,7 +37,11 @@ func init() { // is only returned to the entry point at the server if there is an // additional error returned from the errors routes. type Subroute struct { - Routes RouteList `json:"routes,omitempty"` + // The primary list of routes to compile and execute. + Routes RouteList `json:"routes,omitempty"` + + // If the primary routes return an error, error handling + // can be promoted to this configuration instead. Errors *HTTPErrorConfig `json:"errors,omitempty"` } diff --git a/modules/caddyhttp/templates/caddyfile.go b/modules/caddyhttp/templates/caddyfile.go index 5dc124f..ea6aa9f 100644 --- a/modules/caddyhttp/templates/caddyfile.go +++ b/modules/caddyhttp/templates/caddyfile.go @@ -47,7 +47,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) return nil, h.ArgErr() } case "root": - if !h.Args(&t.IncludeRoot) { + if !h.Args(&t.FileRoot) { return nil, h.ArgErr() } } diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go index 791203b..6299dcf 100644 --- a/modules/caddyhttp/vars.go +++ b/modules/caddyhttp/vars.go @@ -26,7 +26,8 @@ func init() { } // VarsMiddleware is an HTTP middleware which sets variables -// in the context, mainly for use by placeholders. +// in the context, mainly for use by placeholders. The +// placeholders have the form: `{http.vars.variable_name}` type VarsMiddleware map[string]string // CaddyModule returns the Caddy module information. diff --git a/modules/logging/encoders.go b/modules/logging/encoders.go index 28cda55..49ad11a 100644 --- a/modules/logging/encoders.go +++ b/modules/logging/encoders.go @@ -36,7 +36,7 @@ func init() { // ConsoleEncoder encodes log entries that are mostly human-readable. type ConsoleEncoder struct { - zapcore.Encoder + zapcore.Encoder `json:"-"` LogEncoderConfig } @@ -56,8 +56,8 @@ func (ce *ConsoleEncoder) Provision(_ caddy.Context) error { // JSONEncoder encodes entries as JSON. type JSONEncoder struct { - zapcore.Encoder - *LogEncoderConfig + zapcore.Encoder `json:"-"` + LogEncoderConfig } // CaddyModule returns the Caddy module information. @@ -77,7 +77,7 @@ func (je *JSONEncoder) Provision(_ caddy.Context) error { // LogfmtEncoder encodes log entries as logfmt: // https://www.brandur.org/logfmt type LogfmtEncoder struct { - zapcore.Encoder + zapcore.Encoder `json:"-"` LogEncoderConfig } @@ -100,9 +100,9 @@ func (lfe *LogfmtEncoder) Provision(_ caddy.Context) error { // for custom, self-encoded log entries that consist of a // single field in the structured log entry. type StringEncoder struct { - zapcore.Encoder - FieldName string `json:"field,omitempty"` - FallbackRaw json.RawMessage `json:"fallback,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"` + zapcore.Encoder `json:"-"` + FieldName string `json:"field,omitempty"` + FallbackRaw json.RawMessage `json:"fallback,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"` } // CaddyModule returns the Caddy module information. |