Improve docs, especially w.r.t. placeholders and template actions

This commit is contained in:
Matthew Holt 2019-12-29 13:16:34 -07:00
parent 95d944613b
commit fdabac51a8
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
8 changed files with 265 additions and 63 deletions

View File

@ -506,7 +506,7 @@ func goModule(mod *debug.Module) *debug.Module {
// TODO: track related Go issue: https://github.com/golang/go/issues/29228 // TODO: track related Go issue: https://github.com/golang/go/issues/29228
// once that issue is fixed, we should just be able to use bi.Main... hopefully. // once that issue is fixed, we should just be able to use bi.Main... hopefully.
for _, dep := range bi.Deps { for _, dep := range bi.Deps {
if dep.Path == "github.com/caddyserver/caddy/v2" { if dep.Path == ImportPath {
return dep return dep
} }
} }
@ -543,3 +543,6 @@ var (
// path, for converting /id/ paths to /config/ paths. // path, for converting /id/ paths to /config/ paths.
rawCfgIndex map[string]string rawCfgIndex map[string]string
) )
// ImportPath is the package import path for Caddy core.
const ImportPath = "github.com/caddyserver/caddy/v2"

View File

@ -75,16 +75,16 @@ type ModuleInfo struct {
// label is the module name, and the labels before that constitute // label is the module name, and the labels before that constitute
// the namespace (or scope). // the namespace (or scope).
// //
// Thus, a module ID has the form: <namespace>.<id> // Thus, a module ID has the form: <namespace>.<name>
// //
// An ID with no dot has the empty namespace, which is appropriate // An ID with no dot has the empty namespace, which is appropriate
// for app modules (these are "top-level" modules that Caddy core // for app modules (these are "top-level" modules that Caddy core
// loads and runs). // loads and runs).
// //
// Module IDs should be lowercase and use underscore (_) instead of // Module IDs should be lowercase and use underscores (_) instead of
// spaces. // spaces.
// //
// Example valid names: // Examples of valid IDs:
// - http // - http
// - http.handlers.file_server // - http.handlers.file_server
// - caddy.logging.encoders.json // - caddy.logging.encoders.json
@ -92,7 +92,7 @@ type ModuleID string
// Namespace returns the namespace (or scope) portion of a module ID, // Namespace returns the namespace (or scope) portion of a module ID,
// which is all but the last label of the ID. If the ID has only one // which is all but the last label of the ID. If the ID has only one
// label, then // label, then the namespace is empty.
func (id ModuleID) Namespace() string { func (id ModuleID) Namespace() string {
lastDot := strings.LastIndex(string(id), ".") lastDot := strings.LastIndex(string(id), ".")
if lastDot < 0 { if lastDot < 0 {

View File

@ -44,7 +44,47 @@ func init() {
} }
} }
// App is a robust, flexible HTTP server for Caddy. // App is a robust, production-ready HTTP server.
//
// HTTPS is enabled by default if host matchers with qualifying names are used
// in any of routes; certificates are automatically provisioned and renewed.
// Additionally, automatic HTTPS will also enable HTTPS for servers that listen
// only on the HTTPS port but which do not have any TLS connection policies
// defined by adding a good, default TLS connection policy.
//
// In HTTP routes, additional placeholders are available:
//
// Placeholder | Description
// ------------|---------------
// `{http.request.cookie.*}` | HTTP request cookie
// `{http.request.header.*}` | Specific request header field
// `{http.request.host.labels.*}` | Request host labels (0-based from right); e.g. for foo.example.com: 0=com, 1=example, 2=foo
// `{http.request.host}` | The host part of the request's Host header
// `{http.request.hostport}` | The host and port from the request's Host header
// `{http.request.method}` | The request method
// `{http.request.orig.method}` | The request's original method
// `{http.request.orig.path.dir}` | The request's original directory
// `{http.request.orig.path.file}` | The request's original filename
// `{http.request.orig.uri.path}` | The request's original path
// `{http.request.orig.uri.query_string}` | The request's original full query string (with `?`)
// `{http.request.orig.uri.query}` | The request's original query string (without `?`)
// `{http.request.orig.uri}` | The request's original URI
// `{http.request.port}` | The port part of the request's Host header
// `{http.request.proto}` | The protocol of the request
// `{http.request.remote.host}` | The host part of the remote client's address
// `{http.request.remote.port}` | The port part of the remote client's address
// `{http.request.remote}` | The address of the remote client
// `{http.request.scheme}` | The request scheme
// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
// `{http.request.uri.path.file}` | The filename of the path, excluding directory
// `{http.request.uri.path}` | The path component of the request URI
// `{http.request.uri.query_string}` | The full query string (with `?`)
// `{http.request.uri.query.*}` | Individual query string value
// `{http.request.uri.query}` | The query string (without `?`)
// `{http.request.uri}` | The full request URI
// `{http.response.header.*}` | Specific response header field
// `{http.vars.*}` | Custom variables in the HTTP handler chain
type App struct { type App struct {
// HTTPPort specifies the port to use for HTTP (as opposed to HTTPS), // HTTPPort specifies the port to use for HTTP (as opposed to HTTPS),
// which is used when setting up HTTP->HTTPS redirects or ACME HTTP // which is used when setting up HTTP->HTTPS redirects or ACME HTTP

View File

@ -33,10 +33,20 @@ func init() {
// MatchFile is an HTTP request matcher that can match // MatchFile is an HTTP request matcher that can match
// requests based upon file existence. // requests based upon file existence.
//
// Upon matching, two new placeholders will be made
// available:
//
// - `{http.matchers.file.relative}` The root-relative
// path of the file. This is often useful when rewriting
// requests.
// - `{http.matchers.file.absolute}` The absolute path
// of the matched file.
type MatchFile struct { type MatchFile struct {
// The root directory, used for creating absolute // The root directory, used for creating absolute
// file paths, and required when working with // file paths, and required when working with
// relative paths; if not specified, the current // relative paths; if not specified, `{http.vars.root}`
// will be used, if set; otherwise, the current
// directory is assumed. Accepts placeholders. // directory is assumed. Accepts placeholders.
Root string `json:"root,omitempty"` Root string `json:"root,omitempty"`

View File

@ -40,6 +40,12 @@ type (
MatchPath []string MatchPath []string
// MatchPathRE matches requests by a regular expression on the URI's path. // MatchPathRE matches requests by a regular expression on the URI's path.
//
// Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}`
// where `name` is the regular expression's name, and `capture_group` is either
// the named or positional capture group from the expression itself. If no name
// is given, then the placeholder omits the name: `{http.regexp.capture_group}`
// (potentially leading to collisions).
MatchPathRE struct{ MatchRegexp } MatchPathRE struct{ MatchRegexp }
// MatchMethod matches requests by the method. // MatchMethod matches requests by the method.
@ -52,6 +58,12 @@ type (
MatchHeader http.Header MatchHeader http.Header
// MatchHeaderRE matches requests by a regular expression on header fields. // MatchHeaderRE matches requests by a regular expression on header fields.
//
// Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}`
// where `name` is the regular expression's name, and `capture_group` is either
// the named or positional capture group from the expression itself. If no name
// is given, then the placeholder omits the name: `{http.regexp.capture_group}`
// (potentially leading to collisions).
MatchHeaderRE map[string]*MatchRegexp MatchHeaderRE map[string]*MatchRegexp
// MatchProtocol matches requests by protocol. // MatchProtocol matches requests by protocol.
@ -65,6 +77,8 @@ type (
} }
// MatchNegate matches requests by negating its matchers' results. // MatchNegate matches requests by negating its matchers' results.
// To use, simply specify a set of matchers like you normally would;
// the only difference is that their result will be negated.
MatchNegate struct { MatchNegate struct {
MatchersRaw caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"` MatchersRaw caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"`
@ -624,11 +638,26 @@ func (m MatchStarlarkExpr) Match(r *http.Request) bool {
} }
// MatchRegexp is an embeddable type for matching // MatchRegexp is an embeddable type for matching
// using regular expressions. // using regular expressions. It adds placeholders
// to the request's replacer.
type MatchRegexp struct { type MatchRegexp struct {
// A unique name for this regular expression. Optional,
// but useful to prevent overwriting captures from other
// regexp matchers.
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
// The regular expression to evaluate, in RE2 syntax,
// which is the same general syntax used by Go, Perl,
// and Python. For details, see
// [Go's regexp package](https://golang.org/pkg/regexp/).
// Captures are accessible via placeholders. Unnamed
// capture groups are exposed as their numeric, 1-based
// index, while named capture groups are available by
// the capture group name.
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
compiled *regexp.Regexp compiled *regexp.Regexp
phPrefix string
} }
// Provision compiles the regular expression. // Provision compiles the regular expression.
@ -638,6 +667,10 @@ func (mre *MatchRegexp) Provision(caddy.Context) error {
return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err) return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err)
} }
mre.compiled = re mre.compiled = re
mre.phPrefix = regexpPlaceholderPrefix
if mre.Name != "" {
mre.phPrefix += "." + mre.Name
}
return nil return nil
} }
@ -661,14 +694,14 @@ func (mre *MatchRegexp) Match(input string, repl *caddy.Replacer) bool {
// save all capture groups, first by index // save all capture groups, first by index
for i, match := range matches { for i, match := range matches {
key := fmt.Sprintf("http.regexp.%s.%d", mre.Name, i) key := fmt.Sprintf("%s.%d", mre.phPrefix, i)
repl.Set(key, match) repl.Set(key, match)
} }
// then by name // then by name
for i, name := range mre.compiled.SubexpNames() { for i, name := range mre.compiled.SubexpNames() {
if i != 0 && name != "" { if i != 0 && name != "" {
key := fmt.Sprintf("http.regexp.%s.%s", mre.Name, name) key := fmt.Sprintf("%s.%s", mre.phPrefix, name)
repl.Set(key, matches[i]) repl.Set(key, matches[i])
} }
} }
@ -752,6 +785,8 @@ func (rm ResponseMatcher) matchHeaders(hdr http.Header) bool {
var wordRE = regexp.MustCompile(`\w+`) var wordRE = regexp.MustCompile(`\w+`)
const regexpPlaceholderPrefix = "http.regexp"
// Interface guards // Interface guards
var ( var (
_ RequestMatcher = (*MatchHost)(nil) _ RequestMatcher = (*MatchHost)(nil)

View File

@ -41,24 +41,19 @@ func init() {
} }
// Handler implements a highly configurable and production-ready reverse proxy. // Handler implements a highly configurable and production-ready reverse proxy.
//
// Upon proxying, this module sets the following placeholders (which can be used // Upon proxying, this module sets the following placeholders (which can be used
// both within and after this handler): // both within and after this handler):
// //
// {http.reverse_proxy.upstream.address} // Placeholder | Description
// The full address to the upstream as given in the config // ------------|-------------
// {http.reverse_proxy.upstream.hostport} // `{http.reverse_proxy.upstream.address}` | The full address to the upstream as given in the config
// The host:port of the upstream // `{http.reverse_proxy.upstream.hostport}` | The host:port of the upstream
// {http.reverse_proxy.upstream.host} // `{http.reverse_proxy.upstream.host}` | The host of the upstream
// The host of the upstream // `{http.reverse_proxy.upstream.port}` | The port of the upstream
// {http.reverse_proxy.upstream.port} // `{http.reverse_proxy.upstream.requests}` | The approximate current number of requests to the upstream
// The port of the upstream // `{http.reverse_proxy.upstream.max_requests}` | The maximum approximate number of requests allowed to the upstream
// {http.reverse_proxy.upstream.requests} // `{http.reverse_proxy.upstream.fails}` | The number of recent failed requests to the upstream
// 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 { type Handler struct {
// Configures the method of transport for the proxy. A transport // Configures the method of transport for the proxy. A transport
// is what performs the actual "round trip" to the backend. // is what performs the actual "round trip" to the backend.

View File

@ -33,39 +33,9 @@ import (
// Server describes an HTTP server. // Server describes an HTTP server.
type Server struct { type Server struct {
// Socket interfaces to which to bind listeners. Caddy network // Socket addresses to which to bind listeners. Accepts
// addresses have the following form: // [network addresses](/docs/conventions#network-addresses)
// // that may include port ranges.
// 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"` Listen []string `json:"listen,omitempty"`
// How long to allow a read from a client's upload. Setting this // How long to allow a read from a client's upload. Setting this
@ -105,12 +75,15 @@ type Server struct {
// The error routes work exactly like the normal routes. // The error routes work exactly like the normal routes.
Errors *HTTPErrorConfig `json:"errors,omitempty"` Errors *HTTPErrorConfig `json:"errors,omitempty"`
// How to handle TLS connections. // How to handle TLS connections. At least one policy is
// required to enable HTTPS on this server if automatic
// HTTPS is disabled or does not apply.
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"` TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
// AutoHTTPS configures or disables automatic HTTPS within this server. // AutoHTTPS configures or disables automatic HTTPS within this server.
// HTTPS is enabled automatically and by default when qualifying names // HTTPS is enabled automatically and by default when qualifying names
// are present in a Host matcher. // are present in a Host matcher and/or when the server is listening
// only on the HTTPS port.
AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"` AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"`
// MaxRehandles is the maximum number of times to allow a // MaxRehandles is the maximum number of times to allow a

View File

@ -29,7 +29,153 @@ func init() {
caddy.RegisterModule(Templates{}) caddy.RegisterModule(Templates{})
} }
// Templates is a middleware which execute response bodies as templates. // Templates is a middleware which executes response bodies as Go templates.
// The syntax is documented in the Go standard library's
// [text/template package](https://golang.org/pkg/text/template/).
//
// [All Sprig functions](https://masterminds.github.io/sprig/) are supported.
//
// In addition to the standard functions and Sprig functions, Caddy adds
// extra functions and data that are available to a template:
//
// ##### **`.Args`**
//
// Access arguments passed to this page/context, for example as the result of a `include`.
//
// ```
// {{.Args 0}} // first argument
// ```
//
// ##### `.Cookie`
//
// Gets the value of a cookie by name.
//
// ```
// {{.Cookie "cookiename"}}
// ```
//
// ##### `.Host`
//
// Returns the hostname portion (no port) of the Host header of the HTTP request.
//
// ```
// {{.Host}}
// ```
//
// ##### `httpInclude`
//
// Includes the contents of another file by making a virtual HTTP request (also known as a sub-request). The URI path must exist on the same virtual server because the request does not use sockets; instead, the request is crafted in memory and the handler is invoked directly for increased efficiency.
//
// ```
// {{httpInclude "/foo/bar?q=val"}}
// ```
//
// ##### `include`
//
// Includes the contents of another file. Optionally can pass key-value pairs as arguments to be accessed by the included file.
//
// ```
// {{include "path/to/file.html"}} // no arguments
// {{include "path/to/file.html" "arg1" 2 "value 3"}} // with arguments
// ```
//
// ##### `listFiles`
//
// Returns a list of the files in the given directory, which is relative to the template context's file root.
//
// ```
// {{listFiles "/mydir"}}
// ```
//
// ##### `markdown`
//
// Renders the given Markdown text as HTML.
//
// ```
// {{markdown "My _markdown_ text"}}
// ```
//
// ##### `.RemoteIP`
//
// Returns the client's IP address.
//
// ```
// {{.RemoteIP}}
// ```
//
// ##### `.RespHeader.Add`
//
// Adds a header field to the HTTP response.
//
// ```
// {{.RespHeader.Add "Field-Name" "val"}}
// ```
//
// ##### `.RespHeader.Del`
//
// Deletes a header field on the HTTP response.
//
// ```
// {{.RespHeader.Del "Field-Name"}}
// ```
//
// ##### `.RespHeader.Set`
//
// Sets a header field on the HTTP response, replacing any existing value.
//
// ```
// {{.RespHeader.Set "Field-Name" "val"}}
// ```
//
// ##### `splitFrontMatter`
//
// Splits front matter out from the body. Front matter is metadata that appears at the very beginning of a file or string. Front matter can be in YAML, TOML, or JSON formats:
//
// **TOML** front matter starts and ends with `+++`:
//
// ```
// +++
// template = "blog"
// title = "Blog Homepage"
// sitename = "A Caddy site"
// +++
// ```
//
// **YAML** is surrounded by `---`:
//
// ```
// ---
// template: blog
// title: Blog Homepage
// sitename: A Caddy site
// ---
// ```
//
//
// **JSON** is simply `{` and `}`:
//
// ```
// {
// "template": "blog",
// "title": "Blog Homepage",
// "sitename": "A Caddy site"
// }
// ```
//
// The resulting front matter will be made available like so:
//
// - `.Meta` to access the metadata fields, for example: `{{$parsed.Meta.title}}`
// - `.Body` to access the body after the front matter, for example: `{{markdown $parsed.Body}}`
//
//
// ##### `stripHTML`
//
// Removes HTML from a string.
//
// ```
// {{stripHTML "Shows <b>only</b> text content"}}
// ```
//
type Templates struct { type Templates struct {
// The root path from which to load files. Required if template functions // The root path from which to load files. Required if template functions
// accessing the file system are used (such as include). Default is // accessing the file system are used (such as include). Default is