mirror of
https://github.com/caddyserver/caddy.git
synced 2025-03-12 00:49:03 -04:00
Merge branch 'master' into forward-proxy
This commit is contained in:
commit
0896d92d03
@ -111,7 +111,7 @@ archives:
|
|||||||
- id: default
|
- id: default
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
format: zip
|
formats: zip
|
||||||
name_template: >-
|
name_template: >-
|
||||||
{{ .ProjectName }}_
|
{{ .ProjectName }}_
|
||||||
{{- .Version }}_
|
{{- .Version }}_
|
||||||
|
@ -176,7 +176,7 @@ The docs are also open source. You can contribute to them here: https://github.c
|
|||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
|
|
||||||
- We advise companies using Caddy to secure a support contract through [Ardan Labs](https://www.ardanlabs.com/my/contact-us?dd=caddy) before help is needed.
|
- We advise companies using Caddy to secure a support contract through [Ardan Labs](https://www.ardanlabs.com) before help is needed.
|
||||||
|
|
||||||
- A [sponsorship](https://github.com/sponsors/mholt) goes a long way! We can offer private help to sponsors. If Caddy is benefitting your company, please consider a sponsorship. This not only helps fund full-time work to ensure the longevity of the project, it provides your company the resources, support, and discounts you need; along with being a great look for your company to your customers and potential customers!
|
- A [sponsorship](https://github.com/sponsors/mholt) goes a long way! We can offer private help to sponsors. If Caddy is benefitting your company, please consider a sponsorship. This not only helps fund full-time work to ensure the longevity of the project, it provides your company the resources, support, and discounts you need; along with being a great look for your company to your customers and potential customers!
|
||||||
|
|
||||||
|
2
admin.go
2
admin.go
@ -1139,7 +1139,7 @@ traverseLoop:
|
|||||||
return fmt.Errorf("[%s] invalid array index '%s': %v",
|
return fmt.Errorf("[%s] invalid array index '%s': %v",
|
||||||
path, idxStr, err)
|
path, idxStr, err)
|
||||||
}
|
}
|
||||||
if idx < 0 || idx >= len(arr) {
|
if idx < 0 || (method != http.MethodPut && idx >= len(arr)) || idx > len(arr) {
|
||||||
return fmt.Errorf("[%s] array index out of bounds: %s", path, idxStr)
|
return fmt.Errorf("[%s] array index out of bounds: %s", path, idxStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,14 @@
|
|||||||
@images path /images/*
|
@images path /images/*
|
||||||
header @images {
|
header @images {
|
||||||
Cache-Control "public, max-age=3600, stale-while-revalidate=86400"
|
Cache-Control "public, max-age=3600, stale-while-revalidate=86400"
|
||||||
|
match {
|
||||||
|
status 200
|
||||||
|
}
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
+Link "Foo"
|
+Link "Foo"
|
||||||
+Link "Bar"
|
+Link "Bar"
|
||||||
|
match status 200
|
||||||
}
|
}
|
||||||
header >Set Defer
|
header >Set Defer
|
||||||
header >Replace Deferred Replacement
|
header >Replace Deferred Replacement
|
||||||
@ -42,6 +46,11 @@
|
|||||||
{
|
{
|
||||||
"handler": "headers",
|
"handler": "headers",
|
||||||
"response": {
|
"response": {
|
||||||
|
"require": {
|
||||||
|
"status_code": [
|
||||||
|
200
|
||||||
|
]
|
||||||
|
},
|
||||||
"set": {
|
"set": {
|
||||||
"Cache-Control": [
|
"Cache-Control": [
|
||||||
"public, max-age=3600, stale-while-revalidate=86400"
|
"public, max-age=3600, stale-while-revalidate=86400"
|
||||||
@ -136,6 +145,11 @@
|
|||||||
"Foo",
|
"Foo",
|
||||||
"Bar"
|
"Bar"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"status_code": [
|
||||||
|
200
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
23
cmd/main.go
23
cmd/main.go
@ -24,6 +24,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -33,10 +34,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/KimMachineGun/automemlimit/memlimit"
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"go.uber.org/automaxprocs/maxprocs"
|
"go.uber.org/automaxprocs/maxprocs"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/exp/zapslog"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
@ -66,12 +69,30 @@ func Main() {
|
|||||||
os.Exit(caddy.ExitCodeFailedStartup)
|
os.Exit(caddy.ExitCodeFailedStartup)
|
||||||
}
|
}
|
||||||
|
|
||||||
undo, err := maxprocs.Set()
|
logger := caddy.Log()
|
||||||
|
|
||||||
|
// Configure the maximum number of CPUs to use to match the Linux container quota (if any)
|
||||||
|
// See https://pkg.go.dev/runtime#GOMAXPROCS
|
||||||
|
undo, err := maxprocs.Set(maxprocs.Logger(logger.Sugar().Infof))
|
||||||
defer undo()
|
defer undo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
|
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure the maximum memory to use to match the Linux container quota (if any) or system memory
|
||||||
|
// See https://pkg.go.dev/runtime/debug#SetMemoryLimit
|
||||||
|
_, _ = memlimit.SetGoMemLimitWithOpts(
|
||||||
|
memlimit.WithLogger(
|
||||||
|
slog.New(zapslog.NewHandler(logger.Core())),
|
||||||
|
),
|
||||||
|
memlimit.WithProvider(
|
||||||
|
memlimit.ApplyFallback(
|
||||||
|
memlimit.FromCgroup,
|
||||||
|
memlimit.FromSystem,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if err := defaultFactory.Build().Execute(); err != nil {
|
if err := defaultFactory.Build().Execute(); err != nil {
|
||||||
var exitError *exitError
|
var exitError *exitError
|
||||||
if errors.As(err, &exitError) {
|
if errors.As(err, &exitError) {
|
||||||
|
12
go.mod
12
go.mod
@ -6,10 +6,11 @@ toolchain go1.23.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.4.0
|
github.com/BurntSushi/toml v1.4.0
|
||||||
|
github.com/KimMachineGun/automemlimit v0.7.0
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
github.com/Masterminds/sprig/v3 v3.3.0
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0
|
github.com/alecthomas/chroma/v2 v2.14.0
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
|
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
|
||||||
github.com/caddyserver/certmagic v0.21.5
|
github.com/caddyserver/certmagic v0.21.7
|
||||||
github.com/caddyserver/zerossl v0.1.3
|
github.com/caddyserver/zerossl v0.1.3
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
@ -17,9 +18,9 @@ require (
|
|||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/klauspost/compress v1.17.11
|
github.com/klauspost/compress v1.17.11
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9
|
github.com/klauspost/cpuid/v2 v2.2.9
|
||||||
github.com/mholt/acmez/v3 v3.0.0
|
github.com/mholt/acmez/v3 v3.0.1
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/quic-go/quic-go v0.48.2
|
github.com/quic-go/quic-go v0.49.0
|
||||||
github.com/smallstep/certificates v0.26.1
|
github.com/smallstep/certificates v0.26.1
|
||||||
github.com/smallstep/nosql v0.6.1
|
github.com/smallstep/nosql v0.6.1
|
||||||
github.com/smallstep/truststore v0.13.0
|
github.com/smallstep/truststore v0.13.0
|
||||||
@ -56,13 +57,14 @@ require (
|
|||||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||||
github.com/go-kit/log v0.2.1 // indirect
|
github.com/go-kit/log v0.2.1 // indirect
|
||||||
github.com/golang/glog v1.2.2 // indirect
|
github.com/golang/glog v1.2.4 // indirect
|
||||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect
|
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect
|
||||||
github.com/google/go-tpm v0.9.0 // indirect
|
github.com/google/go-tpm v0.9.0 // indirect
|
||||||
github.com/google/go-tspi v0.3.0 // indirect
|
github.com/google/go-tspi v0.3.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
|
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
|
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect
|
||||||
@ -74,7 +76,7 @@ require (
|
|||||||
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
|
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect
|
||||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
|
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect
|
||||||
go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect
|
go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||||
|
24
go.sum
24
go.sum
@ -31,6 +31,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/KimMachineGun/automemlimit v0.7.0 h1:7G06p/dMSf7G8E6oq+f2uOPuVncFyIlDI/pBWK49u88=
|
||||||
|
github.com/KimMachineGun/automemlimit v0.7.0/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
@ -89,8 +91,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
github.com/caddyserver/certmagic v0.21.5 h1:iIga4nZRgd27EIEbX7RZmoRMul+EVBn/h7bAGL83dnY=
|
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
|
||||||
github.com/caddyserver/certmagic v0.21.5/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw=
|
github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI=
|
||||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
@ -186,8 +188,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh
|
|||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
|
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
||||||
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
@ -344,8 +346,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E=
|
github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8=
|
||||||
github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
@ -366,6 +368,8 @@ github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe
|
|||||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||||
@ -392,8 +396,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
|
|||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
|
||||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
@ -564,8 +568,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
|||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
@ -529,21 +529,6 @@ func (app *App) Start() error {
|
|||||||
// enable TLS if there is a policy and if this is not the HTTP port
|
// enable TLS if there is a policy and if this is not the HTTP port
|
||||||
useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
|
useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
|
||||||
|
|
||||||
// enable HTTP/3 if configured
|
|
||||||
if h3ok && useTLS {
|
|
||||||
app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
|
|
||||||
if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if h3ok && !useTLS {
|
|
||||||
// Can only serve h3 with TLS enabled
|
|
||||||
app.logger.Warn("HTTP/3 skipped because it requires TLS",
|
|
||||||
zap.String("network", listenAddr.Network),
|
|
||||||
zap.String("addr", hostport))
|
|
||||||
}
|
|
||||||
|
|
||||||
if h1ok || h2ok && useTLS || h2cok {
|
if h1ok || h2ok && useTLS || h2cok {
|
||||||
// create the listener for this socket
|
// create the listener for this socket
|
||||||
lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)})
|
lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)})
|
||||||
@ -614,6 +599,33 @@ func (app *App) Start() error {
|
|||||||
zap.String("network", listenAddr.Network),
|
zap.String("network", listenAddr.Network),
|
||||||
zap.String("addr", hostport))
|
zap.String("addr", hostport))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h3ok {
|
||||||
|
// Can't serve HTTP/3 on the same socket as HTTP/1 and 2 because it uses
|
||||||
|
// a different transport mechanism... which is fine, but the OS doesn't
|
||||||
|
// differentiate between a SOCK_STREAM file and a SOCK_DGRAM file; they
|
||||||
|
// are still one file on the system. So even though "unixpacket" and
|
||||||
|
// "unixgram" are different network types just as "tcp" and "udp" are,
|
||||||
|
// the OS will not let us use the same file as both STREAM and DGRAM.
|
||||||
|
if listenAddr.IsUnixNetwork() {
|
||||||
|
app.logger.Warn("HTTP/3 disabled because Unix can't multiplex STREAM and DGRAM on same socket",
|
||||||
|
zap.String("file", hostport))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if useTLS {
|
||||||
|
// enable HTTP/3 if configured
|
||||||
|
app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
|
||||||
|
if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Can only serve h3 with TLS enabled
|
||||||
|
app.logger.Warn("HTTP/3 skipped because it requires TLS",
|
||||||
|
zap.String("network", listenAddr.Network),
|
||||||
|
zap.String("addr", hostport))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,16 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
|||||||
handler.Response.Deferred = true
|
handler.Response.Deferred = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if field == "match" {
|
||||||
|
responseMatchers := make(map[string]caddyhttp.ResponseMatcher)
|
||||||
|
err := caddyhttp.ParseNamedResponseMatcher(h.NewFromNextSegment(), responseMatchers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
matcher := responseMatchers["match"]
|
||||||
|
handler.Response.Require = &matcher
|
||||||
|
continue
|
||||||
|
}
|
||||||
if hasArgs {
|
if hasArgs {
|
||||||
return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird
|
return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,28 @@ func TestHandler(t *testing.T) {
|
|||||||
"Cache-Control": []string{"no-cache"},
|
"Cache-Control": []string{"no-cache"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{ // same as above, but checks that response headers are left alone when "Require" conditions are unmet
|
||||||
|
handler: Handler{
|
||||||
|
Response: &RespHeaderOps{
|
||||||
|
Require: &caddyhttp.ResponseMatcher{
|
||||||
|
Headers: http.Header{
|
||||||
|
"Cache-Control": nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HeaderOps: &HeaderOps{
|
||||||
|
Add: http.Header{
|
||||||
|
"Cache-Control": []string{"no-cache"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
respHeader: http.Header{
|
||||||
|
"Cache-Control": []string{"something"},
|
||||||
|
},
|
||||||
|
expectedRespHeader: http.Header{
|
||||||
|
"Cache-Control": []string{"something"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: Handler{
|
handler: Handler{
|
||||||
Response: &RespHeaderOps{
|
Response: &RespHeaderOps{
|
||||||
|
@ -211,6 +211,11 @@ func errLogValues(err error) (status int, msg string, fields func() []zapcore.Fi
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fields = func() []zapcore.Field {
|
||||||
|
return []zapcore.Field{
|
||||||
|
zap.Error(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
return
|
return
|
||||||
|
@ -154,16 +154,16 @@ func (rr *responseRecorder) WriteHeader(statusCode int) {
|
|||||||
// connections by manually setting headers and writing status 101
|
// connections by manually setting headers and writing status 101
|
||||||
rr.statusCode = statusCode
|
rr.statusCode = statusCode
|
||||||
|
|
||||||
|
// decide whether we should buffer the response
|
||||||
|
if rr.shouldBuffer == nil {
|
||||||
|
rr.stream = true
|
||||||
|
} else {
|
||||||
|
rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header())
|
||||||
|
}
|
||||||
|
|
||||||
// 1xx responses aren't final; just informational
|
// 1xx responses aren't final; just informational
|
||||||
if statusCode < 100 || statusCode > 199 {
|
if statusCode < 100 || statusCode > 199 {
|
||||||
rr.wroteHeader = true
|
rr.wroteHeader = true
|
||||||
|
|
||||||
// decide whether we should buffer the response
|
|
||||||
if rr.shouldBuffer == nil {
|
|
||||||
rr.stream = true
|
|
||||||
} else {
|
|
||||||
rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if informational or not buffered, immediately write header
|
// if informational or not buffered, immediately write header
|
||||||
|
@ -131,15 +131,18 @@ func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||||||
// is equivalent to a route consisting of:
|
// is equivalent to a route consisting of:
|
||||||
//
|
//
|
||||||
// # Add trailing slash for directory requests
|
// # Add trailing slash for directory requests
|
||||||
|
// # This redirection is automatically disabled if "{http.request.uri.path}/index.php"
|
||||||
|
// # doesn't appear in the try_files list
|
||||||
// @canonicalPath {
|
// @canonicalPath {
|
||||||
// file {path}/index.php
|
// file {path}/index.php
|
||||||
// not path */
|
// not path */
|
||||||
// }
|
// }
|
||||||
// redir @canonicalPath {path}/ 308
|
// redir @canonicalPath {path}/ 308
|
||||||
//
|
//
|
||||||
// # If the requested file does not exist, try index files
|
// # If the requested file does not exist, try index files and assume index.php always exists
|
||||||
// @indexFiles file {
|
// @indexFiles file {
|
||||||
// try_files {path} {path}/index.php index.php
|
// try_files {path} {path}/index.php index.php
|
||||||
|
// try_policy first_exist_fallback
|
||||||
// split_path .php
|
// split_path .php
|
||||||
// }
|
// }
|
||||||
// rewrite @indexFiles {http.matchers.file.relative}
|
// rewrite @indexFiles {http.matchers.file.relative}
|
||||||
|
@ -1,32 +1,47 @@
|
|||||||
package reverseproxy
|
package reverseproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reverseProxyMetrics = struct {
|
var reverseProxyMetrics = struct {
|
||||||
init sync.Once
|
once sync.Once
|
||||||
upstreamsHealthy *prometheus.GaugeVec
|
upstreamsHealthy *prometheus.GaugeVec
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
func initReverseProxyMetrics(handler *Handler) {
|
func initReverseProxyMetrics(handler *Handler, registry *prometheus.Registry) {
|
||||||
const ns, sub = "caddy", "reverse_proxy"
|
const ns, sub = "caddy", "reverse_proxy"
|
||||||
|
|
||||||
upstreamsLabels := []string{"upstream"}
|
upstreamsLabels := []string{"upstream"}
|
||||||
reverseProxyMetrics.upstreamsHealthy = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
reverseProxyMetrics.once.Do(func() {
|
||||||
Namespace: ns,
|
reverseProxyMetrics.upstreamsHealthy = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
Subsystem: sub,
|
Namespace: ns,
|
||||||
Name: "upstreams_healthy",
|
Subsystem: sub,
|
||||||
Help: "Health status of reverse proxy upstreams.",
|
Name: "upstreams_healthy",
|
||||||
}, upstreamsLabels)
|
Help: "Health status of reverse proxy upstreams.",
|
||||||
|
}, upstreamsLabels)
|
||||||
|
})
|
||||||
|
|
||||||
|
// duplicate registration could happen if multiple sites with reverse proxy are configured; so ignore the error because
|
||||||
|
// there's no good way to capture having multiple sites with reverse proxy. If this happens, the metrics will be
|
||||||
|
// registered twice, but the second registration will be ignored.
|
||||||
|
if err := registry.Register(reverseProxyMetrics.upstreamsHealthy); err != nil &&
|
||||||
|
!errors.Is(err, prometheus.AlreadyRegisteredError{
|
||||||
|
ExistingCollector: reverseProxyMetrics.upstreamsHealthy,
|
||||||
|
NewCollector: reverseProxyMetrics.upstreamsHealthy,
|
||||||
|
}) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics")
|
reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics")
|
||||||
}
|
}
|
||||||
@ -35,17 +50,14 @@ type metricsUpstreamsHealthyUpdater struct {
|
|||||||
handler *Handler
|
handler *Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMetricsUpstreamsHealthyUpdater(handler *Handler) *metricsUpstreamsHealthyUpdater {
|
func newMetricsUpstreamsHealthyUpdater(handler *Handler, ctx caddy.Context) *metricsUpstreamsHealthyUpdater {
|
||||||
reverseProxyMetrics.init.Do(func() {
|
initReverseProxyMetrics(handler, ctx.GetMetricsRegistry())
|
||||||
initReverseProxyMetrics(handler)
|
|
||||||
})
|
|
||||||
|
|
||||||
reverseProxyMetrics.upstreamsHealthy.Reset()
|
reverseProxyMetrics.upstreamsHealthy.Reset()
|
||||||
|
|
||||||
return &metricsUpstreamsHealthyUpdater{handler}
|
return &metricsUpstreamsHealthyUpdater{handler}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metricsUpstreamsHealthyUpdater) Init() {
|
func (m *metricsUpstreamsHealthyUpdater) init() {
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
|
@ -382,8 +382,8 @@ func (h *Handler) Provision(ctx caddy.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamHealthyUpdater := newMetricsUpstreamsHealthyUpdater(h)
|
upstreamHealthyUpdater := newMetricsUpstreamsHealthyUpdater(h, ctx)
|
||||||
upstreamHealthyUpdater.Init()
|
upstreamHealthyUpdater.init()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -683,7 +683,7 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http.
|
|||||||
req.Header.Set("Early-Data", "1")
|
req.Header.Set("Early-Data", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
reqUpType := upgradeType(req.Header)
|
reqUpgradeType := upgradeType(req.Header)
|
||||||
removeConnectionHeaders(req.Header)
|
removeConnectionHeaders(req.Header)
|
||||||
|
|
||||||
// Remove hop-by-hop headers to the backend. Especially
|
// Remove hop-by-hop headers to the backend. Especially
|
||||||
@ -704,9 +704,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http.
|
|||||||
|
|
||||||
// After stripping all the hop-by-hop connection headers above, add back any
|
// After stripping all the hop-by-hop connection headers above, add back any
|
||||||
// necessary for protocol upgrades, such as for websockets.
|
// necessary for protocol upgrades, such as for websockets.
|
||||||
if reqUpType != "" {
|
if reqUpgradeType != "" {
|
||||||
req.Header.Set("Connection", "Upgrade")
|
req.Header.Set("Connection", "Upgrade")
|
||||||
req.Header.Set("Upgrade", reqUpType)
|
req.Header.Set("Upgrade", reqUpgradeType)
|
||||||
normalizeWebsocketHeaders(req.Header)
|
normalizeWebsocketHeaders(req.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,6 +732,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Via header(s)
|
||||||
|
req.Header.Add("Via", fmt.Sprintf("%d.%d Caddy", req.ProtoMajor, req.ProtoMinor))
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,13 +885,15 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const logMessage = "upstream roundtrip"
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil {
|
if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil {
|
||||||
c.Write(zap.Error(err))
|
c.Write(zap.Error(err))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil {
|
if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil {
|
||||||
c.Write(
|
c.Write(
|
||||||
zap.Object("headers", caddyhttp.LoggableHTTPHeader{
|
zap.Object("headers", caddyhttp.LoggableHTTPHeader{
|
||||||
Header: res.Header,
|
Header: res.Header,
|
||||||
@ -1024,6 +1029,14 @@ func (h *Handler) finalizeResponse(
|
|||||||
res.Header.Del(h)
|
res.Header.Del(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete our Server header and use Via instead (see #6275)
|
||||||
|
rw.Header().Del("Server")
|
||||||
|
var protoPrefix string
|
||||||
|
if !strings.HasPrefix(strings.ToUpper(res.Proto), "HTTP/") {
|
||||||
|
protoPrefix = res.Proto[:strings.Index(res.Proto, "/")+1]
|
||||||
|
}
|
||||||
|
rw.Header().Add("Via", fmt.Sprintf("%s%d.%d Caddy", protoPrefix, res.ProtoMajor, res.ProtoMinor))
|
||||||
|
|
||||||
// apply any response header operations
|
// apply any response header operations
|
||||||
if h.Headers != nil && h.Headers.Response != nil {
|
if h.Headers != nil && h.Headers.Response != nil {
|
||||||
if h.Headers.Response.Require == nil ||
|
if h.Headers.Response.Require == nil ||
|
||||||
|
@ -408,7 +408,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if fields == nil {
|
if fields == nil {
|
||||||
fields = errFields()
|
fields = errFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Write(fields...)
|
c.Write(fields...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,14 @@ type ACMEIssuer struct {
|
|||||||
// other than ACME transactions.
|
// other than ACME transactions.
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
// Optionally select an ACME profile to use for certificate
|
||||||
|
// orders. Must be a profile name offered by the ACME server,
|
||||||
|
// which are listed at its directory endpoint.
|
||||||
|
//
|
||||||
|
// EXPERIMENTAL: Subject to change.
|
||||||
|
// See https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/
|
||||||
|
Profile string `json:"profile,omitempty"`
|
||||||
|
|
||||||
// If you have an existing account with the ACME server, put
|
// If you have an existing account with the ACME server, put
|
||||||
// the private key here in PEM format. The ACME client will
|
// the private key here in PEM format. The ACME client will
|
||||||
// look up your account information with this key first before
|
// look up your account information with this key first before
|
||||||
@ -187,6 +195,7 @@ func (iss *ACMEIssuer) makeIssuerTemplate(ctx caddy.Context) (certmagic.ACMEIssu
|
|||||||
CA: iss.CA,
|
CA: iss.CA,
|
||||||
TestCA: iss.TestCA,
|
TestCA: iss.TestCA,
|
||||||
Email: iss.Email,
|
Email: iss.Email,
|
||||||
|
Profile: iss.Profile,
|
||||||
AccountKeyPEM: iss.AccountKey,
|
AccountKeyPEM: iss.AccountKey,
|
||||||
CertObtainTimeout: time.Duration(iss.ACMETimeout),
|
CertObtainTimeout: time.Duration(iss.ACMETimeout),
|
||||||
TrustedRoots: iss.rootPool,
|
TrustedRoots: iss.rootPool,
|
||||||
@ -353,6 +362,7 @@ func (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct a
|
|||||||
// dir <directory_url>
|
// dir <directory_url>
|
||||||
// test_dir <test_directory_url>
|
// test_dir <test_directory_url>
|
||||||
// email <email>
|
// email <email>
|
||||||
|
// profile <profile_name>
|
||||||
// timeout <duration>
|
// timeout <duration>
|
||||||
// disable_http_challenge
|
// disable_http_challenge
|
||||||
// disable_tlsalpn_challenge
|
// disable_tlsalpn_challenge
|
||||||
@ -415,6 +425,11 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "profile":
|
||||||
|
if !d.AllArgs(&iss.Profile) {
|
||||||
|
return d.ArgErr()
|
||||||
|
}
|
||||||
|
|
||||||
case "timeout":
|
case "timeout":
|
||||||
var timeoutStr string
|
var timeoutStr string
|
||||||
if !d.AllArgs(&timeoutStr) {
|
if !d.AllArgs(&timeoutStr) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package caddytls
|
package caddytls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -55,7 +56,7 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Match matches hello based on SNI.
|
// Match matches hello based on SNI.
|
||||||
func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
|
func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
|
||||||
repl := caddy.NewReplacer()
|
var repl *caddy.Replacer
|
||||||
// caddytls.TestServerNameMatcher calls this function without any context
|
// caddytls.TestServerNameMatcher calls this function without any context
|
||||||
if ctx := hello.Context(); ctx != nil {
|
if ctx := hello.Context(); ctx != nil {
|
||||||
// In some situations the existing context may have no replacer
|
// In some situations the existing context may have no replacer
|
||||||
@ -64,6 +65,10 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repl == nil {
|
||||||
|
repl = caddy.NewReplacer()
|
||||||
|
}
|
||||||
|
|
||||||
for _, name := range m {
|
for _, name := range m {
|
||||||
rs := repl.ReplaceAll(name, "")
|
rs := repl.ReplaceAll(name, "")
|
||||||
if certmagic.MatchWildcard(hello.ServerName, rs) {
|
if certmagic.MatchWildcard(hello.ServerName, rs) {
|
||||||
@ -224,15 +229,28 @@ func (MatchServerNameRE) CaddyModule() caddy.ModuleInfo {
|
|||||||
|
|
||||||
// Match matches hello based on SNI using a regular expression.
|
// Match matches hello based on SNI using a regular expression.
|
||||||
func (m MatchServerNameRE) Match(hello *tls.ClientHelloInfo) bool {
|
func (m MatchServerNameRE) Match(hello *tls.ClientHelloInfo) bool {
|
||||||
repl := caddy.NewReplacer()
|
// Note: caddytls.TestServerNameMatcher calls this function without any context
|
||||||
// caddytls.TestServerNameMatcher calls this function without any context
|
ctx := hello.Context()
|
||||||
if ctx := hello.Context(); ctx != nil {
|
if ctx == nil {
|
||||||
|
// layer4.Connection implements GetContext() to pass its context here,
|
||||||
|
// since hello.Context() returns nil
|
||||||
|
if mayHaveContext, ok := hello.Conn.(interface{ GetContext() context.Context }); ok {
|
||||||
|
ctx = mayHaveContext.GetContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var repl *caddy.Replacer
|
||||||
|
if ctx != nil {
|
||||||
// In some situations the existing context may have no replacer
|
// In some situations the existing context may have no replacer
|
||||||
if replAny := ctx.Value(caddy.ReplacerCtxKey); replAny != nil {
|
if replAny := ctx.Value(caddy.ReplacerCtxKey); replAny != nil {
|
||||||
repl = replAny.(*caddy.Replacer)
|
repl = replAny.(*caddy.Replacer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repl == nil {
|
||||||
|
repl = caddy.NewReplacer()
|
||||||
|
}
|
||||||
|
|
||||||
return m.MatchRegexp.Match(hello.ServerName, repl)
|
return m.MatchRegexp.Match(hello.ServerName, repl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
@ -146,51 +147,68 @@ func (fw FileWriter) WriterKey() string {
|
|||||||
|
|
||||||
// OpenWriter opens a new file writer.
|
// OpenWriter opens a new file writer.
|
||||||
func (fw FileWriter) OpenWriter() (io.WriteCloser, error) {
|
func (fw FileWriter) OpenWriter() (io.WriteCloser, error) {
|
||||||
if fw.Mode == 0 {
|
modeIfCreating := os.FileMode(fw.Mode)
|
||||||
fw.Mode = 0o600
|
if modeIfCreating == 0 {
|
||||||
|
modeIfCreating = 0o600
|
||||||
}
|
}
|
||||||
|
|
||||||
// roll log files by default
|
// roll log files as a sensible default to avoid disk space exhaustion
|
||||||
if fw.Roll == nil || *fw.Roll {
|
roll := fw.Roll == nil || *fw.Roll
|
||||||
if fw.RollSizeMB == 0 {
|
|
||||||
fw.RollSizeMB = 100
|
|
||||||
}
|
|
||||||
if fw.RollCompress == nil {
|
|
||||||
compress := true
|
|
||||||
fw.RollCompress = &compress
|
|
||||||
}
|
|
||||||
if fw.RollKeep == 0 {
|
|
||||||
fw.RollKeep = 10
|
|
||||||
}
|
|
||||||
if fw.RollKeepDays == 0 {
|
|
||||||
fw.RollKeepDays = 90
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the file if it does not exist with the right mode.
|
// create the file if it does not exist; create with the configured mode, or default
|
||||||
// lumberjack will reuse the file mode across log rotation.
|
// to restrictive if not set. (lumberjack will reuse the file mode across log rotation)
|
||||||
f_tmp, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode))
|
if err := os.MkdirAll(filepath.Dir(fw.Filename), 0o700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, modeIfCreating)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
info, err := file.Stat()
|
||||||
|
if roll {
|
||||||
|
file.Close() // lumberjack will reopen it on its own
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure already existing files have the right mode, since OpenFile will not set the mode in such case.
|
||||||
|
if configuredMode := os.FileMode(fw.Mode); configuredMode != 0 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to stat log file to see if we need to set permissions: %v", err)
|
||||||
}
|
}
|
||||||
f_tmp.Close()
|
// only chmod if the configured mode is different
|
||||||
// ensure already existing files have the right mode,
|
if info.Mode()&os.ModePerm != configuredMode&os.ModePerm {
|
||||||
// since OpenFile will not set the mode in such case.
|
if err = os.Chmod(fw.Filename, configuredMode); err != nil {
|
||||||
if err = os.Chmod(fw.Filename, os.FileMode(fw.Mode)); err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lumberjack.Logger{
|
|
||||||
Filename: fw.Filename,
|
|
||||||
MaxSize: fw.RollSizeMB,
|
|
||||||
MaxAge: fw.RollKeepDays,
|
|
||||||
MaxBackups: fw.RollKeep,
|
|
||||||
LocalTime: fw.RollLocalTime,
|
|
||||||
Compress: *fw.RollCompress,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise just open a regular file
|
// if not rolling, then the plain file handle is all we need
|
||||||
return os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode))
|
if !roll {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, return a rolling log
|
||||||
|
if fw.RollSizeMB == 0 {
|
||||||
|
fw.RollSizeMB = 100
|
||||||
|
}
|
||||||
|
if fw.RollCompress == nil {
|
||||||
|
compress := true
|
||||||
|
fw.RollCompress = &compress
|
||||||
|
}
|
||||||
|
if fw.RollKeep == 0 {
|
||||||
|
fw.RollKeep = 10
|
||||||
|
}
|
||||||
|
if fw.RollKeepDays == 0 {
|
||||||
|
fw.RollKeepDays = 90
|
||||||
|
}
|
||||||
|
return &lumberjack.Logger{
|
||||||
|
Filename: fw.Filename,
|
||||||
|
MaxSize: fw.RollSizeMB,
|
||||||
|
MaxAge: fw.RollKeepDays,
|
||||||
|
MaxBackups: fw.RollKeep,
|
||||||
|
LocalTime: fw.RollLocalTime,
|
||||||
|
Compress: *fw.RollCompress,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
|
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ func TestFileCreationMode(t *testing.T) {
|
|||||||
t.Fatalf("failed to create tempdir: %v", err)
|
t.Fatalf("failed to create tempdir: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
fpath := path.Join(dir, "test.log")
|
fpath := filepath.Join(dir, "test.log")
|
||||||
tt.fw.Filename = fpath
|
tt.fw.Filename = fpath
|
||||||
|
|
||||||
logger, err := tt.fw.OpenWriter()
|
logger, err := tt.fw.OpenWriter()
|
||||||
@ -92,7 +93,7 @@ func TestFileCreationMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if st.Mode() != tt.wantMode {
|
if st.Mode() != tt.wantMode {
|
||||||
t.Errorf("file mode is %v, want %v", st.Mode(), tt.wantMode)
|
t.Errorf("%s: file mode is %v, want %v", tt.name, st.Mode(), tt.wantMode)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user