mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-25 16:22:36 -04:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e43b6d8178 | |||
| bffc258732 | |||
| 616418281b | |||
| 74547f5bed | |||
| 258071d857 | |||
| b6cec37893 | |||
| 48d723c07c | |||
| f1f7a22674 | |||
| 49b7a25264 | |||
| e6c58fdc08 | |||
| 2dc747cf2d | |||
| e338648fed | |||
| 9ad0ebc956 | |||
| a1ad20e472 | |||
| 62b0685375 | |||
| 0b3161aeea |
@@ -763,8 +763,12 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
|||||||
|
|
||||||
// ParseDuration parses a duration string, adding
|
// ParseDuration parses a duration string, adding
|
||||||
// support for the "d" unit meaning number of days,
|
// support for the "d" unit meaning number of days,
|
||||||
// where a day is assumed to be 24h.
|
// where a day is assumed to be 24h. The maximum
|
||||||
|
// input string length is 1024.
|
||||||
func ParseDuration(s string) (time.Duration, error) {
|
func ParseDuration(s string) (time.Duration, error) {
|
||||||
|
if len(s) > 1024 {
|
||||||
|
return 0, fmt.Errorf("parsing duration: input string too long")
|
||||||
|
}
|
||||||
var inNumber bool
|
var inNumber bool
|
||||||
var numStart int
|
var numStart int
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
|
|||||||
@@ -153,7 +153,10 @@ func Format(input []byte) []byte {
|
|||||||
openBraceWritten = true
|
openBraceWritten = true
|
||||||
nextLine()
|
nextLine()
|
||||||
newLines = 0
|
newLines = 0
|
||||||
nesting++
|
// prevent infinite nesting from ridiculous inputs (issue #4169)
|
||||||
|
if nesting < 10 {
|
||||||
|
nesting++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ import (
|
|||||||
// server block that share the same address stay grouped together so the config
|
// server block that share the same address stay grouped together so the config
|
||||||
// isn't repeated unnecessarily. For example, this Caddyfile:
|
// isn't repeated unnecessarily. For example, this Caddyfile:
|
||||||
//
|
//
|
||||||
// example.com {
|
// example.com {
|
||||||
// bind 127.0.0.1
|
// bind 127.0.0.1
|
||||||
// }
|
// }
|
||||||
// www.example.com, example.net/path, localhost:9999 {
|
// www.example.com, example.net/path, localhost:9999 {
|
||||||
// bind 127.0.0.1 1.2.3.4
|
// bind 127.0.0.1 1.2.3.4
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// has two server blocks to start with. But expressed in this Caddyfile are
|
// has two server blocks to start with. But expressed in this Caddyfile are
|
||||||
// actually 4 listener addresses: 127.0.0.1:443, 1.2.3.4:443, 127.0.0.1:9999,
|
// actually 4 listener addresses: 127.0.0.1:443, 1.2.3.4:443, 127.0.0.1:9999,
|
||||||
@@ -219,7 +219,7 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str
|
|||||||
return nil, fmt.Errorf("[%s] scheme and port violate convention", key)
|
return nil, fmt.Errorf("[%s] scheme and port violate convention", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the bind directive specifies hosts, but is optional
|
// the bind directive specifies hosts (and potentially network), but is optional
|
||||||
lnHosts := make([]string, 0, len(sblock.pile["bind"]))
|
lnHosts := make([]string, 0, len(sblock.pile["bind"]))
|
||||||
for _, cfgVal := range sblock.pile["bind"] {
|
for _, cfgVal := range sblock.pile["bind"] {
|
||||||
lnHosts = append(lnHosts, cfgVal.Value.([]string)...)
|
lnHosts = append(lnHosts, cfgVal.Value.([]string)...)
|
||||||
@@ -234,11 +234,23 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str
|
|||||||
|
|
||||||
// use a map to prevent duplication
|
// use a map to prevent duplication
|
||||||
listeners := make(map[string]struct{})
|
listeners := make(map[string]struct{})
|
||||||
for _, host := range lnHosts {
|
for _, lnHost := range lnHosts {
|
||||||
// host can have network + host (e.g. "tcp6/localhost") but
|
// normally we would simply append the port,
|
||||||
// will/should not have port information because this usually
|
// but if lnHost is IPv6, we need to ensure it
|
||||||
// comes from the bind directive, so we append the port
|
// is enclosed in [ ]; net.JoinHostPort does
|
||||||
addr, err := caddy.ParseNetworkAddress(host + ":" + lnPort)
|
// this for us, but lnHost might also have a
|
||||||
|
// network type in front (e.g. "tcp/") leading
|
||||||
|
// to "[tcp/::1]" which causes parsing failures
|
||||||
|
// later; what we need is "tcp/[::1]", so we have
|
||||||
|
// to split the network and host, then re-combine
|
||||||
|
network, host, ok := strings.Cut(lnHost, "/")
|
||||||
|
if !ok {
|
||||||
|
host = network
|
||||||
|
network = ""
|
||||||
|
}
|
||||||
|
host = strings.Trim(host, "[]") // IPv6
|
||||||
|
networkAddr := caddy.JoinNetworkAddress(network, host, lnPort)
|
||||||
|
addr, err := caddy.ParseNetworkAddress(networkAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing network address: %v", err)
|
return nil, fmt.Errorf("parsing network address: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ func init() {
|
|||||||
RegisterHandlerDirective("handle", parseHandle)
|
RegisterHandlerDirective("handle", parseHandle)
|
||||||
RegisterDirective("handle_errors", parseHandleErrors)
|
RegisterDirective("handle_errors", parseHandleErrors)
|
||||||
RegisterDirective("log", parseLog)
|
RegisterDirective("log", parseLog)
|
||||||
|
RegisterHandlerDirective("skip_log", parseSkipLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseBind parses the bind directive. Syntax:
|
// parseBind parses the bind directive. Syntax:
|
||||||
//
|
//
|
||||||
// bind <addresses...>
|
// bind <addresses...>
|
||||||
//
|
|
||||||
func parseBind(h Helper) ([]ConfigValue, error) {
|
func parseBind(h Helper) ([]ConfigValue, error) {
|
||||||
var lnHosts []string
|
var lnHosts []string
|
||||||
for h.Next() {
|
for h.Next() {
|
||||||
@@ -64,28 +64,28 @@ func parseBind(h Helper) ([]ConfigValue, error) {
|
|||||||
|
|
||||||
// parseTLS parses the tls directive. Syntax:
|
// parseTLS parses the tls directive. Syntax:
|
||||||
//
|
//
|
||||||
// tls [<email>|internal]|[<cert_file> <key_file>] {
|
// tls [<email>|internal]|[<cert_file> <key_file>] {
|
||||||
// protocols <min> [<max>]
|
// protocols <min> [<max>]
|
||||||
// ciphers <cipher_suites...>
|
// ciphers <cipher_suites...>
|
||||||
// curves <curves...>
|
// curves <curves...>
|
||||||
// client_auth {
|
// client_auth {
|
||||||
// mode [request|require|verify_if_given|require_and_verify]
|
// mode [request|require|verify_if_given|require_and_verify]
|
||||||
// trusted_ca_cert <base64_der>
|
// trusted_ca_cert <base64_der>
|
||||||
// trusted_ca_cert_file <filename>
|
// trusted_ca_cert_file <filename>
|
||||||
// trusted_leaf_cert <base64_der>
|
// trusted_leaf_cert <base64_der>
|
||||||
// trusted_leaf_cert_file <filename>
|
// trusted_leaf_cert_file <filename>
|
||||||
// }
|
// }
|
||||||
// alpn <values...>
|
// alpn <values...>
|
||||||
// load <paths...>
|
// load <paths...>
|
||||||
// ca <acme_ca_endpoint>
|
// ca <acme_ca_endpoint>
|
||||||
// ca_root <pem_file>
|
// ca_root <pem_file>
|
||||||
// dns <provider_name> [...]
|
// dns <provider_name> [...]
|
||||||
// on_demand
|
// on_demand
|
||||||
// eab <key_id> <mac_key>
|
// eab <key_id> <mac_key>
|
||||||
// issuer <module_name> [...]
|
// issuer <module_name> [...]
|
||||||
// get_certificate <module_name> [...]
|
// get_certificate <module_name> [...]
|
||||||
// }
|
// insecure_secrets_log <log_file>
|
||||||
//
|
// }
|
||||||
func parseTLS(h Helper) ([]ConfigValue, error) {
|
func parseTLS(h Helper) ([]ConfigValue, error) {
|
||||||
cp := new(caddytls.ConnectionPolicy)
|
cp := new(caddytls.ConnectionPolicy)
|
||||||
var fileLoader caddytls.FileLoader
|
var fileLoader caddytls.FileLoader
|
||||||
@@ -395,6 +395,12 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
|
|||||||
}
|
}
|
||||||
onDemand = true
|
onDemand = true
|
||||||
|
|
||||||
|
case "insecure_secrets_log":
|
||||||
|
if !h.NextArg() {
|
||||||
|
return nil, h.ArgErr()
|
||||||
|
}
|
||||||
|
cp.InsecureSecretsLog = h.Val()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, h.Errf("unknown subdirective: %s", h.Val())
|
return nil, h.Errf("unknown subdirective: %s", h.Val())
|
||||||
}
|
}
|
||||||
@@ -515,8 +521,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
|
|||||||
|
|
||||||
// parseRoot parses the root directive. Syntax:
|
// parseRoot parses the root directive. Syntax:
|
||||||
//
|
//
|
||||||
// root [<matcher>] <path>
|
// root [<matcher>] <path>
|
||||||
//
|
|
||||||
func parseRoot(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
func parseRoot(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||||
var root string
|
var root string
|
||||||
for h.Next() {
|
for h.Next() {
|
||||||
@@ -694,12 +699,11 @@ func parseHandleErrors(h Helper) ([]ConfigValue, error) {
|
|||||||
|
|
||||||
// parseLog parses the log directive. Syntax:
|
// parseLog parses the log directive. Syntax:
|
||||||
//
|
//
|
||||||
// log {
|
// log {
|
||||||
// output <writer_module> ...
|
// output <writer_module> ...
|
||||||
// format <encoder_module> ...
|
// format <encoder_module> ...
|
||||||
// level <level>
|
// level <level>
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
func parseLog(h Helper) ([]ConfigValue, error) {
|
func parseLog(h Helper) ([]ConfigValue, error) {
|
||||||
return parseLogHelper(h, nil)
|
return parseLogHelper(h, nil)
|
||||||
}
|
}
|
||||||
@@ -858,3 +862,15 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
|
|||||||
}
|
}
|
||||||
return configValues, nil
|
return configValues, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseSkipLog parses the skip_log directive. Syntax:
|
||||||
|
//
|
||||||
|
// skip_log [<matcher>]
|
||||||
|
func parseSkipLog(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||||
|
for h.Next() {
|
||||||
|
if h.NextArg() {
|
||||||
|
return nil, h.ArgErr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return caddyhttp.VarsMiddleware{"skip_log": true}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ var directiveOrder = []string{
|
|||||||
"map",
|
"map",
|
||||||
"vars",
|
"vars",
|
||||||
"root",
|
"root",
|
||||||
|
"skip_log",
|
||||||
|
|
||||||
"header",
|
"header",
|
||||||
"copy_response_headers", // only in reverse_proxy's handle_response
|
"copy_response_headers", // only in reverse_proxy's handle_response
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
|
|||||||
hasDefaultLog = true
|
hasDefaultLog = true
|
||||||
}
|
}
|
||||||
if _, ok := options["debug"]; ok && ncl.log.Level == "" {
|
if _, ok := options["debug"]; ok && ncl.log.Level == "" {
|
||||||
ncl.log.Level = "DEBUG"
|
ncl.log.Level = zap.DebugLevel.CapitalString()
|
||||||
}
|
}
|
||||||
customLogs = append(customLogs, ncl)
|
customLogs = append(customLogs, ncl)
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
|
|||||||
if _, ok := options["debug"]; ok {
|
if _, ok := options["debug"]; ok {
|
||||||
customLogs = append(customLogs, namedCustomLog{
|
customLogs = append(customLogs, namedCustomLog{
|
||||||
name: "default",
|
name: "default",
|
||||||
log: &caddy.CustomLog{Level: "DEBUG"},
|
log: &caddy.CustomLog{Level: zap.DebugLevel.CapitalString()},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,13 +421,13 @@ func parseOCSPStaplingOptions(d *caddyfile.Dispenser, _ any) (any, error) {
|
|||||||
|
|
||||||
// parseLogOptions parses the global log option. Syntax:
|
// parseLogOptions parses the global log option. Syntax:
|
||||||
//
|
//
|
||||||
// log [name] {
|
// log [name] {
|
||||||
// output <writer_module> ...
|
// output <writer_module> ...
|
||||||
// format <encoder_module> ...
|
// format <encoder_module> ...
|
||||||
// level <level>
|
// level <level>
|
||||||
// include <namespaces...>
|
// include <namespaces...>
|
||||||
// exclude <namespaces...>
|
// exclude <namespaces...>
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// When the name argument is unspecified, this directive modifies the default
|
// When the name argument is unspecified, this directive modifies the default
|
||||||
// logger.
|
// logger.
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type serverOptions struct {
|
|||||||
Protocols []string
|
Protocols []string
|
||||||
StrictSNIHost *bool
|
StrictSNIHost *bool
|
||||||
ShouldLogCredentials bool
|
ShouldLogCredentials bool
|
||||||
|
Metrics *caddyhttp.Metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
||||||
@@ -175,6 +176,15 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
|||||||
}
|
}
|
||||||
serverOpts.StrictSNIHost = &boolVal
|
serverOpts.StrictSNIHost = &boolVal
|
||||||
|
|
||||||
|
case "metrics":
|
||||||
|
if d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
if d.NextBlock(0) {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
serverOpts.Metrics = new(caddyhttp.Metrics)
|
||||||
|
|
||||||
// TODO: DEPRECATED. (August 2022)
|
// TODO: DEPRECATED. (August 2022)
|
||||||
case "protocol":
|
case "protocol":
|
||||||
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
|
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
|
||||||
@@ -259,6 +269,7 @@ func applyServerOptions(
|
|||||||
server.MaxHeaderBytes = opts.MaxHeaderBytes
|
server.MaxHeaderBytes = opts.MaxHeaderBytes
|
||||||
server.Protocols = opts.Protocols
|
server.Protocols = opts.Protocols
|
||||||
server.StrictSNIHost = opts.StrictSNIHost
|
server.StrictSNIHost = opts.StrictSNIHost
|
||||||
|
server.Metrics = opts.Metrics
|
||||||
if opts.ShouldLogCredentials {
|
if opts.ShouldLogCredentials {
|
||||||
if server.Logs == nil {
|
if server.Logs == nil {
|
||||||
server.Logs = &caddyhttp.ServerLogConfig{}
|
server.Logs = &caddyhttp.ServerLogConfig{}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, warn := range warnings {
|
for _, warn := range warnings {
|
||||||
ctx.Logger(hl).Warn(warn.String())
|
ctx.Logger().Warn(warn.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -129,7 +129,7 @@ func (hl HTTPLoader) makeClient(ctx caddy.Context) (*http.Client, error) {
|
|||||||
|
|
||||||
// client authentication
|
// client authentication
|
||||||
if hl.TLS.UseServerIdentity {
|
if hl.TLS.UseServerIdentity {
|
||||||
certs, err := ctx.IdentityCredentials(ctx.Logger(hl))
|
certs, err := ctx.IdentityCredentials(ctx.Logger())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getting server identity credentials: %v", err)
|
return nil, fmt.Errorf("getting server identity credentials: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
http://localhost:2020 {
|
http://localhost:2020 {
|
||||||
log
|
log
|
||||||
|
skip_log /first-hidden*
|
||||||
|
skip_log /second-hidden*
|
||||||
respond 200
|
respond 200
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +30,36 @@ http://localhost:2020 {
|
|||||||
{
|
{
|
||||||
"handler": "subroute",
|
"handler": "subroute",
|
||||||
"routes": [
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "vars",
|
||||||
|
"skip_log": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"/second-hidden*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "vars",
|
||||||
|
"skip_log": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"/first-hidden*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"handle": [
|
"handle": [
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -679,7 +679,7 @@ func DetermineAdminAPIAddress(address string, config []byte, configFile, configA
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if loadedConfigFile == "" {
|
if loadedConfigFile == "" {
|
||||||
return "", fmt.Errorf("no config file to load")
|
return "", fmt.Errorf("no config file to load; either use --config flag or ensure Caddyfile exists in current directory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -41,10 +41,15 @@ func init() {
|
|||||||
// set a fitting User-Agent for ACME requests
|
// set a fitting User-Agent for ACME requests
|
||||||
version, _ := caddy.Version()
|
version, _ := caddy.Version()
|
||||||
cleanModVersion := strings.TrimPrefix(version, "v")
|
cleanModVersion := strings.TrimPrefix(version, "v")
|
||||||
certmagic.UserAgent = "Caddy/" + cleanModVersion
|
ua := "Caddy/" + cleanModVersion
|
||||||
|
if uaEnv, ok := os.LookupEnv("USERAGENT"); ok {
|
||||||
|
ua = uaEnv + " " + ua
|
||||||
|
}
|
||||||
|
certmagic.UserAgent = ua
|
||||||
|
|
||||||
// by using Caddy, user indicates agreement to CA terms
|
// by using Caddy, user indicates agreement to CA terms
|
||||||
// (very important, or ACME account creation will fail!)
|
// (very important, as Caddy is often non-interactive
|
||||||
|
// and thus ACME account creation will fail!)
|
||||||
certmagic.DefaultACME.Agreed = true
|
certmagic.DefaultACME.Agreed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-18
@@ -442,10 +442,27 @@ func (ctx Context) Storage() certmagic.Storage {
|
|||||||
return ctx.cfg.storage
|
return ctx.cfg.storage
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: aw man, can I please change this?
|
// Logger returns a logger that is intended for use by the most
|
||||||
// Logger returns a logger that can be used by mod.
|
// recent module associated with the context. Callers should not
|
||||||
func (ctx Context) Logger(mod Module) *zap.Logger {
|
// pass in any arguments unless they want to associate with a
|
||||||
// TODO: if mod is nil, use ctx.Module() instead...
|
// different module; it panics if more than 1 value is passed in.
|
||||||
|
//
|
||||||
|
// Originally, this method's signature was `Logger(mod Module)`,
|
||||||
|
// requiring that an instance of a Caddy module be passsed in.
|
||||||
|
// However, that is no longer necessary, as the closest module
|
||||||
|
// most recently associated with the context will be automatically
|
||||||
|
// assumed. To prevent a sudden breaking change, this method's
|
||||||
|
// signature has been changed to be variadic, but we may remove
|
||||||
|
// the parameter altogether in the future. Callers should not
|
||||||
|
// pass in any argument. If there is valid need to specify a
|
||||||
|
// different module, please open an issue to discuss.
|
||||||
|
//
|
||||||
|
// PARTIALLY DEPRECATED: The Logger(module) form is deprecated and
|
||||||
|
// may be removed in the future. Do not pass in any arguments.
|
||||||
|
func (ctx Context) Logger(module ...Module) *zap.Logger {
|
||||||
|
if len(module) > 1 {
|
||||||
|
panic("more than 1 module passed in")
|
||||||
|
}
|
||||||
if ctx.cfg == nil {
|
if ctx.cfg == nil {
|
||||||
// often the case in tests; just use a dev logger
|
// often the case in tests; just use a dev logger
|
||||||
l, err := zap.NewDevelopment()
|
l, err := zap.NewDevelopment()
|
||||||
@@ -454,23 +471,13 @@ func (ctx Context) Logger(mod Module) *zap.Logger {
|
|||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
mod := ctx.Module()
|
||||||
|
if len(module) > 0 {
|
||||||
|
mod = module[0]
|
||||||
|
}
|
||||||
return ctx.cfg.Logging.Logger(mod)
|
return ctx.cfg.Logging.Logger(mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use this
|
|
||||||
// // Logger returns a logger that can be used by the current module.
|
|
||||||
// func (ctx Context) Log() *zap.Logger {
|
|
||||||
// if ctx.cfg == nil {
|
|
||||||
// // often the case in tests; just use a dev logger
|
|
||||||
// l, err := zap.NewDevelopment()
|
|
||||||
// if err != nil {
|
|
||||||
// panic("config missing, unable to create dev logger: " + err.Error())
|
|
||||||
// }
|
|
||||||
// return l
|
|
||||||
// }
|
|
||||||
// return ctx.cfg.Logging.Logger(ctx.Module())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Modules returns the lineage of modules that this context provisioned,
|
// Modules returns the lineage of modules that this context provisioned,
|
||||||
// with the most recent/current module being last in the list.
|
// with the most recent/current module being last in the list.
|
||||||
func (ctx Context) Modules() []Module {
|
func (ctx Context) Modules() []Module {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Listener, error) {
|
func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Listener, error) {
|
||||||
// check to see if plugin provides listener
|
// check to see if plugin provides listener
|
||||||
if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
|
if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
|
||||||
return pipeable(ln), err
|
return ln, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lnKey := listenerKey(network, addr)
|
lnKey := listenerKey(network, addr)
|
||||||
@@ -29,7 +29,7 @@ func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Lis
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &sharedListener{Listener: pipeable(ln), key: lnKey}, nil
|
return &sharedListener{Listener: ln, key: lnKey}, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+2
-3
@@ -14,12 +14,11 @@ import (
|
|||||||
func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
|
func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
|
||||||
// check to see if plugin provides listener
|
// check to see if plugin provides listener
|
||||||
if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
|
if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
|
||||||
return pipeable(ln), err
|
return ln, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod}
|
config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod}
|
||||||
ln, err := config.Listen(context.Background(), network, addr)
|
return config.Listen(context.Background(), network, addr)
|
||||||
return pipeable(ln), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reusePort(network, address string, conn syscall.RawConn) error {
|
func reusePort(network, address string, conn syscall.RawConn) error {
|
||||||
|
|||||||
@@ -45,68 +45,6 @@ func Listen(network, addr string) (net.Listener, error) {
|
|||||||
return ListenTimeout(network, addr, 0)
|
return ListenTimeout(network, addr, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pipeableListener wraps an underlying listener so
|
|
||||||
// that connections can be given to a server that is
|
|
||||||
// calling Accept().
|
|
||||||
type pipeableListener struct {
|
|
||||||
net.Listener
|
|
||||||
bridge chan connAccept
|
|
||||||
done chan struct{}
|
|
||||||
closed *int32 // accessed atomically
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pln pipeableListener) Accept() (net.Conn, error) {
|
|
||||||
accept := <-pln.bridge
|
|
||||||
return accept.conn, accept.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pln pipeableListener) Close() error {
|
|
||||||
if atomic.CompareAndSwapInt32(pln.closed, 0, 1) {
|
|
||||||
close(pln.done)
|
|
||||||
}
|
|
||||||
return pln.Listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// pump pipes real connections from the underlying listener's
|
|
||||||
// Accept() up to the callers of our own Accept().
|
|
||||||
func (pln pipeableListener) pump() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-pln.done:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
pln.Pipe(pln.Listener.Accept())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pipe gives a connection (or an error) to an active Accept() call
|
|
||||||
// on this listener.
|
|
||||||
func (pln pipeableListener) Pipe(conn net.Conn, err error) {
|
|
||||||
pln.bridge <- connAccept{conn, err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pipeable wraps listener so that it can be given connections
|
|
||||||
// for its caller/server to Accept() and use.
|
|
||||||
func pipeable(listener net.Listener) net.Listener {
|
|
||||||
if listener == nil {
|
|
||||||
return listener // don't start a goroutine
|
|
||||||
}
|
|
||||||
pln := pipeableListener{
|
|
||||||
Listener: listener,
|
|
||||||
bridge: make(chan connAccept),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
closed: new(int32),
|
|
||||||
}
|
|
||||||
go pln.pump()
|
|
||||||
return pln
|
|
||||||
}
|
|
||||||
|
|
||||||
type connAccept struct {
|
|
||||||
conn net.Conn
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// getListenerFromPlugin returns a listener on the given network and address
|
// getListenerFromPlugin returns a listener on the given network and address
|
||||||
// if a plugin has registered the network name. It may return (nil, nil) if
|
// if a plugin has registered the network name. It may return (nil, nil) if
|
||||||
// no plugin can provide a listener.
|
// no plugin can provide a listener.
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (App) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up the app.
|
// Provision sets up the app.
|
||||||
func (app *App) Provision(ctx caddy.Context) error {
|
func (app *App) Provision(ctx caddy.Context) error {
|
||||||
app.logger = ctx.Logger(app)
|
app.logger = ctx.Logger()
|
||||||
app.subscriptions = make(map[string]map[caddy.ModuleID][]Handler)
|
app.subscriptions = make(map[string]map[caddy.ModuleID][]Handler)
|
||||||
|
|
||||||
for _, sub := range app.Subscriptions {
|
for _, sub := range app.Subscriptions {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func (app *App) Provision(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
app.tlsApp = tlsAppIface.(*caddytls.TLS)
|
app.tlsApp = tlsAppIface.(*caddytls.TLS)
|
||||||
app.ctx = ctx
|
app.ctx = ctx
|
||||||
app.logger = ctx.Logger(app)
|
app.logger = ctx.Logger()
|
||||||
|
|
||||||
eventsAppIface, err := ctx.App("events")
|
eventsAppIface, err := ctx.App("events")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,7 +178,9 @@ func (app *App) Provision(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepare each server
|
// prepare each server
|
||||||
|
oldContext := ctx.Context
|
||||||
for srvName, srv := range app.Servers {
|
for srvName, srv := range app.Servers {
|
||||||
|
ctx.Context = context.WithValue(oldContext, ServerCtxKey, srv)
|
||||||
srv.name = srvName
|
srv.name = srvName
|
||||||
srv.tlsApp = app.tlsApp
|
srv.tlsApp = app.tlsApp
|
||||||
srv.events = eventsAppIface.(*caddyevents.App)
|
srv.events = eventsAppIface.(*caddyevents.App)
|
||||||
@@ -263,7 +265,7 @@ func (app *App) Provision(ctx caddy.Context) error {
|
|||||||
// route handler so that important security checks are done, etc.
|
// route handler so that important security checks are done, etc.
|
||||||
primaryRoute := emptyHandler
|
primaryRoute := emptyHandler
|
||||||
if srv.Routes != nil {
|
if srv.Routes != nil {
|
||||||
err := srv.Routes.ProvisionHandlers(ctx)
|
err := srv.Routes.ProvisionHandlers(ctx, srv.Metrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
|
return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
|
||||||
}
|
}
|
||||||
@@ -293,7 +295,7 @@ func (app *App) Provision(ctx caddy.Context) error {
|
|||||||
srv.IdleTimeout = defaultIdleTimeout
|
srv.IdleTimeout = defaultIdleTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx.Context = oldContext
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +367,7 @@ func (app *App) Start() error {
|
|||||||
// this TLS config is used by the std lib to choose the actual TLS config for connections
|
// this TLS config is used by the std lib to choose the actual TLS config for connections
|
||||||
// by looking through the connection policies to find the first one that matches
|
// by looking through the connection policies to find the first one that matches
|
||||||
tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
|
tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
|
||||||
|
srv.configureServer(srv.server)
|
||||||
|
|
||||||
// enable H2C if configured
|
// enable H2C if configured
|
||||||
if srv.protocol("h2c") {
|
if srv.protocol("h2c") {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func (Authentication) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up a.
|
// Provision sets up a.
|
||||||
func (a *Authentication) Provision(ctx caddy.Context) error {
|
func (a *Authentication) Provision(ctx caddy.Context) error {
|
||||||
a.logger = ctx.Logger(a)
|
a.logger = ctx.Logger()
|
||||||
a.Providers = make(map[string]Authenticator)
|
a.Providers = make(map[string]Authenticator)
|
||||||
mods, err := ctx.LoadModule(a, "ProvidersRaw")
|
mods, err := ctx.LoadModule(a, "ProvidersRaw")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func (ScryptHash) CaddyModule() caddy.ModuleInfo {
|
|||||||
// Provision sets up s.
|
// Provision sets up s.
|
||||||
func (s *ScryptHash) Provision(ctx caddy.Context) error {
|
func (s *ScryptHash) Provision(ctx caddy.Context) error {
|
||||||
s.SetDefaults()
|
s.SetDefaults()
|
||||||
ctx.Logger(s).Warn("use of 'scrypt' is deprecated, please use 'bcrypt' instead")
|
ctx.Logger().Warn("use of 'scrypt' is deprecated, please use 'bcrypt' instead")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func (m *MatchExpression) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
// Provision sets ups m.
|
// Provision sets ups m.
|
||||||
func (m *MatchExpression) Provision(ctx caddy.Context) error {
|
func (m *MatchExpression) Provision(ctx caddy.Context) error {
|
||||||
m.log = ctx.Logger(m)
|
m.log = ctx.Logger()
|
||||||
|
|
||||||
// replace placeholders with a function call - this is just some
|
// replace placeholders with a function call - this is just some
|
||||||
// light (and possibly naïve) syntactic sugar
|
// light (and possibly naïve) syntactic sugar
|
||||||
@@ -294,13 +294,13 @@ type CELLibraryProducer interface {
|
|||||||
// CELMatcherImpl creates a new cel.Library based on the following pieces of
|
// CELMatcherImpl creates a new cel.Library based on the following pieces of
|
||||||
// data:
|
// data:
|
||||||
//
|
//
|
||||||
// - macroName: the function name to be used within CEL. This will be a macro
|
// - macroName: the function name to be used within CEL. This will be a macro
|
||||||
// and not a function proper.
|
// and not a function proper.
|
||||||
// - funcName: the function overload name generated by the CEL macro used to
|
// - funcName: the function overload name generated by the CEL macro used to
|
||||||
// represent the matcher.
|
// represent the matcher.
|
||||||
// - matcherDataTypes: the argument types to the macro.
|
// - matcherDataTypes: the argument types to the macro.
|
||||||
// - fac: a matcherFactory implementation which converts from CEL constant
|
// - fac: a matcherFactory implementation which converts from CEL constant
|
||||||
// values to a Matcher instance.
|
// values to a Matcher instance.
|
||||||
//
|
//
|
||||||
// Note, macro names and function names must not collide with other macros or
|
// Note, macro names and function names must not collide with other macros or
|
||||||
// functions exposed within CEL expressions, or an error will be produced
|
// functions exposed within CEL expressions, or an error will be produced
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package encode
|
package encode
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
@@ -160,13 +159,12 @@ func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter
|
|||||||
// initResponseWriter initializes the responseWriter instance
|
// initResponseWriter initializes the responseWriter instance
|
||||||
// allocated in openResponseWriter, enabling mid-stack inlining.
|
// allocated in openResponseWriter, enabling mid-stack inlining.
|
||||||
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
|
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
|
||||||
buf := bufPool.Get().(*bytes.Buffer)
|
if httpInterfaces, ok := wrappedRW.(caddyhttp.HTTPInterfaces); ok {
|
||||||
buf.Reset()
|
rw.HTTPInterfaces = httpInterfaces
|
||||||
|
} else {
|
||||||
// The allocation of ResponseWriterWrapper might be optimized as well.
|
rw.HTTPInterfaces = &caddyhttp.ResponseWriterWrapper{ResponseWriter: wrappedRW}
|
||||||
rw.ResponseWriterWrapper = &caddyhttp.ResponseWriterWrapper{ResponseWriter: wrappedRW}
|
}
|
||||||
rw.encodingName = encodingName
|
rw.encodingName = encodingName
|
||||||
rw.buf = buf
|
|
||||||
rw.config = enc
|
rw.config = enc
|
||||||
|
|
||||||
return rw
|
return rw
|
||||||
@@ -176,10 +174,9 @@ func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, w
|
|||||||
// using the encoding represented by encodingName and
|
// using the encoding represented by encodingName and
|
||||||
// configured by config.
|
// configured by config.
|
||||||
type responseWriter struct {
|
type responseWriter struct {
|
||||||
*caddyhttp.ResponseWriterWrapper
|
caddyhttp.HTTPInterfaces
|
||||||
encodingName string
|
encodingName string
|
||||||
w Encoder
|
w Encoder
|
||||||
buf *bytes.Buffer
|
|
||||||
config *Encode
|
config *Encode
|
||||||
statusCode int
|
statusCode int
|
||||||
wroteHeader bool
|
wroteHeader bool
|
||||||
@@ -206,28 +203,33 @@ func (rw *responseWriter) Flush() {
|
|||||||
// to rw.Write (see bug in #4314)
|
// to rw.Write (see bug in #4314)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rw.ResponseWriterWrapper.Flush()
|
rw.HTTPInterfaces.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes to the response. If the response qualifies,
|
// Write writes to the response. If the response qualifies,
|
||||||
// it is encoded using the encoder, which is initialized
|
// it is encoded using the encoder, which is initialized
|
||||||
// if not done so already.
|
// if not done so already.
|
||||||
func (rw *responseWriter) Write(p []byte) (int, error) {
|
func (rw *responseWriter) Write(p []byte) (int, error) {
|
||||||
var n, written int
|
// ignore zero data writes, probably head request
|
||||||
var err error
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
if rw.buf != nil && rw.config.MinLength > 0 {
|
// sniff content-type and determine content-length
|
||||||
written = rw.buf.Len()
|
if !rw.wroteHeader && rw.config.MinLength > 0 {
|
||||||
_, err := rw.buf.Write(p)
|
var gtMinLength bool
|
||||||
if err != nil {
|
if len(p) > rw.config.MinLength {
|
||||||
return 0, err
|
gtMinLength = true
|
||||||
|
} else if cl, err := strconv.Atoi(rw.Header().Get("Content-Length")); err == nil && cl > rw.config.MinLength {
|
||||||
|
gtMinLength = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if gtMinLength {
|
||||||
|
if rw.Header().Get("Content-Type") == "" {
|
||||||
|
rw.Header().Set("Content-Type", http.DetectContentType(p))
|
||||||
|
}
|
||||||
|
rw.init()
|
||||||
}
|
}
|
||||||
rw.init()
|
|
||||||
p = rw.buf.Bytes()
|
|
||||||
defer func() {
|
|
||||||
bufPool.Put(rw.buf)
|
|
||||||
rw.buf = nil
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// before we write to the response, we need to make
|
// before we write to the response, we need to make
|
||||||
@@ -236,63 +238,44 @@ func (rw *responseWriter) Write(p []byte) (int, error) {
|
|||||||
// and if so, that means we haven't written the
|
// and if so, that means we haven't written the
|
||||||
// header OR the default status code will be written
|
// header OR the default status code will be written
|
||||||
// by the standard library
|
// by the standard library
|
||||||
if rw.statusCode > 0 {
|
if !rw.wroteHeader {
|
||||||
rw.ResponseWriter.WriteHeader(rw.statusCode)
|
if rw.statusCode != 0 {
|
||||||
rw.statusCode = 0
|
rw.HTTPInterfaces.WriteHeader(rw.statusCode)
|
||||||
|
} else {
|
||||||
|
rw.HTTPInterfaces.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
rw.wroteHeader = true
|
rw.wroteHeader = true
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
if rw.w != nil {
|
||||||
case rw.w != nil:
|
return rw.w.Write(p)
|
||||||
n, err = rw.w.Write(p)
|
} else {
|
||||||
default:
|
return rw.HTTPInterfaces.Write(p)
|
||||||
n, err = rw.ResponseWriter.Write(p)
|
|
||||||
}
|
}
|
||||||
n -= written
|
|
||||||
if n < 0 {
|
|
||||||
n = 0
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close writes any remaining buffered response and
|
// Close writes any remaining buffered response and
|
||||||
// deallocates any active resources.
|
// deallocates any active resources.
|
||||||
func (rw *responseWriter) Close() error {
|
func (rw *responseWriter) Close() error {
|
||||||
var err error
|
// didn't write, probably head request
|
||||||
// only attempt to write the remaining buffered response
|
if !rw.wroteHeader {
|
||||||
// if there are any bytes left to write; otherwise, if
|
cl, err := strconv.Atoi(rw.Header().Get("Content-Length"))
|
||||||
// the handler above us returned an error without writing
|
if err == nil && cl > rw.config.MinLength {
|
||||||
// anything, we'd write to the response when we instead
|
rw.init()
|
||||||
// should simply let the error propagate back down; this
|
}
|
||||||
// is why the check for rw.buf.Len() > 0 is crucial
|
|
||||||
if rw.buf != nil && rw.buf.Len() > 0 {
|
if rw.statusCode != 0 {
|
||||||
rw.init()
|
rw.HTTPInterfaces.WriteHeader(rw.statusCode)
|
||||||
p := rw.buf.Bytes()
|
} else {
|
||||||
defer func() {
|
rw.HTTPInterfaces.WriteHeader(http.StatusOK)
|
||||||
bufPool.Put(rw.buf)
|
|
||||||
rw.buf = nil
|
|
||||||
}()
|
|
||||||
switch {
|
|
||||||
case rw.w != nil:
|
|
||||||
_, err = rw.w.Write(p)
|
|
||||||
default:
|
|
||||||
_, err = rw.ResponseWriter.Write(p)
|
|
||||||
}
|
}
|
||||||
} else if rw.statusCode != 0 {
|
|
||||||
// it is possible that a body was not written, and
|
|
||||||
// a header was not even written yet, even though
|
|
||||||
// we are closing; ensure the proper status code is
|
|
||||||
// written exactly once, or we risk breaking requests
|
|
||||||
// that rely on If-None-Match, for example
|
|
||||||
rw.ResponseWriter.WriteHeader(rw.statusCode)
|
|
||||||
rw.statusCode = 0
|
|
||||||
rw.wroteHeader = true
|
rw.wroteHeader = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
if rw.w != nil {
|
if rw.w != nil {
|
||||||
err2 := rw.w.Close()
|
err = rw.w.Close()
|
||||||
if err2 != nil && err == nil {
|
rw.w.Reset(nil)
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
rw.config.writerPools[rw.encodingName].Put(rw.w)
|
rw.config.writerPools[rw.encodingName].Put(rw.w)
|
||||||
rw.w = nil
|
rw.w = nil
|
||||||
}
|
}
|
||||||
@@ -302,16 +285,15 @@ func (rw *responseWriter) Close() error {
|
|||||||
// init should be called before we write a response, if rw.buf has contents.
|
// init should be called before we write a response, if rw.buf has contents.
|
||||||
func (rw *responseWriter) init() {
|
func (rw *responseWriter) init() {
|
||||||
if rw.Header().Get("Content-Encoding") == "" &&
|
if rw.Header().Get("Content-Encoding") == "" &&
|
||||||
rw.buf.Len() >= rw.config.MinLength &&
|
|
||||||
rw.config.Match(rw) {
|
rw.config.Match(rw) {
|
||||||
|
|
||||||
rw.w = rw.config.writerPools[rw.encodingName].Get().(Encoder)
|
rw.w = rw.config.writerPools[rw.encodingName].Get().(Encoder)
|
||||||
rw.w.Reset(rw.ResponseWriter)
|
rw.w.Reset(rw.HTTPInterfaces)
|
||||||
rw.Header().Del("Content-Length") // https://github.com/golang/go/issues/14975
|
rw.Header().Del("Content-Length") // https://github.com/golang/go/issues/14975
|
||||||
rw.Header().Set("Content-Encoding", rw.encodingName)
|
rw.Header().Set("Content-Encoding", rw.encodingName)
|
||||||
rw.Header().Add("Vary", "Accept-Encoding")
|
rw.Header().Add("Vary", "Accept-Encoding")
|
||||||
|
rw.Header().Del("Accept-Ranges") // we don't know ranges for dynamically-encoded content
|
||||||
}
|
}
|
||||||
rw.Header().Del("Accept-Ranges") // we don't know ranges for dynamically-encoded content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptedEncodings returns the list of encodings that the
|
// AcceptedEncodings returns the list of encodings that the
|
||||||
@@ -417,12 +399,6 @@ type Precompressed interface {
|
|||||||
Suffix() string
|
Suffix() string
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufPool = sync.Pool{
|
|
||||||
New: func() any {
|
|
||||||
return new(bytes.Buffer)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultMinLength is the minimum length at which to compress content.
|
// defaultMinLength is the minimum length at which to compress content.
|
||||||
const defaultMinLength = 512
|
const defaultMinLength = 512
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
caddytpl "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates"
|
caddytpl "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates"
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -70,6 +71,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
|
|||||||
browse := fs.Bool("browse")
|
browse := fs.Bool("browse")
|
||||||
templates := fs.Bool("templates")
|
templates := fs.Bool("templates")
|
||||||
accessLog := fs.Bool("access-log")
|
accessLog := fs.Bool("access-log")
|
||||||
|
debug := fs.Bool("debug")
|
||||||
|
|
||||||
var handlers []json.RawMessage
|
var handlers []json.RawMessage
|
||||||
|
|
||||||
@@ -130,6 +132,14 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
cfg.Logging = &caddy.Logging{
|
||||||
|
Logs: map[string]*caddy.CustomLog{
|
||||||
|
"default": {Level: zap.DebugLevel.CapitalString()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := caddy.Run(cfg)
|
err := caddy.Run(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
|
|||||||
|
|
||||||
// Provision sets up m's defaults.
|
// Provision sets up m's defaults.
|
||||||
func (m *MatchFile) Provision(ctx caddy.Context) error {
|
func (m *MatchFile) Provision(ctx caddy.Context) error {
|
||||||
m.logger = ctx.Logger(m)
|
m.logger = ctx.Logger()
|
||||||
|
|
||||||
// establish the file system to use
|
// establish the file system to use
|
||||||
if len(m.FileSystemRaw) > 0 {
|
if len(m.FileSystemRaw) > 0 {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (FileServer) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up the static files responder.
|
// Provision sets up the static files responder.
|
||||||
func (fsrv *FileServer) Provision(ctx caddy.Context) error {
|
func (fsrv *FileServer) Provision(ctx caddy.Context) error {
|
||||||
fsrv.logger = ctx.Logger(fsrv)
|
fsrv.logger = ctx.Logger()
|
||||||
|
|
||||||
// establish which file system (possibly a virtual one) we'll be using
|
// establish which file system (possibly a virtual one) we'll be using
|
||||||
if len(fsrv.FileSystemRaw) > 0 {
|
if len(fsrv.FileSystemRaw) > 0 {
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package caddyhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerLogConfig describes a server's logging configuration. If
|
||||||
|
// enabled without customization, all requests to this server are
|
||||||
|
// logged to the default logger; logger destinations may be
|
||||||
|
// customized per-request-host.
|
||||||
|
type ServerLogConfig struct {
|
||||||
|
// The default logger name for all logs emitted by this server for
|
||||||
|
// hostnames that are not in the LoggerNames (logger_names) map.
|
||||||
|
DefaultLoggerName string `json:"default_logger_name,omitempty"`
|
||||||
|
|
||||||
|
// LoggerNames maps request hostnames to a custom logger name.
|
||||||
|
// For example, a mapping of "example.com" to "example" would
|
||||||
|
// cause access logs from requests with a Host of example.com
|
||||||
|
// to be emitted by a logger named "http.log.access.example".
|
||||||
|
LoggerNames map[string]string `json:"logger_names,omitempty"`
|
||||||
|
|
||||||
|
// By default, all requests to this server will be logged if
|
||||||
|
// access logging is enabled. This field lists the request
|
||||||
|
// hosts for which access logging should be disabled.
|
||||||
|
SkipHosts []string `json:"skip_hosts,omitempty"`
|
||||||
|
|
||||||
|
// If true, requests to any host not appearing in the
|
||||||
|
// LoggerNames (logger_names) map will not be logged.
|
||||||
|
SkipUnmappedHosts bool `json:"skip_unmapped_hosts,omitempty"`
|
||||||
|
|
||||||
|
// If true, credentials that are otherwise omitted, will be logged.
|
||||||
|
// The definition of credentials is defined by https://fetch.spec.whatwg.org/#credentials,
|
||||||
|
// and this includes some request and response headers, i.e `Cookie`,
|
||||||
|
// `Set-Cookie`, `Authorization`, and `Proxy-Authorization`.
|
||||||
|
ShouldLogCredentials bool `json:"should_log_credentials,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapLogger wraps logger in a logger named according to user preferences for the given host.
|
||||||
|
func (slc ServerLogConfig) wrapLogger(logger *zap.Logger, host string) *zap.Logger {
|
||||||
|
if loggerName := slc.getLoggerName(host); loggerName != "" {
|
||||||
|
return logger.Named(loggerName)
|
||||||
|
}
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slc ServerLogConfig) getLoggerName(host string) string {
|
||||||
|
tryHost := func(key string) (string, bool) {
|
||||||
|
// first try exact match
|
||||||
|
if loggerName, ok := slc.LoggerNames[key]; ok {
|
||||||
|
return loggerName, ok
|
||||||
|
}
|
||||||
|
// strip port and try again (i.e. Host header of "example.com:1234" should
|
||||||
|
// match "example.com" if there is no "example.com:1234" in the map)
|
||||||
|
hostOnly, _, err := net.SplitHostPort(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
loggerName, ok := slc.LoggerNames[hostOnly]
|
||||||
|
return loggerName, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// try the exact hostname first
|
||||||
|
if loggerName, ok := tryHost(host); ok {
|
||||||
|
return loggerName
|
||||||
|
}
|
||||||
|
|
||||||
|
// try matching wildcard domains if other non-specific loggers exist
|
||||||
|
labels := strings.Split(host, ".")
|
||||||
|
for i := range labels {
|
||||||
|
if labels[i] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labels[i] = "*"
|
||||||
|
wildcardHost := strings.Join(labels, ".")
|
||||||
|
if loggerName, ok := tryHost(wildcardHost); ok {
|
||||||
|
return loggerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slc.DefaultLoggerName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slc *ServerLogConfig) clone() *ServerLogConfig {
|
||||||
|
clone := &ServerLogConfig{
|
||||||
|
DefaultLoggerName: slc.DefaultLoggerName,
|
||||||
|
LoggerNames: make(map[string]string),
|
||||||
|
SkipHosts: append([]string{}, slc.SkipHosts...),
|
||||||
|
SkipUnmappedHosts: slc.SkipUnmappedHosts,
|
||||||
|
ShouldLogCredentials: slc.ShouldLogCredentials,
|
||||||
|
}
|
||||||
|
for k, v := range slc.LoggerNames {
|
||||||
|
clone.LoggerNames[k] = v
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
// errLogValues inspects err and returns the status code
|
||||||
|
// to use, the error log message, and any extra fields.
|
||||||
|
// If err is a HandlerError, the returned values will
|
||||||
|
// have richer information.
|
||||||
|
func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
|
||||||
|
var handlerErr HandlerError
|
||||||
|
if errors.As(err, &handlerErr) {
|
||||||
|
status = handlerErr.StatusCode
|
||||||
|
if handlerErr.Err == nil {
|
||||||
|
msg = err.Error()
|
||||||
|
} else {
|
||||||
|
msg = handlerErr.Err.Error()
|
||||||
|
}
|
||||||
|
fields = []zapcore.Field{
|
||||||
|
zap.Int("status", handlerErr.StatusCode),
|
||||||
|
zap.String("err_id", handlerErr.ID),
|
||||||
|
zap.String("err_trace", handlerErr.Trace),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status = http.StatusInternalServerError
|
||||||
|
msg = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable name used to indicate that this request
|
||||||
|
// should be omitted from the access logs
|
||||||
|
const SkipLogVar = "skip_log"
|
||||||
@@ -1326,7 +1326,7 @@ func (MatchRemoteIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
|||||||
|
|
||||||
// Provision parses m's IP ranges, either from IP or CIDR expressions.
|
// Provision parses m's IP ranges, either from IP or CIDR expressions.
|
||||||
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
|
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
|
||||||
m.logger = ctx.Logger(m)
|
m.logger = ctx.Logger()
|
||||||
for _, str := range m.Ranges {
|
for _, str := range m.Ranges {
|
||||||
// Exclude the zone_id from the IP
|
// Exclude the zone_id from the IP
|
||||||
if strings.Contains(str, "%") {
|
if strings.Contains(str, "%") {
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Metrics configures metrics observations.
|
||||||
|
// EXPERIMENTAL and subject to change or removal.
|
||||||
|
type Metrics struct{}
|
||||||
|
|
||||||
var httpMetrics = struct {
|
var httpMetrics = struct {
|
||||||
init sync.Once
|
init sync.Once
|
||||||
requestInFlight *prometheus.GaugeVec
|
requestInFlight *prometheus.GaugeVec
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up h.
|
// Provision sets up h.
|
||||||
func (h *Handler) Provision(ctx caddy.Context) error {
|
func (h *Handler) Provision(ctx caddy.Context) error {
|
||||||
h.logger = ctx.Logger(h)
|
h.logger = ctx.Logger()
|
||||||
if h.Headers != nil {
|
if h.Headers != nil {
|
||||||
err := h.Headers.Provision(ctx)
|
err := h.Headers.Provision(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -41,6 +42,7 @@ A simple but production-ready reverse proxy. Useful for quick deployments,
|
|||||||
demos, and development.
|
demos, and development.
|
||||||
|
|
||||||
Simply shuttles HTTP(S) traffic from the --from address to the --to address.
|
Simply shuttles HTTP(S) traffic from the --from address to the --to address.
|
||||||
|
Multiple --to addresses may be specified by repeating the flag.
|
||||||
|
|
||||||
Unless otherwise specified in the addresses, the --from address will be
|
Unless otherwise specified in the addresses, the --from address will be
|
||||||
assumed to be HTTPS if a hostname is given, and the --to address will be
|
assumed to be HTTPS if a hostname is given, and the --to address will be
|
||||||
@@ -57,10 +59,11 @@ default, all incoming headers are passed through unmodified.)
|
|||||||
Flags: func() *flag.FlagSet {
|
Flags: func() *flag.FlagSet {
|
||||||
fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError)
|
fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError)
|
||||||
fs.String("from", "localhost", "Address on which to receive traffic")
|
fs.String("from", "localhost", "Address on which to receive traffic")
|
||||||
fs.String("to", "", "Upstream address to which traffic should be sent")
|
fs.Var(&reverseProxyCmdTo, "to", "Upstream address(es) to which traffic should be sent")
|
||||||
fs.Bool("change-host-header", false, "Set upstream Host header to address of upstream")
|
fs.Bool("change-host-header", false, "Set upstream Host header to address of upstream")
|
||||||
fs.Bool("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING SSL CERTIFICATES!)")
|
fs.Bool("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING SSL CERTIFICATES!)")
|
||||||
fs.Bool("internal-certs", false, "Use internal CA for issuing certs")
|
fs.Bool("internal-certs", false, "Use internal CA for issuing certs")
|
||||||
|
fs.Bool("debug", false, "Enable verbose debug logs")
|
||||||
return fs
|
return fs
|
||||||
}(),
|
}(),
|
||||||
})
|
})
|
||||||
@@ -70,15 +73,15 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
|||||||
caddy.TrapSignals()
|
caddy.TrapSignals()
|
||||||
|
|
||||||
from := fs.String("from")
|
from := fs.String("from")
|
||||||
to := fs.String("to")
|
|
||||||
changeHost := fs.Bool("change-host-header")
|
changeHost := fs.Bool("change-host-header")
|
||||||
insecure := fs.Bool("insecure")
|
insecure := fs.Bool("insecure")
|
||||||
internalCerts := fs.Bool("internal-certs")
|
internalCerts := fs.Bool("internal-certs")
|
||||||
|
debug := fs.Bool("debug")
|
||||||
|
|
||||||
httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort)
|
httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort)
|
||||||
httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort)
|
httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort)
|
||||||
|
|
||||||
if to == "" {
|
if len(reverseProxyCmdTo) == 0 {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required")
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +109,18 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set up the upstream address; assume missing information from given parts
|
// set up the upstream address; assume missing information from given parts
|
||||||
toAddr, toScheme, err := parseUpstreamDialAddress(to)
|
// mixing schemes isn't supported, so use first defined (if available)
|
||||||
if err != nil {
|
toAddresses := make([]string, len(reverseProxyCmdTo))
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", to, err)
|
var toScheme string
|
||||||
|
for i, toLoc := range reverseProxyCmdTo {
|
||||||
|
addr, scheme, err := parseUpstreamDialAddress(toLoc)
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", toLoc, err)
|
||||||
|
}
|
||||||
|
if scheme != "" && toScheme != "" {
|
||||||
|
toScheme = scheme
|
||||||
|
}
|
||||||
|
toAddresses[i] = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// proceed to build the handler and server
|
// proceed to build the handler and server
|
||||||
@@ -120,9 +132,16 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstreamPool := UpstreamPool{}
|
||||||
|
for _, toAddr := range toAddresses {
|
||||||
|
upstreamPool = append(upstreamPool, &Upstream{
|
||||||
|
Dial: toAddr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handler := Handler{
|
handler := Handler{
|
||||||
TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
|
TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
|
||||||
Upstreams: UpstreamPool{{Dial: toAddr}},
|
Upstreams: upstreamPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
if changeHost {
|
if changeHost {
|
||||||
@@ -182,12 +201,28 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
|||||||
AppsRaw: appsRaw,
|
AppsRaw: appsRaw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
cfg.Logging = &caddy.Logging{
|
||||||
|
Logs: map[string]*caddy.CustomLog{
|
||||||
|
"default": {Level: zap.DebugLevel.CapitalString()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = caddy.Run(cfg)
|
err = caddy.Run(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Caddy proxying %s -> %s\n", fromAddr.String(), toAddr)
|
for _, to := range toAddresses {
|
||||||
|
fmt.Printf("Caddy proxying %s -> %s\n", fromAddr.String(), to)
|
||||||
|
}
|
||||||
|
if len(toAddresses) > 1 {
|
||||||
|
fmt.Println("Load balancing policy: random")
|
||||||
|
}
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverseProxyCmdTo holds the parsed values from repeated use of the --to flag.
|
||||||
|
var reverseProxyCmdTo caddycmd.StringSlice
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (Transport) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up t.
|
// Provision sets up t.
|
||||||
func (t *Transport) Provision(ctx caddy.Context) error {
|
func (t *Transport) Provision(ctx caddy.Context) error {
|
||||||
t.logger = ctx.Logger(t)
|
t.logger = ctx.Logger()
|
||||||
|
|
||||||
if t.Root == "" {
|
if t.Root == "" {
|
||||||
t.Root = "{http.vars.root}"
|
t.Root = "{http.vars.root}"
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
|||||||
TCPConn: tcpConn,
|
TCPConn: tcpConn,
|
||||||
readTimeout: time.Duration(h.ReadTimeout),
|
readTimeout: time.Duration(h.ReadTimeout),
|
||||||
writeTimeout: time.Duration(h.WriteTimeout),
|
writeTimeout: time.Duration(h.WriteTimeout),
|
||||||
logger: caddyCtx.Logger(h),
|
logger: caddyCtx.Logger(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ func (h *Handler) Provision(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
h.events = eventAppIface.(*caddyevents.App)
|
h.events = eventAppIface.(*caddyevents.App)
|
||||||
h.ctx = ctx
|
h.ctx = ctx
|
||||||
h.logger = ctx.Logger(h)
|
h.logger = ctx.Logger()
|
||||||
h.connections = make(map[io.ReadWriteCloser]openConnection)
|
h.connections = make(map[io.ReadWriteCloser]openConnection)
|
||||||
h.connectionsMu = new(sync.Mutex)
|
h.connectionsMu = new(sync.Mutex)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (SRVUpstreams) CaddyModule() caddy.ModuleInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (su *SRVUpstreams) Provision(ctx caddy.Context) error {
|
func (su *SRVUpstreams) Provision(ctx caddy.Context) error {
|
||||||
su.logger = ctx.Logger(su)
|
su.logger = ctx.Logger()
|
||||||
if su.Refresh == 0 {
|
if su.Refresh == 0 {
|
||||||
su.Refresh = caddy.Duration(time.Minute)
|
su.Refresh = caddy.Duration(time.Minute)
|
||||||
}
|
}
|
||||||
@@ -383,7 +383,7 @@ func (MultiUpstreams) CaddyModule() caddy.ModuleInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mu *MultiUpstreams) Provision(ctx caddy.Context) error {
|
func (mu *MultiUpstreams) Provision(ctx caddy.Context) error {
|
||||||
mu.logger = ctx.Logger(mu)
|
mu.logger = ctx.Logger()
|
||||||
|
|
||||||
if mu.SourcesRaw != nil {
|
if mu.SourcesRaw != nil {
|
||||||
mod, err := ctx.LoadModule(mu, "SourcesRaw")
|
mod, err := ctx.LoadModule(mu, "SourcesRaw")
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (Rewrite) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up rewr.
|
// Provision sets up rewr.
|
||||||
func (rewr *Rewrite) Provision(ctx caddy.Context) error {
|
func (rewr *Rewrite) Provision(ctx caddy.Context) error {
|
||||||
rewr.logger = ctx.Logger(rewr)
|
rewr.logger = ctx.Logger()
|
||||||
|
|
||||||
for i, rep := range rewr.PathRegexp {
|
for i, rep := range rewr.PathRegexp {
|
||||||
if rep.Find == "" {
|
if rep.Find == "" {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func (routes RouteList) Provision(ctx caddy.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return routes.ProvisionHandlers(ctx)
|
return routes.ProvisionHandlers(ctx, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvisionMatchers sets up all the matchers by loading the
|
// ProvisionMatchers sets up all the matchers by loading the
|
||||||
@@ -156,7 +156,7 @@ func (routes RouteList) ProvisionMatchers(ctx caddy.Context) error {
|
|||||||
// handler modules. Only call this method directly if you need
|
// handler modules. Only call this method directly if you need
|
||||||
// to set up matchers and handlers separately without having
|
// to set up matchers and handlers separately without having
|
||||||
// to provision a second time; otherwise use Provision instead.
|
// to provision a second time; otherwise use Provision instead.
|
||||||
func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
|
func (routes RouteList) ProvisionHandlers(ctx caddy.Context, metrics *Metrics) error {
|
||||||
for i := range routes {
|
for i := range routes {
|
||||||
handlersIface, err := ctx.LoadModule(&routes[i], "HandlersRaw")
|
handlersIface, err := ctx.LoadModule(&routes[i], "HandlersRaw")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -168,7 +168,7 @@ func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
|
|||||||
|
|
||||||
// pre-compile the middleware handler chain
|
// pre-compile the middleware handler chain
|
||||||
for _, midhandler := range routes[i].Handlers {
|
for _, midhandler := range routes[i].Handlers {
|
||||||
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(ctx, midhandler))
|
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(ctx, midhandler, metrics))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -270,9 +270,12 @@ func wrapRoute(route Route) Middleware {
|
|||||||
// we need to pull this particular MiddlewareHandler
|
// we need to pull this particular MiddlewareHandler
|
||||||
// pointer into its own stack frame to preserve it so it
|
// pointer into its own stack frame to preserve it so it
|
||||||
// won't be overwritten in future loop iterations.
|
// won't be overwritten in future loop iterations.
|
||||||
func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware {
|
func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler, metrics *Metrics) Middleware {
|
||||||
// wrap the middleware with metrics instrumentation
|
handlerToUse := mh
|
||||||
metricsHandler := newMetricsInstrumentedHandler(caddy.GetModuleName(mh), mh)
|
if metrics != nil {
|
||||||
|
// wrap the middleware with metrics instrumentation
|
||||||
|
handlerToUse = newMetricsInstrumentedHandler(caddy.GetModuleName(mh), mh)
|
||||||
|
}
|
||||||
|
|
||||||
return func(next Handler) Handler {
|
return func(next Handler) Handler {
|
||||||
// copy the next handler (it's an interface, so it's
|
// copy the next handler (it's an interface, so it's
|
||||||
@@ -284,7 +287,7 @@ func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware {
|
|||||||
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
// TODO: This is where request tracing could be implemented
|
// TODO: This is where request tracing could be implemented
|
||||||
// TODO: see what the std lib gives us in terms of stack tracing too
|
// TODO: see what the std lib gives us in terms of stack tracing too
|
||||||
return metricsHandler.ServeHTTP(w, r, nextCopy)
|
return handlerToUse.ServeHTTP(w, r, nextCopy)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-116
@@ -18,7 +18,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -153,6 +152,10 @@ type Server struct {
|
|||||||
// Default: `[h1 h2 h3]`
|
// Default: `[h1 h2 h3]`
|
||||||
Protocols []string `json:"protocols,omitempty"`
|
Protocols []string `json:"protocols,omitempty"`
|
||||||
|
|
||||||
|
// If set, metrics observations will be enabled.
|
||||||
|
// This setting is EXPERIMENTAL and subject to change.
|
||||||
|
Metrics *Metrics `json:"metrics,omitempty"`
|
||||||
|
|
||||||
name string
|
name string
|
||||||
|
|
||||||
primaryHandlerChain Handler
|
primaryHandlerChain Handler
|
||||||
@@ -173,6 +176,11 @@ type Server struct {
|
|||||||
|
|
||||||
shutdownAt time.Time
|
shutdownAt time.Time
|
||||||
shutdownAtMu *sync.RWMutex
|
shutdownAtMu *sync.RWMutex
|
||||||
|
|
||||||
|
// registered callback functions
|
||||||
|
connStateFuncs []func(net.Conn, http.ConnState)
|
||||||
|
connContextFuncs []func(ctx context.Context, c net.Conn) context.Context
|
||||||
|
onShutdownFuncs []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP is the entry point for all HTTP requests.
|
// ServeHTTP is the entry point for all HTTP requests.
|
||||||
@@ -226,6 +234,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
accLog := s.accessLogger.With(loggableReq)
|
accLog := s.accessLogger.With(loggableReq)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
// this request may be flagged as omitted from the logs
|
||||||
|
if skipLog, ok := GetVar(r.Context(), SkipLogVar).(bool); ok && skipLog {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
repl.Set("http.response.status", wrec.Status())
|
repl.Set("http.response.status", wrec.Status())
|
||||||
repl.Set("http.response.size", wrec.Size())
|
repl.Set("http.response.size", wrec.Size())
|
||||||
repl.Set("http.response.duration", duration)
|
repl.Set("http.response.duration", duration)
|
||||||
@@ -505,6 +518,51 @@ func (s *Server) serveHTTP3(hostport string, tlsCfg *tls.Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureServer applies/binds the registered callback functions to the server.
|
||||||
|
func (s *Server) configureServer(server *http.Server) {
|
||||||
|
for _, f := range s.connStateFuncs {
|
||||||
|
if server.ConnState != nil {
|
||||||
|
baseConnStateFunc := server.ConnState
|
||||||
|
server.ConnState = func(conn net.Conn, state http.ConnState) {
|
||||||
|
baseConnStateFunc(conn, state)
|
||||||
|
f(conn, state)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.ConnState = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range s.connContextFuncs {
|
||||||
|
if server.ConnContext != nil {
|
||||||
|
baseConnContextFunc := server.ConnContext
|
||||||
|
server.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
||||||
|
return f(baseConnContextFunc(ctx, c), c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.ConnContext = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range s.onShutdownFuncs {
|
||||||
|
server.RegisterOnShutdown(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterConnState registers f to be invoked on s.ConnState.
|
||||||
|
func (s *Server) RegisterConnState(f func(net.Conn, http.ConnState)) {
|
||||||
|
s.connStateFuncs = append(s.connStateFuncs, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterConnContext registers f to be invoked as part of s.ConnContext.
|
||||||
|
func (s *Server) RegisterConnContext(f func(ctx context.Context, c net.Conn) context.Context) {
|
||||||
|
s.connContextFuncs = append(s.connContextFuncs, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterOnShutdown registers f to be invoked on server shutdown.
|
||||||
|
func (s *Server) RegisterOnShutdown(f func()) {
|
||||||
|
s.onShutdownFuncs = append(s.onShutdownFuncs, f)
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPErrorConfig determines how to handle errors
|
// HTTPErrorConfig determines how to handle errors
|
||||||
// from the HTTP handlers.
|
// from the HTTP handlers.
|
||||||
type HTTPErrorConfig struct {
|
type HTTPErrorConfig struct {
|
||||||
@@ -592,96 +650,6 @@ func (s *Server) protocol(proto string) bool {
|
|||||||
// EXPERIMENTAL: Subject to change or removal.
|
// EXPERIMENTAL: Subject to change or removal.
|
||||||
func (s *Server) Listeners() []net.Listener { return s.listeners }
|
func (s *Server) Listeners() []net.Listener { return s.listeners }
|
||||||
|
|
||||||
// ServerLogConfig describes a server's logging configuration. If
|
|
||||||
// enabled without customization, all requests to this server are
|
|
||||||
// logged to the default logger; logger destinations may be
|
|
||||||
// customized per-request-host.
|
|
||||||
type ServerLogConfig struct {
|
|
||||||
// The default logger name for all logs emitted by this server for
|
|
||||||
// hostnames that are not in the LoggerNames (logger_names) map.
|
|
||||||
DefaultLoggerName string `json:"default_logger_name,omitempty"`
|
|
||||||
|
|
||||||
// LoggerNames maps request hostnames to a custom logger name.
|
|
||||||
// For example, a mapping of "example.com" to "example" would
|
|
||||||
// cause access logs from requests with a Host of example.com
|
|
||||||
// to be emitted by a logger named "http.log.access.example".
|
|
||||||
LoggerNames map[string]string `json:"logger_names,omitempty"`
|
|
||||||
|
|
||||||
// By default, all requests to this server will be logged if
|
|
||||||
// access logging is enabled. This field lists the request
|
|
||||||
// hosts for which access logging should be disabled.
|
|
||||||
SkipHosts []string `json:"skip_hosts,omitempty"`
|
|
||||||
|
|
||||||
// If true, requests to any host not appearing in the
|
|
||||||
// LoggerNames (logger_names) map will not be logged.
|
|
||||||
SkipUnmappedHosts bool `json:"skip_unmapped_hosts,omitempty"`
|
|
||||||
|
|
||||||
// If true, credentials that are otherwise omitted, will be logged.
|
|
||||||
// The definition of credentials is defined by https://fetch.spec.whatwg.org/#credentials,
|
|
||||||
// and this includes some request and response headers, i.e `Cookie`,
|
|
||||||
// `Set-Cookie`, `Authorization`, and `Proxy-Authorization`.
|
|
||||||
ShouldLogCredentials bool `json:"should_log_credentials,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapLogger wraps logger in a logger named according to user preferences for the given host.
|
|
||||||
func (slc ServerLogConfig) wrapLogger(logger *zap.Logger, host string) *zap.Logger {
|
|
||||||
if loggerName := slc.getLoggerName(host); loggerName != "" {
|
|
||||||
return logger.Named(loggerName)
|
|
||||||
}
|
|
||||||
return logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slc ServerLogConfig) getLoggerName(host string) string {
|
|
||||||
tryHost := func(key string) (string, bool) {
|
|
||||||
// first try exact match
|
|
||||||
if loggerName, ok := slc.LoggerNames[key]; ok {
|
|
||||||
return loggerName, ok
|
|
||||||
}
|
|
||||||
// strip port and try again (i.e. Host header of "example.com:1234" should
|
|
||||||
// match "example.com" if there is no "example.com:1234" in the map)
|
|
||||||
hostOnly, _, err := net.SplitHostPort(key)
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
loggerName, ok := slc.LoggerNames[hostOnly]
|
|
||||||
return loggerName, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// try the exact hostname first
|
|
||||||
if loggerName, ok := tryHost(host); ok {
|
|
||||||
return loggerName
|
|
||||||
}
|
|
||||||
|
|
||||||
// try matching wildcard domains if other non-specific loggers exist
|
|
||||||
labels := strings.Split(host, ".")
|
|
||||||
for i := range labels {
|
|
||||||
if labels[i] == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
labels[i] = "*"
|
|
||||||
wildcardHost := strings.Join(labels, ".")
|
|
||||||
if loggerName, ok := tryHost(wildcardHost); ok {
|
|
||||||
return loggerName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slc.DefaultLoggerName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slc *ServerLogConfig) clone() *ServerLogConfig {
|
|
||||||
clone := &ServerLogConfig{
|
|
||||||
DefaultLoggerName: slc.DefaultLoggerName,
|
|
||||||
LoggerNames: make(map[string]string),
|
|
||||||
SkipHosts: append([]string{}, slc.SkipHosts...),
|
|
||||||
SkipUnmappedHosts: slc.SkipUnmappedHosts,
|
|
||||||
ShouldLogCredentials: slc.ShouldLogCredentials,
|
|
||||||
}
|
|
||||||
for k, v := range slc.LoggerNames {
|
|
||||||
clone.LoggerNames[k] = v
|
|
||||||
}
|
|
||||||
return clone
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareRequest fills the request r for use in a Caddy HTTP handler chain. w and s can
|
// PrepareRequest fills the request r for use in a Caddy HTTP handler chain. w and s can
|
||||||
// be nil, but the handlers will lose response placeholders and access to the server.
|
// be nil, but the handlers will lose response placeholders and access to the server.
|
||||||
func PrepareRequest(r *http.Request, repl *caddy.Replacer, w http.ResponseWriter, s *Server) *http.Request {
|
func PrepareRequest(r *http.Request, repl *caddy.Replacer, w http.ResponseWriter, s *Server) *http.Request {
|
||||||
@@ -701,31 +669,6 @@ func PrepareRequest(r *http.Request, repl *caddy.Replacer, w http.ResponseWriter
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// errLogValues inspects err and returns the status code
|
|
||||||
// to use, the error log message, and any extra fields.
|
|
||||||
// If err is a HandlerError, the returned values will
|
|
||||||
// have richer information.
|
|
||||||
func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
|
|
||||||
var handlerErr HandlerError
|
|
||||||
if errors.As(err, &handlerErr) {
|
|
||||||
status = handlerErr.StatusCode
|
|
||||||
if handlerErr.Err == nil {
|
|
||||||
msg = err.Error()
|
|
||||||
} else {
|
|
||||||
msg = handlerErr.Err.Error()
|
|
||||||
}
|
|
||||||
fields = []zapcore.Field{
|
|
||||||
zap.Int("status", handlerErr.StatusCode),
|
|
||||||
zap.String("err_id", handlerErr.ID),
|
|
||||||
zap.String("err_trace", handlerErr.Trace),
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
status = http.StatusInternalServerError
|
|
||||||
msg = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// originalRequest returns a partial, shallow copy of
|
// originalRequest returns a partial, shallow copy of
|
||||||
// req, including: req.Method, deep copy of req.URL
|
// req, including: req.Method, deep copy of req.URL
|
||||||
// (into the urlCopy parameter, which should be on the
|
// (into the urlCopy parameter, which should be on the
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -403,7 +404,7 @@ func cmdRespond(fl caddycmd.Flags) (int, error) {
|
|||||||
if debug {
|
if debug {
|
||||||
cfg.Logging = &caddy.Logging{
|
cfg.Logging = &caddy.Logging{
|
||||||
Logs: map[string]*caddy.CustomLog{
|
Logs: map[string]*caddy.CustomLog{
|
||||||
"default": {Level: "DEBUG"},
|
"default": {Level: zap.DebugLevel.CapitalString()},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (Tracing) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision implements caddy.Provisioner.
|
// Provision implements caddy.Provisioner.
|
||||||
func (ot *Tracing) Provision(ctx caddy.Context) error {
|
func (ot *Tracing) Provision(ctx caddy.Context) error {
|
||||||
ot.logger = ctx.Logger(ot)
|
ot.logger = ctx.Logger()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ot.otel, err = newOpenTelemetryWrapper(ctx, ot.SpanName)
|
ot.otel, err = newOpenTelemetryWrapper(ctx, ot.SpanName)
|
||||||
@@ -66,10 +66,9 @@ func (ot *Tracing) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
|
|||||||
|
|
||||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
|
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
|
||||||
//
|
//
|
||||||
// tracing {
|
// tracing {
|
||||||
// [span <span_name>]
|
// [span <span_name>]
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
func (ot *Tracing) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (ot *Tracing) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
setParameter := func(d *caddyfile.Dispenser, val *string) error {
|
setParameter := func(d *caddyfile.Dispenser, val *string) error {
|
||||||
if d.NextArg() {
|
if d.NextArg() {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up the ACME server handler.
|
// Provision sets up the ACME server handler.
|
||||||
func (ash *Handler) Provision(ctx caddy.Context) error {
|
func (ash *Handler) Provision(ctx caddy.Context) error {
|
||||||
ash.logger = ctx.Logger(ash)
|
ash.logger = ctx.Logger()
|
||||||
// set some defaults
|
// set some defaults
|
||||||
if ash.CA == "" {
|
if ash.CA == "" {
|
||||||
ash.CA = caddypki.DefaultCAID
|
ash.CA = caddypki.DefaultCAID
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (adminAPI) CaddyModule() caddy.ModuleInfo {
|
|||||||
// Provision sets up the adminAPI module.
|
// Provision sets up the adminAPI module.
|
||||||
func (a *adminAPI) Provision(ctx caddy.Context) error {
|
func (a *adminAPI) Provision(ctx caddy.Context) error {
|
||||||
a.ctx = ctx
|
a.ctx = ctx
|
||||||
a.log = ctx.Logger(a)
|
a.log = ctx.Logger(a) // TODO: passing in 'a' is a hack until the admin API is officially extensible (see #5032)
|
||||||
|
|
||||||
// First check if the PKI app was configured, because
|
// First check if the PKI app was configured, because
|
||||||
// a.ctx.App() has the side effect of instantiating
|
// a.ctx.App() has the side effect of instantiating
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (PKI) CaddyModule() caddy.ModuleInfo {
|
|||||||
// Provision sets up the configuration for the PKI app.
|
// Provision sets up the configuration for the PKI app.
|
||||||
func (p *PKI) Provision(ctx caddy.Context) error {
|
func (p *PKI) Provision(ctx caddy.Context) error {
|
||||||
p.ctx = ctx
|
p.ctx = ctx
|
||||||
p.log = ctx.Logger(p)
|
p.log = ctx.Logger()
|
||||||
|
|
||||||
for caID, ca := range p.CAs {
|
for caID, ca := range p.CAs {
|
||||||
err := ca.Provision(ctx, caID, p.log)
|
err := ca.Provision(ctx, caID, p.log)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func (ACMEIssuer) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up iss.
|
// Provision sets up iss.
|
||||||
func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
|
func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
|
||||||
iss.logger = ctx.Logger(iss)
|
iss.logger = ctx.Logger()
|
||||||
|
|
||||||
repl := caddy.NewReplacer()
|
repl := caddy.NewReplacer()
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (Tailscale) CaddyModule() caddy.ModuleInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Tailscale) Provision(ctx caddy.Context) error {
|
func (ts *Tailscale) Provision(ctx caddy.Context) error {
|
||||||
ts.logger = ctx.Logger(ts)
|
ts.logger = ctx.Logger()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,9 @@ func (ts Tailscale) canHazCertificate(ctx context.Context, hello *tls.ClientHell
|
|||||||
status, err := tscert.GetStatus(ctx)
|
status, err := tscert.GetStatus(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ts.Optional {
|
if ts.Optional {
|
||||||
return false, nil // ignore error if we don't expect/require it to work anyway
|
// ignore error if we don't expect/require it to work anyway, but log it for debugging
|
||||||
|
ts.logger.Debug("error getting tailscale status", zap.Error(err), zap.String("server_name", hello.ServerName))
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -80,8 +82,7 @@ func (ts Tailscale) canHazCertificate(ctx context.Context, hello *tls.ClientHell
|
|||||||
|
|
||||||
// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
|
// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
|
||||||
//
|
//
|
||||||
// ... tailscale
|
// ... tailscale
|
||||||
//
|
|
||||||
func (Tailscale) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (Tailscale) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
if d.NextArg() {
|
if d.NextArg() {
|
||||||
@@ -178,8 +179,7 @@ func (hcg HTTPCertGetter) GetCertificate(ctx context.Context, hello *tls.ClientH
|
|||||||
|
|
||||||
// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
|
// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
|
||||||
//
|
//
|
||||||
// ... http <url>
|
// ... http <url>
|
||||||
//
|
|
||||||
func (hcg *HTTPCertGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (hcg *HTTPCertGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
|
|||||||
@@ -20,11 +20,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -156,6 +159,16 @@ type ConnectionPolicy struct {
|
|||||||
// is no policy configured for the empty SNI value.
|
// is no policy configured for the empty SNI value.
|
||||||
DefaultSNI string `json:"default_sni,omitempty"`
|
DefaultSNI string `json:"default_sni,omitempty"`
|
||||||
|
|
||||||
|
// Also known as "SSLKEYLOGFILE", TLS secrets will be written to
|
||||||
|
// this file in NSS key log format which can then be parsed by
|
||||||
|
// Wireshark and other tools. This is INSECURE as it allows other
|
||||||
|
// programs or tools to decrypt TLS connections. However, this
|
||||||
|
// capability can be useful for debugging and troubleshooting.
|
||||||
|
// **ENABLING THIS LOG COMPROMISES SECURITY!**
|
||||||
|
//
|
||||||
|
// This feature is EXPERIMENTAL and subject to change or removal.
|
||||||
|
InsecureSecretsLog string `json:"insecure_secrets_log,omitempty"`
|
||||||
|
|
||||||
// TLSConfig is the fully-formed, standard lib TLS config
|
// TLSConfig is the fully-formed, standard lib TLS config
|
||||||
// used to serve TLS connections. Provision all
|
// used to serve TLS connections. Provision all
|
||||||
// ConnectionPolicies to populate this. It is exported only
|
// ConnectionPolicies to populate this. It is exported only
|
||||||
@@ -280,6 +293,30 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.InsecureSecretsLog != "" {
|
||||||
|
filename, err := caddy.NewReplacer().ReplaceOrErr(p.InsecureSecretsLog, true, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filename, err = filepath.Abs(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logFile, _, err := secretsLogPool.LoadOrNew(filename, func() (caddy.Destructor, error) {
|
||||||
|
w, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||||
|
return destructableWriter{w}, err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.OnCancel(func() { _, _ = secretsLogPool.Delete(filename) })
|
||||||
|
|
||||||
|
cfg.KeyLogWriter = logFile.(io.Writer)
|
||||||
|
|
||||||
|
tlsApp.logger.Warn("TLS SECURITY COMPROMISED: secrets logging is enabled!",
|
||||||
|
zap.String("log_filename", filename))
|
||||||
|
}
|
||||||
|
|
||||||
setDefaultTLSParams(cfg)
|
setDefaultTLSParams(cfg)
|
||||||
|
|
||||||
p.TLSConfig = cfg
|
p.TLSConfig = cfg
|
||||||
@@ -297,7 +334,8 @@ func (p ConnectionPolicy) SettingsEmpty() bool {
|
|||||||
p.ProtocolMin == "" &&
|
p.ProtocolMin == "" &&
|
||||||
p.ProtocolMax == "" &&
|
p.ProtocolMax == "" &&
|
||||||
p.ClientAuthentication == nil &&
|
p.ClientAuthentication == nil &&
|
||||||
p.DefaultSNI == ""
|
p.DefaultSNI == "" &&
|
||||||
|
p.InsecureSecretsLog == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientAuthentication configures TLS client auth.
|
// ClientAuthentication configures TLS client auth.
|
||||||
@@ -542,3 +580,9 @@ type ClientCertificateVerifier interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var defaultALPN = []string{"h2", "http/1.1"}
|
var defaultALPN = []string{"h2", "http/1.1"}
|
||||||
|
|
||||||
|
type destructableWriter struct{ *os.File }
|
||||||
|
|
||||||
|
func (d destructableWriter) Destruct() error { return d.Close() }
|
||||||
|
|
||||||
|
var secretsLogPool = caddy.NewUsagePool()
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (InternalIssuer) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up the issuer.
|
// Provision sets up the issuer.
|
||||||
func (iss *InternalIssuer) Provision(ctx caddy.Context) error {
|
func (iss *InternalIssuer) Provision(ctx caddy.Context) error {
|
||||||
iss.logger = ctx.Logger(iss)
|
iss.logger = ctx.Logger()
|
||||||
|
|
||||||
// set some defaults
|
// set some defaults
|
||||||
if iss.CA == "" {
|
if iss.CA == "" {
|
||||||
@@ -148,12 +148,11 @@ func (iss InternalIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
|
|||||||
|
|
||||||
// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
|
// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
|
||||||
//
|
//
|
||||||
// ... internal {
|
// ... internal {
|
||||||
// ca <name>
|
// ca <name>
|
||||||
// lifetime <duration>
|
// lifetime <duration>
|
||||||
// sign_with_root
|
// sign_with_root
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (iss *InternalIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
for d.NextBlock(0) {
|
for d.NextBlock(0) {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision parses m's IP ranges, either from IP or CIDR expressions.
|
// Provision parses m's IP ranges, either from IP or CIDR expressions.
|
||||||
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
|
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
|
||||||
m.logger = ctx.Logger(m)
|
m.logger = ctx.Logger()
|
||||||
for _, str := range m.Ranges {
|
for _, str := range m.Ranges {
|
||||||
cidrs, err := m.parseIPRange(str)
|
cidrs, err := m.parseIPRange(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
t.events = eventsAppIface.(*caddyevents.App)
|
t.events = eventsAppIface.(*caddyevents.App)
|
||||||
t.ctx = ctx
|
t.ctx = ctx
|
||||||
t.logger = ctx.Logger(t)
|
t.logger = ctx.Logger()
|
||||||
repl := caddy.NewReplacer()
|
repl := caddy.NewReplacer()
|
||||||
|
|
||||||
// set up a new certificate cache; this (re)loads all certificates
|
// set up a new certificate cache; this (re)loads all certificates
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func (*ZeroSSLIssuer) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Provision sets up iss.
|
// Provision sets up iss.
|
||||||
func (iss *ZeroSSLIssuer) Provision(ctx caddy.Context) error {
|
func (iss *ZeroSSLIssuer) Provision(ctx caddy.Context) error {
|
||||||
iss.logger = ctx.Logger(iss)
|
iss.logger = ctx.Logger()
|
||||||
if iss.ACMEIssuer == nil {
|
if iss.ACMEIssuer == nil {
|
||||||
iss.ACMEIssuer = new(ACMEIssuer)
|
iss.ACMEIssuer = new(ACMEIssuer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (l *zapLogger) Println(v ...any) {
|
|||||||
|
|
||||||
// Provision sets up m.
|
// Provision sets up m.
|
||||||
func (m *Metrics) Provision(ctx caddy.Context) error {
|
func (m *Metrics) Provision(ctx caddy.Context) error {
|
||||||
log := ctx.Logger(m)
|
log := ctx.Logger()
|
||||||
m.metricsHandler = createMetricsHandler(&zapLogger{log}, !m.DisableOpenMetrics)
|
m.metricsHandler = createMetricsHandler(&zapLogger{log}, !m.DisableOpenMetrics)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -75,10 +75,9 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
|||||||
|
|
||||||
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
|
// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
|
||||||
//
|
//
|
||||||
// metrics [<matcher>] {
|
// metrics [<matcher>] {
|
||||||
// disable_openmetrics
|
// disable_openmetrics
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
func (m *Metrics) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (m *Metrics) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
args := d.RemainingArgs()
|
args := d.RemainingArgs()
|
||||||
|
|||||||
+12
-1
@@ -144,9 +144,11 @@ func (r *Replacer) replace(input, empty string,
|
|||||||
// iterate the input to find each placeholder
|
// iterate the input to find each placeholder
|
||||||
var lastWriteCursor int
|
var lastWriteCursor int
|
||||||
|
|
||||||
|
// fail fast if too many placeholders are unclosed
|
||||||
|
var unclosedCount int
|
||||||
|
|
||||||
scan:
|
scan:
|
||||||
for i := 0; i < len(input); i++ {
|
for i := 0; i < len(input); i++ {
|
||||||
|
|
||||||
// check for escaped braces
|
// check for escaped braces
|
||||||
if i > 0 && input[i-1] == phEscape && (input[i] == phClose || input[i] == phOpen) {
|
if i > 0 && input[i-1] == phEscape && (input[i] == phClose || input[i] == phOpen) {
|
||||||
sb.WriteString(input[lastWriteCursor : i-1])
|
sb.WriteString(input[lastWriteCursor : i-1])
|
||||||
@@ -158,9 +160,17 @@ scan:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// our iterator is now on an unescaped open brace (start of placeholder)
|
||||||
|
|
||||||
|
// too many unclosed placeholders in absolutely ridiculous input can be extremely slow (issue #4170)
|
||||||
|
if unclosedCount > 100 {
|
||||||
|
return "", fmt.Errorf("too many unclosed placeholders")
|
||||||
|
}
|
||||||
|
|
||||||
// find the end of the placeholder
|
// find the end of the placeholder
|
||||||
end := strings.Index(input[i:], string(phClose)) + i
|
end := strings.Index(input[i:], string(phClose)) + i
|
||||||
if end < i {
|
if end < i {
|
||||||
|
unclosedCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +178,7 @@ scan:
|
|||||||
for end > 0 && end < len(input)-1 && input[end-1] == phEscape {
|
for end > 0 && end < len(input)-1 && input[end-1] == phEscape {
|
||||||
nextEnd := strings.Index(input[end+1:], string(phClose))
|
nextEnd := strings.Index(input[end+1:], string(phClose))
|
||||||
if nextEnd < 0 {
|
if nextEnd < 0 {
|
||||||
|
unclosedCount++
|
||||||
continue scan
|
continue scan
|
||||||
}
|
}
|
||||||
end += nextEnd + 1
|
end += nextEnd + 1
|
||||||
|
|||||||
Reference in New Issue
Block a user