mirror of
https://github.com/caddyserver/caddy.git
synced 2025-04-01 02:42:35 -05:00
Merge remote-tracking branch 'upstream/master' into otel-client
This commit is contained in:
commit
a2e4f98954
24 changed files with 305 additions and 98 deletions
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
@ -23,18 +23,18 @@ jobs:
|
|||
- mac
|
||||
- windows
|
||||
go:
|
||||
- '1.21'
|
||||
- '1.22'
|
||||
- '1.23'
|
||||
|
||||
include:
|
||||
# Set the minimum Go patch version for the given Go minor
|
||||
# Usable via ${{ matrix.GO_SEMVER }}
|
||||
- go: '1.21'
|
||||
GO_SEMVER: '~1.21.0'
|
||||
|
||||
- go: '1.22'
|
||||
GO_SEMVER: '~1.22.3'
|
||||
|
||||
- go: '1.23'
|
||||
GO_SEMVER: '~1.23.0'
|
||||
|
||||
# Set some variables per OS, usable via ${{ matrix.VAR }}
|
||||
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
|
||||
# CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
|
||||
|
@ -150,6 +150,7 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
- name: Run Tests
|
||||
run: |
|
||||
set +e
|
||||
mkdir -p ~/.ssh && echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_ecdsa && chmod og-rwx ~/.ssh/id_ecdsa
|
||||
|
||||
# short sha is enough?
|
||||
|
|
4
.github/workflows/cross-build.yml
vendored
4
.github/workflows/cross-build.yml
vendored
|
@ -28,6 +28,7 @@ jobs:
|
|||
- 'netbsd'
|
||||
go:
|
||||
- '1.22'
|
||||
- '1.23'
|
||||
|
||||
include:
|
||||
# Set the minimum Go patch version for the given Go minor
|
||||
|
@ -35,6 +36,9 @@ jobs:
|
|||
- go: '1.22'
|
||||
GO_SEMVER: '~1.22.3'
|
||||
|
||||
- go: '1.23'
|
||||
GO_SEMVER: '~1.23.0'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
|
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
@ -43,13 +43,13 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '~1.22.3'
|
||||
go-version: '~1.23'
|
||||
check-latest: true
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.55
|
||||
version: v1.60
|
||||
|
||||
# Windows times out frequently after about 5m50s if we don't set a longer timeout.
|
||||
args: --timeout 10m
|
||||
|
@ -63,5 +63,5 @@ jobs:
|
|||
- name: govulncheck
|
||||
uses: golang/govulncheck-action@v1
|
||||
with:
|
||||
go-version-input: '~1.22.3'
|
||||
go-version-input: '~1.23.0'
|
||||
check-latest: true
|
||||
|
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
@ -13,13 +13,13 @@ jobs:
|
|||
os:
|
||||
- ubuntu-latest
|
||||
go:
|
||||
- '1.22'
|
||||
- '1.23'
|
||||
|
||||
include:
|
||||
# Set the minimum Go patch version for the given Go minor
|
||||
# Usable via ${{ matrix.GO_SEMVER }}
|
||||
- go: '1.22'
|
||||
GO_SEMVER: '~1.22.3'
|
||||
- go: '1.23'
|
||||
GO_SEMVER: '~1.23.0'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
# https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
|
||||
|
|
|
@ -87,7 +87,7 @@ See [our online documentation](https://caddyserver.com/docs/install) for other i
|
|||
|
||||
Requirements:
|
||||
|
||||
- [Go 1.21 or newer](https://golang.org/dl/)
|
||||
- [Go 1.22.3 or newer](https://golang.org/dl/)
|
||||
|
||||
### For development
|
||||
|
||||
|
|
|
@ -415,7 +415,7 @@ func (d *Dispenser) EOFErr() error {
|
|||
|
||||
// Err generates a custom parse-time error with a message of msg.
|
||||
func (d *Dispenser) Err(msg string) error {
|
||||
return d.Errf(msg)
|
||||
return d.WrapErr(errors.New(msg))
|
||||
}
|
||||
|
||||
// Errf is like Err, but for formatted error messages
|
||||
|
|
|
@ -84,7 +84,6 @@ func TestLoadUnorderedJSON(t *testing.T) {
|
|||
"servers": {
|
||||
"s_server": {
|
||||
"listen": [
|
||||
":9443",
|
||||
":9080"
|
||||
],
|
||||
"routes": [
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
:8884
|
||||
|
||||
reverse_proxy 127.0.0.1:65535 {
|
||||
health_uri /health
|
||||
health_request_body "test body"
|
||||
}
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":8884"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "reverse_proxy",
|
||||
"health_checks": {
|
||||
"active": {
|
||||
"body": "test body",
|
||||
"uri": "/health"
|
||||
}
|
||||
},
|
||||
"upstreams": [
|
||||
{
|
||||
"dial": "127.0.0.1:65535"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
https://example.com {
|
||||
reverse_proxy http://localhost:54321 {
|
||||
transport http {
|
||||
local_address 192.168.0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":443"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
{
|
||||
"host": [
|
||||
"example.com"
|
||||
]
|
||||
}
|
||||
],
|
||||
"handle": [
|
||||
{
|
||||
"handler": "subroute",
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "reverse_proxy",
|
||||
"transport": {
|
||||
"local_address": "192.168.0.1",
|
||||
"protocol": "http"
|
||||
},
|
||||
"upstreams": [
|
||||
{
|
||||
"dial": "localhost:54321"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"terminal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
cmd/cobra.go
26
cmd/cobra.go
|
@ -8,9 +8,10 @@ import (
|
|||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "caddy",
|
||||
Long: `Caddy is an extensible server platform written in Go.
|
||||
var defaultFactory = newRootCommandFactory(func() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "caddy",
|
||||
Long: `Caddy is an extensible server platform written in Go.
|
||||
|
||||
At its core, Caddy merely manages configuration. Modules are plugged
|
||||
in statically at compile-time to provide useful functionality. Caddy's
|
||||
|
@ -91,23 +92,26 @@ package installers: https://caddyserver.com/docs/install
|
|||
Instructions for running Caddy in production are also available:
|
||||
https://caddyserver.com/docs/running
|
||||
`,
|
||||
Example: ` $ caddy run
|
||||
Example: ` $ caddy run
|
||||
$ caddy run --config caddy.json
|
||||
$ caddy reload --config caddy.json
|
||||
$ caddy stop`,
|
||||
|
||||
// kind of annoying to have all the help text printed out if
|
||||
// caddy has an error provisioning its modules, for instance...
|
||||
SilenceUsage: true,
|
||||
Version: onlyVersionText(),
|
||||
}
|
||||
// kind of annoying to have all the help text printed out if
|
||||
// caddy has an error provisioning its modules, for instance...
|
||||
SilenceUsage: true,
|
||||
Version: onlyVersionText(),
|
||||
}
|
||||
})
|
||||
|
||||
const fullDocsFooter = `Full documentation is available at:
|
||||
https://caddyserver.com/docs/command-line`
|
||||
|
||||
func init() {
|
||||
rootCmd.SetVersionTemplate("{{.Version}}\n")
|
||||
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
||||
defaultFactory.Use(func(rootCmd *cobra.Command) {
|
||||
rootCmd.SetVersionTemplate("{{.Version}}\n")
|
||||
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
||||
})
|
||||
}
|
||||
|
||||
func onlyVersionText() string {
|
||||
|
|
28
cmd/commandfactory.go
Normal file
28
cmd/commandfactory.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package caddycmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rootCommandFactory struct {
|
||||
constructor func() *cobra.Command
|
||||
options []func(*cobra.Command)
|
||||
}
|
||||
|
||||
func newRootCommandFactory(fn func() *cobra.Command) *rootCommandFactory {
|
||||
return &rootCommandFactory{
|
||||
constructor: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *rootCommandFactory) Use(fn func(cmd *cobra.Command)) {
|
||||
f.options = append(f.options, fn)
|
||||
}
|
||||
|
||||
func (f *rootCommandFactory) Build() *cobra.Command {
|
||||
o := f.constructor()
|
||||
for _, v := range f.options {
|
||||
v(o)
|
||||
}
|
||||
return o
|
||||
}
|
|
@ -74,6 +74,10 @@ func cmdStart(fl Flags) (int, error) {
|
|||
// sure by giving it some random bytes and having it echo
|
||||
// them back to us)
|
||||
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
|
||||
// we should be able to run caddy in relative paths
|
||||
if errors.Is(cmd.Err, exec.ErrDot) {
|
||||
cmd.Err = nil
|
||||
}
|
||||
if configFlag != "" {
|
||||
cmd.Args = append(cmd.Args, "--config", configFlag)
|
||||
}
|
||||
|
|
100
cmd/commands.go
100
cmd/commands.go
|
@ -438,43 +438,44 @@ EXPERIMENTAL: May be changed or removed.
|
|||
},
|
||||
})
|
||||
|
||||
RegisterCommand(Command{
|
||||
Name: "manpage",
|
||||
Usage: "--directory <path>",
|
||||
Short: "Generates the manual pages for Caddy commands",
|
||||
Long: `
|
||||
defaultFactory.Use(func(rootCmd *cobra.Command) {
|
||||
RegisterCommand(Command{
|
||||
Name: "manpage",
|
||||
Usage: "--directory <path>",
|
||||
Short: "Generates the manual pages for Caddy commands",
|
||||
Long: `
|
||||
Generates the manual pages for Caddy commands into the designated directory
|
||||
tagged into section 8 (System Administration).
|
||||
|
||||
The manual page files are generated into the directory specified by the
|
||||
argument of --directory. If the directory does not exist, it will be created.
|
||||
`,
|
||||
CobraFunc: func(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
|
||||
cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) {
|
||||
dir := strings.TrimSpace(fl.String("directory"))
|
||||
if dir == "" {
|
||||
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
|
||||
Title: "Caddy",
|
||||
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
|
||||
}, dir); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
return caddy.ExitCodeSuccess, nil
|
||||
})
|
||||
},
|
||||
})
|
||||
CobraFunc: func(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
|
||||
cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) {
|
||||
dir := strings.TrimSpace(fl.String("directory"))
|
||||
if dir == "" {
|
||||
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
|
||||
Title: "Caddy",
|
||||
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
|
||||
}, dir); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
return caddy.ExitCodeSuccess, nil
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Long: fmt.Sprintf(`To load completions:
|
||||
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Long: fmt.Sprintf(`To load completions:
|
||||
|
||||
Bash:
|
||||
|
||||
|
@ -513,23 +514,24 @@ argument of --directory. If the directory does not exist, it will be created.
|
|||
PS> %[1]s completion powershell > %[1]s.ps1
|
||||
# and source this file from your PowerShell profile.
|
||||
`, rootCmd.Root().Name()),
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
return cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
return cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||
default:
|
||||
return fmt.Errorf("unrecognized shell: %s", args[0])
|
||||
}
|
||||
},
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
return cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
return cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||
default:
|
||||
return fmt.Errorf("unrecognized shell: %s", args[0])
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -563,7 +565,9 @@ func RegisterCommand(cmd Command) {
|
|||
if !commandNameRegex.MatchString(cmd.Name) {
|
||||
panic("invalid command name")
|
||||
}
|
||||
rootCmd.AddCommand(caddyCmdToCobra(cmd))
|
||||
defaultFactory.Use(func(rootCmd *cobra.Command) {
|
||||
rootCmd.AddCommand(caddyCmdToCobra(cmd))
|
||||
})
|
||||
}
|
||||
|
||||
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
|
||||
|
|
|
@ -72,7 +72,7 @@ func Main() {
|
|||
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
|
||||
}
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
if err := defaultFactory.Build().Execute(); err != nil {
|
||||
var exitError *exitError
|
||||
if errors.As(err, &exitError) {
|
||||
os.Exit(exitError.ExitCode)
|
||||
|
|
4
go.mod
4
go.mod
|
@ -1,8 +1,8 @@
|
|||
module github.com/caddyserver/caddy/v2
|
||||
|
||||
go 1.21.0
|
||||
go 1.22.3
|
||||
|
||||
toolchain go1.22.2
|
||||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
|
|
|
@ -340,7 +340,7 @@ func (celTypeAdapter) NativeToValue(value any) ref.Val {
|
|||
case time.Time:
|
||||
return types.Timestamp{Time: v}
|
||||
case error:
|
||||
types.NewErr(v.Error())
|
||||
return types.WrapErr(v)
|
||||
}
|
||||
return types.DefaultTypeAdapter.NativeToValue(value)
|
||||
}
|
||||
|
@ -499,7 +499,7 @@ func CELMatcherRuntimeFunction(funcName string, fac CELMatcherFactory) functions
|
|||
return func(celReq, matcherData ref.Val) ref.Val {
|
||||
matcher, err := fac(matcherData)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
httpReq := celReq.Value().(celHTTPRequest)
|
||||
return types.Bool(matcher.Match(httpReq.Request))
|
||||
|
|
|
@ -128,3 +128,10 @@ var (
|
|||
_ caddyfile.Unmarshaler = (*StaticIPRange)(nil)
|
||||
_ IPRangeSource = (*StaticIPRange)(nil)
|
||||
)
|
||||
|
||||
// PrivateRangesCIDR returns a list of private CIDR range
|
||||
// strings, which can be used as a configuration shortcut.
|
||||
// Note: this function is used at least by mholt/caddy-l4.
|
||||
func PrivateRangesCIDR() []string {
|
||||
return internal.PrivateRangesCIDR()
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ type ListenerWrapper struct {
|
|||
Allow []string `json:"allow,omitempty"`
|
||||
allow []netip.Prefix
|
||||
|
||||
// Denby is an optional list of CIDR ranges to
|
||||
// Deny is an optional list of CIDR ranges to
|
||||
// deny PROXY headers from.
|
||||
Deny []string `json:"deny,omitempty"`
|
||||
deny []netip.Prefix
|
||||
|
|
|
@ -69,19 +69,20 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
|||
// lb_retry_match <request-matcher>
|
||||
//
|
||||
// # active health checking
|
||||
// health_uri <uri>
|
||||
// health_port <port>
|
||||
// health_interval <interval>
|
||||
// health_passes <num>
|
||||
// health_fails <num>
|
||||
// health_timeout <duration>
|
||||
// health_status <status>
|
||||
// health_body <regexp>
|
||||
// health_uri <uri>
|
||||
// health_port <port>
|
||||
// health_interval <interval>
|
||||
// health_passes <num>
|
||||
// health_fails <num>
|
||||
// health_timeout <duration>
|
||||
// health_status <status>
|
||||
// health_body <regexp>
|
||||
// health_method <value>
|
||||
// health_request_body <value>
|
||||
// health_follow_redirects
|
||||
// health_headers {
|
||||
// <field> [<values...>]
|
||||
// }
|
||||
// health_method <value>
|
||||
//
|
||||
// # passive health checking
|
||||
// fail_duration <duration>
|
||||
|
@ -425,6 +426,18 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
}
|
||||
h.HealthChecks.Active.Method = d.Val()
|
||||
|
||||
case "health_request_body":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
if h.HealthChecks == nil {
|
||||
h.HealthChecks = new(HealthChecks)
|
||||
}
|
||||
if h.HealthChecks.Active == nil {
|
||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||
}
|
||||
h.HealthChecks.Active.Body = d.Val()
|
||||
|
||||
case "health_interval":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
|
@ -1313,7 +1326,11 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
return d.Err("cannot specify \"tls_trust_pool\" twice in caddyfile")
|
||||
}
|
||||
h.TLS.CARaw = caddyconfig.JSONModuleObject(ca, "provider", modStem, nil)
|
||||
|
||||
case "local_address":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
h.LocalAddress = d.Val()
|
||||
default:
|
||||
return d.Errf("unrecognized subdirective %s", d.Val())
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"regexp"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
@ -97,6 +98,9 @@ type ActiveHealthChecks struct {
|
|||
// The HTTP method to use for health checks (default "GET").
|
||||
Method string `json:"method,omitempty"`
|
||||
|
||||
// The body to send with the health check request.
|
||||
Body string `json:"body,omitempty"`
|
||||
|
||||
// Whether to follow HTTP redirects in response to active health checks (default off).
|
||||
FollowRedirects bool `json:"follow_redirects,omitempty"`
|
||||
|
||||
|
@ -405,6 +409,16 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
|
|||
u.Path = h.HealthChecks.Active.Path
|
||||
}
|
||||
|
||||
// replacer used for both body and headers. Only globals (env vars, system info, etc.) are available
|
||||
repl := caddy.NewReplacer()
|
||||
|
||||
// if body is provided, create a reader for it, otherwise nil
|
||||
var requestBody io.Reader
|
||||
if h.HealthChecks.Active.Body != "" {
|
||||
// set body, using replacer
|
||||
requestBody = strings.NewReader(repl.ReplaceAll(h.HealthChecks.Active.Body, ""))
|
||||
}
|
||||
|
||||
// attach dialing information to this request, as well as context values that
|
||||
// may be expected by handlers of this request
|
||||
ctx := h.ctx.Context
|
||||
|
@ -418,15 +432,14 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
|
|||
ctx, span := tr.Start(ctx, "healthcheck")
|
||||
defer span.End()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), nil)
|
||||
req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), requestBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making request: %v", err)
|
||||
}
|
||||
ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
// set headers, using a replacer with only globals (env vars, system info, etc.)
|
||||
repl := caddy.NewReplacer()
|
||||
// set headers, using replacer
|
||||
repl.Set("http.reverse_proxy.active.target_upstream", networkAddr)
|
||||
for key, vals := range h.HealthChecks.Active.Headers {
|
||||
key = repl.ReplaceAll(key, "")
|
||||
|
|
|
@ -132,6 +132,10 @@ type HTTPTransport struct {
|
|||
// to change or removal while experimental.
|
||||
Versions []string `json:"versions,omitempty"`
|
||||
|
||||
// Specify the address to bind to when connecting to an upstream. In other words,
|
||||
// it is the address the upstream sees as the remote address.
|
||||
LocalAddress string `json:"local_address,omitempty"`
|
||||
|
||||
// The pre-configured underlying HTTP transport.
|
||||
Transport *http.Transport `json:"-"`
|
||||
|
||||
|
@ -185,6 +189,31 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
|||
FallbackDelay: time.Duration(h.FallbackDelay),
|
||||
}
|
||||
|
||||
if h.LocalAddress != "" {
|
||||
netaddr, err := caddy.ParseNetworkAddressWithDefaults(h.LocalAddress, "tcp", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if netaddr.PortRangeSize() > 1 {
|
||||
return nil, fmt.Errorf("local_address must be a single address, not a port range")
|
||||
}
|
||||
switch netaddr.Network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
dialer.LocalAddr, err = net.ResolveTCPAddr(netaddr.Network, netaddr.JoinHostPort(0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
dialer.LocalAddr, err = net.ResolveUnixAddr(netaddr.Network, netaddr.JoinHostPort(0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "udp", "udp4", "udp6":
|
||||
return nil, fmt.Errorf("local_address must be a TCP address, not a UDP address")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported network")
|
||||
}
|
||||
}
|
||||
if h.Resolver != nil {
|
||||
err := h.Resolver.ParseAddresses()
|
||||
if err != nil {
|
||||
|
|
|
@ -981,7 +981,7 @@ func (h *Handler) finalizeResponse(
|
|||
// we'll just log the error and abort the stream here and panic just as
|
||||
// the standard lib's proxy to propagate the stream error.
|
||||
// see issue https://github.com/caddyserver/caddy/issues/5951
|
||||
logger.Error("aborting with incomplete response", zap.Error(err))
|
||||
logger.Warn("aborting with incomplete response", zap.Error(err))
|
||||
// no extra logging from stdlib
|
||||
panic(http.ErrAbortHandler)
|
||||
}
|
||||
|
|
|
@ -105,8 +105,7 @@ func (e StaticError) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler
|
|||
}
|
||||
statusCode = intVal
|
||||
}
|
||||
|
||||
return Error(statusCode, fmt.Errorf("%s", e.Error))
|
||||
return Error(statusCode, fmt.Errorf("%s", repl.ReplaceKnown(e.Error, "")))
|
||||
}
|
||||
|
||||
// Interface guard
|
||||
|
|
|
@ -50,12 +50,13 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo {
|
|||
|
||||
// Match matches hello based on SNI.
|
||||
func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
|
||||
repl := caddy.NewReplacer()
|
||||
// caddytls.TestServerNameMatcher calls this function without any context
|
||||
var repl *caddy.Replacer
|
||||
if ctx := hello.Context(); ctx != nil {
|
||||
repl = ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
} else {
|
||||
repl = caddy.NewReplacer()
|
||||
// In some situations the existing context may have no replacer
|
||||
if replAny := ctx.Value(caddy.ReplacerCtxKey); replAny != nil {
|
||||
repl = replAny.(*caddy.Replacer)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range m {
|
||||
|
|
Loading…
Add table
Reference in a new issue