Compare commits

..

95 Commits

Author SHA1 Message Date
WeidiDeng d26cd24a11 fix tests 2024-10-17 16:36:45 +08:00
WeidiDeng 8c803b503c check if body is less than anticipated when there is no error reported 2024-10-17 16:22:17 +08:00
WeidiDeng 7d483ff3ef set temp file limiter for buffered body 2024-10-17 15:58:53 +08:00
WeidiDeng 02b8640e44 set status for the error response 2024-10-17 15:53:26 +08:00
WeidiDeng d395633749 fix caddyfile parsing for php_fastcgi 2024-10-17 15:50:24 +08:00
WeidiDeng 4715bbfd64 initial implementation of buffering for requests with unknown content length for fastcgi 2024-10-17 15:34:13 +08:00
WeidiDeng c6f2979986 caddyhttp: Close http3 server gracefully (#6213)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m34s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m49s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
Lint / govulncheck (push) Successful in 1m19s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* close http3 server gracefully

* update server field

* update from upstream

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-10-15 21:28:20 -04:00
Marten Seemann a211c656f1 chore: update quic-go to v0.48.0 (#6627)
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m38s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m22s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m49s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m24s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m4s
Lint / govulncheck (push) Successful in 1m17s
2024-10-15 09:38:10 -04:00
WeidiDeng 48ce47f1d4 reverseproxy: Use correct cases for websocket related headers (#6621)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m38s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m23s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m55s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m37s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m6s
Lint / govulncheck (push) Successful in 1m16s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-10-11 09:02:23 +00:00
Francis Lavoie ef4e0224a8 caddyfile: Fix comma edgecase in address parsing (#6616)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m58s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m25s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m59s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m39s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m37s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m36s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m27s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m41s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m43s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m37s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m42s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m41s
Lint / govulncheck (push) Successful in 1m39s
2024-10-10 20:26:59 +00:00
Mohammed Al Sahaf c8a76d003f docs: expand proxy protocol docs (#6620) 2024-10-10 16:21:26 -04:00
Mohammed Al Sahaf dd5decabe7 tests: fix caddyfile adapt warnings (#6619)
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-10-10 13:38:49 -06:00
Francis Lavoie d7564d632f caddytls: Drop rate_limit and burst, has been deprecated (#6611)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m59s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m23s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 3m26s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m44s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m52s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m46s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m48s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m47s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m57s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m44s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m50s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m41s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m41s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m39s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m39s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m43s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m40s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m38s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m40s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m36s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m47s
Lint / govulncheck (push) Successful in 1m45s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2024-10-07 17:39:47 -04:00
Matthew Holt 88fd5f3491 caddyhttp: Use internal issuer for IPs when no APs configured
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m33s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m22s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m51s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m22s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m3s
Lint / govulncheck (push) Successful in 1m14s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
This fixes a regression in 2.8 where IP addresses
would be considered qualifying for public certs
by auto-HTTPS. The default issuers do not issue
IP certs at this time, so if no APs are explicitly
configured, we assign them to the internal
issuer. We have to add a couple lines of code because
CertMagic can no longer consider IPs as not
qualifying for public certs, since there are public CAs
that issue IP certs. This edge case is specific to Caddy's
auto-HTTPS.

Without this patch, Caddy will try using Let's Encrypt
or ZeroSSL's ACME endpoint to get IP certs, neither
of which support that.
2024-10-04 10:23:30 -06:00
Matthew Holt 2ae58ac13e go.mod: Upgrade some dependencies
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m42s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m31s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m49s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m29s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m2s
Lint / govulncheck (push) Successful in 1m16s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2024-10-02 16:00:48 -06:00
Mohammed Al Sahaf 01be1b54a8 ci: install xcaddy to fix release flow (#6602)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m27s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m22s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 2m40s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m36s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m23s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m3s
Lint / govulncheck (push) Successful in 1m18s
2024-10-02 16:12:29 +00:00
Mohammed Al Sahaf 41f5dd56e1 metrics: scope metrics to active config, add optional per-host metrics (#6531)
* Add per host config

* Pass host label when option is enabled

* Test per host enabled

* metrics: scope metrics per loaded config

* doc and linter

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* inject the custom registry into the admin handler

Co-Authored-By: Dave Henderson <dhenderson@gmail.com>

* remove `TODO` comment

* fixes

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* refactor to delay metrics admin handler provision

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Hussam Almarzooq <me@hussam.io>
Co-authored-by: Dave Henderson <dhenderson@gmail.com>
2024-10-02 08:23:26 -06:00
Francis Lavoie 16724842d9 caddyhttp: Implement auto_https prefer_wildcard option (#6146)
* Allow specifying multiple `auto_https` options

* Implement `auto_https prefer_wildcard` option

* Adapt tests, add mock DNS module for config testing

* Rebase fix
2024-10-02 07:31:58 -06:00
Francis Lavoie 792f1c7ed7 caddyhttp: Escaping placeholders in CEL, add vars and vars_regexp (#6594)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m31s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m20s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 22s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m28s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m20s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
Lint / govulncheck (push) Successful in 1m19s
* caddyhttp: Escaping placeholders in CEL

* Simplify some of the test cases

* Implement vars and vars_regexp in CEL

* dupl lint is dumb

* Better consts for the placeholder CEL shortcut

* Bump CEL version, register a few extensions

* Refactor s390x test script for readability

* Add retries for s390x to smooth over flakiness

* Switch to `ph` for the CEL shortcut (match it in templates cause why not)
2024-10-02 06:34:04 -06:00
Matt Holt c8adb1b553 cmd: Better error handling when reloading (#6601)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m44s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 28s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m30s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 12m16s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m12s
Lint / govulncheck (push) Successful in 1m34s
* caddyhttp: Limit auto-HTTPS error logs to 100 domains

* Improve error message and increase error size limit
2024-10-01 20:31:30 -06:00
Matt Holt 9b4acc2449 caddytls: Support new tls.context module (#6369)
* caddytls: Support new tls.context module

This allows modules to manipulate the context passed into CertMagic's GetCertificate function, which can be useful for tracing/metrics, or other
custom logic.

This is experimental and may resolve the request of a sponsor, so we'll see how it goes!

* Derpy derp
2024-10-01 17:18:17 -06:00
WeidiDeng f3aead0e4d http: ReponseWriter prefer ReadFrom if available (#6565)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m38s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m25s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 22s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m40s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m37s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m39s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m27s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m29s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m25s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m1s
Lint / govulncheck (push) Successful in 1m15s
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-10-01 11:19:03 -06:00
Francis Lavoie 571f88d86f chore: Adjust incorrect reverse_proxy Caddyfile comment (#6598) 2024-10-01 10:56:30 -06:00
Aaron Paterson 0e829bc418 caddyhttp: Fix listener wrapper regression from #6573 (#6599)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m37s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m35s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 23s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m32s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m5s
Lint / govulncheck (push) Successful in 1m17s
2024-10-01 01:47:21 -04:00
Aaron Paterson 4b1a9b6cc1 core: Implement socket activation listeners (#6573)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m46s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m29s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 22s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m40s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m45s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m49s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m42s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m40s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m31s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m15s
Lint / govulncheck (push) Successful in 1m24s
* caddy adapt for listen_protocols

* adapt listen_socket

* allow multiple listen sockets for port ranges and readd socket fd listen logic

* readd logic to start servers according to listener protocols

* gofmt

* adapt caddytest

* gosec

* fmt and rename listen to listenWithSocket

* fmt and rename listen to listenWithSocket

* more consistent error msg

* non unix listenReusableWithSocketFile

* remove unused func

* doc comment typo

* nonosec

* commit

* doc comments

* more doc comments

* comment was misleading, cardinality did not change

* addressesWithProtocols

* update test

* fd/ and fdgram/

* rm addr

* actually write...

* i guess we doin' "skip": now

* wrong var in placeholder

* wrong var in placeholder II

* update param name in comment

* dont save nil file pointers

* windows

* key -> parsedKey

* osx

* multiple default_bind with protocols

* check for h1 and h2 listener netw
2024-09-30 10:55:03 -06:00
Mohammed Al Sahaf 1a345b4fa6 doc: remove docs of deprecated directives (#6566)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m52s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m20s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 24s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m28s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m33s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m28s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m0s
Lint / govulncheck (push) Successful in 1m20s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-09-29 09:12:52 +00:00
Alexander Stecher 22c98ea165 caddyhttp: Optimize logs using zap's WithLazy() (#6590)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m32s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m20s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 23s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m35s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m29s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m2s
Lint / govulncheck (push) Successful in 1m19s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* uses zap's .WithLazy with a cloned request

* fixes the cloning

* adds comment explaining why cloning is faster
2024-09-26 12:23:12 -06:00
Francis Lavoie 2faeac0a10 chore: Use slices package where possible (#6585)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m34s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m25s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 24s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m34s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m31s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m32s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m23s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m20s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m22s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m24s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m12s
Lint / govulncheck (push) Successful in 1m20s
* chore: Use slices package where possible

* More, mostly using ContainsFunc

* Even more slice operations
2024-09-25 14:30:56 -06:00
Francis Lavoie 9dda8fbf84 caddytls: Give a better error message when given encrypted private keys (#6591)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m42s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m32s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 23s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m39s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m39s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m42s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m47s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m40s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m29s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m27s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m31s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m29s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m21s
Lint / govulncheck (push) Successful in 1m24s
2024-09-25 06:00:48 -06:00
Marten Seemann ff67b97126 caddyhttp: enable qlog, controlled by QLOGDIR env (#6581)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m49s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m30s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 22s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m47s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m45s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m44s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m46s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m47s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m45s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m48s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m47s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m39s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m38s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m40s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m37s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m39s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m39s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m37s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m37s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m34s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m28s
Lint / govulncheck (push) Successful in 1m22s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2024-09-21 05:47:18 +02:00
Marten Seemann 5b44d6cea8 update quic-go to v0.47.0 (#6582)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2m14s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m33s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 27s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m39s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m46s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m54s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m38s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m42s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m36s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m41s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m41s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 1m26s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m28s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m30s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m25s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 1m41s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m35s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m38s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m33s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 1m40s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m34s
Lint / govulncheck (push) Successful in 1m38s
2024-09-20 17:00:13 -04:00
Mohammed Al Sahaf 6ab9fb6f74 ci: update the linter action version (#6575)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 3m22s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 2m39s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 32s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 2m52s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 2m52s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 2m54s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 2m50s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 2m51s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 13m24s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 10m16s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 2m50s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 2m54s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 3m5s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m42s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 2m39s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 2m40s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 2m49s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m45s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Failing after 13m28s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 2m39s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 2m46s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 2m39s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 2m49s
Lint / govulncheck (push) Successful in 2m17s
Lint / lint (ubuntu-latest, linux) (push) Failing after 10m22s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* ci: update the linter action version

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* exclude rule `G115`; disable deprecated linter

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-09-16 07:50:26 -06:00
Kévin Dunglas f4bf4e0097 perf: use zap's Check() to prevent useless allocs (#6560)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2m43s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 2m26s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 40s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 2m55s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 3m1s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 3m1s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 3m1s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 3m1s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 13m23s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m40s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 11m51s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 11m47s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 2m29s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m39s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Failing after 13m27s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Failing after 13m27s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 2m41s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 2m54s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 2m40s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Failing after 14m19s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Failing after 14m10s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Failing after 14m2s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Failing after 11m58s
Lint / lint (ubuntu-latest, linux) (push) Failing after 3m44s
Lint / govulncheck (push) Successful in 2m18s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* perf: use zap's Check() to prevent useless allocs

* fix

* fix

* fix

* fix

* restore previous replacer behavior

* fix linter
2024-09-13 11:16:37 -06:00
mister-turtle 21f9c20a04 rewrite: Avoid panic on bad arg count for uri (#6571)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 3m2s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 2m42s
Tests / test (s390x on IBM Z) (push) Has been skipped
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 2m57s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 3m33s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 2m55s
Tests / goreleaser-check (push) Failing after 13m11s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Failing after 13m10s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 3m39s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 2m45s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 3m4s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 2m47s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 3m2s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 2m51s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m35s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 2m42s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 2m40s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 2m37s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m29s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 2m49s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Failing after 10m5s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 2m31s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 2m45s
Lint / lint (ubuntu-latest, linux) (push) Failing after 4m35s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Failing after 11m18s
Lint / govulncheck (push) Failing after 11m22s
2024-09-13 03:22:03 -04:00
vnxme 2d12fb7ac6 caddytls: Add sni_regexp matcher (#6569)
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 2m28s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 28s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 2m59s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 12m25s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 3m25s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Failing after 14m5s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 14m0s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 13m57s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 13m52s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Failing after 13m48s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 3m4s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 2m53s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 3m9s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m42s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 2m52s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 2m36s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 2m49s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 2m53s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m42s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 2m49s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 2m39s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 2m40s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 2m40s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
2024-09-11 20:51:59 -06:00
Jesper Brix Rosenkilde 91e62db666 caddyhttp: Make route provisioning idempotent (#6558)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2m17s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 1m53s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 30s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 2m7s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 2m22s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 2m4s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 2m14s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 2m25s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 2m20s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 2m12s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 2m20s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 2m12s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 2m8s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m4s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 2m10s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 2m6s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 2m6s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m58s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m12s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 2m6s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 2m20s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 2m7s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 2m11s
Lint / lint (ubuntu-latest, linux) (push) Failing after 3m10s
Lint / govulncheck (push) Successful in 1m50s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
ref: https://github.com/caddyserver/caddy/issues/6551
2024-09-03 11:57:55 -06:00
Steffen Busch c050a37e1c reverse_proxy: add placeholder http.reverse_proxy.retries (#6553)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, aix) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, linux) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
* Add placeholder http.reverse_proxy.lb.retries

* Renamed placeholder to http.reverse_proxy.retries
2024-08-30 11:53:56 -06:00
lollipopkit🏳️‍⚧️ 5c47c2f147 fileserver: browse: Configurable default sort (#6502)
* fileserver: add `sort` options

* fix: test

* fileserver: check options in `Provison`

* fileserver: more obvious err alerts in sort options

* fileserver: move `sort` to `browse`

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-08-30 09:01:37 -06:00
Alexander Stecher ffd28be90a rewrite: Only serialize request if necessary (#6541)
* Prevents serializing the caddy request if log level is not debug.

* Extracts message to const.
2024-08-30 08:46:51 -06:00
WeidiDeng 141c785420 ci: prepare syso files for windows embedding in release (#6406)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, aix) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, linux) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Waiting to run
Cross-Build / build (~1.23.0, 1.23, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
* prepare syso files for windows embedding

* don't specify main so version info will be embedded correctly

---------

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-08-29 20:11:25 +00:00
Bas Westerbaan dcbf38d0b3 tls: use Go default kex for the moment that include PQC (#6542)
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2m58s
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Failing after 2m35s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 32s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 2m23s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 2m13s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 2m27s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 2m12s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 2m18s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 2m6s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 2m10s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 2m3s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 2m8s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 2m6s
Cross-Build / build (~1.23.0, 1.23, aix) (push) Successful in 2m3s
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Successful in 1m52s
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Successful in 2m2s
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Successful in 1m59s
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Successful in 1m54s
Cross-Build / build (~1.23.0, 1.23, linux) (push) Successful in 2m8s
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Successful in 1m57s
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Successful in 1m53s
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Successful in 1m54s
Cross-Build / build (~1.23.0, 1.23, windows) (push) Successful in 2m2s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m53s
Lint / govulncheck (push) Successful in 1m54s
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
By default Go 1.23 enables X25519Kyber768, a post-quantum key agreement
method that is enabled by default on Chrome. Go 1.23 does not expose
the CurveID, so we cannot add it by specifying it in CurvePreferences.
The reason is that X25519Kyber768 is a preliminary key agreement that
will be supplanted by X25519MLKEM768. For the moment there is value
in enabling it.

A consequence of this is that by default Caddy will enable support
for P-384 and P-521.

This PR also removes the special code to add support for X25519Kyber768
via the Cloudflare Go branch.

Cf #6540
2024-08-27 17:08:16 -06:00
Kévin Dunglas 2028da4e74 ci: build and test with Go 1.23 (#6526)
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, macos-14, 0, 1.23, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.23.0, ubuntu-latest, 0, 1.23, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.23.0, windows-latest, True, 1.23, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, aix) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, darwin) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, freebsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, illumos) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, linux) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, netbsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, openbsd) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, solaris) (push) Has been cancelled
Cross-Build / build (~1.23.0, 1.23, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
* chore: build and test with Go 1.23

* ci: bump golangci-lint to v1.60

* fix: make properly wrap errors

* ci: remove Go 1.21
2024-08-23 11:01:28 -06:00
Mohammed Al Sahaf 4ade967005 reverseproxy: allow user to define source address (#6504)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
* reverseproxy: allow user to define source address

Closes #6503

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* reverse_proxy: caddyfile support for local_address

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-08-22 19:52:05 +00:00
Mohammed Al Sahaf 8af646730b caddyhttp: run error (msg) through replacer (#6536)
* error: run `error` (msg) through replacer

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* fix integration test

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-08-22 11:32:44 -06:00
Cuckoo Chickoo 098897bdea chore: Fix a typo (#6534)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 3s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Failing after 1s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1s
Lint / govulncheck (push) Failing after 1s
Fixes Typo in Docs
2024-08-22 13:15:58 +03:00
a 8ccfedf2bb cmd: Use a factory to create the caddy root command (#6533)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-08-22 03:29:42 +00:00
Jens-Uwe Mager 2bb2ecc549 reverseproxy: Change errors writing the response to warning. (#6532)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 3s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Failing after 2s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2s
Lint / govulncheck (push) Failing after 2s
Most of the errors that can be seen here are write errors due to clients
aborting the request from their side. Often seen ones include:

	* writing: ... write: broken pipe
	* writing: ... connection timed out
	* writing: http2: stream closed
	* writing: timeout...
	* writing: h3 error...

Most of these errors are beyond of the control of caddy on the client side,
probably nothing can be done on the server side. It still warrants
researching when these errors occur very often, so a change in level from
error to warn is better here to not polute the logs with errors in the
normal case.
2024-08-21 11:39:20 -06:00
Jesper Brix Rosenkilde 54a0c8f948 reverseproxy: Active health checks request body option (#6520)
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 4s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 2s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 3s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Failing after 3s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2s
Lint / govulncheck (push) Failing after 1s
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* Add an option to specify the body used for active health checks

* Replacer on request body
2024-08-19 10:55:55 -06:00
Mohammed Al Sahaf 043fe41ab8 ci: don't exit early on error in remote CI machine (#6519)
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 2m51s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m50s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 32s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Failing after 21h15m13s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Failing after 3s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Failing after 1s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Failing after 2s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Failing after 2s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2s
Lint / govulncheck (push) Failing after 2s
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2024-08-18 12:54:12 +03:00
WeidiDeng 9ddb78fadc cmd: ignore exec.ErrDot when starting caddy in background (#6512)
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 2m2s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m43s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 23s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m49s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m59s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m52s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 2m2s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m51s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m49s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m45s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m42s
Lint / govulncheck (push) Failing after 8h23m28s
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-08-12 20:26:20 -06:00
vnxme 3a48b03369 Move PrivateRangesCIDR() back: add a pass-through function (#6514)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Failing after 2m49s
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Failing after 1m58s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Successful in 29s
Cross-Build / build (~1.22.3, 1.22, aix) (push) Successful in 1m52s
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Successful in 1m51s
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Successful in 1m47s
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, linux) (push) Successful in 1m51s
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Successful in 1m52s
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Successful in 1m50s
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Successful in 1m49s
Cross-Build / build (~1.22.3, 1.22, windows) (push) Successful in 1m50s
Lint / lint (ubuntu-latest, linux) (push) Successful in 3m2s
Lint / govulncheck (push) Successful in 1m46s
2024-08-12 05:47:05 -04:00
vnxme 7cf8376e63 matchers: fix a regression in #6480 (#6510)
The context may have no replacer
2024-08-12 10:01:09 +03:00
WeidiDeng 21af88fefc reverseproxy: Disable keep alive for h2c requests (#6343)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
2024-08-08 06:53:30 -06:00
WeidiDeng 52bad45181 go.mod: update update golang/x/net (#6500)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
2024-08-08 01:52:50 +00:00
Steffen Busch b85b6c6469 replacer: {file.*} global placeholder strips trailing newline (#6411)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
Co-authored-by: Kanashimia <chad@redpilled.dev>
2024-08-07 19:39:15 +00:00
vnxme 59cbb2c83a caddytls,caddyhttp: Placeholders for some TLS and HTTP matchers (#6480)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
* Runtime placeholders for caddytls matchers (1/3):

- remove IPs validation in UnmarshalCaddyfile

* Runtime placeholders for caddytls matchers (2/3):

- add placeholder replacement for IPs in Provision

* Runtime placeholders for caddytls matchers (3/3):

- add placeholder replacement for other strings

* Runtime placeholders for caddyhttp matchers (1/1):

- add placeholder replacement for IPs in Provision

* Runtime placeholders for caddyhttp/caddytls matchers:

- move PrivateRandesCIDR under internal
2024-08-07 11:02:23 -06:00
WeidiDeng a8b0dfa8da go.mod: update quic-go package (#6498)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, aix) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, linux) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Waiting to run
Cross-Build / build (~1.22.3, 1.22, windows) (push) Waiting to run
Lint / lint (macos-14, mac) (push) Waiting to run
Lint / lint (ubuntu-latest, linux) (push) Waiting to run
Lint / lint (windows-latest, windows) (push) Waiting to run
Lint / govulncheck (push) Waiting to run
2024-08-06 22:08:32 -06:00
lollipopkit🏳️‍⚧️ b198678174 browse: Customizable default sort options (#6468)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
* fileserver: add `sort` options

* fix: test

* fileserver: check options in `Provison`

* fileserver: more obvious err alerts in sort options
2024-08-05 08:27:45 -06:00
Prakhar Awasthi 840094ac65 proxyprotocol: Update WrapListener to use ConnPolicyFunc for PROXY protocol (#6485)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
* proxyprotocol : Update WrapListener to use ConnPolicyFunc for PROXY protocol support

* proxyprotocol : Updated dependency pires/go-proxyproto to pseudo latest version
2024-08-03 19:51:50 +03:00
WeidiDeng 976469ca0d encode: flush already compressed data from the encoder (#6471)
Tests / test (./cmd/caddy/caddy, ~1.21.0, macos-14, 0, 1.21, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.21.0, ubuntu-latest, 0, 1.21, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, macos-14, 0, 1.22, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.22.3, ubuntu-latest, 0, 1.22, linux) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.21.0, windows-latest, True, 1.21, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.22.3, windows-latest, True, 1.22, windows) (push) Has been cancelled
Tests / test (s390x on IBM Z) (push) Has been cancelled
Tests / goreleaser-check (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, aix) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, darwin) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, dragonfly) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, freebsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, illumos) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, linux) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, netbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, openbsd) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, solaris) (push) Has been cancelled
Cross-Build / build (~1.22.3, 1.22, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (ubuntu-latest, linux) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Lint / govulncheck (push) Has been cancelled
2024-07-27 17:46:56 -06:00
Mohammed Al Sahaf 9cc26ee7bf chore: update golangci config (#6479)
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-07-27 17:20:09 -06:00
vnxme 3579815a6c caddytls: Caddyfile support for TLS conn and cert sel policies (#6462)
* Caddyfile support for TLS custom certificate selection policy

* Caddyfile support for TLS connection policy
2024-07-24 11:01:06 -06:00
vnxme 61fe152c60 caddytls: Caddyfile support for TLS handshake matchers (#6461)
* Caddyfile support for TLS handshake matchers:

- caddytls.MatchLocalIP
- caddytls.MatchRemoteIP
- caddytls.MatchServerName

* Caddyfile support for TLS handshake matchers:

- fix imports order

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-07-24 09:26:09 -06:00
June 🍓🦴 3afa02ba4e ci: correct -tags nobadger on binary build (#6470) 2024-07-22 23:37:44 +00:00
Matthew Holt 806f5b1117 reverseproxy: Fix panic when using header-related flags (fix #6464) 2024-07-18 21:31:07 -06:00
schultzie b2492f8567 reverseproxy: add health_upstream subdirective (#6451)
* Add health_upstream

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>

* Add health_upstream to caddyfile parsing

* Add Active Upstream case for health checks

* Update ignore health port comment

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>

* Update Upstream json doc

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>

* Update modules/caddyhttp/reverseproxy/healthchecks.go

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

* Use error rather than log for health_port override

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>

* Add comment about port being ignore if using upstream

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>

---------

Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com>
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-07-15 17:00:12 +00:00
Jesper Brix Rosenkilde 07c863637d reverseproxy: Caddyfile support for health_method (#6454)
* Add Caddyfile support of setting active health check request method

* Add integration test for active health check request method
2024-07-12 17:01:58 -04:00
Jesper Brix Rosenkilde dc2a5d5c52 reverseproxy: Configurable method for active health checks (#6453)
* Add option to set which HTTP method to use for active health checks

* Default Method to GET if not set
2024-07-11 09:24:13 -04:00
schultzie 4943a4fc52 reverseproxy: Add placeholder for networkAddr in active health check headers (#6450)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-07-09 18:08:25 +00:00
Aziz Rmadi 630c62b313 fixed bug in resolving ip version in dynamic upstreams (#6448) 2024-07-09 03:06:30 -04:00
Francis Lavoie 9338741ca7 browse: Exclude symlink target size from total, show arrow on size (#6412)
* fileserver: Exclude symlink target size from total, show arrow on size

* Keep both totals

* Linter doesn't like my spelling :(

* Stop parallelizing tests for now

* Update modules/caddyhttp/fileserver/browse.html

* Minor renamings

---------

Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
2024-07-07 07:01:07 -06:00
Steffen Busch 88c7e53da5 browse: fix Content-Security-Policy warnings in Firefox (#6443)
* Remove 'strict-dynamic' + block-all-mixed-content

* CSP: remove 'unsafe-inline' from script-src
2024-07-07 06:56:47 -06:00
Steffen Busch 4ef360745d browse: add Content-Security-Policy w/ nonce (#6425)
* browse: add Content-Security-Policy w/ nonce

* Add backward-compat values to script-src

* Remove dummy "#" href from layout anchors
2024-07-06 10:46:08 -06:00
Francis Lavoie 7142d7c1e4 reverseproxy: Add placeholder for host in active health check headers (#6440) 2024-07-06 10:43:19 -06:00
Matt Holt c3fb5f4d3f caddyhttp: Reject 0-RTT early data in IP matchers and set Early-Data header when proxying (#6427)
* caddyhttp: Reject 0-RTT early data in IP matchers and set Early-Data header when proxying

See RFC 8470: https://httpwg.org/specs/rfc8470.html

Thanks to Michael Wedl (@MWedl)  at the University of Applied Sciences St. Poelten for reporting this.

* Don't return value for {remote} placeholder in early data

* Add Caddyfile support
2024-07-05 10:46:20 -06:00
Kévin Dunglas 15d986e1c9 encode: Don't compress already-compressed fonts (#6432)
* fix: don't compress already compressed fonts

* fix: remove WOFF
2024-07-04 14:57:13 -06:00
klaxa f350e001b6 reverseproxy: Only log host is up status on change (fixes #6415) (#6419) 2024-07-03 19:05:52 +00:00
Kévin Dunglas 0287009ee5 intercept: fix http.intercept.header.* placeholder (#6429) 2024-07-03 08:43:13 -06:00
Matthew Holt f8861ca16b reverseproxy: Wire up TLS options for H3 transport 2024-06-28 12:15:41 -06:00
Aziz Rmadi c2ccf8690f fileserver: Remove newline characters from precomputed etags (#6394)
* Removed newline characters from precomputed etags

* Update modules/caddyhttp/fileserver/staticfiles.go

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2024-06-19 13:27:10 +00:00
Matthew Holt 99dcdf7e42 caddyhttp: Convert IDNs to ASCII when provisioning Host matcher 2024-06-18 14:44:05 -06:00
Jason Yuan fab6375a8b reverseproxy: add Max-Age option to sticky cookie (#6398)
* reverseproxy: add Max-Age option to sticky cookie

* Update selectionpolicies.go

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

* Update selectionpolicies.go

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-06-15 07:50:31 -06:00
a aca4002fd8 caddyfile: Pass blocks to import for snippets (#6130)
* a

* a

* a

* a

* a

* a
2024-06-14 11:27:51 -06:00
Ririsoft 8e0d3e1ec5 logging: set file mode when the file already exist (#6391)
101d3e7 introduced a configuration option to set the log file mode.
This option was not taken into account if the file already exists,
making users having to delete their logs to have new logs created
with the right mode.
2024-06-12 15:17:46 -06:00
Omar Ramadan d85cc2ec10 logging: Customizable zap cores (#6381) 2024-06-10 09:03:24 -06:00
Will Norris 04fb9fe87f go.mod: update tscert package (#6384)
The latest tscert allows callers to provide a custom http.Transport for
calling Tailscale's local API.

Updates tailscale/caddy-tailscale#66
2024-06-10 07:28:30 -06:00
Ririsoft 0bc27e5fb1 logging: fix file mode configuration parsing (#6383)
Commit 101d3e7 introduced file mode setting,
but was missing a JSON Marshaller so that
CaddyFile can be converted to JSON safely.
2024-06-08 11:34:18 -06:00
Andreas Kohn 9be4f194e0 caddyhttp: Write header if needed in responseRecorder.WriteResponse (#6380) 2024-06-07 07:25:36 -06:00
Andreas Kohn a10117f8bd core: Split run into a public ProvisionContext and a private method (#6378)
* Split `run` into a public `BuildContext` and a private part

`BuildContext` can be used to set up a caddy context from a config, but not start any listeners
or active components: The returned context has the configured apps provisioned, but otherwise is
inert.

This is EXPERIMENTAL: Minimally it's missing documentation and the example for how this can be
used to run unit tests.

* Use the config from the context

The config passed into `BuildContext` can be nil, in which case `BuildContext` will just make one
up that works. In either case that will end up in the finished context.

* Rename `BuildContext` to `ProvisionContext` to better match the function

* Hide the `replaceAdminServer` parts

The admin server is a global thing, and in the envisioned use case for `ProvisionContext`
shouldn't actually exist. Hide this detail in a private `provisionContext` instead, and
only expose it publicly with `replaceAdminServer` set to `false`.

This should reduce foot-shooting potential further; in addition the documentation comment
now clearly spells out that the exact interface and implementation details of `ProvisionContext`
are experimental and subject to change.
2024-06-06 14:36:06 -06:00
Ririsoft 101d3e7407 logging: Customize log file permissions (#6314)
Adding a "mode" option to overwrite the default logfile permissions.
Default remains "0600" which is the one currently used by lumberjack.
2024-06-06 08:33:34 -06:00
Matthew Holt 3f1add6c9f events: Getters for event info (close #6377) 2024-06-06 07:11:28 -06:00
Mohammed Al Sahaf 5db2f81695 ci: add version key for .goreleaser.yml (#6376)
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2024-06-06 11:33:19 +03:00
Mohammed Al Sahaf 243351b2b1 cmd: remove zealous check of Caddyfile auto-detection (#6370)
* cmd: remove zealous check of Caddyfile auto-detection

* add test case

* remove redundant check, add comment

* one more case
2024-06-05 08:57:15 -06:00
Matt Holt 198f4385d2 caddyhttp: Add test cases to corpus (#6374)
* caddyhttp: Add test case to corpus

* One more test case

* Clean up stray comment

* More tests
2024-06-04 14:23:55 -06:00
Andreas Kohn e7ecc7ede2 Make it possible to configure the DisableStorageCheck setting for certmagic (#6368)
See discussion about this setting in https://github.com/caddyserver/certmagic/issues/201
2024-06-04 07:00:15 -06:00
142 changed files with 6149 additions and 1624 deletions
+47 -9
View File
@@ -23,18 +23,18 @@ jobs:
- mac - mac
- windows - windows
go: go:
- '1.21'
- '1.22' - '1.22'
- '1.23'
include: include:
# Set the minimum Go patch version for the given Go minor # Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }} # Usable via ${{ matrix.GO_SEMVER }}
- go: '1.21'
GO_SEMVER: '~1.21.0'
- go: '1.22' - go: '1.22'
GO_SEMVER: '~1.22.3' GO_SEMVER: '~1.22.3'
- go: '1.23'
GO_SEMVER: '~1.23.0'
# Set some variables per OS, usable via ${{ matrix.VAR }} # 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) # 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 # CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
@@ -99,7 +99,7 @@ jobs:
env: env:
CGO_ENABLED: 0 CGO_ENABLED: 0
run: | run: |
go build -tags nobdger -trimpath -ldflags="-w -s" -v go build -tags nobadger -trimpath -ldflags="-w -s" -v
- name: Smoke test Caddy - name: Smoke test Caddy
working-directory: ./cmd/caddy working-directory: ./cmd/caddy
@@ -150,18 +150,41 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run Tests - name: Run Tests
run: | run: |
set +e
mkdir -p ~/.ssh && echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_ecdsa && chmod og-rwx ~/.ssh/id_ecdsa mkdir -p ~/.ssh && echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_ecdsa && chmod og-rwx ~/.ssh/id_ecdsa
# short sha is enough? # short sha is enough?
short_sha=$(git rev-parse --short HEAD) short_sha=$(git rev-parse --short HEAD)
# To shorten the following lines
ssh_opts="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
ssh_host="$CI_USER@ci-s390x.caddyserver.com"
# The environment is fresh, so there's no point in keeping accepting and adding the key. # The environment is fresh, so there's no point in keeping accepting and adding the key.
rsync -arz -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --progress --delete --exclude '.git' . "$CI_USER"@ci-s390x.caddyserver.com:/var/tmp/"$short_sha" rsync -arz -e "ssh $ssh_opts" --progress --delete --exclude '.git' . "$ssh_host":/var/tmp/"$short_sha"
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t "$CI_USER"@ci-s390x.caddyserver.com "cd /var/tmp/$short_sha; go version; go env; printf "\n\n";CGO_ENABLED=0 go test -tags nobadger -v ./..." ssh $ssh_opts -t "$ssh_host" bash <<EOF
cd /var/tmp/$short_sha
go version
go env
printf "\n\n"
retries=3
exit_code=0
while ((retries > 0)); do
CGO_ENABLED=0 go test -p 1 -tags nobadger -v ./...
exit_code=$?
if ((exit_code == 0)); then
break
fi
echo "\n\nTest failed: \$exit_code, retrying..."
((retries--))
done
echo "Remote exit code: \$exit_code"
exit \$exit_code
EOF
test_result=$? test_result=$?
# There's no need leaving the files around # There's no need leaving the files around
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$CI_USER"@ci-s390x.caddyserver.com "rm -rf /var/tmp/'$short_sha'" ssh $ssh_opts "$ssh_host" "rm -rf /var/tmp/'$short_sha'"
echo "Test exit code: $test_result" echo "Test exit code: $test_result"
exit $test_result exit $test_result
@@ -175,7 +198,22 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: goreleaser/goreleaser-action@v5 - uses: goreleaser/goreleaser-action@v6
with: with:
version: latest version: latest
args: check args: check
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: "~1.23"
check-latest: true
- name: Install xcaddy
run: |
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy version
- uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: build --single-target --snapshot
env:
TAG: "master"
+4
View File
@@ -28,6 +28,7 @@ jobs:
- 'netbsd' - 'netbsd'
go: go:
- '1.22' - '1.22'
- '1.23'
include: include:
# Set the minimum Go patch version for the given Go minor # Set the minimum Go patch version for the given Go minor
@@ -35,6 +36,9 @@ jobs:
- go: '1.22' - go: '1.22'
GO_SEMVER: '~1.22.3' GO_SEMVER: '~1.22.3'
- go: '1.23'
GO_SEMVER: '~1.23.0'
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
steps: steps:
+3 -3
View File
@@ -43,13 +43,13 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '~1.22.3' go-version: '~1.23'
check-latest: true check-latest: true
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: v1.55 version: latest
# Windows times out frequently after about 5m50s if we don't set a longer timeout. # Windows times out frequently after about 5m50s if we don't set a longer timeout.
args: --timeout 10m args: --timeout 10m
@@ -63,5 +63,5 @@ jobs:
- name: govulncheck - name: govulncheck
uses: golang/govulncheck-action@v1 uses: golang/govulncheck-action@v1
with: with:
go-version-input: '~1.22.3' go-version-input: '~1.23.0'
check-latest: true check-latest: true
+8 -4
View File
@@ -13,13 +13,13 @@ jobs:
os: os:
- ubuntu-latest - ubuntu-latest
go: go:
- '1.22' - '1.23'
include: include:
# Set the minimum Go patch version for the given Go minor # Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }} # Usable via ${{ matrix.GO_SEMVER }}
- go: '1.22' - go: '1.23'
GO_SEMVER: '~1.22.3' GO_SEMVER: '~1.23.0'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
# https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233 # https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
@@ -104,9 +104,13 @@ jobs:
uses: anchore/sbom-action/download-syft@main uses: anchore/sbom-action/download-syft@main
- name: Syft version - name: Syft version
run: syft version run: syft version
- name: Install xcaddy
run: |
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy version
# GoReleaser will take care of publishing those artifacts into the release # GoReleaser will take care of publishing those artifacts into the release
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5 uses: goreleaser/goreleaser-action@v6
with: with:
version: latest version: latest
args: release --clean --timeout 60m args: release --clean --timeout 60m
+19 -5
View File
@@ -1,7 +1,9 @@
linters-settings: linters-settings:
errcheck: errcheck:
ignore: fmt:.*,go.uber.org/zap/zapcore:^Add.* exclude-functions:
ignoretests: true - fmt.*
- (go.uber.org/zap/zapcore.ObjectEncoder).AddObject
- (go.uber.org/zap/zapcore.ObjectEncoder).AddArray
gci: gci:
sections: sections:
- standard # Standard section: captures all standard packages. - standard # Standard section: captures all standard packages.
@@ -33,7 +35,6 @@ linters:
- errcheck - errcheck
- errname - errname
- exhaustive - exhaustive
- exportloopref
- gci - gci
- gofmt - gofmt
- goimports - goimports
@@ -130,18 +131,22 @@ linters:
run: run:
# default concurrency is a available CPU number. # default concurrency is a available CPU number.
# concurrency: 4 # explicitly omit this value to fully utilize available resources. # concurrency: 4 # explicitly omit this value to fully utilize available resources.
deadline: 5m timeout: 5m
issues-exit-code: 1 issues-exit-code: 1
tests: false tests: false
# output configuration options # output configuration options
output: output:
format: 'colored-line-number' formats:
- format: 'colored-line-number'
print-issued-lines: true print-issued-lines: true
print-linter-name: true print-linter-name: true
issues: issues:
exclude-rules: exclude-rules:
- text: 'G115' # TODO: Either we should fix the issues or nuke the linter if it's bad
linters:
- gosec
# we aren't calling unknown URL # we aren't calling unknown URL
- text: 'G107' # G107: Url provided to HTTP request as taint input - text: 'G107' # G107: Url provided to HTTP request as taint input
linters: linters:
@@ -166,3 +171,12 @@ issues:
- path: modules/logging/filters.go - path: modules/logging/filters.go
linters: linters:
- dupl - dupl
- path: modules/caddyhttp/matchers.go
linters:
- dupl
- path: modules/caddyhttp/vars.go
linters:
- dupl
- path: _test\.go
linters:
- errcheck
+5 -1
View File
@@ -1,3 +1,5 @@
version: 2
before: before:
hooks: hooks:
# The build is done in this particular way to build Caddy in a designated directory named in .gitignore. # The build is done in this particular way to build Caddy in a designated directory named in .gitignore.
@@ -10,6 +12,9 @@ before:
- mkdir -p caddy-build - mkdir -p caddy-build
- cp cmd/caddy/main.go caddy-build/main.go - cp cmd/caddy/main.go caddy-build/main.go
- /bin/sh -c 'cd ./caddy-build && go mod init caddy' - /bin/sh -c 'cd ./caddy-build && go mod init caddy'
# prepare syso files for windows embedding
- /bin/sh -c 'for a in amd64 arm arm64; do XCADDY_SKIP_BUILD=1 GOOS=windows GOARCH=$a xcaddy build {{.Env.TAG}}; done'
- /bin/sh -c 'mv /tmp/buildenv_*/*.syso caddy-build'
# GoReleaser doesn't seem to offer {{.Tag}} at this stage, so we have to embed it into the env # GoReleaser doesn't seem to offer {{.Tag}} at this stage, so we have to embed it into the env
# so we run: TAG=$(git describe --abbrev=0) goreleaser release --rm-dist --skip-publish --skip-validate # so we run: TAG=$(git describe --abbrev=0) goreleaser release --rm-dist --skip-publish --skip-validate
- go mod edit -require=github.com/caddyserver/caddy/v2@{{.Env.TAG}} ./caddy-build/go.mod - go mod edit -require=github.com/caddyserver/caddy/v2@{{.Env.TAG}} ./caddy-build/go.mod
@@ -29,7 +34,6 @@ builds:
- env: - env:
- CGO_ENABLED=0 - CGO_ENABLED=0
- GO111MODULE=on - GO111MODULE=on
main: main.go
dir: ./caddy-build dir: ./caddy-build
binary: caddy binary: caddy
goos: goos:
+1 -1
View File
@@ -87,7 +87,7 @@ See [our online documentation](https://caddyserver.com/docs/install) for other i
Requirements: Requirements:
- [Go 1.21 or newer](https://golang.org/dl/) - [Go 1.22.3 or newer](https://golang.org/dl/)
### For development ### For development
+13 -21
View File
@@ -34,6 +34,7 @@ import (
"os" "os"
"path" "path"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -213,7 +214,7 @@ type AdminPermissions struct {
// newAdminHandler reads admin's config and returns an http.Handler suitable // newAdminHandler reads admin's config and returns an http.Handler suitable
// for use in an admin endpoint server, which will be listening on listenAddr. // for use in an admin endpoint server, which will be listening on listenAddr.
func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool) adminHandler { func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, ctx Context) adminHandler {
muxWrap := adminHandler{mux: http.NewServeMux()} muxWrap := adminHandler{mux: http.NewServeMux()}
// secure the local or remote endpoint respectively // secure the local or remote endpoint respectively
@@ -269,7 +270,6 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool) admi
// register third-party module endpoints // register third-party module endpoints
for _, m := range GetModules("admin.api") { for _, m := range GetModules("admin.api") {
router := m.New().(AdminRouter) router := m.New().(AdminRouter)
handlerLabel := m.ID.Name()
for _, route := range router.Routes() { for _, route := range router.Routes() {
addRoute(route.Pattern, handlerLabel, route.Handler) addRoute(route.Pattern, handlerLabel, route.Handler)
} }
@@ -312,7 +312,7 @@ func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL {
} }
if admin.Origins == nil { if admin.Origins == nil {
if addr.isLoopback() { if addr.isLoopback() {
if addr.IsUnixNetwork() { if addr.IsUnixNetwork() || addr.IsFdNetwork() {
// RFC 2616, Section 14.26: // RFC 2616, Section 14.26:
// "A client MUST include a Host header field in all HTTP/1.1 request // "A client MUST include a Host header field in all HTTP/1.1 request
// messages. If the requested URI does not include an Internet host // messages. If the requested URI does not include an Internet host
@@ -350,7 +350,7 @@ func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL {
uniqueOrigins[net.JoinHostPort("127.0.0.1", addr.port())] = struct{}{} uniqueOrigins[net.JoinHostPort("127.0.0.1", addr.port())] = struct{}{}
} }
} }
if !addr.IsUnixNetwork() { if !addr.IsUnixNetwork() && !addr.IsFdNetwork() {
uniqueOrigins[addr.JoinHostPort(0)] = struct{}{} uniqueOrigins[addr.JoinHostPort(0)] = struct{}{}
} }
} }
@@ -381,7 +381,9 @@ func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL {
// for the admin endpoint exists in cfg, a default one is used, so // for the admin endpoint exists in cfg, a default one is used, so
// that there is always an admin server (unless it is explicitly // that there is always an admin server (unless it is explicitly
// configured to be disabled). // configured to be disabled).
func replaceLocalAdminServer(cfg *Config) error { // Critically note that some elements and functionality of the context
// may not be ready, e.g. storage. Tread carefully.
func replaceLocalAdminServer(cfg *Config, ctx Context) error {
// always* be sure to close down the old admin endpoint // always* be sure to close down the old admin endpoint
// as gracefully as possible, even if the new one is // as gracefully as possible, even if the new one is
// disabled -- careful to use reference to the current // disabled -- careful to use reference to the current
@@ -423,7 +425,7 @@ func replaceLocalAdminServer(cfg *Config) error {
return err return err
} }
handler := cfg.Admin.newAdminHandler(addr, false) handler := cfg.Admin.newAdminHandler(addr, false, ctx)
ln, err := addr.Listen(context.TODO(), 0, net.ListenConfig{}) ln, err := addr.Listen(context.TODO(), 0, net.ListenConfig{})
if err != nil { if err != nil {
@@ -544,7 +546,7 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
// make the HTTP handler but disable Host/Origin enforcement // make the HTTP handler but disable Host/Origin enforcement
// because we are using TLS authentication instead // because we are using TLS authentication instead
handler := cfg.Admin.newAdminHandler(addr, true) handler := cfg.Admin.newAdminHandler(addr, true, ctx)
// create client certificate pool for TLS mutual auth, and extract public keys // create client certificate pool for TLS mutual auth, and extract public keys
// so that we can enforce access controls at the application layer // so that we can enforce access controls at the application layer
@@ -675,13 +677,7 @@ func (remote RemoteAdmin) enforceAccessControls(r *http.Request) error {
// key recognized; make sure its HTTP request is permitted // key recognized; make sure its HTTP request is permitted
for _, accessPerm := range adminAccess.Permissions { for _, accessPerm := range adminAccess.Permissions {
// verify method // verify method
methodFound := accessPerm.Methods == nil methodFound := accessPerm.Methods == nil || slices.Contains(accessPerm.Methods, r.Method)
for _, method := range accessPerm.Methods {
if method == r.Method {
methodFound = true
break
}
}
if !methodFound { if !methodFound {
return APIError{ return APIError{
HTTPStatus: http.StatusForbidden, HTTPStatus: http.StatusForbidden,
@@ -877,13 +873,9 @@ func (h adminHandler) handleError(w http.ResponseWriter, r *http.Request, err er
// a trustworthy/expected value. This helps to mitigate DNS // a trustworthy/expected value. This helps to mitigate DNS
// rebinding attacks. // rebinding attacks.
func (h adminHandler) checkHost(r *http.Request) error { func (h adminHandler) checkHost(r *http.Request) error {
var allowed bool allowed := slices.ContainsFunc(h.allowedOrigins, func(u *url.URL) bool {
for _, allowedOrigin := range h.allowedOrigins { return r.Host == u.Host
if r.Host == allowedOrigin.Host { })
allowed = true
break
}
}
if !allowed { if !allowed {
return APIError{ return APIError{
HTTPStatus: http.StatusForbidden, HTTPStatus: http.StatusForbidden,
+68 -44
View File
@@ -397,6 +397,62 @@ func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error {
// will want to use Run instead, which also // will want to use Run instead, which also
// updates the config's raw state. // updates the config's raw state.
func run(newCfg *Config, start bool) (Context, error) { func run(newCfg *Config, start bool) (Context, error) {
ctx, err := provisionContext(newCfg, start)
if err != nil {
globalMetrics.configSuccess.Set(0)
return ctx, err
}
if !start {
return ctx, nil
}
// Provision any admin routers which may need to access
// some of the other apps at runtime
err = ctx.cfg.Admin.provisionAdminRouters(ctx)
if err != nil {
globalMetrics.configSuccess.Set(0)
return ctx, err
}
// Start
err = func() error {
started := make([]string, 0, len(ctx.cfg.apps))
for name, a := range ctx.cfg.apps {
err := a.Start()
if err != nil {
// an app failed to start, so we need to stop
// all other apps that were already started
for _, otherAppName := range started {
err2 := ctx.cfg.apps[otherAppName].Stop()
if err2 != nil {
err = fmt.Errorf("%v; additionally, aborting app %s: %v",
err, otherAppName, err2)
}
}
return fmt.Errorf("%s app module: start: %v", name, err)
}
started = append(started, name)
}
return nil
}()
if err != nil {
globalMetrics.configSuccess.Set(0)
return ctx, err
}
globalMetrics.configSuccess.Set(1)
globalMetrics.configSuccessTime.SetToCurrentTime()
// now that the user's config is running, finish setting up anything else,
// such as remote admin endpoint, config loader, etc.
return ctx, finishSettingUp(ctx, ctx.cfg)
}
// provisionContext creates a new context from the given configuration and provisions
// storage and apps.
// If `newCfg` is nil a new empty configuration will be created.
// If `replaceAdminServer` is true any currently active admin server will be replaced
// with a new admin server based on the provided configuration.
func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error) {
// because we will need to roll back any state // because we will need to roll back any state
// modifications if this function errors, we // modifications if this function errors, we
// keep a single error value and scope all // keep a single error value and scope all
@@ -419,6 +475,7 @@ func run(newCfg *Config, start bool) (Context, error) {
ctx, cancel := NewContext(Context{Context: context.Background(), cfg: newCfg}) ctx, cancel := NewContext(Context{Context: context.Background(), cfg: newCfg})
defer func() { defer func() {
if err != nil { if err != nil {
globalMetrics.configSuccess.Set(0)
// if there were any errors during startup, // if there were any errors during startup,
// we should cancel the new context we created // we should cancel the new context we created
// since the associated config won't be used; // since the associated config won't be used;
@@ -444,8 +501,8 @@ func run(newCfg *Config, start bool) (Context, error) {
} }
// start the admin endpoint (and stop any prior one) // start the admin endpoint (and stop any prior one)
if start { if replaceAdminServer {
err = replaceLocalAdminServer(newCfg) err = replaceLocalAdminServer(newCfg, ctx)
if err != nil { if err != nil {
return ctx, fmt.Errorf("starting caddy administration endpoint: %v", err) return ctx, fmt.Errorf("starting caddy administration endpoint: %v", err)
} }
@@ -491,49 +548,16 @@ func run(newCfg *Config, start bool) (Context, error) {
} }
return nil return nil
}() }()
if err != nil { return ctx, err
return ctx, err }
}
if !start { // ProvisionContext creates a new context from the configuration and provisions storage
return ctx, nil // and app modules.
} // The function is intended for testing and advanced use cases only, typically `Run` should be
// use to ensure a fully functional caddy instance.
// Provision any admin routers which may need to access // EXPERIMENTAL: While this is public the interface and implementation details of this function may change.
// some of the other apps at runtime func ProvisionContext(newCfg *Config) (Context, error) {
err = newCfg.Admin.provisionAdminRouters(ctx) return provisionContext(newCfg, false)
if err != nil {
return ctx, err
}
// Start
err = func() error {
started := make([]string, 0, len(newCfg.apps))
for name, a := range newCfg.apps {
err := a.Start()
if err != nil {
// an app failed to start, so we need to stop
// all other apps that were already started
for _, otherAppName := range started {
err2 := newCfg.apps[otherAppName].Stop()
if err2 != nil {
err = fmt.Errorf("%v; additionally, aborting app %s: %v",
err, otherAppName, err2)
}
}
return fmt.Errorf("%s app module: start: %v", name, err)
}
started = append(started, name)
}
return nil
}()
if err != nil {
return ctx, err
}
// now that the user's config is running, finish setting up anything else,
// such as remote admin endpoint, config loader, etc.
return ctx, finishSettingUp(ctx, newCfg)
} }
// finishSettingUp should be run after all apps have successfully started. // finishSettingUp should be run after all apps have successfully started.
+1 -1
View File
@@ -415,7 +415,7 @@ func (d *Dispenser) EOFErr() error {
// Err generates a custom parse-time error with a message of msg. // Err generates a custom parse-time error with a message of msg.
func (d *Dispenser) Err(msg string) error { 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 // Errf is like Err, but for formatted error messages
+2 -6
View File
@@ -16,6 +16,7 @@ package caddyfile
import ( import (
"fmt" "fmt"
"slices"
) )
type adjacency map[string][]string type adjacency map[string][]string
@@ -91,12 +92,7 @@ func (i *importGraph) areConnected(from, to string) bool {
if !ok { if !ok {
return false return false
} }
for _, v := range al { return slices.Contains(al, to)
if v == to {
return true
}
}
return false
} }
func (i *importGraph) willCycle(from, to string) bool { func (i *importGraph) willCycle(from, to string) bool {
+72 -4
View File
@@ -264,8 +264,13 @@ func (p *parser) addresses() error {
return p.Errf("Site addresses cannot contain a comma ',': '%s' - put a space after the comma to separate site addresses", value) return p.Errf("Site addresses cannot contain a comma ',': '%s' - put a space after the comma to separate site addresses", value)
} }
token.Text = value // After the above, a comma surrounded by spaces would result
p.block.Keys = append(p.block.Keys, token) // in an empty token which we should ignore
if value != "" {
// Add the token as a site address
token.Text = value
p.block.Keys = append(p.block.Keys, token)
}
} }
// Advance token and possibly break out of loop or return error // Advance token and possibly break out of loop or return error
@@ -364,9 +369,45 @@ func (p *parser) doImport(nesting int) error {
// set up a replacer for non-variadic args replacement // set up a replacer for non-variadic args replacement
repl := makeArgsReplacer(args) repl := makeArgsReplacer(args)
// grab all the tokens (if it exists) from within a block that follows the import
var blockTokens []Token
for currentNesting := p.Nesting(); p.NextBlock(currentNesting); {
blockTokens = append(blockTokens, p.Token())
}
// initialize with size 1
blockMapping := make(map[string][]Token, 1)
if len(blockTokens) > 0 {
// use such tokens to create a new dispenser, and then use it to parse each block
bd := NewDispenser(blockTokens)
for bd.Next() {
// see if we can grab a key
var currentMappingKey string
if bd.Val() == "{" {
return p.Err("anonymous blocks are not supported")
}
currentMappingKey = bd.Val()
currentMappingTokens := []Token{}
// read all args until end of line / {
if bd.NextArg() {
currentMappingTokens = append(currentMappingTokens, bd.Token())
for bd.NextArg() {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
// TODO(elee1766): we don't enter another mapping here because it's annoying to extract the { and } properly.
// maybe someone can do that in the future
} else {
// attempt to enter a block and add tokens to the currentMappingTokens
for mappingNesting := bd.Nesting(); bd.NextBlock(mappingNesting); {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
}
blockMapping[currentMappingKey] = currentMappingTokens
}
}
// splice out the import directive and its arguments // splice out the import directive and its arguments
// (2 tokens, plus the length of args) // (2 tokens, plus the length of args)
tokensBefore := p.tokens[:p.cursor-1-len(args)] tokensBefore := p.tokens[:p.cursor-1-len(args)-len(blockTokens)]
tokensAfter := p.tokens[p.cursor+1:] tokensAfter := p.tokens[p.cursor+1:]
var importedTokens []Token var importedTokens []Token
var nodes []string var nodes []string
@@ -495,6 +536,33 @@ func (p *parser) doImport(nesting int) error {
maybeSnippet = false maybeSnippet = false
} }
} }
// if it is {block}, we substitute with all tokens in the block
// if it is {blocks.*}, we substitute with the tokens in the mapping for the *
var skip bool
var tokensToAdd []Token
switch {
case token.Text == "{block}":
tokensToAdd = blockTokens
case strings.HasPrefix(token.Text, "{blocks.") && strings.HasSuffix(token.Text, "}"):
// {blocks.foo.bar} will be extracted to key `foo.bar`
blockKey := strings.TrimPrefix(strings.TrimSuffix(token.Text, "}"), "{blocks.")
val, ok := blockMapping[blockKey]
if ok {
tokensToAdd = val
}
default:
skip = true
}
if !skip {
if len(tokensToAdd) == 0 {
// if there is no content in the snippet block, don't do any replacement
// this allows snippets which contained {block}/{block.*} before this change to continue functioning as normal
tokensCopy = append(tokensCopy, token)
} else {
tokensCopy = append(tokensCopy, tokensToAdd...)
}
continue
}
if maybeSnippet { if maybeSnippet {
tokensCopy = append(tokensCopy, token) tokensCopy = append(tokensCopy, token)
@@ -516,7 +584,7 @@ func (p *parser) doImport(nesting int) error {
// splice the imported tokens in the place of the import statement // splice the imported tokens in the place of the import statement
// and rewind cursor so Next() will land on first imported token // and rewind cursor so Next() will land on first imported token
p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...) p.tokens = append(tokensBefore, append(tokensCopy, tokensAfter...)...)
p.cursor -= len(args) + 1 p.cursor -= len(args) + len(blockTokens) + 1
return nil return nil
} }
+6 -2
View File
@@ -555,6 +555,10 @@ func TestParseAll(t *testing.T) {
{"localhost:1234", "http://host2"}, {"localhost:1234", "http://host2"},
}}, }},
{`foo.example.com , example.com`, false, [][]string{
{"foo.example.com", "example.com"},
}},
{`localhost:1234, http://host2,`, true, [][]string{}}, {`localhost:1234, http://host2,`, true, [][]string{}},
{`http://host1.com, http://host2.com { {`http://host1.com, http://host2.com {
@@ -614,8 +618,8 @@ func TestParseAll(t *testing.T) {
} }
for j, block := range blocks { for j, block := range blocks {
if len(block.Keys) != len(test.keys[j]) { if len(block.Keys) != len(test.keys[j]) {
t.Errorf("Test %d: Expected %d keys in block %d, got %d", t.Errorf("Test %d: Expected %d keys in block %d, got %d: %v",
i, len(test.keys[j]), j, len(block.Keys)) i, len(test.keys[j]), j, len(block.Keys), block.Keys)
continue continue
} }
for k, addr := range block.GetKeysText() { for k, addr := range block.GetKeysText() {
+189 -117
View File
@@ -77,10 +77,15 @@ import (
// repetition may be undesirable, so call consolidateAddrMappings() to map // repetition may be undesirable, so call consolidateAddrMappings() to map
// multiple addresses to the same lists of server blocks (a many:many mapping). // multiple addresses to the same lists of server blocks (a many:many mapping).
// (Doing this is essentially a map-reduce technique.) // (Doing this is essentially a map-reduce technique.)
func (st *ServerType) mapAddressToServerBlocks(originalServerBlocks []serverBlock, func (st *ServerType) mapAddressToProtocolToServerBlocks(originalServerBlocks []serverBlock,
options map[string]any, options map[string]any,
) (map[string][]serverBlock, error) { ) (map[string]map[string][]serverBlock, error) {
sbmap := make(map[string][]serverBlock) addrToProtocolToServerBlocks := map[string]map[string][]serverBlock{}
type keyWithParsedKey struct {
key caddyfile.Token
parsedKey Address
}
for i, sblock := range originalServerBlocks { for i, sblock := range originalServerBlocks {
// within a server block, we need to map all the listener addresses // within a server block, we need to map all the listener addresses
@@ -88,27 +93,48 @@ func (st *ServerType) mapAddressToServerBlocks(originalServerBlocks []serverBloc
// will be served by them; this has the effect of treating each // will be served by them; this has the effect of treating each
// key of a server block as its own, but without having to repeat its // key of a server block as its own, but without having to repeat its
// contents in cases where multiple keys really can be served together // contents in cases where multiple keys really can be served together
addrToKeys := make(map[string][]caddyfile.Token) addrToProtocolToKeyWithParsedKeys := map[string]map[string][]keyWithParsedKey{}
for j, key := range sblock.block.Keys { for j, key := range sblock.block.Keys {
parsedKey, err := ParseAddress(key.Text)
if err != nil {
return nil, fmt.Errorf("parsing key: %v", err)
}
parsedKey = parsedKey.Normalize()
// a key can have multiple listener addresses if there are multiple // a key can have multiple listener addresses if there are multiple
// arguments to the 'bind' directive (although they will all have // arguments to the 'bind' directive (although they will all have
// the same port, since the port is defined by the key or is implicit // the same port, since the port is defined by the key or is implicit
// through automatic HTTPS) // through automatic HTTPS)
addrs, err := st.listenerAddrsForServerBlockKey(sblock, key.Text, options) listeners, err := st.listenersForServerBlockAddress(sblock, parsedKey, options)
if err != nil { if err != nil {
return nil, fmt.Errorf("server block %d, key %d (%s): determining listener address: %v", i, j, key.Text, err) return nil, fmt.Errorf("server block %d, key %d (%s): determining listener address: %v", i, j, key.Text, err)
} }
// associate this key with each listener address it is served on // associate this key with its protocols and each listener address served with them
for _, addr := range addrs { kwpk := keyWithParsedKey{key, parsedKey}
addrToKeys[addr] = append(addrToKeys[addr], key) for addr, protocols := range listeners {
protocolToKeyWithParsedKeys, ok := addrToProtocolToKeyWithParsedKeys[addr]
if !ok {
protocolToKeyWithParsedKeys = map[string][]keyWithParsedKey{}
addrToProtocolToKeyWithParsedKeys[addr] = protocolToKeyWithParsedKeys
}
// an empty protocol indicates the default, a nil or empty value in the ListenProtocols array
if len(protocols) == 0 {
protocols[""] = struct{}{}
}
for prot := range protocols {
protocolToKeyWithParsedKeys[prot] = append(
protocolToKeyWithParsedKeys[prot],
kwpk)
}
} }
} }
// make a slice of the map keys so we can iterate in sorted order // make a slice of the map keys so we can iterate in sorted order
addrs := make([]string, 0, len(addrToKeys)) addrs := make([]string, 0, len(addrToProtocolToKeyWithParsedKeys))
for k := range addrToKeys { for addr := range addrToProtocolToKeyWithParsedKeys {
addrs = append(addrs, k) addrs = append(addrs, addr)
} }
sort.Strings(addrs) sort.Strings(addrs)
@@ -118,85 +144,132 @@ func (st *ServerType) mapAddressToServerBlocks(originalServerBlocks []serverBloc
// server block are only the ones which use the address; but // server block are only the ones which use the address; but
// the contents (tokens) are of course the same // the contents (tokens) are of course the same
for _, addr := range addrs { for _, addr := range addrs {
keys := addrToKeys[addr] protocolToKeyWithParsedKeys := addrToProtocolToKeyWithParsedKeys[addr]
// parse keys so that we only have to do it once
parsedKeys := make([]Address, 0, len(keys)) prots := make([]string, 0, len(protocolToKeyWithParsedKeys))
for _, key := range keys { for prot := range protocolToKeyWithParsedKeys {
addr, err := ParseAddress(key.Text) prots = append(prots, prot)
if err != nil {
return nil, fmt.Errorf("parsing key '%s': %v", key.Text, err)
}
parsedKeys = append(parsedKeys, addr.Normalize())
} }
sbmap[addr] = append(sbmap[addr], serverBlock{ sort.Strings(prots)
block: caddyfile.ServerBlock{
Keys: keys, protocolToServerBlocks, ok := addrToProtocolToServerBlocks[addr]
Segments: sblock.block.Segments, if !ok {
}, protocolToServerBlocks = map[string][]serverBlock{}
pile: sblock.pile, addrToProtocolToServerBlocks[addr] = protocolToServerBlocks
keys: parsedKeys, }
for _, prot := range prots {
keyWithParsedKeys := protocolToKeyWithParsedKeys[prot]
keys := make([]caddyfile.Token, len(keyWithParsedKeys))
parsedKeys := make([]Address, len(keyWithParsedKeys))
for k, keyWithParsedKey := range keyWithParsedKeys {
keys[k] = keyWithParsedKey.key
parsedKeys[k] = keyWithParsedKey.parsedKey
}
protocolToServerBlocks[prot] = append(protocolToServerBlocks[prot], serverBlock{
block: caddyfile.ServerBlock{
Keys: keys,
Segments: sblock.block.Segments,
},
pile: sblock.pile,
parsedKeys: parsedKeys,
})
}
}
}
return addrToProtocolToServerBlocks, nil
}
// consolidateAddrMappings eliminates repetition of identical server blocks in a mapping of
// single listener addresses to protocols to lists of server blocks. Since multiple addresses
// may serve multiple protocols to identical sites (server block contents), this function turns
// a 1:many mapping into a many:many mapping. Server block contents (tokens) must be
// exactly identical so that reflect.DeepEqual returns true in order for the addresses to be combined.
// Identical entries are deleted from the addrToServerBlocks map. Essentially, each pairing (each
// association from multiple addresses to multiple server blocks; i.e. each element of
// the returned slice) becomes a server definition in the output JSON.
func (st *ServerType) consolidateAddrMappings(addrToProtocolToServerBlocks map[string]map[string][]serverBlock) []sbAddrAssociation {
sbaddrs := make([]sbAddrAssociation, 0, len(addrToProtocolToServerBlocks))
addrs := make([]string, 0, len(addrToProtocolToServerBlocks))
for addr := range addrToProtocolToServerBlocks {
addrs = append(addrs, addr)
}
sort.Strings(addrs)
for _, addr := range addrs {
protocolToServerBlocks := addrToProtocolToServerBlocks[addr]
prots := make([]string, 0, len(protocolToServerBlocks))
for prot := range protocolToServerBlocks {
prots = append(prots, prot)
}
sort.Strings(prots)
for _, prot := range prots {
serverBlocks := protocolToServerBlocks[prot]
// now find other addresses that map to identical
// server blocks and add them to our map of listener
// addresses and protocols, while removing them from
// the original map
listeners := map[string]map[string]struct{}{}
for otherAddr, otherProtocolToServerBlocks := range addrToProtocolToServerBlocks {
for otherProt, otherServerBlocks := range otherProtocolToServerBlocks {
if addr == otherAddr && prot == otherProt || reflect.DeepEqual(serverBlocks, otherServerBlocks) {
listener, ok := listeners[otherAddr]
if !ok {
listener = map[string]struct{}{}
listeners[otherAddr] = listener
}
listener[otherProt] = struct{}{}
delete(otherProtocolToServerBlocks, otherProt)
}
}
}
addresses := make([]string, 0, len(listeners))
for lnAddr := range listeners {
addresses = append(addresses, lnAddr)
}
sort.Strings(addresses)
addressesWithProtocols := make([]addressWithProtocols, 0, len(listeners))
for _, lnAddr := range addresses {
lnProts := listeners[lnAddr]
prots := make([]string, 0, len(lnProts))
for prot := range lnProts {
prots = append(prots, prot)
}
sort.Strings(prots)
addressesWithProtocols = append(addressesWithProtocols, addressWithProtocols{
address: lnAddr,
protocols: prots,
})
}
sbaddrs = append(sbaddrs, sbAddrAssociation{
addressesWithProtocols: addressesWithProtocols,
serverBlocks: serverBlocks,
}) })
} }
} }
return sbmap, nil
}
// consolidateAddrMappings eliminates repetition of identical server blocks in a mapping of
// single listener addresses to lists of server blocks. Since multiple addresses may serve
// identical sites (server block contents), this function turns a 1:many mapping into a
// many:many mapping. Server block contents (tokens) must be exactly identical so that
// reflect.DeepEqual returns true in order for the addresses to be combined. Identical
// entries are deleted from the addrToServerBlocks map. Essentially, each pairing (each
// association from multiple addresses to multiple server blocks; i.e. each element of
// the returned slice) becomes a server definition in the output JSON.
func (st *ServerType) consolidateAddrMappings(addrToServerBlocks map[string][]serverBlock) []sbAddrAssociation {
sbaddrs := make([]sbAddrAssociation, 0, len(addrToServerBlocks))
for addr, sblocks := range addrToServerBlocks {
// we start with knowing that at least this address
// maps to these server blocks
a := sbAddrAssociation{
addresses: []string{addr},
serverBlocks: sblocks,
}
// now find other addresses that map to identical
// server blocks and add them to our list of
// addresses, while removing them from the map
for otherAddr, otherSblocks := range addrToServerBlocks {
if addr == otherAddr {
continue
}
if reflect.DeepEqual(sblocks, otherSblocks) {
a.addresses = append(a.addresses, otherAddr)
delete(addrToServerBlocks, otherAddr)
}
}
sort.Strings(a.addresses)
sbaddrs = append(sbaddrs, a)
}
// sort them by their first address (we know there will always be at least one)
// to avoid problems with non-deterministic ordering (makes tests flaky)
sort.Slice(sbaddrs, func(i, j int) bool {
return sbaddrs[i].addresses[0] < sbaddrs[j].addresses[0]
})
return sbaddrs return sbaddrs
} }
// listenerAddrsForServerBlockKey essentially converts the Caddyfile // listenersForServerBlockAddress essentially converts the Caddyfile site addresses to a map from
// site addresses to Caddy listener addresses for each server block. // Caddy listener addresses and the protocols to serve them with to the parsed address for each server block.
func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key string, func (st *ServerType) listenersForServerBlockAddress(sblock serverBlock, addr Address,
options map[string]any, options map[string]any,
) ([]string, error) { ) (map[string]map[string]struct{}, error) {
addr, err := ParseAddress(key)
if err != nil {
return nil, fmt.Errorf("parsing key: %v", err)
}
addr = addr.Normalize()
switch addr.Scheme { switch addr.Scheme {
case "wss": case "wss":
return nil, fmt.Errorf("the scheme wss:// is only supported in browsers; use https:// instead") return nil, fmt.Errorf("the scheme wss:// is only supported in browsers; use https:// instead")
@@ -230,55 +303,54 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str
// error if scheme and port combination violate convention // error if scheme and port combination violate convention
if (addr.Scheme == "http" && lnPort == httpsPort) || (addr.Scheme == "https" && lnPort == httpPort) { if (addr.Scheme == "http" && lnPort == httpsPort) || (addr.Scheme == "https" && lnPort == httpPort) {
return nil, fmt.Errorf("[%s] scheme and port violate convention", key) return nil, fmt.Errorf("[%s] scheme and port violate convention", addr.String())
} }
// the bind directive specifies hosts (and potentially network), but is optional // the bind directive specifies hosts (and potentially network), and the protocols to serve them with, but is optional
lnHosts := make([]string, 0, len(sblock.pile["bind"])) lnCfgVals := make([]addressesWithProtocols, 0, len(sblock.pile["bind"]))
for _, cfgVal := range sblock.pile["bind"] { for _, cfgVal := range sblock.pile["bind"] {
lnHosts = append(lnHosts, cfgVal.Value.([]string)...) if val, ok := cfgVal.Value.(addressesWithProtocols); ok {
lnCfgVals = append(lnCfgVals, val)
}
} }
if len(lnHosts) == 0 { if len(lnCfgVals) == 0 {
if defaultBind, ok := options["default_bind"].([]string); ok { if defaultBindValues, ok := options["default_bind"].([]ConfigValue); ok {
lnHosts = defaultBind for _, defaultBindValue := range defaultBindValues {
lnCfgVals = append(lnCfgVals, defaultBindValue.Value.(addressesWithProtocols))
}
} else { } else {
lnHosts = []string{""} lnCfgVals = []addressesWithProtocols{{
addresses: []string{""},
protocols: nil,
}}
} }
} }
// use a map to prevent duplication // use a map to prevent duplication
listeners := make(map[string]struct{}) listeners := map[string]map[string]struct{}{}
for _, lnHost := range lnHosts { for _, lnCfgVal := range lnCfgVals {
// normally we would simply append the port, for _, lnHost := range lnCfgVal.addresses {
// but if lnHost is IPv6, we need to ensure it networkAddr, err := caddy.ParseNetworkAddressFromHostPort(lnHost, lnPort)
// is enclosed in [ ]; net.JoinHostPort does if err != nil {
// this for us, but lnHost might also have a return nil, fmt.Errorf("parsing network address: %v", err)
// network type in front (e.g. "tcp/") leading }
// to "[tcp/::1]" which causes parsing failures if _, ok := listeners[addr.String()]; !ok {
// later; what we need is "tcp/[::1]", so we have listeners[networkAddr.String()] = map[string]struct{}{}
// to split the network and host, then re-combine }
network, host, ok := strings.Cut(lnHost, "/") for _, protocol := range lnCfgVal.protocols {
if !ok { listeners[networkAddr.String()][protocol] = struct{}{}
host = network }
network = ""
} }
host = strings.Trim(host, "[]") // IPv6
networkAddr := caddy.JoinNetworkAddress(network, host, lnPort)
addr, err := caddy.ParseNetworkAddress(networkAddr)
if err != nil {
return nil, fmt.Errorf("parsing network address: %v", err)
}
listeners[addr.String()] = struct{}{}
} }
// now turn map into list return listeners, nil
listenersList := make([]string, 0, len(listeners)) }
for lnStr := range listeners {
listenersList = append(listenersList, lnStr)
}
sort.Strings(listenersList)
return listenersList, nil // addressesWithProtocols associates a list of listen addresses
// with a list of protocols to serve them with
type addressesWithProtocols struct {
addresses []string
protocols []string
} }
// Address represents a site address. It contains // Address represents a site address. It contains
+39 -2
View File
@@ -56,10 +56,30 @@ func init() {
// parseBind parses the bind directive. Syntax: // parseBind parses the bind directive. Syntax:
// //
// bind <addresses...> // bind <addresses...> [{
// protocols [h1|h2|h2c|h3] [...]
// }]
func parseBind(h Helper) ([]ConfigValue, error) { func parseBind(h Helper) ([]ConfigValue, error) {
h.Next() // consume directive name h.Next() // consume directive name
return []ConfigValue{{Class: "bind", Value: h.RemainingArgs()}}, nil var addresses, protocols []string
addresses = h.RemainingArgs()
for h.NextBlock(0) {
switch h.Val() {
case "protocols":
protocols = h.RemainingArgs()
if len(protocols) == 0 {
return nil, h.Errf("protocols requires one or more arguments")
}
default:
return nil, h.Errf("unknown subdirective: %s", h.Val())
}
}
return []ConfigValue{{Class: "bind", Value: addressesWithProtocols{
addresses: addresses,
protocols: protocols,
}}}, nil
} }
// parseTLS parses the tls directive. Syntax: // parseTLS parses the tls directive. Syntax:
@@ -849,6 +869,7 @@ func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
// log <logger_name> { // log <logger_name> {
// hostnames <hostnames...> // hostnames <hostnames...>
// output <writer_module> ... // output <writer_module> ...
// core <core_module> ...
// format <encoder_module> ... // format <encoder_module> ...
// level <level> // level <level>
// } // }
@@ -960,6 +981,22 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
} }
cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings) cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
case "core":
if !h.NextArg() {
return nil, h.ArgErr()
}
moduleName := h.Val()
moduleID := "caddy.logging.cores." + moduleName
unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
if err != nil {
return nil, err
}
core, ok := unm.(zapcore.Core)
if !ok {
return nil, h.Errf("module %s (%T) is not a zapcore.Core", moduleID, unm)
}
cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
case "format": case "format":
if !h.NextArg() { if !h.NextArg() {
return nil, h.ArgErr() return nil, h.ArgErr()
+4 -2
View File
@@ -25,11 +25,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{ {
input: `:8080 { input: `:8080 {
log { log {
core mock
output file foo.log output file foo.log
} }
} }
`, `,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`, output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
expectError: false, expectError: false,
}, },
{ {
@@ -53,11 +54,12 @@ func TestLogDirectiveSyntax(t *testing.T) {
{ {
input: `:8080 { input: `:8080 {
log name-override { log name-override {
core mock
output file foo.log output file foo.log
} }
} }
`, `,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`, output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.name-override"]},"name-override":{"writer":{"filename":"foo.log","output":"file"},"core":{"module":"mock"},"include":["http.log.access.name-override"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"name-override"}}}}}}`,
expectError: false, expectError: false,
}, },
} { } {
+14 -35
View File
@@ -17,6 +17,7 @@ package httpcaddyfile
import ( import (
"encoding/json" "encoding/json"
"net" "net"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -100,17 +101,6 @@ var defaultDirectiveOrder = []string{
// plugins or by the user via the "order" global option. // plugins or by the user via the "order" global option.
var directiveOrder = defaultDirectiveOrder var directiveOrder = defaultDirectiveOrder
// directiveIsOrdered returns true if dir is
// a known, ordered (sorted) directive.
func directiveIsOrdered(dir string) bool {
for _, d := range directiveOrder {
if d == dir {
return true
}
}
return false
}
// RegisterDirective registers a unique directive dir with an // RegisterDirective registers a unique directive dir with an
// associated unmarshaling (setup) function. When directive dir // associated unmarshaling (setup) function. When directive dir
// is encountered in a Caddyfile, setupFunc will be called to // is encountered in a Caddyfile, setupFunc will be called to
@@ -161,7 +151,7 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
// EXPERIMENTAL: This API may change or be removed. // EXPERIMENTAL: This API may change or be removed.
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) { func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
// check if directive was already ordered // check if directive was already ordered
if directiveIsOrdered(dir) { if slices.Contains(directiveOrder, dir) {
panic("directive '" + dir + "' already ordered") panic("directive '" + dir + "' already ordered")
} }
@@ -172,12 +162,7 @@ func RegisterDirectiveOrder(dir string, position Positional, standardDir string)
// check if directive exists in standard distribution, since // check if directive exists in standard distribution, since
// we can't allow plugins to depend on one another; we can't // we can't allow plugins to depend on one another; we can't
// guarantee the order that plugins are loaded in. // guarantee the order that plugins are loaded in.
foundStandardDir := false foundStandardDir := slices.Contains(defaultDirectiveOrder, standardDir)
for _, d := range defaultDirectiveOrder {
if d == standardDir {
foundStandardDir = true
}
}
if !foundStandardDir { if !foundStandardDir {
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy") panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
} }
@@ -531,9 +516,9 @@ func sortRoutes(routes []ConfigValue) {
// a "pile" of config values, keyed by class name, // a "pile" of config values, keyed by class name,
// as well as its parsed keys for convenience. // as well as its parsed keys for convenience.
type serverBlock struct { type serverBlock struct {
block caddyfile.ServerBlock block caddyfile.ServerBlock
pile map[string][]ConfigValue // config values obtained from directives pile map[string][]ConfigValue // config values obtained from directives
keys []Address parsedKeys []Address
} }
// hostsFromKeys returns a list of all the non-empty hostnames found in // hostsFromKeys returns a list of all the non-empty hostnames found in
@@ -550,7 +535,7 @@ type serverBlock struct {
func (sb serverBlock) hostsFromKeys(loggerMode bool) []string { func (sb serverBlock) hostsFromKeys(loggerMode bool) []string {
// ensure each entry in our list is unique // ensure each entry in our list is unique
hostMap := make(map[string]struct{}) hostMap := make(map[string]struct{})
for _, addr := range sb.keys { for _, addr := range sb.parsedKeys {
if addr.Host == "" { if addr.Host == "" {
if !loggerMode { if !loggerMode {
// server block contains a key like ":443", i.e. the host portion // server block contains a key like ":443", i.e. the host portion
@@ -582,7 +567,7 @@ func (sb serverBlock) hostsFromKeys(loggerMode bool) []string {
func (sb serverBlock) hostsFromKeysNotHTTP(httpPort string) []string { func (sb serverBlock) hostsFromKeysNotHTTP(httpPort string) []string {
// ensure each entry in our list is unique // ensure each entry in our list is unique
hostMap := make(map[string]struct{}) hostMap := make(map[string]struct{})
for _, addr := range sb.keys { for _, addr := range sb.parsedKeys {
if addr.Host == "" { if addr.Host == "" {
continue continue
} }
@@ -603,23 +588,17 @@ func (sb serverBlock) hostsFromKeysNotHTTP(httpPort string) []string {
// hasHostCatchAllKey returns true if sb has a key that // hasHostCatchAllKey returns true if sb has a key that
// omits a host portion, i.e. it "catches all" hosts. // omits a host portion, i.e. it "catches all" hosts.
func (sb serverBlock) hasHostCatchAllKey() bool { func (sb serverBlock) hasHostCatchAllKey() bool {
for _, addr := range sb.keys { return slices.ContainsFunc(sb.parsedKeys, func(addr Address) bool {
if addr.Host == "" { return addr.Host == ""
return true })
}
}
return false
} }
// isAllHTTP returns true if all sb keys explicitly specify // isAllHTTP returns true if all sb keys explicitly specify
// the http:// scheme // the http:// scheme
func (sb serverBlock) isAllHTTP() bool { func (sb serverBlock) isAllHTTP() bool {
for _, addr := range sb.keys { return !slices.ContainsFunc(sb.parsedKeys, func(addr Address) bool {
if addr.Scheme != "http" { return addr.Scheme != "http"
return false })
}
}
return true
} }
// Positional are the supported modes for ordering directives. // Positional are the supported modes for ordering directives.
+1 -1
View File
@@ -78,7 +78,7 @@ func TestHostsFromKeys(t *testing.T) {
[]string{"example.com:2015"}, []string{"example.com:2015"},
}, },
} { } {
sb := serverBlock{keys: tc.keys} sb := serverBlock{parsedKeys: tc.keys}
// test in normal mode // test in normal mode
actual := sb.hostsFromKeys(false) actual := sb.hostsFromKeys(false)
+122 -47
View File
@@ -171,7 +171,7 @@ func (st ServerType) Setup(
} }
// map // map
sbmap, err := st.mapAddressToServerBlocks(originalServerBlocks, options) sbmap, err := st.mapAddressToProtocolToServerBlocks(originalServerBlocks, options)
if err != nil { if err != nil {
return nil, warnings, err return nil, warnings, err
} }
@@ -402,6 +402,20 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
options[opt] = append(existingOpts, logOpts...) options[opt] = append(existingOpts, logOpts...)
continue continue
} }
// Also fold multiple "default_bind" options together into an
// array so that server blocks can have multiple binds by default.
if opt == "default_bind" {
existingOpts, ok := options[opt].([]ConfigValue)
if !ok {
existingOpts = []ConfigValue{}
}
defaultBindOpts, ok := val.([]ConfigValue)
if !ok {
return nil, fmt.Errorf("unexpected type from 'default_bind' global options: %T", val)
}
options[opt] = append(existingOpts, defaultBindOpts...)
continue
}
options[opt] = val options[opt] = val
} }
@@ -520,8 +534,8 @@ func (st *ServerType) serversFromPairings(
if hsp, ok := options["https_port"].(int); ok { if hsp, ok := options["https_port"].(int); ok {
httpsPort = strconv.Itoa(hsp) httpsPort = strconv.Itoa(hsp)
} }
autoHTTPS := "on" autoHTTPS := []string{}
if ah, ok := options["auto_https"].(string); ok { if ah, ok := options["auto_https"].([]string); ok {
autoHTTPS = ah autoHTTPS = ah
} }
@@ -536,29 +550,81 @@ func (st *ServerType) serversFromPairings(
if k == j { if k == j {
continue continue
} }
if sliceContains(sblock2.block.GetKeysText(), key) { if slices.Contains(sblock2.block.GetKeysText(), key) {
return nil, fmt.Errorf("ambiguous site definition: %s", key) return nil, fmt.Errorf("ambiguous site definition: %s", key)
} }
} }
} }
} }
var (
addresses []string
protocols [][]string
)
for _, addressWithProtocols := range p.addressesWithProtocols {
addresses = append(addresses, addressWithProtocols.address)
protocols = append(protocols, addressWithProtocols.protocols)
}
srv := &caddyhttp.Server{ srv := &caddyhttp.Server{
Listen: p.addresses, Listen: addresses,
ListenProtocols: protocols,
}
// remove srv.ListenProtocols[j] if it only contains the default protocols
for j, lnProtocols := range srv.ListenProtocols {
srv.ListenProtocols[j] = nil
for _, lnProtocol := range lnProtocols {
if lnProtocol != "" {
srv.ListenProtocols[j] = lnProtocols
break
}
}
}
// remove srv.ListenProtocols if it only contains the default protocols for all listen addresses
listenProtocols := srv.ListenProtocols
srv.ListenProtocols = nil
for _, lnProtocols := range listenProtocols {
if lnProtocols != nil {
srv.ListenProtocols = listenProtocols
break
}
} }
// handle the auto_https global option // handle the auto_https global option
if autoHTTPS != "on" { for _, val := range autoHTTPS {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig) switch val {
switch autoHTTPS {
case "off": case "off":
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
srv.AutoHTTPS.Disabled = true srv.AutoHTTPS.Disabled = true
case "disable_redirects": case "disable_redirects":
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
srv.AutoHTTPS.DisableRedir = true srv.AutoHTTPS.DisableRedir = true
case "disable_certs": case "disable_certs":
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
srv.AutoHTTPS.DisableCerts = true srv.AutoHTTPS.DisableCerts = true
case "ignore_loaded_certs": case "ignore_loaded_certs":
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
srv.AutoHTTPS.IgnoreLoadedCerts = true srv.AutoHTTPS.IgnoreLoadedCerts = true
case "prefer_wildcard":
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
srv.AutoHTTPS.PreferWildcard = true
} }
} }
@@ -566,7 +632,7 @@ func (st *ServerType) serversFromPairings(
// See ParseAddress() where parsing should later reject paths // See ParseAddress() where parsing should later reject paths
// See https://github.com/caddyserver/caddy/pull/4728 for a full explanation // See https://github.com/caddyserver/caddy/pull/4728 for a full explanation
for _, sblock := range p.serverBlocks { for _, sblock := range p.serverBlocks {
for _, addr := range sblock.keys { for _, addr := range sblock.parsedKeys {
if addr.Path != "" { if addr.Path != "" {
caddy.Log().Named("caddyfile").Warn("Using a path in a site address is deprecated; please use the 'handle' directive instead", zap.String("address", addr.String())) caddy.Log().Named("caddyfile").Warn("Using a path in a site address is deprecated; please use the 'handle' directive instead", zap.String("address", addr.String()))
} }
@@ -584,7 +650,7 @@ func (st *ServerType) serversFromPairings(
var iLongestPath, jLongestPath string var iLongestPath, jLongestPath string
var iLongestHost, jLongestHost string var iLongestHost, jLongestHost string
var iWildcardHost, jWildcardHost bool var iWildcardHost, jWildcardHost bool
for _, addr := range p.serverBlocks[i].keys { for _, addr := range p.serverBlocks[i].parsedKeys {
if strings.Contains(addr.Host, "*") || addr.Host == "" { if strings.Contains(addr.Host, "*") || addr.Host == "" {
iWildcardHost = true iWildcardHost = true
} }
@@ -595,7 +661,7 @@ func (st *ServerType) serversFromPairings(
iLongestPath = addr.Path iLongestPath = addr.Path
} }
} }
for _, addr := range p.serverBlocks[j].keys { for _, addr := range p.serverBlocks[j].parsedKeys {
if strings.Contains(addr.Host, "*") || addr.Host == "" { if strings.Contains(addr.Host, "*") || addr.Host == "" {
jWildcardHost = true jWildcardHost = true
} }
@@ -627,7 +693,7 @@ func (st *ServerType) serversFromPairings(
}) })
var hasCatchAllTLSConnPolicy, addressQualifiesForTLS bool var hasCatchAllTLSConnPolicy, addressQualifiesForTLS bool
autoHTTPSWillAddConnPolicy := autoHTTPS != "off" autoHTTPSWillAddConnPolicy := srv.AutoHTTPS == nil || !srv.AutoHTTPS.Disabled
// if needed, the ServerLogConfig is initialized beforehand so // if needed, the ServerLogConfig is initialized beforehand so
// that all server blocks can populate it with data, even when not // that all server blocks can populate it with data, even when not
@@ -711,7 +777,14 @@ func (st *ServerType) serversFromPairings(
} }
} }
for _, addr := range sblock.keys { wildcardHosts := []string{}
for _, addr := range sblock.parsedKeys {
if strings.HasPrefix(addr.Host, "*.") {
wildcardHosts = append(wildcardHosts, addr.Host[2:])
}
}
for _, addr := range sblock.parsedKeys {
// if server only uses HTTP port, auto-HTTPS will not apply // if server only uses HTTP port, auto-HTTPS will not apply
if listenersUseAnyPortOtherThan(srv.Listen, httpPort) { if listenersUseAnyPortOtherThan(srv.Listen, httpPort) {
// exclude any hosts that were defined explicitly with "http://" // exclude any hosts that were defined explicitly with "http://"
@@ -720,12 +793,24 @@ func (st *ServerType) serversFromPairings(
if srv.AutoHTTPS == nil { if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig) srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
} }
if !sliceContains(srv.AutoHTTPS.Skip, addr.Host) { if !slices.Contains(srv.AutoHTTPS.Skip, addr.Host) {
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host) srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
} }
} }
} }
// If prefer wildcard is enabled, then we add hosts that are
// already covered by the wildcard to the skip list
if srv.AutoHTTPS != nil && srv.AutoHTTPS.PreferWildcard && addr.Scheme == "https" {
baseDomain := addr.Host
if idx := strings.Index(baseDomain, "."); idx != -1 {
baseDomain = baseDomain[idx+1:]
}
if !strings.HasPrefix(addr.Host, "*.") && slices.Contains(wildcardHosts, baseDomain) {
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
}
}
// If TLS is specified as directive, it will also result in 1 or more connection policy being created // If TLS is specified as directive, it will also result in 1 or more connection policy being created
// Thus, catch-all address with non-standard port, e.g. :8443, can have TLS enabled without // Thus, catch-all address with non-standard port, e.g. :8443, can have TLS enabled without
// specifying prefix "https://" // specifying prefix "https://"
@@ -734,7 +819,7 @@ func (st *ServerType) serversFromPairings(
// https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761 // https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761
createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"] createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"]
hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) || hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) ||
(addr.Host != "" && srv.AutoHTTPS != nil && !sliceContains(srv.AutoHTTPS.Skip, addr.Host)) (addr.Host != "" && srv.AutoHTTPS != nil && !slices.Contains(srv.AutoHTTPS.Skip, addr.Host))
// we'll need to remember if the address qualifies for auto-HTTPS, so we // we'll need to remember if the address qualifies for auto-HTTPS, so we
// can add a TLS conn policy if necessary // can add a TLS conn policy if necessary
@@ -873,7 +958,10 @@ func (st *ServerType) serversFromPairings(
if addressQualifiesForTLS && if addressQualifiesForTLS &&
!hasCatchAllTLSConnPolicy && !hasCatchAllTLSConnPolicy &&
(len(srv.TLSConnPolicies) > 0 || !autoHTTPSWillAddConnPolicy || defaultSNI != "" || fallbackSNI != "") { (len(srv.TLSConnPolicies) > 0 || !autoHTTPSWillAddConnPolicy || defaultSNI != "" || fallbackSNI != "") {
srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{DefaultSNI: defaultSNI, FallbackSNI: fallbackSNI}) srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{
DefaultSNI: defaultSNI,
FallbackSNI: fallbackSNI,
})
} }
// tidy things up a bit // tidy things up a bit
@@ -886,8 +974,7 @@ func (st *ServerType) serversFromPairings(
servers[fmt.Sprintf("srv%d", i)] = srv servers[fmt.Sprintf("srv%d", i)] = srv
} }
err := applyServerOptions(servers, options, warnings) if err := applyServerOptions(servers, options, warnings); err != nil {
if err != nil {
return nil, fmt.Errorf("applying global server options: %v", err) return nil, fmt.Errorf("applying global server options: %v", err)
} }
@@ -932,7 +1019,7 @@ func detectConflictingSchemes(srv *caddyhttp.Server, serverBlocks []serverBlock,
} }
for _, sblock := range serverBlocks { for _, sblock := range serverBlocks {
for _, addr := range sblock.keys { for _, addr := range sblock.parsedKeys {
if addr.Scheme == "http" || addr.Port == httpPort { if addr.Scheme == "http" || addr.Port == httpPort {
if err := checkAndSetHTTP(addr); err != nil { if err := checkAndSetHTTP(addr); err != nil {
return err return err
@@ -1061,7 +1148,7 @@ func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.Connecti
} else if cps[i].CertSelection != nil && cps[j].CertSelection != nil { } else if cps[i].CertSelection != nil && cps[j].CertSelection != nil {
// if both have one, then combine AnyTag // if both have one, then combine AnyTag
for _, tag := range cps[j].CertSelection.AnyTag { for _, tag := range cps[j].CertSelection.AnyTag {
if !sliceContains(cps[i].CertSelection.AnyTag, tag) { if !slices.Contains(cps[i].CertSelection.AnyTag, tag) {
cps[i].CertSelection.AnyTag = append(cps[i].CertSelection.AnyTag, tag) cps[i].CertSelection.AnyTag = append(cps[i].CertSelection.AnyTag, tag)
} }
} }
@@ -1144,7 +1231,7 @@ func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) { func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) {
if needsSorting { if needsSorting {
for _, val := range routes { for _, val := range routes {
if !directiveIsOrdered(val.directive) { if !slices.Contains(directiveOrder, val.directive) {
return nil, fmt.Errorf("directive '%s' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option", val.directive) return nil, fmt.Errorf("directive '%s' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option", val.directive)
} }
} }
@@ -1322,7 +1409,7 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
var matcherPairs []*hostPathPair var matcherPairs []*hostPathPair
var catchAllHosts bool var catchAllHosts bool
for _, addr := range sblock.keys { for _, addr := range sblock.parsedKeys {
// choose a matcher pair that should be shared by this // choose a matcher pair that should be shared by this
// server block; if none exists yet, create one // server block; if none exists yet, create one
var chosenMatcherPair *hostPathPair var chosenMatcherPair *hostPathPair
@@ -1354,17 +1441,8 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
// add this server block's keys to the matcher // add this server block's keys to the matcher
// pair if it doesn't already exist // pair if it doesn't already exist
if addr.Host != "" { if addr.Host != "" && !slices.Contains(chosenMatcherPair.hostm, addr.Host) {
var found bool chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
for _, h := range chosenMatcherPair.hostm {
if h == addr.Host {
found = true
break
}
}
if !found {
chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
}
} }
} }
@@ -1540,16 +1618,6 @@ func tryDuration(val any, warnings *[]caddyconfig.Warning) caddy.Duration {
return durationVal return durationVal
} }
// sliceContains returns true if needle is in haystack.
func sliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
// listenersUseAnyPortOtherThan returns true if there are any // listenersUseAnyPortOtherThan returns true if there are any
// listeners in addresses that use a port which is not otherPort. // listeners in addresses that use a port which is not otherPort.
// Mostly borrowed from unexported method in caddyhttp package. // Mostly borrowed from unexported method in caddyhttp package.
@@ -1613,12 +1681,19 @@ type namedCustomLog struct {
noHostname bool noHostname bool
} }
// addressWithProtocols associates a listen address with
// the protocols to serve it with
type addressWithProtocols struct {
address string
protocols []string
}
// sbAddrAssociation is a mapping from a list of // sbAddrAssociation is a mapping from a list of
// addresses to a list of server blocks that are // addresses with protocols, and a list of server
// served on those addresses. // blocks that are served on those addresses.
type sbAddrAssociation struct { type sbAddrAssociation struct {
addresses []string addressesWithProtocols []addressWithProtocols
serverBlocks []serverBlock serverBlocks []serverBlock
} }
const ( const (
+61 -61
View File
@@ -15,6 +15,7 @@
package httpcaddyfile package httpcaddyfile
import ( import (
"slices"
"strconv" "strconv"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
@@ -30,7 +31,7 @@ func init() {
RegisterGlobalOption("debug", parseOptTrue) RegisterGlobalOption("debug", parseOptTrue)
RegisterGlobalOption("http_port", parseOptHTTPPort) RegisterGlobalOption("http_port", parseOptHTTPPort)
RegisterGlobalOption("https_port", parseOptHTTPSPort) RegisterGlobalOption("https_port", parseOptHTTPSPort)
RegisterGlobalOption("default_bind", parseOptStringList) RegisterGlobalOption("default_bind", parseOptDefaultBind)
RegisterGlobalOption("grace_period", parseOptDuration) RegisterGlobalOption("grace_period", parseOptDuration)
RegisterGlobalOption("shutdown_delay", parseOptDuration) RegisterGlobalOption("shutdown_delay", parseOptDuration)
RegisterGlobalOption("default_sni", parseOptSingleString) RegisterGlobalOption("default_sni", parseOptSingleString)
@@ -110,17 +111,12 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
pos := Positional(d.Val()) pos := Positional(d.Val())
newOrder := directiveOrder // if directive already had an order, drop it
newOrder := slices.DeleteFunc(directiveOrder, func(d string) bool {
return d == dirName
})
// if directive exists, first remove it // act on the positional; if it's First or Last, we're done right away
for i, d := range newOrder {
if d == dirName {
newOrder = append(newOrder[:i], newOrder[i+1:]...)
break
}
}
// act on the positional
switch pos { switch pos {
case First: case First:
newOrder = append([]string{dirName}, newOrder...) newOrder = append([]string{dirName}, newOrder...)
@@ -129,6 +125,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
directiveOrder = newOrder directiveOrder = newOrder
return newOrder, nil return newOrder, nil
case Last: case Last:
newOrder = append(newOrder, dirName) newOrder = append(newOrder, dirName)
if d.NextArg() { if d.NextArg() {
@@ -136,8 +133,11 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
directiveOrder = newOrder directiveOrder = newOrder
return newOrder, nil return newOrder, nil
// if it's Before or After, continue
case Before: case Before:
case After: case After:
default: default:
return nil, d.Errf("unknown positional '%s'", pos) return nil, d.Errf("unknown positional '%s'", pos)
} }
@@ -151,17 +151,17 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
// insert directive into proper position // get the position of the target directive
for i, d := range newOrder { targetIndex := slices.Index(newOrder, otherDir)
if d == otherDir { if targetIndex == -1 {
if pos == Before { return nil, d.Errf("directive '%s' not found", otherDir)
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
} else if pos == After {
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
}
break
}
} }
// if we're inserting after, we need to increment the index to go after
if pos == After {
targetIndex++
}
// insert the directive into the new order
newOrder = slices.Insert(newOrder, targetIndex, dirName)
directiveOrder = newOrder directiveOrder = newOrder
@@ -284,13 +284,32 @@ func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {
return val, nil return val, nil
} }
func parseOptStringList(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptDefaultBind(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name d.Next() // consume option name
val := d.RemainingArgs()
if len(val) == 0 { var addresses, protocols []string
return "", d.ArgErr() addresses = d.RemainingArgs()
if len(addresses) == 0 {
addresses = append(addresses, "")
} }
return val, nil
for d.NextBlock(0) {
switch d.Val() {
case "protocols":
protocols = d.RemainingArgs()
if len(protocols) == 0 {
return nil, d.Errf("protocols requires one or more arguments")
}
default:
return nil, d.Errf("unknown subdirective: %s", d.Val())
}
}
return []ConfigValue{{Class: "bind", Value: addressesWithProtocols{
addresses: addresses,
protocols: protocols,
}}}, nil
} }
func parseOptAdmin(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptAdmin(d *caddyfile.Dispenser, _ any) (any, error) {
@@ -375,36 +394,10 @@ func parseOptOnDemand(d *caddyfile.Dispenser, _ any) (any, error) {
ond.PermissionRaw = caddyconfig.JSONModuleObject(perm, "module", modName, nil) ond.PermissionRaw = caddyconfig.JSONModuleObject(perm, "module", modName, nil)
case "interval": case "interval":
if !d.NextArg() { return nil, d.Errf("the on_demand_tls 'interval' option is no longer supported, remove it from your config")
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Interval = caddy.Duration(dur)
case "burst": case "burst":
if !d.NextArg() { return nil, d.Errf("the on_demand_tls 'burst' option is no longer supported, remove it from your config")
return nil, d.ArgErr()
}
burst, err := strconv.Atoi(d.Val())
if err != nil {
return nil, err
}
if ond == nil {
ond = new(caddytls.OnDemandConfig)
}
if ond.RateLimit == nil {
ond.RateLimit = new(caddytls.RateLimit)
}
ond.RateLimit.Burst = burst
default: default:
return nil, d.Errf("unrecognized parameter '%s'", d.Val()) return nil, d.Errf("unrecognized parameter '%s'", d.Val())
@@ -433,15 +426,22 @@ func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) { func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name d.Next() // consume option name
if !d.Next() { val := d.RemainingArgs()
if len(val) == 0 {
return "", d.ArgErr() return "", d.ArgErr()
} }
val := d.Val() for _, v := range val {
if d.Next() { switch v {
return "", d.ArgErr() case "off":
} case "disable_redirects":
if val != "off" && val != "disable_redirects" && val != "disable_certs" && val != "ignore_loaded_certs" { case "disable_certs":
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'") case "ignore_loaded_certs":
case "prefer_wildcard":
break
default:
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', 'ignore_loaded_certs', or 'prefer_wildcard'")
}
} }
return val, nil return val, nil
} }
+14 -22
View File
@@ -17,6 +17,7 @@ package httpcaddyfile
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
@@ -180,7 +181,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" { if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto) return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
} }
if sliceContains(serverOpts.Protocols, proto) { if slices.Contains(serverOpts.Protocols, proto) {
return nil, d.Errf("protocol %s specified more than once", proto) return nil, d.Errf("protocol %s specified more than once", proto)
} }
serverOpts.Protocols = append(serverOpts.Protocols, proto) serverOpts.Protocols = append(serverOpts.Protocols, proto)
@@ -229,7 +230,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
case "client_ip_headers": case "client_ip_headers":
headers := d.RemainingArgs() headers := d.RemainingArgs()
for _, header := range headers { for _, header := range headers {
if sliceContains(serverOpts.ClientIPHeaders, header) { if slices.Contains(serverOpts.ClientIPHeaders, header) {
return nil, d.Errf("client IP header %s specified more than once", header) return nil, d.Errf("client IP header %s specified more than once", header)
} }
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header) serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
@@ -239,13 +240,13 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
} }
case "metrics": case "metrics":
if d.NextArg() {
return nil, d.ArgErr()
}
if nesting := d.Nesting(); d.NextBlock(nesting) {
return nil, d.ArgErr()
}
serverOpts.Metrics = new(caddyhttp.Metrics) serverOpts.Metrics = new(caddyhttp.Metrics)
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "per_host":
serverOpts.Metrics.PerHost = true
}
}
case "trace": case "trace":
if d.NextArg() { if d.NextArg() {
@@ -288,24 +289,15 @@ func applyServerOptions(
for key, server := range servers { for key, server := range servers {
// find the options that apply to this server // find the options that apply to this server
opts := func() *serverOptions { optsIndex := slices.IndexFunc(serverOpts, func(s serverOptions) bool {
for _, entry := range serverOpts { return s.ListenerAddress == "" || slices.Contains(server.Listen, s.ListenerAddress)
if entry.ListenerAddress == "" { })
return &entry
}
for _, listener := range server.Listen {
if entry.ListenerAddress == listener {
return &entry
}
}
}
return nil
}()
// if none apply, then move to the next server // if none apply, then move to the next server
if opts == nil { if optsIndex == -1 {
continue continue
} }
opts := serverOpts[optsIndex]
// set all the options // set all the options
server.ListenerWrappersRaw = opts.ListenerWrappersRaw server.ListenerWrappersRaw = opts.ListenerWrappersRaw
+69 -41
View File
@@ -19,6 +19,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -44,8 +45,8 @@ func (st ServerType) buildTLSApp(
if hp, ok := options["http_port"].(int); ok { if hp, ok := options["http_port"].(int); ok {
httpPort = strconv.Itoa(hp) httpPort = strconv.Itoa(hp)
} }
autoHTTPS := "on" autoHTTPS := []string{}
if ah, ok := options["auto_https"].(string); ok { if ah, ok := options["auto_https"].([]string); ok {
autoHTTPS = ah autoHTTPS = ah
} }
@@ -53,23 +54,25 @@ func (st ServerType) buildTLSApp(
// key, so that they don't get forgotten/omitted by auto-HTTPS // key, so that they don't get forgotten/omitted by auto-HTTPS
// (since they won't appear in route matchers) // (since they won't appear in route matchers)
httpsHostsSharedWithHostlessKey := make(map[string]struct{}) httpsHostsSharedWithHostlessKey := make(map[string]struct{})
if autoHTTPS != "off" { if !slices.Contains(autoHTTPS, "off") {
for _, pair := range pairings { for _, pair := range pairings {
for _, sb := range pair.serverBlocks { for _, sb := range pair.serverBlocks {
for _, addr := range sb.keys { for _, addr := range sb.parsedKeys {
if addr.Host == "" { if addr.Host != "" {
// this server block has a hostless key, now continue
// go through and add all the hosts to the set
for _, otherAddr := range sb.keys {
if otherAddr.Original == addr.Original {
continue
}
if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
}
}
break
} }
// this server block has a hostless key, now
// go through and add all the hosts to the set
for _, otherAddr := range sb.parsedKeys {
if otherAddr.Original == addr.Original {
continue
}
if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
}
}
break
} }
} }
} }
@@ -91,7 +94,11 @@ func (st ServerType) buildTLSApp(
for _, p := range pairings { for _, p := range pairings {
// avoid setting up TLS automation policies for a server that is HTTP-only // avoid setting up TLS automation policies for a server that is HTTP-only
if !listenersUseAnyPortOtherThan(p.addresses, httpPort) { var addresses []string
for _, addressWithProtocols := range p.addressesWithProtocols {
addresses = append(addresses, addressWithProtocols.address)
}
if !listenersUseAnyPortOtherThan(addresses, httpPort) {
continue continue
} }
@@ -181,8 +188,8 @@ func (st ServerType) buildTLSApp(
if acmeIssuer.Challenges.BindHost == "" { if acmeIssuer.Challenges.BindHost == "" {
// only binding to one host is supported // only binding to one host is supported
var bindHost string var bindHost string
if bindHosts, ok := cfgVal.Value.([]string); ok && len(bindHosts) > 0 { if asserted, ok := cfgVal.Value.(addressesWithProtocols); ok && len(asserted.addresses) > 0 {
bindHost = bindHosts[0] bindHost = asserted.addresses[0]
} }
acmeIssuer.Challenges.BindHost = bindHost acmeIssuer.Challenges.BindHost = bindHost
} }
@@ -344,7 +351,7 @@ func (st ServerType) buildTLSApp(
internalAP := &caddytls.AutomationPolicy{ internalAP := &caddytls.AutomationPolicy{
IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)}, IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)},
} }
if autoHTTPS != "off" && autoHTTPS != "disable_certs" { if !slices.Contains(autoHTTPS, "off") && !slices.Contains(autoHTTPS, "disable_certs") {
for h := range httpsHostsSharedWithHostlessKey { for h := range httpsHostsSharedWithHostlessKey {
al = append(al, h) al = append(al, h)
if !certmagic.SubjectQualifiesForPublicCert(h) { if !certmagic.SubjectQualifiesForPublicCert(h) {
@@ -411,7 +418,10 @@ func (st ServerType) buildTLSApp(
} }
// consolidate automation policies that are the exact same // consolidate automation policies that are the exact same
tlsApp.Automation.Policies = consolidateAutomationPolicies(tlsApp.Automation.Policies) tlsApp.Automation.Policies = consolidateAutomationPolicies(
tlsApp.Automation.Policies,
slices.Contains(autoHTTPS, "prefer_wildcard"),
)
// ensure automation policies don't overlap subjects (this should be // ensure automation policies don't overlap subjects (this should be
// an error at provision-time as well, but catch it in the adapt phase // an error at provision-time as well, but catch it in the adapt phase
@@ -465,7 +475,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalACMECA != nil && acmeIssuer.CA == "" { if globalACMECA != nil && acmeIssuer.CA == "" {
acmeIssuer.CA = globalACMECA.(string) acmeIssuer.CA = globalACMECA.(string)
} }
if globalACMECARoot != nil && !sliceContains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) { if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
} }
if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) { if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) {
@@ -557,7 +567,7 @@ func newBaseAutomationPolicy(
// consolidateAutomationPolicies combines automation policies that are the same, // consolidateAutomationPolicies combines automation policies that are the same,
// for a cleaner overall output. // for a cleaner overall output.
func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls.AutomationPolicy { func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy, preferWildcard bool) []*caddytls.AutomationPolicy {
// sort from most specific to least specific; we depend on this ordering // sort from most specific to least specific; we depend on this ordering
sort.SliceStable(aps, func(i, j int) bool { sort.SliceStable(aps, func(i, j int) bool {
if automationPolicyIsSubset(aps[i], aps[j]) { if automationPolicyIsSubset(aps[i], aps[j]) {
@@ -580,7 +590,7 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
if !automationPolicyHasAllPublicNames(aps[i]) { if !automationPolicyHasAllPublicNames(aps[i]) {
// if this automation policy has internal names, we might as well remove it // if this automation policy has internal names, we might as well remove it
// so auto-https can implicitly use the internal issuer // so auto-https can implicitly use the internal issuer
aps = append(aps[:i], aps[i+1:]...) aps = slices.Delete(aps, i, i+1)
i-- i--
} }
} }
@@ -597,7 +607,7 @@ outer:
for j := i + 1; j < len(aps); j++ { for j := i + 1; j < len(aps); j++ {
// if they're exactly equal in every way, just keep one of them // if they're exactly equal in every way, just keep one of them
if reflect.DeepEqual(aps[i], aps[j]) { if reflect.DeepEqual(aps[i], aps[j]) {
aps = append(aps[:j], aps[j+1:]...) aps = slices.Delete(aps, j, j+1)
// must re-evaluate current i against next j; can't skip it! // must re-evaluate current i against next j; can't skip it!
// even if i decrements to -1, will be incremented to 0 immediately // even if i decrements to -1, will be incremented to 0 immediately
i-- i--
@@ -627,21 +637,46 @@ outer:
// cause example.com to be served by the less specific policy for // cause example.com to be served by the less specific policy for
// '*.com', which might be different (yes we've seen this happen) // '*.com', which might be different (yes we've seen this happen)
if automationPolicyShadows(i, aps) >= j { if automationPolicyShadows(i, aps) >= j {
aps = append(aps[:i], aps[i+1:]...) aps = slices.Delete(aps, i, i+1)
i-- i--
continue outer continue outer
} }
} else { } else {
// avoid repeated subjects // avoid repeated subjects
for _, subj := range aps[j].SubjectsRaw { for _, subj := range aps[j].SubjectsRaw {
if !sliceContains(aps[i].SubjectsRaw, subj) { if !slices.Contains(aps[i].SubjectsRaw, subj) {
aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj) aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj)
} }
} }
aps = append(aps[:j], aps[j+1:]...) aps = slices.Delete(aps, j, j+1)
j-- j--
} }
} }
if preferWildcard {
// remove subjects from i if they're covered by a wildcard in j
iSubjs := aps[i].SubjectsRaw
for iSubj := 0; iSubj < len(iSubjs); iSubj++ {
for jSubj := range aps[j].SubjectsRaw {
if !strings.HasPrefix(aps[j].SubjectsRaw[jSubj], "*.") {
continue
}
if certmagic.MatchWildcard(aps[i].SubjectsRaw[iSubj], aps[j].SubjectsRaw[jSubj]) {
iSubjs = slices.Delete(iSubjs, iSubj, iSubj+1)
iSubj--
break
}
}
}
aps[i].SubjectsRaw = iSubjs
// remove i if it has no subjects left
if len(aps[i].SubjectsRaw) == 0 {
aps = slices.Delete(aps, i, i+1)
i--
continue outer
}
}
} }
} }
@@ -658,13 +693,9 @@ func automationPolicyIsSubset(a, b *caddytls.AutomationPolicy) bool {
return false return false
} }
for _, aSubj := range a.SubjectsRaw { for _, aSubj := range a.SubjectsRaw {
var inSuperset bool inSuperset := slices.ContainsFunc(b.SubjectsRaw, func(bSubj string) bool {
for _, bSubj := range b.SubjectsRaw { return certmagic.MatchWildcard(aSubj, bSubj)
if certmagic.MatchWildcard(aSubj, bSubj) { })
inSuperset = true
break
}
}
if !inSuperset { if !inSuperset {
return false return false
} }
@@ -709,12 +740,9 @@ func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) b
// automationPolicyHasAllPublicNames returns true if all the names on the policy // automationPolicyHasAllPublicNames returns true if all the names on the policy
// do NOT qualify for public certs OR are tailscale domains. // do NOT qualify for public certs OR are tailscale domains.
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool { func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
for _, subj := range ap.SubjectsRaw { return !slices.ContainsFunc(ap.SubjectsRaw, func(i string) bool {
if !subjectQualifiesForPublicCert(ap, subj) || isTailscaleDomain(subj) { return !subjectQualifiesForPublicCert(ap, i) || isTailscaleDomain(i)
return false })
}
}
return true
} }
func isTailscaleDomain(name string) bool { func isTailscaleDomain(name string) bool {
-1
View File
@@ -84,7 +84,6 @@ func TestLoadUnorderedJSON(t *testing.T) {
"servers": { "servers": {
"s_server": { "s_server": {
"listen": [ "listen": [
":9443",
":9080" ":9080"
], ],
"routes": [ "routes": [
@@ -5,15 +5,15 @@
root_cn "Internal Root Cert" root_cn "Internal Root Cert"
intermediate_cn "Internal Intermediate Cert" intermediate_cn "Internal Intermediate Cert"
} }
} }
} }
acme.example.com { acme.example.com {
acme_server { acme_server {
ca internal ca internal
sign_with_root sign_with_root
} }
} }
---------- ----------
{ {
"apps": { "apps": {
@@ -0,0 +1,106 @@
{
auto_https prefer_wildcard
}
*.example.com {
tls {
dns mock
}
respond "fallback"
}
foo.example.com {
respond "foo"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"foo.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "foo",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"*.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "fallback",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"automatic_https": {
"prefer_wildcard": true
}
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"*.example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}
@@ -0,0 +1,142 @@
{
auto_https disable_redirects
admin off
}
http://localhost {
bind fd/{env.CADDY_HTTP_FD} {
protocols h1
}
log
respond "Hello, HTTP!"
}
https://localhost {
bind fd/{env.CADDY_HTTPS_FD} {
protocols h1 h2
}
bind fdgram/{env.CADDY_HTTP3_FD} {
protocols h3
}
log
respond "Hello, HTTPS!"
}
----------
{
"admin": {
"disabled": true
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
"fd/{env.CADDY_HTTPS_FD}",
"fdgram/{env.CADDY_HTTP3_FD}"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Hello, HTTPS!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"automatic_https": {
"disable_redirects": true
},
"logs": {
"logger_names": {
"localhost": [
""
]
}
},
"listen_protocols": [
[
"h1",
"h2"
],
[
"h3"
]
]
},
"srv1": {
"automatic_https": {
"disable_redirects": true
}
},
"srv2": {
"listen": [
"fd/{env.CADDY_HTTP_FD}"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Hello, HTTP!",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"automatic_https": {
"disable_redirects": true,
"skip": [
"localhost"
]
},
"logs": {
"logger_names": {
"localhost": [
""
]
}
},
"listen_protocols": [
[
"h1"
]
]
}
}
}
}
}
@@ -0,0 +1,39 @@
:80
file_server {
browse {
sort size desc
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"browse": {
"sort": [
"size",
"desc"
]
},
"handler": "file_server",
"hide": [
"./Caddyfile"
]
}
]
}
]
}
}
}
}
}
@@ -17,8 +17,6 @@
admin off admin off
on_demand_tls { on_demand_tls {
ask https://example.com ask https://example.com
interval 30s
burst 20
} }
local_certs local_certs
key_type ed25519 key_type ed25519
@@ -72,10 +70,6 @@
"permission": { "permission": {
"endpoint": "https://example.com", "endpoint": "https://example.com",
"module": "http" "module": "http"
},
"rate_limit": {
"interval": 30000000000,
"burst": 20
} }
} }
}, },
@@ -17,8 +17,6 @@
admin off admin off
on_demand_tls { on_demand_tls {
ask https://example.com ask https://example.com
interval 30s
burst 20
} }
storage_clean_interval 7d storage_clean_interval 7d
renew_interval 1d renew_interval 1d
@@ -89,10 +87,6 @@
"permission": { "permission": {
"endpoint": "https://example.com", "endpoint": "https://example.com",
"module": "http" "module": "http"
},
"rate_limit": {
"interval": 30000000000,
"burst": 20
} }
}, },
"ocsp_interval": 172800000000000, "ocsp_interval": 172800000000000,
@@ -16,8 +16,6 @@
} }
on_demand_tls { on_demand_tls {
ask https://example.com ask https://example.com
interval 30s
burst 20
} }
local_certs local_certs
key_type ed25519 key_type ed25519
@@ -74,10 +72,6 @@
"permission": { "permission": {
"endpoint": "https://example.com", "endpoint": "https://example.com",
"module": "http" "module": "http"
},
"rate_limit": {
"interval": 30000000000,
"burst": 20
} }
} }
} }
@@ -1,11 +1,12 @@
example.com { example.com {
respond <<EOF respond <<EOF
<html> <html>
<head><title>Foo</title> <head><title>Foo</title>
<body>Foo</body> <body>Foo</body>
</html> </html>
EOF 200 EOF 200
} }
---------- ----------
{ {
"apps": { "apps": {
@@ -0,0 +1,58 @@
(snippet) {
header {
{block}
}
}
example.com {
import snippet {
foo bar
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"bar"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,56 @@
(snippet) {
{block}
}
example.com {
import snippet {
header foo bar
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"bar"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,76 @@
(snippet) {
header {
{blocks.foo}
}
header {
{blocks.bar}
}
}
example.com {
import snippet {
foo {
foo a
}
bar {
bar b
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"a"
]
}
}
},
{
"handler": "headers",
"response": {
"set": {
"Bar": [
"b"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,82 @@
(snippet) {
header {
{blocks.bar}
}
import sub_snippet {
bar {
{blocks.foo}
}
}
}
(sub_snippet) {
header {
{blocks.bar}
}
}
example.com {
import snippet {
foo {
foo a
}
bar {
bar b
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Bar": [
"b"
]
}
}
},
{
"handler": "headers",
"response": {
"set": {
"Foo": [
"a"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -1,23 +1,23 @@
example.com example.com
map {host} {my_placeholder} {magic_number} { map {host} {my_placeholder} {magic_number} {
# Should output boolean "true" and an integer # Should output boolean "true" and an integer
example.com true 3 example.com true 3
# Should output a string and null # Should output a string and null
foo.example.com "string value" foo.example.com "string value"
# Should output two strings (quoted int) # Should output two strings (quoted int)
(.*)\.example.com "${1} subdomain" "5" (.*)\.example.com "${1} subdomain" "5"
# Should output null and a string (quoted int) # Should output null and a string (quoted int)
~.*\.net$ - `7` ~.*\.net$ - `7`
# Should output a float and the string "false" # Should output a float and the string "false"
~.*\.xyz$ 123.456 "false" ~.*\.xyz$ 123.456 "false"
# Should output two strings, second being escaped quote # Should output two strings, second being escaped quote
default "unknown domain" \""" default "unknown domain" \"""
} }
vars foo bar vars foo bar
@@ -27,6 +27,7 @@ vars {
ghi 2.3 ghi 2.3
jkl "mn op" jkl "mn op"
} }
---------- ----------
{ {
"apps": { "apps": {
@@ -0,0 +1,37 @@
{
servers :80 {
metrics {
per_host
}
}
}
:80 {
respond "Hello"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"body": "Hello",
"handler": "static_response"
}
]
}
],
"metrics": {
"per_host": true
}
}
}
}
}
}
@@ -0,0 +1,40 @@
:8884
reverse_proxy 127.0.0.1:65535 {
health_uri /health
health_method HEAD
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"health_checks": {
"active": {
"method": "HEAD",
"uri": "/health"
}
},
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
@@ -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
}
]
}
}
}
}
}
@@ -0,0 +1,157 @@
*.example.com {
tls foo@example.com {
dns mock
}
@foo host foo.example.com
handle @foo {
respond "Foo!"
}
@bar host bar.example.com
handle @bar {
respond "Bar!"
}
# Fallback for otherwise unhandled domains
handle {
abort
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"*.example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Foo!",
"handler": "static_response"
}
]
}
]
}
],
"match": [
{
"host": [
"foo.example.com"
]
}
]
},
{
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Bar!",
"handler": "static_response"
}
]
}
]
}
],
"match": [
{
"host": [
"bar.example.com"
]
}
]
},
{
"group": "group3",
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"abort": true,
"handler": "static_response"
}
]
}
]
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"*.example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
}
}
},
"email": "foo@example.com",
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"challenges": {
"dns": {
"provider": {
"name": "mock"
}
}
},
"email": "foo@example.com",
"module": "acme"
}
]
}
]
}
}
}
}
+7 -1
View File
@@ -18,17 +18,23 @@ func TestIntercept(t *testing.T) {
localhost:9080 { localhost:9080 {
respond /intercept "I'm a teapot" 408 respond /intercept "I'm a teapot" 408
header /intercept To-Intercept ok
respond /no-intercept "I'm not a teapot" respond /no-intercept "I'm not a teapot"
intercept { intercept {
@teapot status 408 @teapot status 408
handle_response @teapot { handle_response @teapot {
header /intercept intercepted {resp.header.To-Intercept}
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503 respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
} }
} }
} }
`, "caddyfile") `, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee") r, _ := tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
if r.Header.Get("intercepted") != "ok" {
t.Fatalf(`header "intercepted" value is not "ok": %s`, r.Header.Get("intercepted"))
}
tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot") tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot")
} }
+61
View File
@@ -0,0 +1,61 @@
package integration
import (
"context"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/certmagic"
"github.com/libdns/libdns"
)
func init() {
caddy.RegisterModule(MockDNSProvider{})
}
// MockDNSProvider is a mock DNS provider, for testing config with DNS modules.
type MockDNSProvider struct{}
// CaddyModule returns the Caddy module information.
func (MockDNSProvider) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "dns.providers.mock",
New: func() caddy.Module { return new(MockDNSProvider) },
}
}
// Provision sets up the module.
func (MockDNSProvider) Provision(ctx caddy.Context) error {
return nil
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
// AppendsRecords appends DNS records to the zone.
func (MockDNSProvider) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
return nil, nil
}
// DeleteRecords deletes DNS records from the zone.
func (MockDNSProvider) DeleteRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
return nil, nil
}
// GetRecords gets DNS records from the zone.
func (MockDNSProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
return nil, nil
}
// SetRecords sets DNS records in the zone.
func (MockDNSProvider) SetRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
return nil, nil
}
// Interface guard
var _ caddyfile.Unmarshaler = (*MockDNSProvider)(nil)
var _ certmagic.DNSProvider = (*MockDNSProvider)(nil)
var _ caddy.Provisioner = (*MockDNSProvider)(nil)
var _ caddy.Module = (*MockDNSProvider)(nil)
@@ -0,0 +1,2 @@
foo
@@ -0,0 +1 @@
foo
+5
View File
@@ -1,3 +1,8 @@
// The below line is required to enable post-quantum key agreement in Go 1.23
// by default without insisting on setting a minimum version of 1.23 in go.mod.
// See https://github.com/caddyserver/caddy/issues/6540#issuecomment-2313094905
//go:debug tlskyber=1
// Copyright 2015 Matthew Holt and The Caddy Authors // Copyright 2015 Matthew Holt and The Caddy Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
+15 -11
View File
@@ -8,9 +8,10 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
) )
var rootCmd = &cobra.Command{ var defaultFactory = newRootCommandFactory(func() *cobra.Command {
Use: "caddy", return &cobra.Command{
Long: `Caddy is an extensible server platform written in Go. Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.
At its core, Caddy merely manages configuration. Modules are plugged At its core, Caddy merely manages configuration. Modules are plugged
in statically at compile-time to provide useful functionality. Caddy's 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: Instructions for running Caddy in production are also available:
https://caddyserver.com/docs/running https://caddyserver.com/docs/running
`, `,
Example: ` $ caddy run Example: ` $ caddy run
$ caddy run --config caddy.json $ caddy run --config caddy.json
$ caddy reload --config caddy.json $ caddy reload --config caddy.json
$ caddy stop`, $ caddy stop`,
// kind of annoying to have all the help text printed out if // kind of annoying to have all the help text printed out if
// caddy has an error provisioning its modules, for instance... // caddy has an error provisioning its modules, for instance...
SilenceUsage: true, SilenceUsage: true,
Version: onlyVersionText(), Version: onlyVersionText(),
} }
})
const fullDocsFooter = `Full documentation is available at: const fullDocsFooter = `Full documentation is available at:
https://caddyserver.com/docs/command-line` https://caddyserver.com/docs/command-line`
func init() { func init() {
rootCmd.SetVersionTemplate("{{.Version}}\n") defaultFactory.Use(func(rootCmd *cobra.Command) {
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n") rootCmd.SetVersionTemplate("{{.Version}}\n")
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
})
} }
func onlyVersionText() string { func onlyVersionText() string {
+28
View 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
}
+12 -6
View File
@@ -74,6 +74,10 @@ func cmdStart(fl Flags) (int, error) {
// sure by giving it some random bytes and having it echo // sure by giving it some random bytes and having it echo
// them back to us) // them back to us)
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String()) 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 != "" { if configFlag != "" {
cmd.Args = append(cmd.Args, "--config", configFlag) cmd.Args = append(cmd.Args, "--config", configFlag)
} }
@@ -656,6 +660,8 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
return nil, err return nil, err
} }
parsedAddr.Host = addr parsedAddr.Host = addr
} else if parsedAddr.IsFdNetwork() {
origin = "http://127.0.0.1"
} }
// form the request // form the request
@@ -663,13 +669,13 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
if err != nil { if err != nil {
return nil, fmt.Errorf("making request: %v", err) return nil, fmt.Errorf("making request: %v", err)
} }
if parsedAddr.IsUnixNetwork() { if parsedAddr.IsUnixNetwork() || parsedAddr.IsFdNetwork() {
// We used to conform to RFC 2616 Section 14.26 which requires // We used to conform to RFC 2616 Section 14.26 which requires
// an empty host header when there is no host, as is the case // an empty host header when there is no host, as is the case
// with unix sockets. However, Go required a Host value so we // with unix sockets and socket fds. However, Go required a
// used a hack of a space character as the host (it would see // Host value so we used a hack of a space character as the host
// the Host was non-empty, then trim the space later). As of // (it would see the Host was non-empty, then trim the space later).
// Go 1.20.6 (July 2023), this hack no longer works. See: // As of Go 1.20.6 (July 2023), this hack no longer works. See:
// https://github.com/golang/go/issues/60374 // https://github.com/golang/go/issues/60374
// See also the discussion here: // See also the discussion here:
// https://github.com/golang/go/issues/61431 // https://github.com/golang/go/issues/61431
@@ -710,7 +716,7 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
// if it didn't work, let the user know // if it didn't work, let the user know
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 1024*10)) respBody, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024*2))
if err != nil { if err != nil {
return nil, fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err) return nil, fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err)
} }
+52 -48
View File
@@ -438,43 +438,44 @@ EXPERIMENTAL: May be changed or removed.
}, },
}) })
RegisterCommand(Command{ defaultFactory.Use(func(rootCmd *cobra.Command) {
Name: "manpage", rootCmd.AddCommand(caddyCmdToCobra(Command{
Usage: "--directory <path>", Name: "manpage",
Short: "Generates the manual pages for Caddy commands", Usage: "--directory <path>",
Long: ` Short: "Generates the manual pages for Caddy commands",
Long: `
Generates the manual pages for Caddy commands into the designated directory Generates the manual pages for Caddy commands into the designated directory
tagged into section 8 (System Administration). tagged into section 8 (System Administration).
The manual page files are generated into the directory specified by the 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. argument of --directory. If the directory does not exist, it will be created.
`, `,
CobraFunc: func(cmd *cobra.Command) { CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated") cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) { cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) {
dir := strings.TrimSpace(fl.String("directory")) dir := strings.TrimSpace(fl.String("directory"))
if dir == "" { if dir == "" {
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required") return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
} }
if err := os.MkdirAll(dir, 0o755); err != nil { if err := os.MkdirAll(dir, 0o755); err != nil {
return caddy.ExitCodeFailedQuit, err return caddy.ExitCodeFailedQuit, err
} }
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{ if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
Title: "Caddy", Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil { }, dir); err != nil {
return caddy.ExitCodeFailedQuit, err return caddy.ExitCodeFailedQuit, err
} }
return caddy.ExitCodeSuccess, nil return caddy.ExitCodeSuccess, nil
}) })
}, },
}) }))
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md // source: https://github.com/spf13/cobra/blob/main/shell_completions.md
rootCmd.AddCommand(&cobra.Command{ rootCmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]", Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script", Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions: Long: fmt.Sprintf(`To load completions:
Bash: 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 PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile. # and source this file from your PowerShell profile.
`, rootCmd.Root().Name()), `, rootCmd.Root().Name()),
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] { switch args[0] {
case "bash": case "bash":
return cmd.Root().GenBashCompletion(os.Stdout) return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh": case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout) return cmd.Root().GenZshCompletion(os.Stdout)
case "fish": case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true) return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell": case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default: default:
return fmt.Errorf("unrecognized shell: %s", args[0]) return fmt.Errorf("unrecognized shell: %s", args[0])
} }
}, },
})
}) })
} }
@@ -563,7 +565,9 @@ func RegisterCommand(cmd Command) {
if !commandNameRegex.MatchString(cmd.Name) { if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command 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]$`) var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
+8 -12
View File
@@ -72,7 +72,7 @@ func Main() {
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err)) 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 var exitError *exitError
if errors.As(err, &exitError) { if errors.As(err, &exitError) {
os.Exit(exitError.ExitCode) os.Exit(exitError.ExitCode)
@@ -119,7 +119,6 @@ func isCaddyfile(configFile, adapterName string) (bool, error) {
baseConfig := strings.ToLower(filepath.Base(configFile)) baseConfig := strings.ToLower(filepath.Base(configFile))
baseConfigExt := filepath.Ext(baseConfig) baseConfigExt := filepath.Ext(baseConfig)
startsOrEndsInCaddyfile := strings.HasPrefix(baseConfig, "caddyfile") || strings.HasSuffix(baseConfig, ".caddyfile") startsOrEndsInCaddyfile := strings.HasPrefix(baseConfig, "caddyfile") || strings.HasSuffix(baseConfig, ".caddyfile")
extNotCaddyfileOrJSON := (baseConfigExt != "" && baseConfigExt != ".caddyfile" && baseConfigExt != ".json")
if baseConfigExt == ".json" { if baseConfigExt == ".json" {
return false, nil return false, nil
@@ -130,18 +129,15 @@ func isCaddyfile(configFile, adapterName string) (bool, error) {
// the config file has an extension, // the config file has an extension,
// and isn't a JSON file (e.g. Caddyfile.yaml), // and isn't a JSON file (e.g. Caddyfile.yaml),
// then we don't know what the config format is. // then we don't know what the config format is.
if adapterName == "" && startsOrEndsInCaddyfile && extNotCaddyfileOrJSON { if adapterName == "" && startsOrEndsInCaddyfile {
return false, fmt.Errorf("ambiguous config file format; please specify adapter (use --adapter)")
}
// If the config file starts or ends with "caddyfile",
// the extension of the config file is not ".json", AND
// the user did not specify an adapter, then we assume it's Caddyfile.
if startsOrEndsInCaddyfile &&
baseConfigExt != ".json" &&
adapterName == "" {
return true, nil return true, nil
} }
// adapter is not empty,
// adapter is not "caddyfile",
// extension is not ".json",
// extension is not ".caddyfile"
// file does not start with "Caddyfile"
return false, nil return false, nil
} }
+24 -3
View File
@@ -226,15 +226,16 @@ func Test_isCaddyfile(t *testing.T) {
wantErr: false, wantErr: false,
}, },
{ {
name: "config is Caddyfile.yaml without adapter", name: "config is Caddyfile.yaml with adapter",
args: args{ args: args{
configFile: "./Caddyfile.yaml", configFile: "./Caddyfile.yaml",
adapterName: "", adapterName: "yaml",
}, },
want: false, want: false,
wantErr: true, wantErr: false,
}, },
{ {
name: "json is not caddyfile but not error", name: "json is not caddyfile but not error",
args: args{ args: args{
configFile: "./Caddyfile.json", configFile: "./Caddyfile.json",
@@ -243,6 +244,26 @@ func Test_isCaddyfile(t *testing.T) {
want: false, want: false,
wantErr: false, wantErr: false,
}, },
{
name: "prefix of Caddyfile and ./ with any extension is Caddyfile",
args: args{
configFile: "./Caddyfile.prd",
adapterName: "",
},
want: true,
wantErr: false,
},
{
name: "prefix of Caddyfile without ./ with any extension is Caddyfile",
args: args{
configFile: "Caddyfile.prd",
adapterName: "",
},
want: true,
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
+21 -1
View File
@@ -23,6 +23,8 @@ import (
"reflect" "reflect"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/exp/zapslog" "go.uber.org/zap/exp/zapslog"
@@ -47,6 +49,7 @@ type Context struct {
ancestry []Module ancestry []Module
cleanupFuncs []func() // invoked at every config unload cleanupFuncs []func() // invoked at every config unload
exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL) exitFuncs []func(context.Context) // invoked at config unload ONLY IF the process is exiting (EXPERIMENTAL)
metricsRegistry *prometheus.Registry
} }
// NewContext provides a new context derived from the given // NewContext provides a new context derived from the given
@@ -58,7 +61,7 @@ type Context struct {
// modules which are loaded will be properly unloaded. // modules which are loaded will be properly unloaded.
// See standard library context package's documentation. // See standard library context package's documentation.
func NewContext(ctx Context) (Context, context.CancelFunc) { func NewContext(ctx Context) (Context, context.CancelFunc) {
newCtx := Context{moduleInstances: make(map[string][]Module), cfg: ctx.cfg} newCtx := Context{moduleInstances: make(map[string][]Module), cfg: ctx.cfg, metricsRegistry: prometheus.NewPedanticRegistry()}
c, cancel := context.WithCancel(ctx.Context) c, cancel := context.WithCancel(ctx.Context)
wrappedCancel := func() { wrappedCancel := func() {
cancel() cancel()
@@ -79,6 +82,7 @@ func NewContext(ctx Context) (Context, context.CancelFunc) {
} }
} }
newCtx.Context = c newCtx.Context = c
newCtx.initMetrics()
return newCtx, wrappedCancel return newCtx, wrappedCancel
} }
@@ -97,6 +101,22 @@ func (ctx *Context) Filesystems() FileSystems {
return ctx.cfg.filesystems return ctx.cfg.filesystems
} }
// Returns the active metrics registry for the context
// EXPERIMENTAL: This API is subject to change.
func (ctx *Context) GetMetricsRegistry() *prometheus.Registry {
return ctx.metricsRegistry
}
func (ctx *Context) initMetrics() {
ctx.metricsRegistry.MustRegister(
collectors.NewBuildInfoCollector(),
adminMetrics.requestCount,
adminMetrics.requestErrors,
globalMetrics.configSuccess,
globalMetrics.configSuccessTime,
)
}
// OnExit executes f when the process exits gracefully. // OnExit executes f when the process exits gracefully.
// The function is only executed if the process is gracefully // The function is only executed if the process is gracefully
// shut down while this context is active. // shut down while this context is active.
+37 -36
View File
@@ -1,56 +1,58 @@
module github.com/caddyserver/caddy/v2 module github.com/caddyserver/caddy/v2
go 1.21.0 go 1.22.3
toolchain go1.22.2 toolchain go1.23.0
require ( require (
github.com/BurntSushi/toml v1.3.2 github.com/BurntSushi/toml v1.4.0
github.com/Masterminds/sprig/v3 v3.2.3 github.com/Masterminds/sprig/v3 v3.3.0
github.com/alecthomas/chroma/v2 v2.13.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.3 github.com/caddyserver/certmagic v0.21.4
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
github.com/google/cel-go v0.20.1 github.com/google/cel-go v0.21.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.17.8 github.com/klauspost/compress v1.17.10
github.com/klauspost/cpuid/v2 v2.2.7 github.com/klauspost/cpuid/v2 v2.2.8
github.com/mholt/acmez/v2 v2.0.1 github.com/mholt/acmez/v2 v2.0.3
github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_golang v1.19.1
github.com/quic-go/quic-go v0.44.0 github.com/quic-go/quic-go v0.48.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
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53
github.com/yuin/goldmark v1.7.1 github.com/yuin/goldmark v1.7.4
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
go.opentelemetry.io/contrib/propagators/autoprop v0.42.0 go.opentelemetry.io/contrib/propagators/autoprop v0.42.0
go.opentelemetry.io/otel v1.24.0 go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/sdk v1.21.0
go.uber.org/automaxprocs v1.5.3 go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
go.uber.org/zap/exp v0.2.0 go.uber.org/zap/exp v0.2.0
golang.org/x/crypto v0.23.0 golang.org/x/crypto v0.27.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 golang.org/x/crypto/x509roots/fallback v0.0.0-20240930154113-a0819fbb0244
golang.org/x/net v0.25.0 golang.org/x/net v0.29.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.8.0
golang.org/x/term v0.20.0 golang.org/x/term v0.24.0
golang.org/x/time v0.5.0 golang.org/x/time v0.6.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
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
@@ -62,12 +64,12 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // 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.4.0 // 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
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.4 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.17.0 // indirect go.opentelemetry.io/contrib/propagators/aws v1.17.0 // indirect
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
@@ -82,13 +84,13 @@ require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 github.com/cespare/xxhash/v2 v2.2.0
github.com/chzyer/readline v1.5.1 // indirect github.com/chzyer/readline v1.5.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/badger v1.6.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect
@@ -103,8 +105,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/huandu/xstrings v1.3.3 // indirect github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgconn v1.14.3 // indirect
@@ -114,27 +115,27 @@ require (
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/pgx/v4 v4.18.3 // indirect github.com/jackc/pgx/v4 v4.18.3 // indirect
github.com/libdns/libdns v0.2.2 // indirect github.com/libdns/libdns v0.2.2
github.com/manifoldco/promptui v0.9.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/miekg/dns v1.1.59 // indirect github.com/miekg/dns v1.1.62 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/slackhq/nebula v1.6.1 // indirect github.com/slackhq/nebula v1.6.1 // indirect
github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cast v1.7.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/urfave/cli v1.22.14 // indirect github.com/urfave/cli v1.22.14 // indirect
go.etcd.io/bbolt v1.3.9 // indirect go.etcd.io/bbolt v1.3.9 // indirect
@@ -146,10 +147,10 @@ require (
go.step.sm/crypto v0.45.0 go.step.sm/crypto v0.45.0
go.step.sm/linkedca v0.20.1 // indirect go.step.sm/linkedca v0.20.1 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.18.0 // indirect
golang.org/x/sys v0.20.0 golang.org/x/sys v0.25.0
golang.org/x/text v0.15.0 // indirect golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.21.0 // indirect golang.org/x/tools v0.22.0 // indirect
google.golang.org/grpc v1.63.2 // indirect google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.34.1 // indirect google.golang.org/protobuf v1.34.1 // indirect
howett.net/plist v1.0.0 // indirect howett.net/plist v1.0.0 // indirect
+214 -79
View File
@@ -1,3 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg= cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro= cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
@@ -12,32 +16,41 @@ cloud.google.com/go/kms v1.16.0 h1:1yZsRPhmargZOmY+fVAh8IKiR9HzCb0U1zsxb5g2nRY=
cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc= cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc=
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU= cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
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/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
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=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -71,10 +84,13 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1q
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 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/caddyserver/certmagic v0.21.3 h1:pqRRry3yuB4CWBVq9+cUqu+Y6E2z8TswbhNx1AZeYm0= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/caddyserver/certmagic v0.21.3/go.mod h1:Zq6pklO9nVRl3DIFUw9gVUfXKdpc/0qwTUAQMBlfgtI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
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.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
@@ -93,17 +109,19 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -128,11 +146,19 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -158,55 +184,68 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
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/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.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.0/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo= github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k= github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98= github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY= github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e h1:bwOy7hAFd0C91URzMIEBfr6BAz29yk7Qj0cy6S7DJlU= github.com/google/pprof v0.0.0-20231212022811-ec68065c825e h1:bwOy7hAFd0C91URzMIEBfr6BAz29yk7Qj0cy6S7DJlU=
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20231212022811-ec68065c825e/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -257,14 +296,16 @@ github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -272,6 +313,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -284,7 +326,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
@@ -297,31 +341,36 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k= github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U= github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
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/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=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964 h1:ct/vxNBgHpASQ4sT8NaBX9LtsEtluZqaUJydLG50U3E=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -329,18 +378,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
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.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= github.com/quic-go/quic-go v0.48.0 h1:2TCyvBrMu1Z25rvIAlnp2dPT4lgh/uTqLqiXVpp5AeU=
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= github.com/quic-go/quic-go v0.48.0/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
@@ -355,11 +408,35 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -381,17 +458,18 @@ github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjI
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU= github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4= github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A= github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -415,29 +493,33 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 h1:pV0H+XIvFoP7pl1MRtyPXh5hqoxB5I7snOtTHgrn6HU= github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 h1:uxMgm0C+EjytfAqyfBG55ZONKQ7mvd7x4YYCWsf8QHQ=
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
@@ -478,8 +560,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
@@ -497,8 +579,12 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -508,44 +594,67 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 h1:TgSqweA595vD0Zt86JzLv3Pb/syKg8gd5KMGGbJPYFw= golang.org/x/crypto/x509roots/fallback v0.0.0-20240930154113-a0819fbb0244 h1:3uziZWNwkTfxhMOxJB13NpTR+svHLMMVDhTrEyZOd3k=
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8= golang.org/x/crypto/x509roots/fallback v0.0.0-20240930154113-a0819fbb0244/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -562,37 +671,41 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -603,21 +716,37 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4= google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE= google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw=
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw=
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk= google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y= google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
@@ -629,16 +758,22 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+14
View File
@@ -0,0 +1,14 @@
package internal
// PrivateRangesCIDR returns a list of private CIDR range
// strings, which can be used as a configuration shortcut.
func PrivateRangesCIDR() []string {
return []string{
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1",
}
}
+68 -12
View File
@@ -18,7 +18,11 @@ package caddy
import ( import (
"context" "context"
"fmt"
"net" "net"
"os"
"slices"
"strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -31,10 +35,49 @@ func reuseUnixSocket(network, addr string) (any, error) {
} }
func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) { func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) {
switch network { var socketFile *os.File
case "udp", "udp4", "udp6", "unixgram":
fd := slices.Contains([]string{"fd", "fdgram"}, network)
if fd {
socketFd, err := strconv.ParseUint(address, 0, strconv.IntSize)
if err != nil {
return nil, fmt.Errorf("invalid file descriptor: %v", err)
}
func() {
socketFilesMu.Lock()
defer socketFilesMu.Unlock()
socketFdWide := uintptr(socketFd)
var ok bool
socketFile, ok = socketFiles[socketFdWide]
if !ok {
socketFile = os.NewFile(socketFdWide, lnKey)
if socketFile != nil {
socketFiles[socketFdWide] = socketFile
}
}
}()
if socketFile == nil {
return nil, fmt.Errorf("invalid socket file descriptor: %d", socketFd)
}
}
datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
if datagram {
sharedPc, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) { sharedPc, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
pc, err := config.ListenPacket(ctx, network, address) var (
pc net.PacketConn
err error
)
if fd {
pc, err = net.FilePacketConn(socketFile)
} else {
pc, err = config.ListenPacket(ctx, network, address)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -44,20 +87,27 @@ func listenReusable(ctx context.Context, lnKey string, network, address string,
return nil, err return nil, err
} }
return &fakeClosePacketConn{sharedPacketConn: sharedPc.(*sharedPacketConn)}, nil return &fakeClosePacketConn{sharedPacketConn: sharedPc.(*sharedPacketConn)}, nil
}
default: sharedLn, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
sharedLn, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) { var (
ln, err := config.Listen(ctx, network, address) ln net.Listener
if err != nil { err error
return nil, err )
} if fd {
return &sharedListener{Listener: ln, key: lnKey}, nil ln, err = net.FileListener(socketFile)
}) } else {
ln, err = config.Listen(ctx, network, address)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &fakeCloseListener{sharedListener: sharedLn.(*sharedListener), keepAlivePeriod: config.KeepAlive}, nil return &sharedListener{Listener: ln, key: lnKey}, nil
})
if err != nil {
return nil, err
} }
return &fakeCloseListener{sharedListener: sharedLn.(*sharedListener), keepAlivePeriod: config.KeepAlive}, nil
} }
// fakeCloseListener is a private wrapper over a listener that // fakeCloseListener is a private wrapper over a listener that
@@ -260,3 +310,9 @@ var (
Unwrap() net.PacketConn Unwrap() net.PacketConn
}) = (*fakeClosePacketConn)(nil) }) = (*fakeClosePacketConn)(nil)
) )
// socketFiles is a fd -> *os.File map used to make a FileListener/FilePacketConn from a socket file descriptor.
var socketFiles = map[uintptr]*os.File{}
// socketFilesMu synchronizes socketFiles insertions
var socketFilesMu sync.Mutex
+122 -52
View File
@@ -22,10 +22,14 @@ package caddy
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"io/fs" "io/fs"
"net" "net"
"os" "os"
"slices"
"strconv"
"sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
@@ -34,12 +38,9 @@ import (
) )
// reuseUnixSocket copies and reuses the unix domain socket (UDS) if we already // reuseUnixSocket copies and reuses the unix domain socket (UDS) if we already
// have it open; if not, unlink it so we can have it. No-op if not a unix network. // have it open; if not, unlink it so we can have it.
// No-op if not a unix network.
func reuseUnixSocket(network, addr string) (any, error) { func reuseUnixSocket(network, addr string) (any, error) {
if !IsUnixNetwork(network) {
return nil, nil
}
socketKey := listenerKey(network, addr) socketKey := listenerKey(network, addr)
socket, exists := unixSockets[socketKey] socket, exists := unixSockets[socketKey]
@@ -71,7 +72,7 @@ func reuseUnixSocket(network, addr string) (any, error) {
return nil, err return nil, err
} }
atomic.AddInt32(unixSocket.count, 1) atomic.AddInt32(unixSocket.count, 1)
unixSockets[socketKey] = &unixConn{pc.(*net.UnixConn), addr, socketKey, unixSocket.count} unixSockets[socketKey] = &unixConn{pc.(*net.UnixConn), socketKey, unixSocket.count}
} }
return unixSockets[socketKey], nil return unixSockets[socketKey], nil
@@ -89,56 +90,107 @@ func reuseUnixSocket(network, addr string) (any, error) {
return nil, nil return nil, nil
} }
// listenReusable creates a new listener for the given network and address, and adds it to listenerPool.
func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) { func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) {
// wrap any Control function set by the user so we can also add our reusePort control without clobbering theirs
oldControl := config.Control
config.Control = func(network, address string, c syscall.RawConn) error {
if oldControl != nil {
if err := oldControl(network, address, c); err != nil {
return err
}
}
return reusePort(network, address, c)
}
// even though SO_REUSEPORT lets us bind the socket multiple times, // even though SO_REUSEPORT lets us bind the socket multiple times,
// we still put it in the listenerPool so we can count how many // we still put it in the listenerPool so we can count how many
// configs are using this socket; necessary to ensure we can know // configs are using this socket; necessary to ensure we can know
// whether to enforce shutdown delays, for example (see #5393). // whether to enforce shutdown delays, for example (see #5393).
var ln io.Closer var (
var err error ln io.Closer
switch network { err error
case "udp", "udp4", "udp6", "unixgram": socketFile *os.File
ln, err = config.ListenPacket(ctx, network, address) )
default:
ln, err = config.Listen(ctx, network, address) fd := slices.Contains([]string{"fd", "fdgram"}, network)
if fd {
socketFd, err := strconv.ParseUint(address, 0, strconv.IntSize)
if err != nil {
return nil, fmt.Errorf("invalid file descriptor: %v", err)
}
func() {
socketFilesMu.Lock()
defer socketFilesMu.Unlock()
socketFdWide := uintptr(socketFd)
var ok bool
socketFile, ok = socketFiles[socketFdWide]
if !ok {
socketFile = os.NewFile(socketFdWide, lnKey)
if socketFile != nil {
socketFiles[socketFdWide] = socketFile
}
}
}()
if socketFile == nil {
return nil, fmt.Errorf("invalid socket file descriptor: %d", socketFd)
}
} else {
// wrap any Control function set by the user so we can also add our reusePort control without clobbering theirs
oldControl := config.Control
config.Control = func(network, address string, c syscall.RawConn) error {
if oldControl != nil {
if err := oldControl(network, address, c); err != nil {
return err
}
}
return reusePort(network, address, c)
}
} }
datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
if datagram {
if fd {
ln, err = net.FilePacketConn(socketFile)
} else {
ln, err = config.ListenPacket(ctx, network, address)
}
} else {
if fd {
ln, err = net.FileListener(socketFile)
} else {
ln, err = config.Listen(ctx, network, address)
}
}
if err == nil { if err == nil {
listenerPool.LoadOrStore(lnKey, nil) listenerPool.LoadOrStore(lnKey, nil)
} }
// if new listener is a unix socket, make sure we can reuse it later if datagram {
// (we do our own "unlink on close" -- not required, but more tidy) if !fd {
one := int32(1) // TODO: Not 100% sure this is necessary, but we do this for net.UnixListener, so...
if unix, ok := ln.(*net.UnixListener); ok { if unix, ok := ln.(*net.UnixConn); ok {
unix.SetUnlinkOnClose(false) one := int32(1)
ln = &unixListener{unix, lnKey, &one} ln = &unixConn{unix, lnKey, &one}
unixSockets[lnKey] = ln.(*unixListener) unixSockets[lnKey] = ln.(*unixConn)
} }
}
// TODO: Not 100% sure this is necessary, but we do this for net.UnixListener in listen_unix.go, so... // lightly wrap the connection so that when it is closed,
if unix, ok := ln.(*net.UnixConn); ok { // we can decrement the usage pool counter
ln = &unixConn{unix, address, lnKey, &one} if specificLn, ok := ln.(net.PacketConn); ok {
unixSockets[lnKey] = ln.(*unixConn) ln = deletePacketConn{specificLn, lnKey}
} }
} else {
// lightly wrap the listener so that when it is closed, if !fd {
// we can decrement the usage pool counter // if new listener is a unix socket, make sure we can reuse it later
switch specificLn := ln.(type) { // (we do our own "unlink on close" -- not required, but more tidy)
case net.Listener: if unix, ok := ln.(*net.UnixListener); ok {
return deleteListener{specificLn, lnKey}, err unix.SetUnlinkOnClose(false)
case net.PacketConn: one := int32(1)
return deletePacketConn{specificLn, lnKey}, err ln = &unixListener{unix, lnKey, &one}
unixSockets[lnKey] = ln.(*unixListener)
}
}
// lightly wrap the listener so that when it is closed,
// we can decrement the usage pool counter
if specificLn, ok := ln.(net.Listener); ok {
ln = deleteListener{specificLn, lnKey}
}
} }
// other types, I guess we just return them directly // other types, I guess we just return them directly
@@ -170,12 +222,18 @@ type unixListener struct {
func (uln *unixListener) Close() error { func (uln *unixListener) Close() error {
newCount := atomic.AddInt32(uln.count, -1) newCount := atomic.AddInt32(uln.count, -1)
if newCount == 0 { if newCount == 0 {
file, err := uln.File()
var name string
if err == nil {
name = file.Name()
}
defer func() { defer func() {
addr := uln.Addr().String()
unixSocketsMu.Lock() unixSocketsMu.Lock()
delete(unixSockets, uln.mapKey) delete(unixSockets, uln.mapKey)
unixSocketsMu.Unlock() unixSocketsMu.Unlock()
_ = syscall.Unlink(addr) if err == nil {
_ = syscall.Unlink(name)
}
}() }()
} }
return uln.UnixListener.Close() return uln.UnixListener.Close()
@@ -183,19 +241,25 @@ func (uln *unixListener) Close() error {
type unixConn struct { type unixConn struct {
*net.UnixConn *net.UnixConn
filename string mapKey string
mapKey string count *int32 // accessed atomically
count *int32 // accessed atomically
} }
func (uc *unixConn) Close() error { func (uc *unixConn) Close() error {
newCount := atomic.AddInt32(uc.count, -1) newCount := atomic.AddInt32(uc.count, -1)
if newCount == 0 { if newCount == 0 {
file, err := uc.File()
var name string
if err == nil {
name = file.Name()
}
defer func() { defer func() {
unixSocketsMu.Lock() unixSocketsMu.Lock()
delete(unixSockets, uc.mapKey) delete(unixSockets, uc.mapKey)
unixSocketsMu.Unlock() unixSocketsMu.Unlock()
_ = syscall.Unlink(uc.filename) if err == nil {
_ = syscall.Unlink(name)
}
}() }()
} }
return uc.UnixConn.Close() return uc.UnixConn.Close()
@@ -211,6 +275,12 @@ var unixSockets = make(map[string]interface {
File() (*os.File, error) File() (*os.File, error)
}) })
// socketFiles is a fd -> *os.File map used to make a FileListener/FilePacketConn from a socket file descriptor.
var socketFiles = map[uintptr]*os.File{}
// socketFilesMu synchronizes socketFiles insertions
var socketFilesMu sync.Mutex
// deleteListener is a type that simply deletes itself // deleteListener is a type that simply deletes itself
// from the listenerPool when it closes. It is used // from the listenerPool when it closes. It is used
// solely for the purpose of reference counting (i.e. // solely for the purpose of reference counting (i.e.
+82 -40
View File
@@ -31,6 +31,7 @@ import (
"github.com/quic-go/quic-go" "github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/http3"
"github.com/quic-go/quic-go/qlog"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/time/rate" "golang.org/x/time/rate"
@@ -57,11 +58,9 @@ type NetworkAddress struct {
EndPort uint EndPort uint
} }
// ListenAll calls Listen() for all addresses represented by this struct, i.e. all ports in the range. // ListenAll calls Listen for all addresses represented by this struct, i.e. all ports in the range.
// (If the address doesn't use ports or has 1 port only, then only 1 listener will be created.) // (If the address doesn't use ports or has 1 port only, then only 1 listener will be created.)
// It returns an error if any listener failed to bind, and closes any listeners opened up to that point. // It returns an error if any listener failed to bind, and closes any listeners opened up to that point.
//
// TODO: Experimental API: subject to change or removal.
func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig) ([]any, error) { func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig) ([]any, error) {
var listeners []any var listeners []any
var err error var err error
@@ -107,7 +106,8 @@ func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig)
// portOffset to the start port. (For network types that do not use ports, the // portOffset to the start port. (For network types that do not use ports, the
// portOffset is ignored.) // portOffset is ignored.)
// //
// The provided ListenConfig is used to create the listener. Its Control function, // First Listen checks if a plugin can provide a listener from this address. Otherwise,
// the provided ListenConfig is used to create the listener. Its Control function,
// if set, may be wrapped by an internally-used Control function. The provided // if set, may be wrapped by an internally-used Control function. The provided
// context may be used to cancel long operations early. The context is not used // context may be used to cancel long operations early. The context is not used
// to close the listener after it has been created. // to close the listener after it has been created.
@@ -130,8 +130,8 @@ func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig)
// Unix sockets will be unlinked before being created, to ensure we can bind to // Unix sockets will be unlinked before being created, to ensure we can bind to
// it even if the previous program using it exited uncleanly; it will also be // it even if the previous program using it exited uncleanly; it will also be
// unlinked upon a graceful exit (or when a new config does not use that socket). // unlinked upon a graceful exit (or when a new config does not use that socket).
// // Listen synchronizes binds to unix domain sockets to avoid race conditions
// TODO: Experimental API: subject to change or removal. // while an existing socket is unlinked.
func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) { func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) {
if na.IsUnixNetwork() { if na.IsUnixNetwork() {
unixSocketsMu.Lock() unixSocketsMu.Lock()
@@ -149,54 +149,53 @@ func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net
func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) { func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) {
var ( var (
ln any ln any
err error err error
address string address string
unixFileMode fs.FileMode unixFileMode fs.FileMode
isAbstractUnixSocket bool
) )
// split unix socket addr early so lnKey // split unix socket addr early so lnKey
// is independent of permissions bits // is independent of permissions bits
if na.IsUnixNetwork() { if na.IsUnixNetwork() {
var err error
address, unixFileMode, err = internal.SplitUnixSocketPermissionsBits(na.Host) address, unixFileMode, err = internal.SplitUnixSocketPermissionsBits(na.Host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
isAbstractUnixSocket = strings.HasPrefix(address, "@") } else if na.IsFdNetwork() {
address = na.Host
} else { } else {
address = na.JoinHostPort(portOffset) address = na.JoinHostPort(portOffset)
} }
// if this is a unix socket, see if we already have it open,
// force socket permissions on it and return early
if socket, err := reuseUnixSocket(na.Network, address); socket != nil || err != nil {
if !isAbstractUnixSocket {
if err := os.Chmod(address, unixFileMode); err != nil {
return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
}
}
return socket, err
}
lnKey := listenerKey(na.Network, address)
if strings.HasPrefix(na.Network, "ip") { if strings.HasPrefix(na.Network, "ip") {
ln, err = config.ListenPacket(ctx, na.Network, address) ln, err = config.ListenPacket(ctx, na.Network, address)
} else { } else {
ln, err = listenReusable(ctx, lnKey, na.Network, address, config) if na.IsUnixNetwork() {
// if this is a unix socket, see if we already have it open
ln, err = reuseUnixSocket(na.Network, address)
}
if ln == nil && err == nil {
// otherwise, create a new listener
lnKey := listenerKey(na.Network, address)
ln, err = listenReusable(ctx, lnKey, na.Network, address, config)
}
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ln == nil { if ln == nil {
return nil, fmt.Errorf("unsupported network type: %s", na.Network) return nil, fmt.Errorf("unsupported network type: %s", na.Network)
} }
if IsUnixNetwork(na.Network) { if IsUnixNetwork(na.Network) {
isAbstractUnixSocket := strings.HasPrefix(address, "@")
if !isAbstractUnixSocket { if !isAbstractUnixSocket {
if err := os.Chmod(address, unixFileMode); err != nil { err = os.Chmod(address, unixFileMode)
if err != nil {
return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err) return nil, fmt.Errorf("unable to set permissions (%s) on %s: %v", unixFileMode, address, err)
} }
} }
@@ -211,18 +210,22 @@ func (na NetworkAddress) IsUnixNetwork() bool {
return IsUnixNetwork(na.Network) return IsUnixNetwork(na.Network)
} }
// IsUnixNetwork returns true if na.Network is
// fd or fdgram.
func (na NetworkAddress) IsFdNetwork() bool {
return IsFdNetwork(na.Network)
}
// JoinHostPort is like net.JoinHostPort, but where the port // JoinHostPort is like net.JoinHostPort, but where the port
// is StartPort + offset. // is StartPort + offset.
func (na NetworkAddress) JoinHostPort(offset uint) string { func (na NetworkAddress) JoinHostPort(offset uint) string {
if na.IsUnixNetwork() { if na.IsUnixNetwork() || na.IsFdNetwork() {
return na.Host return na.Host
} }
return net.JoinHostPort(na.Host, strconv.Itoa(int(na.StartPort+offset))) return net.JoinHostPort(na.Host, strconv.FormatUint(uint64(na.StartPort+offset), 10))
} }
// Expand returns one NetworkAddress for each port in the port range. // Expand returns one NetworkAddress for each port in the port range.
//
// This is EXPERIMENTAL and subject to change or removal.
func (na NetworkAddress) Expand() []NetworkAddress { func (na NetworkAddress) Expand() []NetworkAddress {
size := na.PortRangeSize() size := na.PortRangeSize()
addrs := make([]NetworkAddress, size) addrs := make([]NetworkAddress, size)
@@ -253,7 +256,7 @@ func (na NetworkAddress) PortRangeSize() uint {
} }
func (na NetworkAddress) isLoopback() bool { func (na NetworkAddress) isLoopback() bool {
if na.IsUnixNetwork() { if na.IsUnixNetwork() || na.IsFdNetwork() {
return true return true
} }
if na.Host == "localhost" { if na.Host == "localhost" {
@@ -297,6 +300,30 @@ func IsUnixNetwork(netw string) bool {
return strings.HasPrefix(netw, "unix") return strings.HasPrefix(netw, "unix")
} }
// IsFdNetwork returns true if the netw is a fd network.
func IsFdNetwork(netw string) bool {
return strings.HasPrefix(netw, "fd")
}
// normally we would simply append the port,
// but if host is IPv6, we need to ensure it
// is enclosed in [ ]; net.JoinHostPort does
// this for us, but host 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
func ParseNetworkAddressFromHostPort(host, port string) (NetworkAddress, error) {
network, addr, ok := strings.Cut(host, "/")
if !ok {
addr = network
network = ""
}
addr = strings.Trim(addr, "[]") // IPv6
networkAddr := JoinNetworkAddress(network, addr, port)
return ParseNetworkAddress(networkAddr)
}
// ParseNetworkAddress parses addr into its individual // ParseNetworkAddress parses addr into its individual
// components. The input string is expected to be of // components. The input string is expected to be of
// the form "network/host:port-range" where any part is // the form "network/host:port-range" where any part is
@@ -327,6 +354,12 @@ func ParseNetworkAddressWithDefaults(addr, defaultNetwork string, defaultPort ui
Host: host, Host: host,
}, err }, err
} }
if IsFdNetwork(network) {
return NetworkAddress{
Network: network,
Host: host,
}, nil
}
var start, end uint64 var start, end uint64
if port == "" { if port == "" {
start = uint64(defaultPort) start = uint64(defaultPort)
@@ -367,7 +400,7 @@ func SplitNetworkAddress(a string) (network, host, port string, err error) {
network = strings.ToLower(strings.TrimSpace(beforeSlash)) network = strings.ToLower(strings.TrimSpace(beforeSlash))
a = afterSlash a = afterSlash
} }
if IsUnixNetwork(network) { if IsUnixNetwork(network) || IsFdNetwork(network) {
host = a host = a
return return
} }
@@ -398,7 +431,7 @@ func JoinNetworkAddress(network, host, port string) string {
if network != "" { if network != "" {
a = network + "/" a = network + "/"
} }
if (host != "" && port == "") || IsUnixNetwork(network) { if (host != "" && port == "") || IsUnixNetwork(network) || IsFdNetwork(network) {
a += host a += host
} else if port != "" { } else if port != "" {
a += net.JoinHostPort(host, port) a += net.JoinHostPort(host, port)
@@ -406,9 +439,11 @@ func JoinNetworkAddress(network, host, port string) string {
return a return a
} }
// ListenQUIC returns a quic.EarlyListener suitable for use in a Caddy module. // ListenQUIC returns a http3.QUICEarlyListener suitable for use in a Caddy module.
// The network will be transformed into a QUIC-compatible type (if unix, then //
// unixgram will be used; otherwise, udp will be used). // The network will be transformed into a QUIC-compatible type if the same address can be used with
// different networks. Currently this just means that for tcp, udp will be used with the same
// address instead.
// //
// NOTE: This API is EXPERIMENTAL and may be changed or removed. // NOTE: This API is EXPERIMENTAL and may be changed or removed.
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICEarlyListener, error) { func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICEarlyListener, error) {
@@ -443,7 +478,13 @@ func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config
Conn: h3ln, Conn: h3ln,
VerifySourceAddress: func(addr net.Addr) bool { return !limiter.Allow() }, VerifySourceAddress: func(addr net.Addr) bool { return !limiter.Allow() },
} }
earlyLn, err := tr.ListenEarly(http3.ConfigureTLSConfig(quicTlsConfig), &quic.Config{Allow0RTT: true}) earlyLn, err := tr.ListenEarly(
http3.ConfigureTLSConfig(quicTlsConfig),
&quic.Config{
Allow0RTT: true,
Tracer: qlog.DefaultConnectionTracer,
},
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -616,7 +657,8 @@ func RegisterNetwork(network string, getListener ListenerFunc) {
if network == "tcp" || network == "tcp4" || network == "tcp6" || if network == "tcp" || network == "tcp4" || network == "tcp6" ||
network == "udp" || network == "udp4" || network == "udp6" || network == "udp" || network == "udp4" || network == "udp6" ||
network == "unix" || network == "unixpacket" || network == "unixgram" || network == "unix" || network == "unixpacket" || network == "unixgram" ||
strings.HasPrefix("ip:", network) || strings.HasPrefix("ip4:", network) || strings.HasPrefix("ip6:", network) { strings.HasPrefix("ip:", network) || strings.HasPrefix("ip4:", network) || strings.HasPrefix("ip6:", network) ||
network == "fd" || network == "fdgram" {
panic("network type " + network + " is reserved") panic("network type " + network + " is reserved")
} }
+12
View File
@@ -292,6 +292,10 @@ type BaseLog struct {
// The encoder is how the log entries are formatted or encoded. // The encoder is how the log entries are formatted or encoded.
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"` EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
// Tees entries through a zap.Core module which can extract
// log entry metadata and fields for further processing.
CoreRaw json.RawMessage `json:"core,omitempty" caddy:"namespace=caddy.logging.cores inline_key=module"`
// Level is the minimum level to emit, and is inclusive. // Level is the minimum level to emit, and is inclusive.
// Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL // Possible levels: DEBUG, INFO, WARN, ERROR, PANIC, and FATAL
Level string `json:"level,omitempty"` Level string `json:"level,omitempty"`
@@ -366,6 +370,14 @@ func (cl *BaseLog) provisionCommon(ctx Context, logging *Logging) error {
cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener) cl.encoder = newDefaultProductionLogEncoder(cl.writerOpener)
} }
cl.buildCore() cl.buildCore()
if cl.CoreRaw != nil {
mod, err := ctx.LoadModule(cl, "CoreRaw")
if err != nil {
return fmt.Errorf("loading log core module: %v", err)
}
core := mod.(zapcore.Core)
cl.core = zapcore.NewTee(cl.core, core)
}
return nil return nil
} }
+16 -7
View File
@@ -4,30 +4,33 @@ import (
"net/http" "net/http"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/caddyserver/caddy/v2/internal/metrics" "github.com/caddyserver/caddy/v2/internal/metrics"
) )
// define and register the metrics used in this package. // define and register the metrics used in this package.
func init() { func init() {
prometheus.MustRegister(collectors.NewBuildInfoCollector())
const ns, sub = "caddy", "admin" const ns, sub = "caddy", "admin"
adminMetrics.requestCount = prometheus.NewCounterVec(prometheus.CounterOpts{
adminMetrics.requestCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "http_requests_total", Name: "http_requests_total",
Help: "Counter of requests made to the Admin API's HTTP endpoints.", Help: "Counter of requests made to the Admin API's HTTP endpoints.",
}, []string{"handler", "path", "code", "method"}) }, []string{"handler", "path", "code", "method"})
adminMetrics.requestErrors = promauto.NewCounterVec(prometheus.CounterOpts{ adminMetrics.requestErrors = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "http_request_errors_total", Name: "http_request_errors_total",
Help: "Number of requests resulting in middleware errors.", Help: "Number of requests resulting in middleware errors.",
}, []string{"handler", "path", "method"}) }, []string{"handler", "path", "method"})
globalMetrics.configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "caddy_config_last_reload_successful",
Help: "Whether the last configuration reload attempt was successful.",
})
globalMetrics.configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "caddy_config_last_reload_success_timestamp_seconds",
Help: "Timestamp of the last successful configuration reload.",
})
} }
// adminMetrics is a collection of metrics that can be tracked for the admin API. // adminMetrics is a collection of metrics that can be tracked for the admin API.
@@ -36,6 +39,12 @@ var adminMetrics = struct {
requestErrors *prometheus.CounterVec requestErrors *prometheus.CounterVec
}{} }{}
// globalMetrics is a collection of metrics that can be tracked for Caddy global state
var globalMetrics = struct {
configSuccess prometheus.Gauge
configSuccessTime prometheus.Gauge
}{}
// Similar to promhttp.InstrumentHandlerCounter, but upper-cases method names // Similar to promhttp.InstrumentHandlerCounter, but upper-cases method names
// instead of lower-casing them. // instead of lower-casing them.
// //
+18 -12
View File
@@ -124,18 +124,19 @@ func (app *App) Provision(ctx caddy.Context) error {
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 {
if sub.HandlersRaw != nil { if sub.HandlersRaw == nil {
handlersIface, err := ctx.LoadModule(sub, "HandlersRaw") continue
if err != nil { }
return fmt.Errorf("loading event subscriber modules: %v", err) handlersIface, err := ctx.LoadModule(sub, "HandlersRaw")
} if err != nil {
for _, h := range handlersIface.([]any) { return fmt.Errorf("loading event subscriber modules: %v", err)
sub.Handlers = append(sub.Handlers, h.(Handler)) }
} for _, h := range handlersIface.([]any) {
if len(sub.Handlers) == 0 { sub.Handlers = append(sub.Handlers, h.(Handler))
// pointless to bind without any handlers }
return fmt.Errorf("no handlers defined") if len(sub.Handlers) == 0 {
} // pointless to bind without any handlers
return fmt.Errorf("no handlers defined")
} }
} }
@@ -355,6 +356,11 @@ type Event struct {
origin caddy.Module origin caddy.Module
} }
func (e Event) ID() uuid.UUID { return e.id }
func (e Event) Timestamp() time.Time { return e.ts }
func (e Event) Name() string { return e.name }
func (e Event) Origin() caddy.Module { return e.origin }
// CloudEvent exports event e as a structure that, when // CloudEvent exports event e as a structure that, when
// serialized as JSON, is compatible with the // serialized as JSON, is compatible with the
// CloudEvents spec. // CloudEvents spec.
+168 -95
View File
@@ -18,6 +18,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"maps"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
@@ -203,17 +204,75 @@ func (app *App) Provision(ctx caddy.Context) error {
} }
} }
// the Go standard library does not let us serve only HTTP/2 using
// http.Server; we would probably need to write our own server
if !srv.protocol("h1") && (srv.protocol("h2") || srv.protocol("h2c")) {
return fmt.Errorf("server %s: cannot enable HTTP/2 or H2C without enabling HTTP/1.1; add h1 to protocols or remove h2/h2c", srvName)
}
// if no protocols configured explicitly, enable all except h2c // if no protocols configured explicitly, enable all except h2c
if len(srv.Protocols) == 0 { if len(srv.Protocols) == 0 {
srv.Protocols = []string{"h1", "h2", "h3"} srv.Protocols = []string{"h1", "h2", "h3"}
} }
srvProtocolsUnique := map[string]struct{}{}
for _, srvProtocol := range srv.Protocols {
srvProtocolsUnique[srvProtocol] = struct{}{}
}
_, h1ok := srvProtocolsUnique["h1"]
_, h2ok := srvProtocolsUnique["h2"]
_, h2cok := srvProtocolsUnique["h2c"]
// the Go standard library does not let us serve only HTTP/2 using
// http.Server; we would probably need to write our own server
if !h1ok && (h2ok || h2cok) {
return fmt.Errorf("server %s: cannot enable HTTP/2 or H2C without enabling HTTP/1.1; add h1 to protocols or remove h2/h2c", srvName)
}
if srv.ListenProtocols != nil {
if len(srv.ListenProtocols) != len(srv.Listen) {
return fmt.Errorf("server %s: listener protocols count does not match address count: %d != %d",
srvName, len(srv.ListenProtocols), len(srv.Listen))
}
for i, lnProtocols := range srv.ListenProtocols {
if lnProtocols != nil {
// populate empty listen protocols with server protocols
lnProtocolsDefault := false
var lnProtocolsInclude []string
srvProtocolsInclude := maps.Clone(srvProtocolsUnique)
// keep existing listener protocols unless they are empty
for _, lnProtocol := range lnProtocols {
if lnProtocol == "" {
lnProtocolsDefault = true
} else {
lnProtocolsInclude = append(lnProtocolsInclude, lnProtocol)
delete(srvProtocolsInclude, lnProtocol)
}
}
// append server protocols to listener protocols if any listener protocols were empty
if lnProtocolsDefault {
for _, srvProtocol := range srv.Protocols {
if _, ok := srvProtocolsInclude[srvProtocol]; ok {
lnProtocolsInclude = append(lnProtocolsInclude, srvProtocol)
}
}
}
lnProtocolsIncludeUnique := map[string]struct{}{}
for _, lnProtocol := range lnProtocolsInclude {
lnProtocolsIncludeUnique[lnProtocol] = struct{}{}
}
_, h1ok := lnProtocolsIncludeUnique["h1"]
_, h2ok := lnProtocolsIncludeUnique["h2"]
_, h2cok := lnProtocolsIncludeUnique["h2c"]
// check if any listener protocols contain h2 or h2c without h1
if !h1ok && (h2ok || h2cok) {
return fmt.Errorf("server %s, listener %d: cannot enable HTTP/2 or H2C without enabling HTTP/1.1; add h1 to protocols or remove h2/h2c", srvName, i)
}
srv.ListenProtocols[i] = lnProtocolsInclude
}
}
}
// if not explicitly configured by the user, disallow TLS // if not explicitly configured by the user, disallow TLS
// client auth bypass (domain fronting) which could // client auth bypass (domain fronting) which could
// otherwise be exploited by sending an unprotected SNI // otherwise be exploited by sending an unprotected SNI
@@ -288,6 +347,10 @@ 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 {
if srv.Metrics != nil {
srv.Metrics.init = sync.Once{}
srv.Metrics.httpMetrics = &httpMetrics{}
}
err := srv.Routes.ProvisionHandlers(ctx, srv.Metrics) 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)
@@ -344,7 +407,7 @@ func (app *App) Validate() error {
// check that every address in the port range is unique to this server; // check that every address in the port range is unique to this server;
// we do not use <= here because PortRangeSize() adds 1 to EndPort for us // we do not use <= here because PortRangeSize() adds 1 to EndPort for us
for i := uint(0); i < listenAddr.PortRangeSize(); i++ { for i := uint(0); i < listenAddr.PortRangeSize(); i++ {
addr := caddy.JoinNetworkAddress(listenAddr.Network, listenAddr.Host, strconv.Itoa(int(listenAddr.StartPort+i))) addr := caddy.JoinNetworkAddress(listenAddr.Network, listenAddr.Host, strconv.FormatUint(uint64(listenAddr.StartPort+i), 10))
if sn, ok := lnAddrs[addr]; ok { if sn, ok := lnAddrs[addr]; ok {
return fmt.Errorf("server %s: listener address repeated: %s (already claimed by server '%s')", srvName, addr, sn) return fmt.Errorf("server %s: listener address repeated: %s (already claimed by server '%s')", srvName, addr, sn)
} }
@@ -422,99 +485,118 @@ func (app *App) Start() error {
srv.server.Handler = h2c.NewHandler(srv, h2server) srv.server.Handler = h2c.NewHandler(srv, h2server)
} }
for _, lnAddr := range srv.Listen { for lnIndex, lnAddr := range srv.Listen {
listenAddr, err := caddy.ParseNetworkAddress(lnAddr) listenAddr, err := caddy.ParseNetworkAddress(lnAddr)
if err != nil { if err != nil {
return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err) return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
} }
srv.addresses = append(srv.addresses, listenAddr) srv.addresses = append(srv.addresses, listenAddr)
for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ { protocols := srv.Protocols
// create the listener for this socket if srv.ListenProtocols != nil && srv.ListenProtocols[lnIndex] != nil {
hostport := listenAddr.JoinHostPort(portOffset) protocols = srv.ListenProtocols[lnIndex]
lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)}) }
if err != nil {
return fmt.Errorf("listening on %s: %v", listenAddr.At(portOffset), err)
}
ln := lnAny.(net.Listener)
// wrap listener before TLS (up to the TLS placeholder wrapper) protocolsUnique := map[string]struct{}{}
var lnWrapperIdx int for _, protocol := range protocols {
for i, lnWrapper := range srv.listenerWrappers { protocolsUnique[protocol] = struct{}{}
if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok { }
lnWrapperIdx = i + 1 // mark the next wrapper's spot _, h1ok := protocolsUnique["h1"]
break _, h2ok := protocolsUnique["h2"]
} _, h2cok := protocolsUnique["h2c"]
ln = lnWrapper.WrapListener(ln) _, h3ok := protocolsUnique["h3"]
}
for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
hostport := listenAddr.JoinHostPort(portOffset)
// 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()
if useTLS {
// create TLS listener - this enables and terminates TLS
ln = tls.NewListener(ln, tlsCfg)
// enable HTTP/3 if configured // enable HTTP/3 if configured
if srv.protocol("h3") { if h3ok && useTLS {
// Can't serve HTTP/3 on the same socket as HTTP/1 and 2 because it uses app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
// a different transport mechanism... which is fine, but the OS doesn't if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil {
// differentiate between a SOCK_STREAM file and a SOCK_DGRAM file; they return err
// 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 len(srv.Protocols) > 1 && listenAddr.IsUnixNetwork() { if h3ok && !useTLS {
app.logger.Warn("HTTP/3 disabled because Unix can't multiplex STREAM and DGRAM on same socket", // Can only serve h3 with TLS enabled
zap.String("file", hostport)) app.logger.Warn("HTTP/3 skipped because it requires TLS",
for i := range srv.Protocols { zap.String("network", listenAddr.Network),
if srv.Protocols[i] == "h3" { zap.String("addr", hostport))
srv.Protocols = append(srv.Protocols[:i], srv.Protocols[i+1:]...) }
break
} if h1ok || h2ok && useTLS || h2cok {
} // create the listener for this socket
} else { lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)})
app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport)) if err != nil {
if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil { return fmt.Errorf("listening on %s: %v", listenAddr.At(portOffset), err)
return err }
} ln, ok := lnAny.(net.Listener)
if !ok {
return fmt.Errorf("network '%s' cannot handle HTTP/1 or HTTP/2 connections", listenAddr.Network)
}
// wrap listener before TLS (up to the TLS placeholder wrapper)
var lnWrapperIdx int
for i, lnWrapper := range srv.listenerWrappers {
if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok {
lnWrapperIdx = i + 1 // mark the next wrapper's spot
break
} }
ln = lnWrapper.WrapListener(ln)
}
if useTLS {
// create TLS listener - this enables and terminates TLS
ln = tls.NewListener(ln, tlsCfg)
}
// finish wrapping listener where we left off before TLS
for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ {
ln = srv.listenerWrappers[i].WrapListener(ln)
}
// handle http2 if use tls listener wrapper
if h2ok {
http2lnWrapper := &http2Listener{
Listener: ln,
server: srv.server,
h2server: h2server,
}
srv.h2listeners = append(srv.h2listeners, http2lnWrapper)
ln = http2lnWrapper
}
// if binding to port 0, the OS chooses a port for us;
// but the user won't know the port unless we print it
if !listenAddr.IsUnixNetwork() && !listenAddr.IsFdNetwork() && listenAddr.StartPort == 0 && listenAddr.EndPort == 0 {
app.logger.Info("port 0 listener",
zap.String("input_address", lnAddr),
zap.String("actual_address", ln.Addr().String()))
}
app.logger.Debug("starting server loop",
zap.String("address", ln.Addr().String()),
zap.Bool("tls", useTLS),
zap.Bool("http3", srv.h3server != nil))
srv.listeners = append(srv.listeners, ln)
// enable HTTP/1 if configured
if h1ok {
//nolint:errcheck
go srv.server.Serve(ln)
} }
} }
// finish wrapping listener where we left off before TLS if h2ok && !useTLS {
for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ { // Can only serve h2 with TLS enabled
ln = srv.listenerWrappers[i].WrapListener(ln) app.logger.Warn("HTTP/2 skipped because it requires TLS",
} zap.String("network", listenAddr.Network),
zap.String("addr", hostport))
// handle http2 if use tls listener wrapper
if useTLS {
http2lnWrapper := &http2Listener{
Listener: ln,
server: srv.server,
h2server: h2server,
}
srv.h2listeners = append(srv.h2listeners, http2lnWrapper)
ln = http2lnWrapper
}
// if binding to port 0, the OS chooses a port for us;
// but the user won't know the port unless we print it
if !listenAddr.IsUnixNetwork() && listenAddr.StartPort == 0 && listenAddr.EndPort == 0 {
app.logger.Info("port 0 listener",
zap.String("input_address", lnAddr),
zap.String("actual_address", ln.Addr().String()))
}
app.logger.Debug("starting server loop",
zap.String("address", ln.Addr().String()),
zap.Bool("tls", useTLS),
zap.Bool("http3", srv.h3server != nil))
srv.listeners = append(srv.listeners, ln)
// enable HTTP/1 if configured
if srv.protocol("h1") {
//nolint:errcheck
go srv.server.Serve(ln)
} }
} }
} }
@@ -607,16 +689,7 @@ func (app *App) Stop() error {
return return
} }
// First close h3server then close listeners unlike stdlib for several reasons: if err := server.h3server.Shutdown(ctx); err != nil {
// 1, udp has only a single socket, once closed, no more data can be read and
// written. In contrast, closing tcp listeners won't affect established connections.
// This have something to do with graceful shutdown when upstream implements it.
// 2, h3server will only close listeners it's registered (quic listeners). Closing
// listener first and these listeners maybe unregistered thus won't be closed. caddy
// distinguishes quic-listener and underlying datagram sockets.
// TODO: CloseGracefully, once implemented upstream (see https://github.com/quic-go/quic-go/issues/2103)
if err := server.h3server.Close(); err != nil {
app.logger.Error("HTTP/3 server shutdown", app.logger.Error("HTTP/3 server shutdown",
zap.Error(err), zap.Error(err),
zap.Strings("addresses", server.Listen)) zap.Strings("addresses", server.Listen))
+43 -16
View File
@@ -17,6 +17,7 @@ package caddyhttp
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
@@ -64,17 +65,12 @@ type AutoHTTPSConfig struct {
// enabled. To force automated certificate management // enabled. To force automated certificate management
// regardless of loaded certificates, set this to true. // regardless of loaded certificates, set this to true.
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"` IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
}
// Skipped returns true if name is in skipSlice, which // If true, automatic HTTPS will prefer wildcard names
// should be either the Skip or SkipCerts field on ahc. // and ignore non-wildcard names if both are available.
func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool { // This allows for writing a config with top-level host
for _, n := range skipSlice { // matchers without having those names produce certificates.
if name == n { PreferWildcard bool `json:"prefer_wildcard,omitempty"`
return true
}
}
return false
} }
// automaticHTTPSPhase1 provisions all route matchers, determines // automaticHTTPSPhase1 provisions all route matchers, determines
@@ -158,7 +154,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v", return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err) srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
} }
if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.Skip) { if !slices.Contains(srv.AutoHTTPS.Skip, d) {
serverDomainSet[d] = struct{}{} serverDomainSet[d] = struct{}{}
} }
} }
@@ -167,6 +163,27 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
} }
} }
if srv.AutoHTTPS.PreferWildcard {
wildcards := make(map[string]struct{})
for d := range serverDomainSet {
if strings.HasPrefix(d, "*.") {
wildcards[d[2:]] = struct{}{}
}
}
for d := range serverDomainSet {
if strings.HasPrefix(d, "*.") {
continue
}
base := d
if idx := strings.Index(d, "."); idx != -1 {
base = d[idx+1:]
}
if _, ok := wildcards[base]; ok {
delete(serverDomainSet, d)
}
}
}
// nothing more to do here if there are no domains that qualify for // nothing more to do here if there are no domains that qualify for
// automatic HTTPS and there are no explicit TLS connection policies: // automatic HTTPS and there are no explicit TLS connection policies:
// if there is at least one domain but no TLS conn policy (F&&T), we'll // if there is at least one domain but no TLS conn policy (F&&T), we'll
@@ -193,7 +210,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
} else { } else {
for d := range serverDomainSet { for d := range serverDomainSet {
if certmagic.SubjectQualifiesForCert(d) && if certmagic.SubjectQualifiesForCert(d) &&
!srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) { !slices.Contains(srv.AutoHTTPS.SkipCerts, d) {
// if a certificate for this name is already loaded, // if a certificate for this name is already loaded,
// don't obtain another one for it, unless we are // don't obtain another one for it, unless we are
// supposed to ignore loaded certificates // supposed to ignore loaded certificates
@@ -303,11 +320,21 @@ uniqueDomainsLoop:
} }
} }
// if no automation policy exists for the name yet, we // if no automation policy exists for the name yet, we will associate it with an implicit one;
// will associate it with an implicit one // we handle tailscale domains specially, and we also separate out identifiers that need the
// internal issuer (self-signed certs); certmagic does not consider public IP addresses to be
// disqualified for public certs, because there are public CAs that will issue certs for IPs.
// However, with auto-HTTPS, many times there is no issuer explicitly defined, and the default
// issuers do not (currently, as of 2024) issue IP certificates; so assign all IP subjects to
// the internal issuer when there are no explicit automation policies
shouldUseInternal := func(ident string) bool {
usingDefaultIssuersAndIsIP := certmagic.SubjectIsIP(ident) &&
(app.tlsApp == nil || app.tlsApp.Automation == nil || len(app.tlsApp.Automation.Policies) == 0)
return !certmagic.SubjectQualifiesForPublicCert(d) || usingDefaultIssuersAndIsIP
}
if isTailscaleDomain(d) { if isTailscaleDomain(d) {
tailscale = append(tailscale, d) tailscale = append(tailscale, d)
} else if !certmagic.SubjectQualifiesForPublicCert(d) { } else if shouldUseInternal(d) {
internal = append(internal, d) internal = append(internal, d)
} }
} }
@@ -742,7 +769,7 @@ func (app *App) automaticHTTPSPhase2() error {
) )
err := app.tlsApp.Manage(app.allCertDomains) err := app.tlsApp.Manage(app.allCertDomains)
if err != nil { if err != nil {
return fmt.Errorf("managing certificates for %v: %s", app.allCertDomains, err) return fmt.Errorf("managing certificates for %d domains: %s", len(app.allCertDomains), err)
} }
app.allCertDomains = nil // no longer needed; allow GC to deallocate app.allCertDomains = nil // no longer needed; allow GC to deallocate
return nil return nil
+4 -3
View File
@@ -19,6 +19,7 @@ import (
"net/http" "net/http"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -76,9 +77,9 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
for provName, prov := range a.Providers { for provName, prov := range a.Providers {
user, authed, err = prov.Authenticate(w, r) user, authed, err = prov.Authenticate(w, r)
if err != nil { if err != nil {
a.logger.Error("auth provider returned error", if c := a.logger.Check(zapcore.ErrorLevel, "auth provider returned error"); c != nil {
zap.String("provider", provName), c.Write(zap.String("provider", provName), zap.Error(err))
zap.Error(err)) }
continue continue
} }
if authed { if authed {
+25 -1
View File
@@ -94,7 +94,31 @@ func TestSanitizedPathJoin(t *testing.T) {
inputRoot: "C:\\www", inputRoot: "C:\\www",
inputPath: "/D:\\foo\\bar", inputPath: "/D:\\foo\\bar",
expect: filepath.Join("C:\\www", "D:\\foo\\bar"), expect: filepath.Join("C:\\www", "D:\\foo\\bar"),
expectWindows: filepath.Join("C:\\www"), // inputPath fails IsLocal on Windows expectWindows: "C:\\www", // inputPath fails IsLocal on Windows
},
{
inputRoot: `C:\www`,
inputPath: `/..\windows\win.ini`,
expect: `C:\www/..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..%5cwindows%5cwin.ini`,
expect: `C:\www/..\windows\win.ini`,
expectWindows: `C:\www`,
},
{
inputRoot: `C:\www`,
inputPath: `/..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5cwindows%5cwin.ini`,
expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
expectWindows: `C:\www`,
}, },
{ {
// https://github.com/golang/go/issues/56336#issuecomment-1416214885 // https://github.com/golang/go/issues/56336#issuecomment-1416214885
+36 -19
View File
@@ -126,6 +126,10 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
// light (and possibly naïve) syntactic sugar // light (and possibly naïve) syntactic sugar
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion) m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
// as a second pass, we'll strip the escape character from an escaped
// placeholder, so that it can be used as an input to other CEL functions
m.expandedExpr = escapedPlaceholderRegexp.ReplaceAllString(m.expandedExpr, escapedPlaceholderExpansion)
// our type adapter expands CEL's standard type support // our type adapter expands CEL's standard type support
m.ta = celTypeAdapter{} m.ta = celTypeAdapter{}
@@ -159,14 +163,17 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
// create the CEL environment // create the CEL environment
env, err := cel.NewEnv( env, err := cel.NewEnv(
cel.Function(placeholderFuncName, cel.SingletonBinaryBinding(m.caddyPlaceholderFunc), cel.Overload( cel.Function(CELPlaceholderFuncName, cel.SingletonBinaryBinding(m.caddyPlaceholderFunc), cel.Overload(
placeholderFuncName+"_httpRequest_string", CELPlaceholderFuncName+"_httpRequest_string",
[]*cel.Type{httpRequestObjectType, cel.StringType}, []*cel.Type{httpRequestObjectType, cel.StringType},
cel.AnyType, cel.AnyType,
)), )),
cel.Variable("request", httpRequestObjectType), cel.Variable(CELRequestVarName, httpRequestObjectType),
cel.CustomTypeAdapter(m.ta), cel.CustomTypeAdapter(m.ta),
ext.Strings(), ext.Strings(),
ext.Bindings(),
ext.Lists(),
ext.Math(),
matcherLib, matcherLib,
) )
if err != nil { if err != nil {
@@ -247,7 +254,7 @@ func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
return types.NewErr( return types.NewErr(
"invalid request of type '%v' to %s(request, placeholderVarName)", "invalid request of type '%v' to %s(request, placeholderVarName)",
lhs.Type(), lhs.Type(),
placeholderFuncName, CELPlaceholderFuncName,
) )
} }
phStr, ok := rhs.(types.String) phStr, ok := rhs.(types.String)
@@ -255,7 +262,7 @@ func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
return types.NewErr( return types.NewErr(
"invalid placeholder variable name of type '%v' to %s(request, placeholderVarName)", "invalid placeholder variable name of type '%v' to %s(request, placeholderVarName)",
rhs.Type(), rhs.Type(),
placeholderFuncName, CELPlaceholderFuncName,
) )
} }
@@ -275,7 +282,7 @@ var httpRequestCELType = cel.ObjectType("http.Request", traits.ReceiverType)
type celHTTPRequest struct{ *http.Request } type celHTTPRequest struct{ *http.Request }
func (cr celHTTPRequest) ResolveName(name string) (any, bool) { func (cr celHTTPRequest) ResolveName(name string) (any, bool) {
if name == "request" { if name == CELRequestVarName {
return cr, true return cr, true
} }
return nil, false return nil, false
@@ -340,7 +347,7 @@ func (celTypeAdapter) NativeToValue(value any) ref.Val {
case time.Time: case time.Time:
return types.Timestamp{Time: v} return types.Timestamp{Time: v}
case error: case error:
types.NewErr(v.Error()) return types.WrapErr(v)
} }
return types.DefaultTypeAdapter.NativeToValue(value) return types.DefaultTypeAdapter.NativeToValue(value)
} }
@@ -457,15 +464,15 @@ func CELMatcherDecorator(funcName string, fac CELMatcherFactory) interpreter.Int
callArgs := call.Args() callArgs := call.Args()
reqAttr, ok := callArgs[0].(interpreter.InterpretableAttribute) reqAttr, ok := callArgs[0].(interpreter.InterpretableAttribute)
if !ok { if !ok {
return nil, errors.New("missing 'request' argument") return nil, errors.New("missing 'req' argument")
} }
nsAttr, ok := reqAttr.Attr().(interpreter.NamespacedAttribute) nsAttr, ok := reqAttr.Attr().(interpreter.NamespacedAttribute)
if !ok { if !ok {
return nil, errors.New("missing 'request' argument") return nil, errors.New("missing 'req' argument")
} }
varNames := nsAttr.CandidateVariableNames() varNames := nsAttr.CandidateVariableNames()
if len(varNames) != 1 || len(varNames) == 1 && varNames[0] != "request" { if len(varNames) != 1 || len(varNames) == 1 && varNames[0] != CELRequestVarName {
return nil, errors.New("missing 'request' argument") return nil, errors.New("missing 'req' argument")
} }
matcherData, ok := callArgs[1].(interpreter.InterpretableConst) matcherData, ok := callArgs[1].(interpreter.InterpretableConst)
if !ok { if !ok {
@@ -499,7 +506,7 @@ func CELMatcherRuntimeFunction(funcName string, fac CELMatcherFactory) functions
return func(celReq, matcherData ref.Val) ref.Val { return func(celReq, matcherData ref.Val) ref.Val {
matcher, err := fac(matcherData) matcher, err := fac(matcherData)
if err != nil { if err != nil {
return types.NewErr(err.Error()) return types.WrapErr(err)
} }
httpReq := celReq.Value().(celHTTPRequest) httpReq := celReq.Value().(celHTTPRequest)
return types.Bool(matcher.Match(httpReq.Request)) return types.Bool(matcher.Match(httpReq.Request))
@@ -524,7 +531,7 @@ func celMatcherStringListMacroExpander(funcName string) cel.MacroFactory {
return nil, eh.NewError(arg.ID(), "matcher arguments must be string constants") return nil, eh.NewError(arg.ID(), "matcher arguments must be string constants")
} }
} }
return eh.NewCall(funcName, eh.NewIdent("request"), eh.NewList(matchArgs...)), nil return eh.NewCall(funcName, eh.NewIdent(CELRequestVarName), eh.NewList(matchArgs...)), nil
} }
} }
@@ -538,7 +545,7 @@ func celMatcherStringMacroExpander(funcName string) parser.MacroExpander {
return nil, eh.NewError(0, "matcher requires one argument") return nil, eh.NewError(0, "matcher requires one argument")
} }
if isCELStringExpr(args[0]) { if isCELStringExpr(args[0]) {
return eh.NewCall(funcName, eh.NewIdent("request"), args[0]), nil return eh.NewCall(funcName, eh.NewIdent(CELRequestVarName), args[0]), nil
} }
return nil, eh.NewError(args[0].ID(), "matcher argument must be a string literal") return nil, eh.NewError(args[0].ID(), "matcher argument must be a string literal")
} }
@@ -572,7 +579,7 @@ func celMatcherJSONMacroExpander(funcName string) parser.MacroExpander {
return nil, eh.NewError(entry.AsMapEntry().Value().ID(), "matcher map values must be string or list literals") return nil, eh.NewError(entry.AsMapEntry().Value().ID(), "matcher map values must be string or list literals")
} }
} }
return eh.NewCall(funcName, eh.NewIdent("request"), arg), nil return eh.NewCall(funcName, eh.NewIdent(CELRequestVarName), arg), nil
case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.SelectKind: case ast.UnspecifiedExprKind, ast.CallKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.SelectKind:
// appeasing the linter :) // appeasing the linter :)
} }
@@ -646,7 +653,7 @@ func isCELCaddyPlaceholderCall(e ast.Expr) bool {
switch e.Kind() { switch e.Kind() {
case ast.CallKind: case ast.CallKind:
call := e.AsCall() call := e.AsCall()
if call.FunctionName() == "caddyPlaceholder" { if call.FunctionName() == CELPlaceholderFuncName {
return true return true
} }
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind: case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
@@ -701,8 +708,15 @@ func isCELStringListLiteral(e ast.Expr) bool {
// expressions with a proper CEL function call; this is // expressions with a proper CEL function call; this is
// just for syntactic sugar. // just for syntactic sugar.
var ( var (
placeholderRegexp = regexp.MustCompile(`{([a-zA-Z][\w.-]+)}`) // The placeholder may not be preceded by a backslash; the expansion
placeholderExpansion = `caddyPlaceholder(request, "${1}")` // will include the preceding character if it is not a backslash.
placeholderRegexp = regexp.MustCompile(`([^\\]|^){([a-zA-Z][\w.-]+)}`)
placeholderExpansion = `${1}ph(req, "${2}")`
// As a second pass, we need to strip the escape character in front of
// the placeholder, if it exists.
escapedPlaceholderRegexp = regexp.MustCompile(`\\{([a-zA-Z][\w.-]+)}`)
escapedPlaceholderExpansion = `{${1}}`
CELTypeJSON = cel.MapType(cel.StringType, cel.DynType) CELTypeJSON = cel.MapType(cel.StringType, cel.DynType)
) )
@@ -710,7 +724,10 @@ var (
var httpRequestObjectType = cel.ObjectType("http.Request") var httpRequestObjectType = cel.ObjectType("http.Request")
// The name of the CEL function which accesses Replacer values. // The name of the CEL function which accesses Replacer values.
const placeholderFuncName = "caddyPlaceholder" const CELPlaceholderFuncName = "ph"
// The name of the CEL request variable.
const CELRequestVarName = "req"
const MatcherNameCtxKey = "matcher_name" const MatcherNameCtxKey = "matcher_name"
+105 -28
View File
@@ -70,13 +70,36 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
wantResult: true, wantResult: true,
}, },
{ {
name: "header error (MatchHeader)", name: "header matches an escaped placeholder value (MatchHeader)",
expression: &MatchExpression{
Expr: `header({'Field': '\\\{foobar}'})`,
},
urlTarget: "https://example.com/foo",
httpHeader: &http.Header{"Field": []string{"{foobar}"}},
wantResult: true,
},
{
name: "header matches an placeholder replaced during the header matcher (MatchHeader)",
expression: &MatchExpression{
Expr: `header({'Field': '\{http.request.uri.path}'})`,
},
urlTarget: "https://example.com/foo",
httpHeader: &http.Header{"Field": []string{"/foo"}},
wantResult: true,
},
{
name: "header error, invalid escape sequence (MatchHeader)",
expression: &MatchExpression{
Expr: `header({'Field': '\\{foobar}'})`,
},
wantErr: true,
},
{
name: "header error, needs to be JSON syntax with field as key (MatchHeader)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `header('foo')`, Expr: `header('foo')`,
}, },
urlTarget: "https://example.com/foo", wantErr: true,
httpHeader: &http.Header{"Field": []string{"foo", "bar"}},
wantErr: true,
}, },
{ {
name: "header_regexp matches (MatchHeaderRE)", name: "header_regexp matches (MatchHeaderRE)",
@@ -110,9 +133,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `header_regexp('foo')`, Expr: `header_regexp('foo')`,
}, },
urlTarget: "https://example.com/foo", wantErr: true,
httpHeader: &http.Header{"Field": []string{"foo", "bar"}},
wantErr: true,
}, },
{ {
name: "host matches localhost (MatchHost)", name: "host matches localhost (MatchHost)",
@@ -143,8 +164,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `host(80)`, Expr: `host(80)`,
}, },
urlTarget: "http://localhost:80", wantErr: true,
wantErr: true,
}, },
{ {
name: "method does not match (MatchMethod)", name: "method does not match (MatchMethod)",
@@ -169,9 +189,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `method()`, Expr: `method()`,
}, },
urlTarget: "https://foo.example.com", wantErr: true,
httpMethod: "PUT",
wantErr: true,
}, },
{ {
name: "path matches substring (MatchPath)", name: "path matches substring (MatchPath)",
@@ -266,24 +284,21 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `protocol()`, Expr: `protocol()`,
}, },
urlTarget: "https://example.com", wantErr: true,
wantErr: true,
}, },
{ {
name: "protocol invocation error too many args (MatchProtocol)", name: "protocol invocation error too many args (MatchProtocol)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `protocol('grpc', 'https')`, Expr: `protocol('grpc', 'https')`,
}, },
urlTarget: "https://example.com", wantErr: true,
wantErr: true,
}, },
{ {
name: "protocol invocation error wrong arg type (MatchProtocol)", name: "protocol invocation error wrong arg type (MatchProtocol)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `protocol(true)`, Expr: `protocol(true)`,
}, },
urlTarget: "https://example.com", wantErr: true,
wantErr: true,
}, },
{ {
name: "query does not match against a specific value (MatchQuery)", name: "query does not match against a specific value (MatchQuery)",
@@ -330,40 +345,35 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `query({1: "1"})`, Expr: `query({1: "1"})`,
}, },
urlTarget: "https://example.com/foo", wantErr: true,
wantErr: true,
}, },
{ {
name: "query error typed struct instead of map (MatchQuery)", name: "query error typed struct instead of map (MatchQuery)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `query(Message{field: "1"})`, Expr: `query(Message{field: "1"})`,
}, },
urlTarget: "https://example.com/foo", wantErr: true,
wantErr: true,
}, },
{ {
name: "query error bad map value type (MatchQuery)", name: "query error bad map value type (MatchQuery)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `query({"debug": 1})`, Expr: `query({"debug": 1})`,
}, },
urlTarget: "https://example.com/foo/?debug=1", wantErr: true,
wantErr: true,
}, },
{ {
name: "query error no args (MatchQuery)", name: "query error no args (MatchQuery)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `query()`, Expr: `query()`,
}, },
urlTarget: "https://example.com/foo/?debug=1", wantErr: true,
wantErr: true,
}, },
{ {
name: "remote_ip error no args (MatchRemoteIP)", name: "remote_ip error no args (MatchRemoteIP)",
expression: &MatchExpression{ expression: &MatchExpression{
Expr: `remote_ip()`, Expr: `remote_ip()`,
}, },
urlTarget: "https://example.com/foo", wantErr: true,
wantErr: true,
}, },
{ {
name: "remote_ip single IP match (MatchRemoteIP)", name: "remote_ip single IP match (MatchRemoteIP)",
@@ -373,6 +383,67 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
urlTarget: "https://example.com/foo", urlTarget: "https://example.com/foo",
wantResult: true, wantResult: true,
}, },
{
name: "vars value (VarsMatcher)",
expression: &MatchExpression{
Expr: `vars({'foo': 'bar'})`,
},
urlTarget: "https://example.com/foo",
wantResult: true,
},
{
name: "vars matches placeholder, needs escape (VarsMatcher)",
expression: &MatchExpression{
Expr: `vars({'\{http.request.uri.path}': '/foo'})`,
},
urlTarget: "https://example.com/foo",
wantResult: true,
},
{
name: "vars error wrong syntax (VarsMatcher)",
expression: &MatchExpression{
Expr: `vars('foo', 'bar')`,
},
wantErr: true,
},
{
name: "vars error no args (VarsMatcher)",
expression: &MatchExpression{
Expr: `vars()`,
},
wantErr: true,
},
{
name: "vars_regexp value (MatchVarsRE)",
expression: &MatchExpression{
Expr: `vars_regexp('foo', 'ba?r')`,
},
urlTarget: "https://example.com/foo",
wantResult: true,
},
{
name: "vars_regexp value with name (MatchVarsRE)",
expression: &MatchExpression{
Expr: `vars_regexp('name', 'foo', 'ba?r')`,
},
urlTarget: "https://example.com/foo",
wantResult: true,
},
{
name: "vars_regexp matches placeholder, needs escape (MatchVarsRE)",
expression: &MatchExpression{
Expr: `vars_regexp('\{http.request.uri.path}', '/fo?o')`,
},
urlTarget: "https://example.com/foo",
wantResult: true,
},
{
name: "vars_regexp error no args (MatchVarsRE)",
expression: &MatchExpression{
Expr: `vars_regexp()`,
},
wantErr: true,
},
} }
) )
@@ -396,6 +467,9 @@ func TestMatchExpressionMatch(t *testing.T) {
} }
repl := caddy.NewReplacer() repl := caddy.NewReplacer()
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl) ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
ctx = context.WithValue(ctx, VarsCtxKey, map[string]any{
"foo": "bar",
})
req = req.WithContext(ctx) req = req.WithContext(ctx)
addHTTPVarsToReplacer(repl, req, httptest.NewRecorder()) addHTTPVarsToReplacer(repl, req, httptest.NewRecorder())
@@ -436,6 +510,9 @@ func BenchmarkMatchExpressionMatch(b *testing.B) {
} }
repl := caddy.NewReplacer() repl := caddy.NewReplacer()
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl) ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
ctx = context.WithValue(ctx, VarsCtxKey, map[string]any{
"foo": "bar",
})
req = req.WithContext(ctx) req = req.WithContext(ctx)
addHTTPVarsToReplacer(repl, req, httptest.NewRecorder()) addHTTPVarsToReplacer(repl, req, httptest.NewRecorder())
if tc.clientCertificate != nil { if tc.clientCertificate != nil {
+15 -7
View File
@@ -24,6 +24,7 @@ import (
"io" "io"
"math" "math"
"net/http" "net/http"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -112,7 +113,8 @@ func (enc *Encode) Provision(ctx caddy.Context) error {
"application/x-ttf*", "application/x-ttf*",
"application/xhtml+xml*", "application/xhtml+xml*",
"application/xml*", "application/xml*",
"font/*", "font/ttf*",
"font/otf*",
"image/svg+xml*", "image/svg+xml*",
"image/vnd.microsoft.icon*", "image/vnd.microsoft.icon*",
"image/x-icon*", "image/x-icon*",
@@ -265,6 +267,14 @@ func (rw *responseWriter) FlushError() error {
// to rw.Write (see bug in #4314) // to rw.Write (see bug in #4314)
return nil return nil
} }
// also flushes the encoder, if any
// see: https://github.com/jjiang-stripe/caddy-slow-gzip
if rw.w != nil {
err := rw.w.Flush()
if err != nil {
return err
}
}
//nolint:bodyclose //nolint:bodyclose
return http.NewResponseController(rw.ResponseWriter).Flush() return http.NewResponseController(rw.ResponseWriter).Flush()
} }
@@ -432,12 +442,9 @@ func AcceptedEncodings(r *http.Request, preferredOrder []string) []string {
} }
// set server preference // set server preference
prefOrder := -1 prefOrder := slices.Index(preferredOrder, encName)
for i, p := range preferredOrder { if prefOrder > -1 {
if encName == p { prefOrder = len(preferredOrder) - prefOrder
prefOrder = len(preferredOrder) - i
break
}
} }
prefs = append(prefs, encodingPreference{ prefs = append(prefs, encodingPreference{
@@ -474,6 +481,7 @@ type encodingPreference struct {
type Encoder interface { type Encoder interface {
io.WriteCloser io.WriteCloser
Reset(io.Writer) Reset(io.Writer)
Flush() error // encoder by default buffers data to maximize compressing rate
} }
// Encoding is a type which can create encoders of its kind // Encoding is a type which can create encoders of its kind
+45 -8
View File
@@ -33,6 +33,7 @@ import (
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -52,14 +53,25 @@ var BrowseTemplate string
type Browse struct { type Browse struct {
// Filename of the template to use instead of the embedded browse template. // Filename of the template to use instead of the embedded browse template.
TemplateFile string `json:"template_file,omitempty"` TemplateFile string `json:"template_file,omitempty"`
// Determines whether or not targets of symlinks should be revealed. // Determines whether or not targets of symlinks should be revealed.
RevealSymlinks bool `json:"reveal_symlinks,omitempty"` RevealSymlinks bool `json:"reveal_symlinks,omitempty"`
// Override the default sort.
// It includes the following options:
// - sort_by: name(default), namedirfirst, size, time
// - order: asc(default), desc
// eg.:
// - `sort time desc` will sort by time in descending order
// - `sort size` will sort by size in ascending order
// The first option must be `sort_by` and the second option must be `order` (if exists).
SortOptions []string `json:"sort,omitempty"`
} }
func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
fsrv.logger.Debug("browse enabled; listing directory contents", if c := fsrv.logger.Check(zapcore.DebugLevel, "browse enabled; listing directory contents"); c != nil {
zap.String("path", dirPath), c.Write(zap.String("path", dirPath), zap.String("root", root))
zap.String("root", root)) }
// Navigation on the client-side gets messed up if the // Navigation on the client-side gets messed up if the
// URL doesn't end in a trailing slash because hrefs to // URL doesn't end in a trailing slash because hrefs to
@@ -81,7 +93,9 @@ func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w ht
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request) origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
if r.URL.Path == "" || path.Base(origReq.URL.Path) == path.Base(r.URL.Path) { if r.URL.Path == "" || path.Base(origReq.URL.Path) == path.Base(r.URL.Path) {
if !strings.HasSuffix(origReq.URL.Path, "/") { if !strings.HasSuffix(origReq.URL.Path, "/") {
fsrv.logger.Debug("redirecting to trailing slash to preserve hrefs", zap.String("request_path", r.URL.Path)) if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to trailing slash to preserve hrefs"); c != nil {
c.Write(zap.String("request_path", r.URL.Path))
}
return redirect(w, r, origReq.URL.Path+"/") return redirect(w, r, origReq.URL.Path+"/")
} }
} }
@@ -206,11 +220,34 @@ func (fsrv *FileServer) loadDirectoryContents(ctx context.Context, fileSystem fs
// browseApplyQueryParams applies query parameters to the listing. // browseApplyQueryParams applies query parameters to the listing.
// It mutates the listing and may set cookies. // It mutates the listing and may set cookies.
func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Request, listing *browseTemplateContext) { func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Request, listing *browseTemplateContext) {
var orderParam, sortParam string
// The configs in Caddyfile have lower priority than Query params,
// so put it at first.
for idx, item := range fsrv.Browse.SortOptions {
// Only `sort` & `order`, 2 params are allowed
if idx >= 2 {
break
}
switch item {
case sortByName, sortByNameDirFirst, sortBySize, sortByTime:
sortParam = item
case sortOrderAsc, sortOrderDesc:
orderParam = item
}
}
layoutParam := r.URL.Query().Get("layout") layoutParam := r.URL.Query().Get("layout")
sortParam := r.URL.Query().Get("sort")
orderParam := r.URL.Query().Get("order")
limitParam := r.URL.Query().Get("limit") limitParam := r.URL.Query().Get("limit")
offsetParam := r.URL.Query().Get("offset") offsetParam := r.URL.Query().Get("offset")
sortParamTmp := r.URL.Query().Get("sort")
if sortParamTmp != "" {
sortParam = sortParamTmp
}
orderParamTmp := r.URL.Query().Get("order")
if orderParamTmp != "" {
orderParam = orderParamTmp
}
switch layoutParam { switch layoutParam {
case "list", "grid", "": case "list", "grid", "":
@@ -233,11 +270,11 @@ func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Re
// then figure out the order // then figure out the order
switch orderParam { switch orderParam {
case "": case "":
orderParam = "asc" orderParam = sortOrderAsc
if orderCookie, orderErr := r.Cookie("order"); orderErr == nil { if orderCookie, orderErr := r.Cookie("order"); orderErr == nil {
orderParam = orderCookie.Value orderParam = orderCookie.Value
} }
case "asc", "desc": case sortOrderAsc, sortOrderDesc:
http.SetCookie(w, &http.Cookie{Name: "order", Value: orderParam, Secure: r.TLS != nil}) http.SetCookie(w, &http.Cookie{Name: "order", Value: orderParam, Secure: r.TLS != nil})
} }
+50 -25
View File
@@ -1,10 +1,17 @@
{{ $nonce := uuidv4 -}}
{{ $nonceAttribute := print "nonce=" (quote $nonce) -}}
{{ $csp := printf "default-src 'none'; img-src 'self'; object-src 'none'; base-uri 'none'; script-src 'nonce-%s'; style-src 'nonce-%s'; frame-ancestors 'self'; form-action 'self';" $nonce $nonce -}}
{{/* To disable the Content-Security-Policy, set this to false */}}{{ $enableCsp := true -}}
{{ if $enableCsp -}}
{{- .RespHeader.Set "Content-Security-Policy" $csp -}}
{{ end -}}
{{- define "icon"}} {{- define "icon"}}
{{- if .IsDir}} {{- if .IsDir}}
{{- if .IsSymlink}} {{- if .IsSymlink}}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z" stroke-width="0" fill="currentColor"/> <path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z" stroke-width="0" fill="currentColor"/>
<path fill="#000" d="M2.795 17.306c0-2.374 1.792-4.314 4.078-4.538v-1.104a.38.38 0 0 1 .651-.272l2.45 2.492a.132.132 0 0 1 0 .188l-2.45 2.492a.381.381 0 0 1-.651-.272V15.24c-1.889.297-3.436 1.39-3.817 3.26a2.809 2.809 0 0 1-.261-1.193Z" style="stroke-width:.127478"/> <path fill="#000" d="M2.795 17.306c0-2.374 1.792-4.314 4.078-4.538v-1.104a.38.38 0 0 1 .651-.272l2.45 2.492a.132.132 0 0 1 0 .188l-2.45 2.492a.381.381 0 0 1-.651-.272V15.24c-1.889.297-3.436 1.39-3.817 3.26a2.809 2.809 0 0 1-.261-1.193Z" stroke-width=".127478"/>
</svg> </svg>
{{- else}} {{- else}}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
@@ -303,7 +310,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style> <style {{ $nonceAttribute }}>
* { padding: 0; margin: 0; box-sizing: border-box; } * { padding: 0; margin: 0; box-sizing: border-box; }
body { body {
@@ -342,6 +349,10 @@ svg,
text-decoration: none; text-decoration: none;
} }
#layout-list, #layout-grid {
cursor: pointer;
}
.wrapper { .wrapper {
max-width: 1200px; max-width: 1200px;
margin-left: auto; margin-left: auto;
@@ -768,10 +779,10 @@ footer {
</style> </style>
{{- if eq .Layout "grid"}} {{- if eq .Layout "grid"}}
<style>.wrapper { max-width: none; } main { margin-top: 1px; }</style> <style {{ $nonceAttribute }}>.wrapper { max-width: none; } main { margin-top: 1px; }</style>
{{- end}} {{- end}}
</head> </head>
<body onload="initPage()"> <body>
<header> <header>
<div class="wrapper"> <div class="wrapper">
<div class="breadcrumbs">Folder Path</div> <div class="breadcrumbs">Folder Path</div>
@@ -799,7 +810,7 @@ footer {
</span> </span>
{{- end}} {{- end}}
</div> </div>
<a href="javascript:queryParam('layout', '')" id="layout-list" class='layout{{if eq $.Layout "list" ""}}current{{end}}'> <a id="layout-list" class='layout{{if eq $.Layout "list" ""}}current{{end}}'>
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-list" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-list" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v2a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/> <path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v2a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
@@ -807,7 +818,7 @@ footer {
</svg> </svg>
List List
</a> </a>
<a href="javascript:queryParam('layout', 'grid')" id="layout-grid" class='layout{{if eq $.Layout "grid"}}current{{end}}'> <a id="layout-grid" class='layout{{if eq $.Layout "grid"}}current{{end}}'>
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-grid" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-grid" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/> <path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/>
@@ -886,7 +897,7 @@ footer {
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/> <path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/>
<path d="M21 21l-6 -6"/> <path d="M21 21l-6 -6"/>
</svg> </svg>
<input type="search" placeholder="Search" id="filter" onkeyup='filter()'> <input type="search" placeholder="Search" id="filter">
</div> </div>
</th> </th>
<th> <th>
@@ -980,7 +991,7 @@ footer {
<div class="sizebar"> <div class="sizebar">
<div class="sizebar-bar"></div> <div class="sizebar-bar"></div>
<div class="sizebar-text"> <div class="sizebar-text">
{{.HumanSize}} {{if .IsSymlink}}↱&nbsp;{{end}}{{.HumanSize}}
</div> </div>
</div> </div>
</td> </td>
@@ -1000,70 +1011,70 @@ footer {
<footer> <footer>
Served with Served with
<a rel="noopener noreferrer" href="https://caddyserver.com"> <a rel="noopener noreferrer" href="https://caddyserver.com">
<svg class="caddy-logo" viewBox="0 0 379 114" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;"> <svg class="caddy-logo" viewBox="0 0 379 114" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" fill-rule="evenodd" clip-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g transform="matrix(1,0,0,1,-1982.99,-530.985)"> <g transform="matrix(1,0,0,1,-1982.99,-530.985)">
<g transform="matrix(1.16548,0,0,1.10195,1823.12,393.466)"> <g transform="matrix(1.16548,0,0,1.10195,1823.12,393.466)">
<g transform="matrix(1,0,0,1,0.233052,1.17986)"> <g transform="matrix(1,0,0,1,0.233052,1.17986)">
<g id="Icon" transform="matrix(0.858013,0,0,0.907485,-3224.99,-1435.83)"> <g id="Icon" transform="matrix(0.858013,0,0,0.907485,-3224.99,-1435.83)">
<g> <g>
<g transform="matrix(-0.191794,-0.715786,0.715786,-0.191794,4329.14,4673.64)"> <g transform="matrix(-0.191794,-0.715786,0.715786,-0.191794,4329.14,4673.64)">
<path d="M3901.56,610.734C3893.53,610.261 3886.06,608.1 3879.2,604.877C3872.24,601.608 3866.04,597.093 3860.8,591.633C3858.71,589.457 3856.76,587.149 3854.97,584.709C3853.2,582.281 3851.57,579.733 3850.13,577.066C3845.89,569.224 3843.21,560.381 3842.89,550.868C3842.57,543.321 3843.64,536.055 3845.94,529.307C3848.37,522.203 3852.08,515.696 3856.83,510.049L3855.79,509.095C3850.39,514.54 3846.02,520.981 3842.9,528.125C3839.84,535.125 3838.03,542.781 3837.68,550.868C3837.34,561.391 3839.51,571.425 3843.79,580.306C3845.27,583.38 3847.03,586.304 3849.01,589.049C3851.01,591.806 3853.24,594.39 3855.69,596.742C3861.75,602.568 3869,607.19 3877.03,610.1C3884.66,612.867 3892.96,614.059 3901.56,613.552L3901.56,610.734Z" style="fill:rgb(0,144,221);"/> <path d="M3901.56,610.734C3893.53,610.261 3886.06,608.1 3879.2,604.877C3872.24,601.608 3866.04,597.093 3860.8,591.633C3858.71,589.457 3856.76,587.149 3854.97,584.709C3853.2,582.281 3851.57,579.733 3850.13,577.066C3845.89,569.224 3843.21,560.381 3842.89,550.868C3842.57,543.321 3843.64,536.055 3845.94,529.307C3848.37,522.203 3852.08,515.696 3856.83,510.049L3855.79,509.095C3850.39,514.54 3846.02,520.981 3842.9,528.125C3839.84,535.125 3838.03,542.781 3837.68,550.868C3837.34,561.391 3839.51,571.425 3843.79,580.306C3845.27,583.38 3847.03,586.304 3849.01,589.049C3851.01,591.806 3853.24,594.39 3855.69,596.742C3861.75,602.568 3869,607.19 3877.03,610.1C3884.66,612.867 3892.96,614.059 3901.56,613.552L3901.56,610.734Z" fill="rgb(0,144,221)"/>
</g> </g>
<g transform="matrix(-0.191794,-0.715786,0.715786,-0.191794,4329.14,4673.64)"> <g transform="matrix(-0.191794,-0.715786,0.715786,-0.191794,4329.14,4673.64)">
<path d="M3875.69,496.573C3879.62,494.538 3883.8,492.897 3888.2,491.786C3892.49,490.704 3896.96,490.124 3901.56,490.032C3903.82,490.13 3906.03,490.332 3908.21,490.688C3917.13,492.147 3925.19,495.814 3932.31,500.683C3936.13,503.294 3939.59,506.335 3942.81,509.619C3947.09,513.98 3950.89,518.816 3953.85,524.232C3958.2,532.197 3960.96,541.186 3961.32,550.868C3961.61,558.748 3960.46,566.345 3957.88,573.322C3956.09,578.169 3953.7,582.753 3950.66,586.838C3947.22,591.461 3942.96,595.427 3938.27,598.769C3933.66,602.055 3928.53,604.619 3923.09,606.478C3922.37,606.721 3921.6,606.805 3920.93,607.167C3920.42,607.448 3920.14,607.854 3919.69,608.224L3920.37,610.389C3920.98,610.432 3921.47,610.573 3922.07,610.474C3922.86,610.344 3923.55,609.883 3924.28,609.566C3931.99,606.216 3938.82,601.355 3944.57,595.428C3947.02,592.903 3949.25,590.174 3951.31,587.319C3953.59,584.168 3955.66,580.853 3957.43,577.348C3961.47,569.34 3964.01,560.422 3964.36,550.868C3964.74,540.511 3962.66,530.628 3958.48,521.868C3955.57,515.775 3951.72,510.163 3946.95,505.478C3943.37,501.962 3939.26,498.99 3934.84,496.562C3926.88,492.192 3917.87,489.76 3908.37,489.229C3906.12,489.104 3903.86,489.054 3901.56,489.154C3896.87,489.06 3892.3,489.519 3887.89,490.397C3883.3,491.309 3878.89,492.683 3874.71,494.525L3875.69,496.573Z" style="fill:rgb(0,144,221);"/> <path d="M3875.69,496.573C3879.62,494.538 3883.8,492.897 3888.2,491.786C3892.49,490.704 3896.96,490.124 3901.56,490.032C3903.82,490.13 3906.03,490.332 3908.21,490.688C3917.13,492.147 3925.19,495.814 3932.31,500.683C3936.13,503.294 3939.59,506.335 3942.81,509.619C3947.09,513.98 3950.89,518.816 3953.85,524.232C3958.2,532.197 3960.96,541.186 3961.32,550.868C3961.61,558.748 3960.46,566.345 3957.88,573.322C3956.09,578.169 3953.7,582.753 3950.66,586.838C3947.22,591.461 3942.96,595.427 3938.27,598.769C3933.66,602.055 3928.53,604.619 3923.09,606.478C3922.37,606.721 3921.6,606.805 3920.93,607.167C3920.42,607.448 3920.14,607.854 3919.69,608.224L3920.37,610.389C3920.98,610.432 3921.47,610.573 3922.07,610.474C3922.86,610.344 3923.55,609.883 3924.28,609.566C3931.99,606.216 3938.82,601.355 3944.57,595.428C3947.02,592.903 3949.25,590.174 3951.31,587.319C3953.59,584.168 3955.66,580.853 3957.43,577.348C3961.47,569.34 3964.01,560.422 3964.36,550.868C3964.74,540.511 3962.66,530.628 3958.48,521.868C3955.57,515.775 3951.72,510.163 3946.95,505.478C3943.37,501.962 3939.26,498.99 3934.84,496.562C3926.88,492.192 3917.87,489.76 3908.37,489.229C3906.12,489.104 3903.86,489.054 3901.56,489.154C3896.87,489.06 3892.3,489.519 3887.89,490.397C3883.3,491.309 3878.89,492.683 3874.71,494.525L3875.69,496.573Z" fill="rgb(0,144,221)"/>
</g> </g>
</g> </g>
<g> <g>
<g transform="matrix(-3.37109,-0.514565,0.514565,-3.37109,4078.07,1806.88)"> <g transform="matrix(-3.37109,-0.514565,0.514565,-3.37109,4078.07,1806.88)">
<path d="M22,12C22,10.903 21.097,10 20,10C19.421,10 18.897,10.251 18.53,10.649C18.202,11.006 18,11.481 18,12C18,13.097 18.903,14 20,14C21.097,14 22,13.097 22,12Z" style="fill:none;fill-rule:nonzero;stroke:rgb(0,144,221);stroke-width:1.05px;"/> <path d="M22,12C22,10.903 21.097,10 20,10C19.421,10 18.897,10.251 18.53,10.649C18.202,11.006 18,11.481 18,12C18,13.097 18.903,14 20,14C21.097,14 22,13.097 22,12Z" fill="none" fill-rule="nonzero" stroke="rgb(0,144,221)" stroke-width="1.05px"/>
</g> </g>
<g transform="matrix(-5.33921,-5.26159,-3.12106,-6.96393,4073.87,1861.55)"> <g transform="matrix(-5.33921,-5.26159,-3.12106,-6.96393,4073.87,1861.55)">
<path d="M10.315,5.333C10.315,5.333 9.748,5.921 9.03,6.673C7.768,7.995 6.054,9.805 6.054,9.805L6.237,9.86C6.237,9.86 8.045,8.077 9.36,6.771C10.107,6.028 10.689,5.444 10.689,5.444L10.315,5.333Z" style="fill:rgb(0,144,221);"/> <path d="M10.315,5.333C10.315,5.333 9.748,5.921 9.03,6.673C7.768,7.995 6.054,9.805 6.054,9.805L6.237,9.86C6.237,9.86 8.045,8.077 9.36,6.771C10.107,6.028 10.689,5.444 10.689,5.444L10.315,5.333Z" fill="rgb(0,144,221)"/>
</g> </g>
</g> </g>
<g id="Padlock" transform="matrix(3.11426,0,0,3.11426,3938.31,1737.25)"> <g id="Padlock" transform="matrix(3.11426,0,0,3.11426,3938.31,1737.25)">
<g> <g>
<path d="M9.876,21L18.162,21C18.625,21 19,20.625 19,20.162L19,11.838C19,11.375 18.625,11 18.162,11L5.838,11C5.375,11 5,11.375 5,11.838L5,16.758" style="fill:none;stroke:rgb(34,182,56);stroke-width:1.89px;stroke-linecap:butt;stroke-linejoin:miter;"/> <path d="M9.876,21L18.162,21C18.625,21 19,20.625 19,20.162L19,11.838C19,11.375 18.625,11 18.162,11L5.838,11C5.375,11 5,11.375 5,11.838L5,16.758" fill="none" stroke="rgb(34,182,56)" stroke-width="1.89px" stroke-linecap="butt" stroke-linejoin="miter"/>
<path d="M8,11L8,7C8,4.806 9.806,3 12,3C14.194,3 16,4.806 16,7L16,11" style="fill:none;fill-rule:nonzero;stroke:rgb(34,182,56);stroke-width:1.89px;"/> <path d="M8,11L8,7C8,4.806 9.806,3 12,3C14.194,3 16,4.806 16,7L16,11" fill="none" fill-rule="nonzero" stroke="rgb(34,182,56)" stroke-width="1.89px"/>
</g> </g>
</g> </g>
<g> <g>
<g transform="matrix(5.30977,0.697415,-0.697415,5.30977,3852.72,1727.97)"> <g transform="matrix(5.30977,0.697415,-0.697415,5.30977,3852.72,1727.97)">
<path d="M22,12C22,11.659 21.913,11.337 21.76,11.055C21.421,10.429 20.756,10 20,10C18.903,10 18,10.903 18,12C18,13.097 18.903,14 20,14C21.097,14 22,13.097 22,12Z" style="fill:none;fill-rule:nonzero;stroke:rgb(0,144,221);stroke-width:0.98px;"/> <path d="M22,12C22,11.659 21.913,11.337 21.76,11.055C21.421,10.429 20.756,10 20,10C18.903,10 18,10.903 18,12C18,13.097 18.903,14 20,14C21.097,14 22,13.097 22,12Z" fill="none" fill-rule="nonzero" stroke="rgb(0,144,221)" stroke-width="0.98px"/>
</g> </g>
<g transform="matrix(4.93114,2.49604,1.11018,5.44847,3921.41,1726.72)"> <g transform="matrix(4.93114,2.49604,1.11018,5.44847,3921.41,1726.72)">
<path d="M8.902,6.77C8.902,6.77 7.235,8.253 6.027,9.366C5.343,9.996 4.819,10.502 4.819,10.502L5.52,11.164C5.52,11.164 6.021,10.637 6.646,9.951C7.749,8.739 9.219,7.068 9.219,7.068L8.902,6.77Z" style="fill:rgb(0,144,221);"/> <path d="M8.902,6.77C8.902,6.77 7.235,8.253 6.027,9.366C5.343,9.996 4.819,10.502 4.819,10.502L5.52,11.164C5.52,11.164 6.021,10.637 6.646,9.951C7.749,8.739 9.219,7.068 9.219,7.068L8.902,6.77Z" fill="rgb(0,144,221)"/>
</g> </g>
</g> </g>
</g> </g>
<g id="Text"> <g id="Text">
<g id="Wordmark" transform="matrix(1.32271,0,0,2.60848,-899.259,-791.691)"> <g id="Wordmark" transform="matrix(1.32271,0,0,2.60848,-899.259,-791.691)">
<g id="y" transform="matrix(0.50291,0,0,0.281607,905.533,304.987)"> <g id="y" transform="matrix(0.50291,0,0,0.281607,905.533,304.987)">
<path d="M192.152,286.875L202.629,268.64C187.804,270.106 183.397,265.779 180.143,263.391C176.888,261.004 174.362,257.99 172.563,254.347C170.765,250.705 169.866,246.691 169.866,242.305L169.866,208.107L183.21,208.107L183.21,242.213C183.21,245.188 183.896,247.822 185.268,250.116C186.64,252.41 188.465,254.197 190.743,255.475C193.022,256.754 195.501,257.393 198.182,257.393C200.894,257.393 203.393,256.75 205.68,255.463C207.966,254.177 209.799,252.391 211.178,250.105C212.558,247.818 213.248,245.188 213.248,242.213L213.248,208.107L226.545,208.107L226.545,242.305C226.545,246.707 225.378,258.46 218.079,268.64C215.735,271.909 207.835,286.875 207.835,286.875L192.152,286.875Z" style="fill:rgb(47,47,47);fill-rule:nonzero;"/> <path d="M192.152,286.875L202.629,268.64C187.804,270.106 183.397,265.779 180.143,263.391C176.888,261.004 174.362,257.99 172.563,254.347C170.765,250.705 169.866,246.691 169.866,242.305L169.866,208.107L183.21,208.107L183.21,242.213C183.21,245.188 183.896,247.822 185.268,250.116C186.64,252.41 188.465,254.197 190.743,255.475C193.022,256.754 195.501,257.393 198.182,257.393C200.894,257.393 203.393,256.75 205.68,255.463C207.966,254.177 209.799,252.391 211.178,250.105C212.558,247.818 213.248,245.188 213.248,242.213L213.248,208.107L226.545,208.107L226.545,242.305C226.545,246.707 225.378,258.46 218.079,268.64C215.735,271.909 207.835,286.875 207.835,286.875L192.152,286.875Z" fill="rgb(47,47,47)" fill-rule="nonzero"/>
</g> </g>
<g id="add" transform="matrix(0.525075,0,0,0.281607,801.871,304.987)"> <g id="add" transform="matrix(0.525075,0,0,0.281607,801.871,304.987)">
<g transform="matrix(116.242,0,0,116.242,161.846,267.39)"> <g transform="matrix(116.242,0,0,116.242,161.846,267.39)">
<path d="M0.276,0.012C0.227,0.012 0.186,0 0.15,-0.024C0.115,-0.048 0.088,-0.08 0.069,-0.12C0.05,-0.161 0.04,-0.205 0.04,-0.254C0.04,-0.305 0.051,-0.35 0.072,-0.39C0.094,-0.431 0.125,-0.463 0.165,-0.487C0.205,-0.51 0.254,-0.522 0.31,-0.522C0.366,-0.522 0.413,-0.51 0.452,-0.486C0.491,-0.463 0.521,-0.431 0.542,-0.39C0.562,-0.35 0.573,-0.305 0.573,-0.256L0.573,-0L0.458,-0L0.458,-0.095L0.456,-0.095C0.446,-0.076 0.433,-0.058 0.417,-0.042C0.401,-0.026 0.381,-0.013 0.358,-0.003C0.335,0.007 0.307,0.012 0.276,0.012ZM0.307,-0.086C0.337,-0.086 0.363,-0.093 0.386,-0.108C0.408,-0.123 0.426,-0.144 0.438,-0.17C0.45,-0.195 0.456,-0.224 0.456,-0.256C0.456,-0.288 0.45,-0.317 0.438,-0.342C0.426,-0.367 0.409,-0.387 0.387,-0.402C0.365,-0.417 0.338,-0.424 0.308,-0.424C0.276,-0.424 0.249,-0.417 0.226,-0.402C0.204,-0.387 0.186,-0.366 0.174,-0.341C0.162,-0.315 0.156,-0.287 0.156,-0.255C0.156,-0.224 0.162,-0.195 0.174,-0.169C0.186,-0.144 0.203,-0.123 0.226,-0.108C0.248,-0.093 0.275,-0.086 0.307,-0.086Z" style="fill:rgb(47,47,47);fill-rule:nonzero;"/> <path d="M0.276,0.012C0.227,0.012 0.186,0 0.15,-0.024C0.115,-0.048 0.088,-0.08 0.069,-0.12C0.05,-0.161 0.04,-0.205 0.04,-0.254C0.04,-0.305 0.051,-0.35 0.072,-0.39C0.094,-0.431 0.125,-0.463 0.165,-0.487C0.205,-0.51 0.254,-0.522 0.31,-0.522C0.366,-0.522 0.413,-0.51 0.452,-0.486C0.491,-0.463 0.521,-0.431 0.542,-0.39C0.562,-0.35 0.573,-0.305 0.573,-0.256L0.573,-0L0.458,-0L0.458,-0.095L0.456,-0.095C0.446,-0.076 0.433,-0.058 0.417,-0.042C0.401,-0.026 0.381,-0.013 0.358,-0.003C0.335,0.007 0.307,0.012 0.276,0.012ZM0.307,-0.086C0.337,-0.086 0.363,-0.093 0.386,-0.108C0.408,-0.123 0.426,-0.144 0.438,-0.17C0.45,-0.195 0.456,-0.224 0.456,-0.256C0.456,-0.288 0.45,-0.317 0.438,-0.342C0.426,-0.367 0.409,-0.387 0.387,-0.402C0.365,-0.417 0.338,-0.424 0.308,-0.424C0.276,-0.424 0.249,-0.417 0.226,-0.402C0.204,-0.387 0.186,-0.366 0.174,-0.341C0.162,-0.315 0.156,-0.287 0.156,-0.255C0.156,-0.224 0.162,-0.195 0.174,-0.169C0.186,-0.144 0.203,-0.123 0.226,-0.108C0.248,-0.093 0.275,-0.086 0.307,-0.086Z" fill="rgb(47,47,47)" fill-rule="nonzero"/>
</g> </g>
<g transform="matrix(116.242,0,0,116.242,226.592,267.39)"> <g transform="matrix(116.242,0,0,116.242,226.592,267.39)">
<path d="M0.306,0.012C0.265,0.012 0.229,0.006 0.196,-0.008C0.163,-0.021 0.135,-0.039 0.112,-0.064C0.089,-0.088 0.071,-0.117 0.059,-0.151C0.046,-0.185 0.04,-0.222 0.04,-0.263C0.04,-0.315 0.051,-0.36 0.072,-0.399C0.093,-0.437 0.122,-0.468 0.159,-0.489C0.196,-0.511 0.239,-0.522 0.287,-0.522C0.311,-0.522 0.333,-0.518 0.355,-0.511C0.377,-0.504 0.396,-0.493 0.413,-0.48C0.431,-0.466 0.445,-0.451 0.455,-0.433L0.456,-0.433L0.456,-0.73L0.571,-0.73L0.571,-0.261C0.571,-0.205 0.56,-0.156 0.537,-0.115C0.515,-0.074 0.484,-0.043 0.444,-0.021C0.405,0.001 0.358,0.012 0.306,0.012ZM0.306,-0.086C0.335,-0.086 0.361,-0.093 0.384,-0.107C0.406,-0.122 0.423,-0.141 0.436,-0.167C0.448,-0.192 0.455,-0.221 0.455,-0.255C0.455,-0.288 0.448,-0.317 0.436,-0.343C0.423,-0.368 0.406,-0.388 0.383,-0.402C0.361,-0.417 0.335,-0.424 0.305,-0.424C0.276,-0.424 0.251,-0.417 0.228,-0.402C0.206,-0.387 0.188,-0.368 0.175,-0.342C0.163,-0.317 0.156,-0.288 0.156,-0.255C0.156,-0.222 0.163,-0.193 0.175,-0.167C0.188,-0.142 0.206,-0.122 0.229,-0.108C0.251,-0.093 0.277,-0.086 0.306,-0.086Z" style="fill:rgb(47,47,47);fill-rule:nonzero;"/> <path d="M0.306,0.012C0.265,0.012 0.229,0.006 0.196,-0.008C0.163,-0.021 0.135,-0.039 0.112,-0.064C0.089,-0.088 0.071,-0.117 0.059,-0.151C0.046,-0.185 0.04,-0.222 0.04,-0.263C0.04,-0.315 0.051,-0.36 0.072,-0.399C0.093,-0.437 0.122,-0.468 0.159,-0.489C0.196,-0.511 0.239,-0.522 0.287,-0.522C0.311,-0.522 0.333,-0.518 0.355,-0.511C0.377,-0.504 0.396,-0.493 0.413,-0.48C0.431,-0.466 0.445,-0.451 0.455,-0.433L0.456,-0.433L0.456,-0.73L0.571,-0.73L0.571,-0.261C0.571,-0.205 0.56,-0.156 0.537,-0.115C0.515,-0.074 0.484,-0.043 0.444,-0.021C0.405,0.001 0.358,0.012 0.306,0.012ZM0.306,-0.086C0.335,-0.086 0.361,-0.093 0.384,-0.107C0.406,-0.122 0.423,-0.141 0.436,-0.167C0.448,-0.192 0.455,-0.221 0.455,-0.255C0.455,-0.288 0.448,-0.317 0.436,-0.343C0.423,-0.368 0.406,-0.388 0.383,-0.402C0.361,-0.417 0.335,-0.424 0.305,-0.424C0.276,-0.424 0.251,-0.417 0.228,-0.402C0.206,-0.387 0.188,-0.368 0.175,-0.342C0.163,-0.317 0.156,-0.288 0.156,-0.255C0.156,-0.222 0.163,-0.193 0.175,-0.167C0.188,-0.142 0.206,-0.122 0.229,-0.108C0.251,-0.093 0.277,-0.086 0.306,-0.086Z" fill="rgb(47,47,47)" fill-rule="nonzero"/>
</g> </g>
<g transform="matrix(116.242,0,0,116.242,290.293,267.39)"> <g transform="matrix(116.242,0,0,116.242,290.293,267.39)">
<path d="M0.306,0.012C0.265,0.012 0.229,0.006 0.196,-0.008C0.163,-0.021 0.135,-0.039 0.112,-0.064C0.089,-0.088 0.071,-0.117 0.059,-0.151C0.046,-0.185 0.04,-0.222 0.04,-0.263C0.04,-0.315 0.051,-0.36 0.072,-0.399C0.093,-0.437 0.122,-0.468 0.159,-0.489C0.196,-0.511 0.239,-0.522 0.287,-0.522C0.311,-0.522 0.333,-0.518 0.355,-0.511C0.377,-0.504 0.396,-0.493 0.413,-0.48C0.431,-0.466 0.445,-0.451 0.455,-0.433L0.456,-0.433L0.456,-0.73L0.571,-0.73L0.571,-0.261C0.571,-0.205 0.56,-0.156 0.537,-0.115C0.515,-0.074 0.484,-0.043 0.444,-0.021C0.405,0.001 0.358,0.012 0.306,0.012ZM0.306,-0.086C0.335,-0.086 0.361,-0.093 0.384,-0.107C0.406,-0.122 0.423,-0.141 0.436,-0.167C0.448,-0.192 0.455,-0.221 0.455,-0.255C0.455,-0.288 0.448,-0.317 0.436,-0.343C0.423,-0.368 0.406,-0.388 0.383,-0.402C0.361,-0.417 0.335,-0.424 0.305,-0.424C0.276,-0.424 0.251,-0.417 0.228,-0.402C0.206,-0.387 0.188,-0.368 0.175,-0.342C0.163,-0.317 0.156,-0.288 0.156,-0.255C0.156,-0.222 0.163,-0.193 0.175,-0.167C0.188,-0.142 0.206,-0.122 0.229,-0.108C0.251,-0.093 0.277,-0.086 0.306,-0.086Z" style="fill:rgb(47,47,47);fill-rule:nonzero;"/> <path d="M0.306,0.012C0.265,0.012 0.229,0.006 0.196,-0.008C0.163,-0.021 0.135,-0.039 0.112,-0.064C0.089,-0.088 0.071,-0.117 0.059,-0.151C0.046,-0.185 0.04,-0.222 0.04,-0.263C0.04,-0.315 0.051,-0.36 0.072,-0.399C0.093,-0.437 0.122,-0.468 0.159,-0.489C0.196,-0.511 0.239,-0.522 0.287,-0.522C0.311,-0.522 0.333,-0.518 0.355,-0.511C0.377,-0.504 0.396,-0.493 0.413,-0.48C0.431,-0.466 0.445,-0.451 0.455,-0.433L0.456,-0.433L0.456,-0.73L0.571,-0.73L0.571,-0.261C0.571,-0.205 0.56,-0.156 0.537,-0.115C0.515,-0.074 0.484,-0.043 0.444,-0.021C0.405,0.001 0.358,0.012 0.306,0.012ZM0.306,-0.086C0.335,-0.086 0.361,-0.093 0.384,-0.107C0.406,-0.122 0.423,-0.141 0.436,-0.167C0.448,-0.192 0.455,-0.221 0.455,-0.255C0.455,-0.288 0.448,-0.317 0.436,-0.343C0.423,-0.368 0.406,-0.388 0.383,-0.402C0.361,-0.417 0.335,-0.424 0.305,-0.424C0.276,-0.424 0.251,-0.417 0.228,-0.402C0.206,-0.387 0.188,-0.368 0.175,-0.342C0.163,-0.317 0.156,-0.288 0.156,-0.255C0.156,-0.222 0.163,-0.193 0.175,-0.167C0.188,-0.142 0.206,-0.122 0.229,-0.108C0.251,-0.093 0.277,-0.086 0.306,-0.086Z" fill="rgb(47,47,47)" fill-rule="nonzero"/>
</g> </g>
</g> </g>
<g id="c" transform="matrix(-0.0716462,0.31304,-0.583685,-0.0384251,1489.76,-444.051)"> <g id="c" transform="matrix(-0.0716462,0.31304,-0.583685,-0.0384251,1489.76,-444.051)">
<path d="M2668.11,700.4C2666.79,703.699 2666.12,707.216 2666.12,710.766C2666.12,726.268 2678.71,738.854 2694.21,738.854C2709.71,738.854 2722.3,726.268 2722.3,710.766C2722.3,704.111 2719.93,697.672 2715.63,692.597L2707.63,699.378C2710.33,702.559 2711.57,706.602 2711.81,710.766C2712.2,717.38 2706.61,724.52 2697.27,726.637C2683.9,728.581 2676.61,720.482 2676.61,710.766C2676.61,708.541 2677.03,706.336 2677.85,704.269L2668.11,700.4Z" style="fill:rgb(46,46,46);"/> <path d="M2668.11,700.4C2666.79,703.699 2666.12,707.216 2666.12,710.766C2666.12,726.268 2678.71,738.854 2694.21,738.854C2709.71,738.854 2722.3,726.268 2722.3,710.766C2722.3,704.111 2719.93,697.672 2715.63,692.597L2707.63,699.378C2710.33,702.559 2711.57,706.602 2711.81,710.766C2712.2,717.38 2706.61,724.52 2697.27,726.637C2683.9,728.581 2676.61,720.482 2676.61,710.766C2676.61,708.541 2677.03,706.336 2677.85,704.269L2668.11,700.4Z" fill="rgb(46,46,46)"/>
</g> </g>
</g> </g>
<g id="R" transform="matrix(0.426446,0,0,0.451034,-1192.44,-722.167)"> <g id="R" transform="matrix(0.426446,0,0,0.451034,-1192.44,-722.167)">
<g transform="matrix(1,0,0,1,-0.10786,0.450801)"> <g transform="matrix(1,0,0,1,-0.10786,0.450801)">
<g transform="matrix(12.1247,0,0,12.1247,3862.61,1929.9)"> <g transform="matrix(12.1247,0,0,12.1247,3862.61,1929.9)">
<path d="M0.073,-0L0.073,-0.7L0.383,-0.7C0.428,-0.7 0.469,-0.69 0.506,-0.67C0.543,-0.651 0.572,-0.623 0.594,-0.588C0.616,-0.553 0.627,-0.512 0.627,-0.465C0.627,-0.418 0.615,-0.377 0.592,-0.342C0.569,-0.306 0.539,-0.279 0.501,-0.259L0.57,-0.128C0.574,-0.12 0.579,-0.115 0.584,-0.111C0.59,-0.107 0.596,-0.106 0.605,-0.106L0.664,-0.106L0.664,-0L0.587,-0C0.56,-0 0.535,-0.007 0.514,-0.02C0.493,-0.034 0.476,-0.052 0.463,-0.075L0.381,-0.232C0.375,-0.231 0.368,-0.231 0.361,-0.231C0.354,-0.231 0.347,-0.231 0.34,-0.231L0.192,-0.231L0.192,-0L0.073,-0ZM0.192,-0.336L0.368,-0.336C0.394,-0.336 0.417,-0.341 0.438,-0.351C0.459,-0.361 0.476,-0.376 0.489,-0.396C0.501,-0.415 0.507,-0.438 0.507,-0.465C0.507,-0.492 0.501,-0.516 0.488,-0.535C0.475,-0.554 0.459,-0.569 0.438,-0.579C0.417,-0.59 0.394,-0.595 0.369,-0.595L0.192,-0.595L0.192,-0.336Z" style="fill:rgb(46,46,46);fill-rule:nonzero;"/> <path d="M0.073,-0L0.073,-0.7L0.383,-0.7C0.428,-0.7 0.469,-0.69 0.506,-0.67C0.543,-0.651 0.572,-0.623 0.594,-0.588C0.616,-0.553 0.627,-0.512 0.627,-0.465C0.627,-0.418 0.615,-0.377 0.592,-0.342C0.569,-0.306 0.539,-0.279 0.501,-0.259L0.57,-0.128C0.574,-0.12 0.579,-0.115 0.584,-0.111C0.59,-0.107 0.596,-0.106 0.605,-0.106L0.664,-0.106L0.664,-0L0.587,-0C0.56,-0 0.535,-0.007 0.514,-0.02C0.493,-0.034 0.476,-0.052 0.463,-0.075L0.381,-0.232C0.375,-0.231 0.368,-0.231 0.361,-0.231C0.354,-0.231 0.347,-0.231 0.34,-0.231L0.192,-0.231L0.192,-0L0.073,-0ZM0.192,-0.336L0.368,-0.336C0.394,-0.336 0.417,-0.341 0.438,-0.351C0.459,-0.361 0.476,-0.376 0.489,-0.396C0.501,-0.415 0.507,-0.438 0.507,-0.465C0.507,-0.492 0.501,-0.516 0.488,-0.535C0.475,-0.554 0.459,-0.569 0.438,-0.579C0.417,-0.59 0.394,-0.595 0.369,-0.595L0.192,-0.595L0.192,-0.336Z" fill="rgb(46,46,46)" fill-rule="nonzero"/>
</g> </g>
</g> </g>
<g transform="matrix(1,0,0,1,0.278569,0.101881)"> <g transform="matrix(1,0,0,1,0.278569,0.101881)">
<circle cx="3866.43" cy="1926.14" r="8.923" style="fill:none;stroke:rgb(46,46,46);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;"/> <circle cx="3866.43" cy="1926.14" r="8.923" fill="none" stroke="rgb(46,46,46)" stroke-width="2px" stroke-linecap="butt" stroke-linejoin="miter"/>
</g> </g>
</g> </g>
</g> </g>
@@ -1074,7 +1085,7 @@ footer {
</a> </a>
</footer> </footer>
<script> <script {{ $nonceAttribute }}>
const filterEl = document.getElementById('filter'); const filterEl = document.getElementById('filter');
filterEl?.focus({ preventScroll: true }); filterEl?.focus({ preventScroll: true });
@@ -1120,6 +1131,20 @@ footer {
}); });
} }
const filterElem = document.getElementById("filter");
if (filterElem) {
filterElem.addEventListener("keyup", filter);
}
document.getElementById("layout-list").addEventListener("click", function() {
queryParam('layout', '');
});
document.getElementById("layout-grid").addEventListener("click", function() {
queryParam('layout', 'grid');
});
window.addEventListener("load", initPage);
function queryParam(k, v) { function queryParam(k, v) {
const qs = new URLSearchParams(window.location.search); const qs = new URLSearchParams(window.location.search);
if (!v) { if (!v) {
@@ -21,6 +21,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -28,6 +29,7 @@ import (
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -57,9 +59,9 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
info, err := entry.Info() info, err := entry.Info()
if err != nil { if err != nil {
fsrv.logger.Error("could not get info about directory entry", if c := fsrv.logger.Check(zapcore.ErrorLevel, "could not get info about directory entry"); c != nil {
zap.String("name", entry.Name()), c.Write(zap.String("name", entry.Name()), zap.String("root", root))
zap.String("root", root)) }
continue continue
} }
@@ -80,6 +82,13 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
} }
size := info.Size() size := info.Size()
if !isDir {
// increase the total by the symlink's size, not the target's size,
// by incrementing before we follow the symlink
tplCtx.TotalFileSize += size
}
fileIsSymlink := isSymlink(info) fileIsSymlink := isSymlink(info)
symlinkPath := "" symlinkPath := ""
if fileIsSymlink { if fileIsSymlink {
@@ -103,7 +112,8 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
} }
if !isDir { if !isDir {
tplCtx.TotalFileSize += size // increase the total including the symlink target's size
tplCtx.TotalFileSizeFollowingSymlinks += size
} }
u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
@@ -150,9 +160,15 @@ type browseTemplateContext struct {
// The number of files (items that aren't directories) in the listing. // The number of files (items that aren't directories) in the listing.
NumFiles int `json:"num_files"` NumFiles int `json:"num_files"`
// The total size of all files in the listing. // The total size of all files in the listing. Only includes the
// size of the files themselves, not the size of symlink targets
// (i.e. the calculation of this value does not follow symlinks).
TotalFileSize int64 `json:"total_file_size"` TotalFileSize int64 `json:"total_file_size"`
// The total size of all files in the listing, including the
// size of the files targeted by symlinks.
TotalFileSizeFollowingSymlinks int64 `json:"total_file_size_following_symlinks"`
// Sort column used // Sort column used
Sort string `json:"sort,omitempty"` Sort string `json:"sort,omitempty"`
@@ -266,12 +282,9 @@ type fileInfo struct {
// HasExt returns true if the filename has any of the given suffixes, case-insensitive. // HasExt returns true if the filename has any of the given suffixes, case-insensitive.
func (fi fileInfo) HasExt(exts ...string) bool { func (fi fileInfo) HasExt(exts ...string) bool {
for _, ext := range exts { return slices.ContainsFunc(exts, func(ext string) bool {
if strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext)) { return strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext))
return true })
}
}
return false
} }
// HumanSize returns the size of the file as a // HumanSize returns the size of the file as a
@@ -288,6 +301,12 @@ func (btc browseTemplateContext) HumanTotalFileSize() string {
return humanize.IBytes(uint64(btc.TotalFileSize)) return humanize.IBytes(uint64(btc.TotalFileSize))
} }
// HumanTotalFileSizeFollowingSymlinks is the same as HumanTotalFileSize
// except the returned value reflects the size of symlink targets.
func (btc browseTemplateContext) HumanTotalFileSizeFollowingSymlinks() string {
return humanize.IBytes(uint64(btc.TotalFileSizeFollowingSymlinks))
}
// HumanModTime returns the modified time of the file // HumanModTime returns the modified time of the file
// as a human-readable string given by format. // as a human-readable string given by format.
func (fi fileInfo) HumanModTime(format string) string { func (fi fileInfo) HumanModTime(format string) string {
@@ -353,4 +372,7 @@ const (
sortByNameDirFirst = "namedirfirst" sortByNameDirFirst = "namedirfirst"
sortBySize = "size" sortBySize = "size"
sortByTime = "time" sortByTime = "time"
sortOrderAsc = "asc"
sortOrderDesc = "desc"
) )
+10
View File
@@ -119,6 +119,16 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.Err("Symlinks path reveal is already enabled") return d.Err("Symlinks path reveal is already enabled")
} }
fsrv.Browse.RevealSymlinks = true fsrv.Browse.RevealSymlinks = true
case "sort":
for d.NextArg() {
dVal := d.Val()
switch dVal {
case sortByName, sortByNameDirFirst, sortBySize, sortByTime, sortOrderAsc, sortOrderDesc:
fsrv.Browse.SortOptions = append(fsrv.Browse.SortOptions, dVal)
default:
return d.Errf("unknown sort option '%s'", dVal)
}
}
default: default:
return d.Errf("unknown subdirective '%s'", d.Val()) return d.Errf("unknown subdirective '%s'", d.Val())
} }
+16 -8
View File
@@ -33,6 +33,7 @@ import (
"github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/parser" "github.com/google/cel-go/parser"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -224,7 +225,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
return func(eh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) { return func(eh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
if len(args) == 0 { if len(args) == 0 {
return eh.NewCall("file", return eh.NewCall("file",
eh.NewIdent("request"), eh.NewIdent(caddyhttp.CELRequestVarName),
eh.NewMap(), eh.NewMap(),
), nil ), nil
} }
@@ -232,7 +233,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
arg := args[0] arg := args[0]
if isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg) { if isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg) {
return eh.NewCall("file", return eh.NewCall("file",
eh.NewIdent("request"), eh.NewIdent(caddyhttp.CELRequestVarName),
eh.NewMap(eh.NewMapEntry( eh.NewMap(eh.NewMapEntry(
eh.NewLiteral(types.String("try_files")), eh.NewLiteral(types.String("try_files")),
eh.NewList(arg), eh.NewList(arg),
@@ -241,7 +242,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
), nil ), nil
} }
if isCELTryFilesLiteral(arg) { if isCELTryFilesLiteral(arg) {
return eh.NewCall("file", eh.NewIdent("request"), arg), nil return eh.NewCall("file", eh.NewIdent(caddyhttp.CELRequestVarName), arg), nil
} }
return nil, &common.Error{ return nil, &common.Error{
Location: eh.OffsetLocation(arg.ID()), Location: eh.OffsetLocation(arg.ID()),
@@ -258,7 +259,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander {
} }
} }
return eh.NewCall("file", return eh.NewCall("file",
eh.NewIdent("request"), eh.NewIdent(caddyhttp.CELRequestVarName),
eh.NewMap(eh.NewMapEntry( eh.NewMap(eh.NewMapEntry(
eh.NewLiteral(types.String("try_files")), eh.NewLiteral(types.String("try_files")),
eh.NewList(args...), eh.NewList(args...),
@@ -326,7 +327,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
fileSystem, ok := m.fsmap.Get(fsName) fileSystem, ok := m.fsmap.Get(fsName)
if !ok { if !ok {
m.logger.Error("use of unregistered filesystem", zap.String("fs", fsName)) if c := m.logger.Check(zapcore.ErrorLevel, "use of unregistered filesystem"); c != nil {
c.Write(zap.String("fs", fsName))
}
return false return false
} }
type matchCandidate struct { type matchCandidate struct {
@@ -356,7 +359,10 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
return val, nil return val, nil
}) })
if err != nil { if err != nil {
m.logger.Error("evaluating placeholders", zap.Error(err)) if c := m.logger.Check(zapcore.ErrorLevel, "evaluating placeholders"); c != nil {
c.Write(zap.Error(err))
}
expandedFile = file // "oh well," I guess? expandedFile = file // "oh well," I guess?
} }
@@ -379,7 +385,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
} else { } else {
globResults, err = fs.Glob(fileSystem, fullPattern) globResults, err = fs.Glob(fileSystem, fullPattern)
if err != nil { if err != nil {
m.logger.Error("expanding glob", zap.Error(err)) if c := m.logger.Check(zapcore.ErrorLevel, "expanding glob"); c != nil {
c.Write(zap.Error(err))
}
} }
} }
@@ -626,7 +634,7 @@ func isCELCaddyPlaceholderCall(e ast.Expr) bool {
switch e.Kind() { switch e.Kind() {
case ast.CallKind: case ast.CallKind:
call := e.AsCall() call := e.AsCall()
if call.FunctionName() == "caddyPlaceholder" { if call.FunctionName() == caddyhttp.CELPlaceholderFuncName {
return true return true
} }
case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind: case ast.UnspecifiedExprKind, ast.ComprehensionKind, ast.IdentKind, ast.ListKind, ast.LiteralKind, ast.MapKind, ast.SelectKind, ast.StructKind:
+86 -28
View File
@@ -15,6 +15,7 @@
package fileserver package fileserver
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -30,6 +31,7 @@ import (
"strings" "strings"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -235,6 +237,24 @@ func (fsrv *FileServer) Provision(ctx caddy.Context) error {
fsrv.precompressors[ae] = p fsrv.precompressors[ae] = p
} }
if fsrv.Browse != nil {
// check sort options
for idx, sortOption := range fsrv.Browse.SortOptions {
switch idx {
case 0:
if sortOption != sortByName && sortOption != sortByNameDirFirst && sortOption != sortBySize && sortOption != sortByTime {
return fmt.Errorf("the first option must be one of the following: %s, %s, %s, %s, but got %s", sortByName, sortByNameDirFirst, sortBySize, sortByTime, sortOption)
}
case 1:
if sortOption != sortOrderAsc && sortOption != sortOrderDesc {
return fmt.Errorf("the second option must be one of the following: %s, %s, but got %s", sortOrderAsc, sortOrderDesc, sortOption)
}
default:
return fmt.Errorf("only max 2 sort options are allowed, but got %d", idx+1)
}
}
}
return nil return nil
} }
@@ -267,11 +287,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
// remove any trailing `/` as it breaks fs.ValidPath() in the stdlib // remove any trailing `/` as it breaks fs.ValidPath() in the stdlib
filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/") filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/")
fsrv.logger.Debug("sanitized path join", if c := fsrv.logger.Check(zapcore.DebugLevel, "sanitized path join"); c != nil {
zap.String("site_root", root), c.Write(
zap.String("fs", fsName), zap.String("site_root", root),
zap.String("request_path", r.URL.Path), zap.String("fs", fsName),
zap.String("result", filename)) zap.String("request_path", r.URL.Path),
zap.String("result", filename),
)
}
// get information about the file // get information about the file
info, err := fs.Stat(fileSystem, filename) info, err := fs.Stat(fileSystem, filename)
@@ -294,9 +317,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
indexPath := caddyhttp.SanitizedPathJoin(filename, indexPage) indexPath := caddyhttp.SanitizedPathJoin(filename, indexPage)
if fileHidden(indexPath, filesToHide) { if fileHidden(indexPath, filesToHide) {
// pretend this file doesn't exist // pretend this file doesn't exist
fsrv.logger.Debug("hiding index file", if c := fsrv.logger.Check(zapcore.DebugLevel, "hiding index file"); c != nil {
zap.String("filename", indexPath), c.Write(
zap.Strings("files_to_hide", filesToHide)) zap.String("filename", indexPath),
zap.Strings("files_to_hide", filesToHide),
)
}
continue continue
} }
@@ -316,7 +342,9 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
info = indexInfo info = indexInfo
filename = indexPath filename = indexPath
implicitIndexFile = true implicitIndexFile = true
fsrv.logger.Debug("located index file", zap.String("filename", filename)) if c := fsrv.logger.Check(zapcore.DebugLevel, "located index file"); c != nil {
c.Write(zap.String("filename", filename))
}
break break
} }
} }
@@ -324,9 +352,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
// if still referencing a directory, delegate // if still referencing a directory, delegate
// to browse or return an error // to browse or return an error
if info.IsDir() { if info.IsDir() {
fsrv.logger.Debug("no index file in directory", if c := fsrv.logger.Check(zapcore.DebugLevel, "no index file in directory"); c != nil {
zap.String("path", filename), c.Write(
zap.Strings("index_filenames", fsrv.IndexNames)) zap.String("path", filename),
zap.Strings("index_filenames", fsrv.IndexNames),
)
}
if fsrv.Browse != nil && !fileHidden(filename, filesToHide) { if fsrv.Browse != nil && !fileHidden(filename, filesToHide) {
return fsrv.serveBrowse(fileSystem, root, filename, w, r, next) return fsrv.serveBrowse(fileSystem, root, filename, w, r, next)
} }
@@ -336,9 +367,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
// one last check to ensure the file isn't hidden (we might // one last check to ensure the file isn't hidden (we might
// have changed the filename from when we last checked) // have changed the filename from when we last checked)
if fileHidden(filename, filesToHide) { if fileHidden(filename, filesToHide) {
fsrv.logger.Debug("hiding file", if c := fsrv.logger.Check(zapcore.DebugLevel, "hiding file"); c != nil {
zap.String("filename", filename), c.Write(
zap.Strings("files_to_hide", filesToHide)) zap.String("filename", filename),
zap.Strings("files_to_hide", filesToHide),
)
}
return fsrv.notFound(w, r, next) return fsrv.notFound(w, r, next)
} }
@@ -356,15 +390,21 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
if path.Base(origReq.URL.Path) == path.Base(r.URL.Path) { if path.Base(origReq.URL.Path) == path.Base(r.URL.Path) {
if implicitIndexFile && !strings.HasSuffix(origReq.URL.Path, "/") { if implicitIndexFile && !strings.HasSuffix(origReq.URL.Path, "/") {
to := origReq.URL.Path + "/" to := origReq.URL.Path + "/"
fsrv.logger.Debug("redirecting to canonical URI (adding trailing slash for directory)", if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to canonical URI (adding trailing slash for directory"); c != nil {
zap.String("from_path", origReq.URL.Path), c.Write(
zap.String("to_path", to)) zap.String("from_path", origReq.URL.Path),
zap.String("to_path", to),
)
}
return redirect(w, r, to) return redirect(w, r, to)
} else if !implicitIndexFile && strings.HasSuffix(origReq.URL.Path, "/") { } else if !implicitIndexFile && strings.HasSuffix(origReq.URL.Path, "/") {
to := origReq.URL.Path[:len(origReq.URL.Path)-1] to := origReq.URL.Path[:len(origReq.URL.Path)-1]
fsrv.logger.Debug("redirecting to canonical URI (removing trailing slash for file)", if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to canonical URI (removing trailing slash for file"); c != nil {
zap.String("from_path", origReq.URL.Path), c.Write(
zap.String("to_path", to)) zap.String("from_path", origReq.URL.Path),
zap.String("to_path", to),
)
}
return redirect(w, r, to) return redirect(w, r, to)
} }
} }
@@ -392,13 +432,19 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
compressedFilename := filename + precompress.Suffix() compressedFilename := filename + precompress.Suffix()
compressedInfo, err := fs.Stat(fileSystem, compressedFilename) compressedInfo, err := fs.Stat(fileSystem, compressedFilename)
if err != nil || compressedInfo.IsDir() { if err != nil || compressedInfo.IsDir() {
fsrv.logger.Debug("precompressed file not accessible", zap.String("filename", compressedFilename), zap.Error(err)) if c := fsrv.logger.Check(zapcore.DebugLevel, "precompressed file not accessible"); c != nil {
c.Write(zap.String("filename", compressedFilename), zap.Error(err))
}
continue continue
} }
fsrv.logger.Debug("opening compressed sidecar file", zap.String("filename", compressedFilename), zap.Error(err)) if c := fsrv.logger.Check(zapcore.DebugLevel, "opening compressed sidecar file"); c != nil {
c.Write(zap.String("filename", compressedFilename), zap.Error(err))
}
file, err = fsrv.openFile(fileSystem, compressedFilename, w) file, err = fsrv.openFile(fileSystem, compressedFilename, w)
if err != nil { if err != nil {
fsrv.logger.Warn("opening precompressed file failed", zap.String("filename", compressedFilename), zap.Error(err)) if c := fsrv.logger.Check(zapcore.WarnLevel, "opening precompressed file failed"); c != nil {
c.Write(zap.String("filename", compressedFilename), zap.Error(err))
}
if caddyErr, ok := err.(caddyhttp.HandlerError); ok && caddyErr.StatusCode == http.StatusServiceUnavailable { if caddyErr, ok := err.(caddyhttp.HandlerError); ok && caddyErr.StatusCode == http.StatusServiceUnavailable {
return err return err
} }
@@ -429,7 +475,9 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
// no precompressed file found, use the actual file // no precompressed file found, use the actual file
if file == nil { if file == nil {
fsrv.logger.Debug("opening file", zap.String("filename", filename)) if c := fsrv.logger.Check(zapcore.DebugLevel, "opening file"); c != nil {
c.Write(zap.String("filename", filename))
}
// open the file // open the file
file, err = fsrv.openFile(fileSystem, filename, w) file, err = fsrv.openFile(fileSystem, filename, w)
@@ -529,10 +577,14 @@ func (fsrv *FileServer) openFile(fileSystem fs.FS, filename string, w http.Respo
if err != nil { if err != nil {
err = fsrv.mapDirOpenError(fileSystem, err, filename) err = fsrv.mapDirOpenError(fileSystem, err, filename)
if errors.Is(err, fs.ErrNotExist) { if errors.Is(err, fs.ErrNotExist) {
fsrv.logger.Debug("file not found", zap.String("filename", filename), zap.Error(err)) if c := fsrv.logger.Check(zapcore.DebugLevel, "file not found"); c != nil {
c.Write(zap.String("filename", filename), zap.Error(err))
}
return nil, caddyhttp.Error(http.StatusNotFound, err) return nil, caddyhttp.Error(http.StatusNotFound, err)
} else if errors.Is(err, fs.ErrPermission) { } else if errors.Is(err, fs.ErrPermission) {
fsrv.logger.Debug("permission denied", zap.String("filename", filename), zap.Error(err)) if c := fsrv.logger.Check(zapcore.DebugLevel, "permission denied"); c != nil {
c.Write(zap.String("filename", filename), zap.Error(err))
}
return nil, caddyhttp.Error(http.StatusForbidden, err) return nil, caddyhttp.Error(http.StatusForbidden, err)
} }
// maybe the server is under load and ran out of file descriptors? // maybe the server is under load and ran out of file descriptors?
@@ -540,7 +592,9 @@ func (fsrv *FileServer) openFile(fileSystem fs.FS, filename string, w http.Respo
//nolint:gosec //nolint:gosec
backoff := weakrand.Intn(maxBackoff-minBackoff) + minBackoff backoff := weakrand.Intn(maxBackoff-minBackoff) + minBackoff
w.Header().Set("Retry-After", strconv.Itoa(backoff)) w.Header().Set("Retry-After", strconv.Itoa(backoff))
fsrv.logger.Debug("retry after backoff", zap.String("filename", filename), zap.Int("backoff", backoff), zap.Error(err)) if c := fsrv.logger.Check(zapcore.DebugLevel, "retry after backoff"); c != nil {
c.Write(zap.String("filename", filename), zap.Int("backoff", backoff), zap.Error(err))
}
return nil, caddyhttp.Error(http.StatusServiceUnavailable, err) return nil, caddyhttp.Error(http.StatusServiceUnavailable, err)
} }
return file, nil return file, nil
@@ -690,6 +744,10 @@ func (fsrv *FileServer) getEtagFromFile(fileSystem fs.FS, filename string) (stri
if err != nil { if err != nil {
return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err) return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err)
} }
// Etags should not contain newline characters
etag = bytes.ReplaceAll(etag, []byte("\n"), []byte{})
return string(etag), nil return string(etag), nil
} }
return "", nil return "", nil
+7 -6
View File
@@ -135,13 +135,14 @@ type HeaderOps struct {
func (ops *HeaderOps) Provision(_ caddy.Context) error { func (ops *HeaderOps) Provision(_ caddy.Context) error {
for fieldName, replacements := range ops.Replace { for fieldName, replacements := range ops.Replace {
for i, r := range replacements { for i, r := range replacements {
if r.SearchRegexp != "" { if r.SearchRegexp == "" {
re, err := regexp.Compile(r.SearchRegexp) continue
if err != nil {
return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
}
replacements[i].re = re
} }
re, err := regexp.Compile(r.SearchRegexp)
if err != nil {
return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
}
replacements[i].re = re
} }
} }
return nil return nil
+5 -3
View File
@@ -23,6 +23,7 @@ import (
"sync" "sync"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -50,7 +51,6 @@ type Intercept struct {
// //
// Three new placeholders are available in this handler chain: // Three new placeholders are available in this handler chain:
// - `{http.intercept.status_code}` The status code from the response // - `{http.intercept.status_code}` The status code from the response
// - `{http.intercept.status_text}` The status text from the response
// - `{http.intercept.header.*}` The headers from the response // - `{http.intercept.header.*}` The headers from the response
HandleResponse []caddyhttp.ResponseHandler `json:"handle_response,omitempty"` HandleResponse []caddyhttp.ResponseHandler `json:"handle_response,omitempty"`
@@ -161,12 +161,14 @@ func (ir Intercept) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy
// set up the replacer so that parts of the original response can be // set up the replacer so that parts of the original response can be
// used for routing decisions // used for routing decisions
for field, value := range r.Header { for field, value := range rec.Header() {
repl.Set("http.intercept.header."+field, strings.Join(value, ",")) repl.Set("http.intercept.header."+field, strings.Join(value, ","))
} }
repl.Set("http.intercept.status_code", rec.Status()) repl.Set("http.intercept.status_code", rec.Status())
ir.logger.Debug("handling response", zap.Int("handler", rec.handlerIndex)) if c := ir.logger.Check(zapcore.DebugLevel, "handling response"); c != nil {
c.Write(zap.Int("handler", rec.handlerIndex))
}
// pass the request through the response handler routes // pass the request through the response handler routes
return rec.handler.Routes.Compile(next).ServeHTTP(w, r) return rec.handler.Routes.Compile(next).ServeHTTP(w, r)
+19 -4
View File
@@ -26,9 +26,11 @@ import (
"github.com/google/cel-go/cel" "github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/ref"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/internal"
) )
// MatchRemoteIP matches requests by the remote IP address, // MatchRemoteIP matches requests by the remote IP address,
@@ -79,7 +81,7 @@ func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.Err("the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead") return d.Err("the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead")
} }
if d.Val() == "private_ranges" { if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)
continue continue
} }
m.Ranges = append(m.Ranges, d.Val()) m.Ranges = append(m.Ranges, d.Val())
@@ -143,15 +145,23 @@ func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
// Match returns true if r matches m. // Match returns true if r matches m.
func (m MatchRemoteIP) Match(r *http.Request) bool { func (m MatchRemoteIP) Match(r *http.Request) bool {
if r.TLS != nil && !r.TLS.HandshakeComplete {
return false // if handshake is not finished, we infer 0-RTT that has not verified remote IP; could be spoofed
}
address := r.RemoteAddr address := r.RemoteAddr
clientIP, zoneID, err := parseIPZoneFromString(address) clientIP, zoneID, err := parseIPZoneFromString(address)
if err != nil { if err != nil {
m.logger.Error("getting remote IP", zap.Error(err)) if c := m.logger.Check(zapcore.ErrorLevel, "getting remote "); c != nil {
c.Write(zap.Error(err))
}
return false return false
} }
matches, zoneFilter := matchIPByCidrZones(clientIP, zoneID, m.cidrs, m.zones) matches, zoneFilter := matchIPByCidrZones(clientIP, zoneID, m.cidrs, m.zones)
if !matches && !zoneFilter { if !matches && !zoneFilter {
m.logger.Debug("zone ID from remote IP did not match", zap.String("zone", zoneID)) if c := m.logger.Check(zapcore.DebugLevel, "zone ID from remote IP did not match"); c != nil {
c.Write(zap.String("zone", zoneID))
}
} }
return matches return matches
} }
@@ -170,7 +180,7 @@ func (m *MatchClientIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { for d.Next() {
for d.NextArg() { for d.NextArg() {
if d.Val() == "private_ranges" { if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)
continue continue
} }
m.Ranges = append(m.Ranges, d.Val()) m.Ranges = append(m.Ranges, d.Val())
@@ -228,6 +238,9 @@ func (m *MatchClientIP) Provision(ctx caddy.Context) error {
// Match returns true if r matches m. // Match returns true if r matches m.
func (m MatchClientIP) Match(r *http.Request) bool { func (m MatchClientIP) Match(r *http.Request) bool {
if r.TLS != nil && !r.TLS.HandshakeComplete {
return false // if handshake is not finished, we infer 0-RTT that has not verified remote IP; could be spoofed
}
address := GetVar(r.Context(), ClientIPVarKey).(string) address := GetVar(r.Context(), ClientIPVarKey).(string)
clientIP, zoneID, err := parseIPZoneFromString(address) clientIP, zoneID, err := parseIPZoneFromString(address)
if err != nil { if err != nil {
@@ -244,7 +257,9 @@ func (m MatchClientIP) Match(r *http.Request) bool {
func provisionCidrsZonesFromRanges(ranges []string) ([]*netip.Prefix, []string, error) { func provisionCidrsZonesFromRanges(ranges []string) ([]*netip.Prefix, []string, error) {
cidrs := []*netip.Prefix{} cidrs := []*netip.Prefix{}
zones := []string{} zones := []string{}
repl := caddy.NewReplacer()
for _, str := range ranges { for _, str := range ranges {
str = repl.ReplaceAll(str, "")
// Exclude the zone_id from the IP // Exclude the zone_id from the IP
if strings.Contains(str, "%") { if strings.Contains(str, "%") {
split := strings.Split(str, "%") split := strings.Split(str, "%")
+9 -14
View File
@@ -22,6 +22,7 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/internal"
) )
func init() { func init() {
@@ -92,7 +93,7 @@ func (m *StaticIPRange) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
} }
for d.NextArg() { for d.NextArg() {
if d.Val() == "private_ranges" { if d.Val() == "private_ranges" {
m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)
continue continue
} }
m.Ranges = append(m.Ranges, d.Val()) m.Ranges = append(m.Ranges, d.Val())
@@ -121,22 +122,16 @@ func CIDRExpressionToPrefix(expr string) (netip.Prefix, error) {
return prefix, nil return prefix, nil
} }
// PrivateRangesCIDR returns a list of private CIDR range
// strings, which can be used as a configuration shortcut.
func PrivateRangesCIDR() []string {
return []string{
"192.168.0.0/16",
"172.16.0.0/12",
"10.0.0.0/8",
"127.0.0.1/8",
"fd00::/8",
"::1",
}
}
// Interface guards // Interface guards
var ( var (
_ caddy.Provisioner = (*StaticIPRange)(nil) _ caddy.Provisioner = (*StaticIPRange)(nil)
_ caddyfile.Unmarshaler = (*StaticIPRange)(nil) _ caddyfile.Unmarshaler = (*StaticIPRange)(nil)
_ IPRangeSource = (*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()
}
+7 -5
View File
@@ -193,7 +193,7 @@ func (sa *StringArray) UnmarshalJSON(b []byte) error {
// to use, the error log message, and any extra fields. // to use, the error log message, and any extra fields.
// If err is a HandlerError, the returned values will // If err is a HandlerError, the returned values will
// have richer information. // have richer information.
func errLogValues(err error) (status int, msg string, fields []zapcore.Field) { func errLogValues(err error) (status int, msg string, fields func() []zapcore.Field) {
var handlerErr HandlerError var handlerErr HandlerError
if errors.As(err, &handlerErr) { if errors.As(err, &handlerErr) {
status = handlerErr.StatusCode status = handlerErr.StatusCode
@@ -202,10 +202,12 @@ func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
} else { } else {
msg = handlerErr.Err.Error() msg = handlerErr.Err.Error()
} }
fields = []zapcore.Field{ fields = func() []zapcore.Field {
zap.Int("status", handlerErr.StatusCode), return []zapcore.Field{
zap.String("err_id", handlerErr.ID), zap.Int("status", handlerErr.StatusCode),
zap.String("err_trace", handlerErr.Trace), zap.String("err_id", handlerErr.ID),
zap.String("err_trace", handlerErr.Trace),
}
} }
return return
} }
+2 -12
View File
@@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"regexp" "regexp"
"slices"
"strings" "strings"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
@@ -126,7 +127,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
// defer work until a variable is actually evaluated by using replacer's Map callback // defer work until a variable is actually evaluated by using replacer's Map callback
repl.Map(func(key string) (any, bool) { repl.Map(func(key string) (any, bool) {
// return early if the variable is not even a configured destination // return early if the variable is not even a configured destination
destIdx := h.destinationIndex(key) destIdx := slices.Index(h.Destinations, key)
if destIdx < 0 { if destIdx < 0 {
return nil, false return nil, false
} }
@@ -170,17 +171,6 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
return next.ServeHTTP(w, r) return next.ServeHTTP(w, r)
} }
// destinationIndex returns the positional index of the destination
// is name is a known destination; otherwise it returns -1.
func (h Handler) destinationIndex(name string) int {
for i, dest := range h.Destinations {
if dest == name {
return i
}
}
return -1
}
// Mapping describes a mapping from input to outputs. // Mapping describes a mapping from input to outputs.
type Mapping struct { type Mapping struct {
// The input value to match. Must be distinct from other mappings. // The input value to match. Must be distinct from other mappings.
+81 -14
View File
@@ -34,6 +34,7 @@ import (
"github.com/google/cel-go/cel" "github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/ref"
"golang.org/x/net/idna"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -177,6 +178,22 @@ type (
// "http/2", "http/3", or minimum versions: "http/2+", etc. // "http/2", "http/3", or minimum versions: "http/2+", etc.
MatchProtocol string MatchProtocol string
// MatchTLS matches HTTP requests based on the underlying
// TLS connection state. If this matcher is specified but
// the request did not come over TLS, it will never match.
// If this matcher is specified but is empty and the request
// did come in over TLS, it will always match.
MatchTLS struct {
// Matches if the TLS handshake has completed. QUIC 0-RTT early
// data may arrive before the handshake completes. Generally, it
// is unsafe to replay these requests if they are not idempotent;
// additionally, the remote IP of early data packets can more
// easily be spoofed. It is conventional to respond with HTTP 425
// Too Early if the request cannot risk being processed in this
// state.
HandshakeComplete *bool `json:"handshake_complete,omitempty"`
}
// MatchNot matches requests by negating the results of its matcher // MatchNot matches requests by negating the results of its matcher
// sets. A single "not" matcher takes one or more matcher sets. Each // sets. A single "not" matcher takes one or more matcher sets. Each
// matcher set is OR'ed; in other words, if any matcher set returns // matcher set is OR'ed; in other words, if any matcher set returns
@@ -212,6 +229,7 @@ func init() {
caddy.RegisterModule(MatchHeader{}) caddy.RegisterModule(MatchHeader{})
caddy.RegisterModule(MatchHeaderRE{}) caddy.RegisterModule(MatchHeaderRE{})
caddy.RegisterModule(new(MatchProtocol)) caddy.RegisterModule(new(MatchProtocol))
caddy.RegisterModule(MatchTLS{})
caddy.RegisterModule(MatchNot{}) caddy.RegisterModule(MatchNot{})
} }
@@ -239,13 +257,20 @@ func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
func (m MatchHost) Provision(_ caddy.Context) error { func (m MatchHost) Provision(_ caddy.Context) error {
// check for duplicates; they are nonsensical and reduce efficiency // check for duplicates; they are nonsensical and reduce efficiency
// (we could just remove them, but the user should know their config is erroneous) // (we could just remove them, but the user should know their config is erroneous)
seen := make(map[string]int) seen := make(map[string]int, len(m))
for i, h := range m { for i, host := range m {
h = strings.ToLower(h) asciiHost, err := idna.ToASCII(host)
if firstI, ok := seen[h]; ok { if err != nil {
return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, h) return fmt.Errorf("converting hostname '%s' to ASCII: %v", host, err)
} }
seen[h] = i if asciiHost != host {
m[i] = asciiHost
}
normalizedHost := strings.ToLower(asciiHost)
if firstI, ok := seen[normalizedHost]; ok {
return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, host)
}
seen[normalizedHost] = i
} }
if m.large() { if m.large() {
@@ -739,12 +764,7 @@ func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// Match returns true if r matches m. // Match returns true if r matches m.
func (m MatchMethod) Match(r *http.Request) bool { func (m MatchMethod) Match(r *http.Request) bool {
for _, method := range m { return slices.Contains(m, r.Method)
if r.Method == method {
return true
}
}
return false
} }
// CELLibrary produces options that expose this matcher for use in CEL // CELLibrary produces options that expose this matcher for use in CEL
@@ -1228,6 +1248,53 @@ func (MatchProtocol) CELLibrary(_ caddy.Context) (cel.Library, error) {
) )
} }
// CaddyModule returns the Caddy module information.
func (MatchTLS) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.matchers.tls",
New: func() caddy.Module { return new(MatchTLS) },
}
}
// Match returns true if r matches m.
func (m MatchTLS) Match(r *http.Request) bool {
if r.TLS == nil {
return false
}
if m.HandshakeComplete != nil {
if (!*m.HandshakeComplete && r.TLS.HandshakeComplete) ||
(*m.HandshakeComplete && !r.TLS.HandshakeComplete) {
return false
}
}
return true
}
// UnmarshalCaddyfile parses Caddyfile tokens for this matcher. Syntax:
//
// ... tls [early_data]
//
// EXPERIMENTAL SYNTAX: Subject to change.
func (m *MatchTLS) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// iterate to merge multiple matchers into one
for d.Next() {
if d.NextArg() {
switch d.Val() {
case "early_data":
var false bool
m.HandshakeComplete = &false
}
}
if d.NextArg() {
return d.ArgErr()
}
if d.NextBlock(0) {
return d.Err("malformed tls matcher: blocks are not supported yet")
}
}
return nil
}
// CaddyModule returns the Caddy module information. // CaddyModule returns the Caddy module information.
func (MatchNot) CaddyModule() caddy.ModuleInfo { func (MatchNot) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{ return caddy.ModuleInfo{
@@ -1495,8 +1562,8 @@ var (
_ CELLibraryProducer = (*MatchHeader)(nil) _ CELLibraryProducer = (*MatchHeader)(nil)
_ CELLibraryProducer = (*MatchHeaderRE)(nil) _ CELLibraryProducer = (*MatchHeaderRE)(nil)
_ CELLibraryProducer = (*MatchProtocol)(nil) _ CELLibraryProducer = (*MatchProtocol)(nil)
// _ CELLibraryProducer = (*VarsMatcher)(nil) _ CELLibraryProducer = (*VarsMatcher)(nil)
// _ CELLibraryProducer = (*MatchVarsRE)(nil) _ CELLibraryProducer = (*MatchVarsRE)(nil)
_ json.Marshaler = (*MatchNot)(nil) _ json.Marshaler = (*MatchNot)(nil)
_ json.Unmarshaler = (*MatchNot)(nil) _ json.Unmarshaler = (*MatchNot)(nil)
+9
View File
@@ -78,6 +78,11 @@ func TestHostMatcher(t *testing.T) {
input: "bar.example.com", input: "bar.example.com",
expect: false, expect: false,
}, },
{
match: MatchHost{"éxàmplê.com"},
input: "xn--xmpl-0na6cm.com",
expect: true,
},
{ {
match: MatchHost{"*.example.com"}, match: MatchHost{"*.example.com"},
input: "example.com", input: "example.com",
@@ -149,6 +154,10 @@ func TestHostMatcher(t *testing.T) {
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl) ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
req = req.WithContext(ctx) req = req.WithContext(ctx)
if err := tc.match.Provision(caddy.Context{}); err != nil {
t.Errorf("Test %d %v: provisioning failed: %v", i, tc.match, err)
}
actual := tc.match.Match(req) actual := tc.match.Match(req)
if actual != tc.expect { if actual != tc.expect {
t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input) t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input)
+43 -25
View File
@@ -10,15 +10,23 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/internal/metrics" "github.com/caddyserver/caddy/v2/internal/metrics"
) )
// Metrics configures metrics observations. // Metrics configures metrics observations.
// EXPERIMENTAL and subject to change or removal. // EXPERIMENTAL and subject to change or removal.
type Metrics struct{} type Metrics struct {
// Enable per-host metrics. Enabling this option may
// incur high-memory consumption, depending on the number of hosts
// managed by Caddy.
PerHost bool `json:"per_host,omitempty"`
var httpMetrics = struct { init sync.Once
init sync.Once httpMetrics *httpMetrics `json:"-"`
}
type httpMetrics struct {
requestInFlight *prometheus.GaugeVec requestInFlight *prometheus.GaugeVec
requestCount *prometheus.CounterVec requestCount *prometheus.CounterVec
requestErrors *prometheus.CounterVec requestErrors *prometheus.CounterVec
@@ -26,27 +34,28 @@ var httpMetrics = struct {
requestSize *prometheus.HistogramVec requestSize *prometheus.HistogramVec
responseSize *prometheus.HistogramVec responseSize *prometheus.HistogramVec
responseDuration *prometheus.HistogramVec responseDuration *prometheus.HistogramVec
}{
init: sync.Once{},
} }
func initHTTPMetrics() { func initHTTPMetrics(ctx caddy.Context, metrics *Metrics) {
const ns, sub = "caddy", "http" const ns, sub = "caddy", "http"
registry := ctx.GetMetricsRegistry()
basicLabels := []string{"server", "handler"} basicLabels := []string{"server", "handler"}
httpMetrics.requestInFlight = promauto.NewGaugeVec(prometheus.GaugeOpts{ if metrics.PerHost {
basicLabels = append(basicLabels, "host")
}
metrics.httpMetrics.requestInFlight = promauto.With(registry).NewGaugeVec(prometheus.GaugeOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "requests_in_flight", Name: "requests_in_flight",
Help: "Number of requests currently handled by this server.", Help: "Number of requests currently handled by this server.",
}, basicLabels) }, basicLabels)
httpMetrics.requestErrors = promauto.NewCounterVec(prometheus.CounterOpts{ metrics.httpMetrics.requestErrors = promauto.With(registry).NewCounterVec(prometheus.CounterOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "request_errors_total", Name: "request_errors_total",
Help: "Number of requests resulting in middleware errors.", Help: "Number of requests resulting in middleware errors.",
}, basicLabels) }, basicLabels)
httpMetrics.requestCount = promauto.NewCounterVec(prometheus.CounterOpts{ metrics.httpMetrics.requestCount = promauto.With(registry).NewCounterVec(prometheus.CounterOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "requests_total", Name: "requests_total",
@@ -58,28 +67,31 @@ func initHTTPMetrics() {
sizeBuckets := prometheus.ExponentialBuckets(256, 4, 8) sizeBuckets := prometheus.ExponentialBuckets(256, 4, 8)
httpLabels := []string{"server", "handler", "code", "method"} httpLabels := []string{"server", "handler", "code", "method"}
httpMetrics.requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ if metrics.PerHost {
httpLabels = append(httpLabels, "host")
}
metrics.httpMetrics.requestDuration = promauto.With(registry).NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "request_duration_seconds", Name: "request_duration_seconds",
Help: "Histogram of round-trip request durations.", Help: "Histogram of round-trip request durations.",
Buckets: durationBuckets, Buckets: durationBuckets,
}, httpLabels) }, httpLabels)
httpMetrics.requestSize = promauto.NewHistogramVec(prometheus.HistogramOpts{ metrics.httpMetrics.requestSize = promauto.With(registry).NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "request_size_bytes", Name: "request_size_bytes",
Help: "Total size of the request. Includes body", Help: "Total size of the request. Includes body",
Buckets: sizeBuckets, Buckets: sizeBuckets,
}, httpLabels) }, httpLabels)
httpMetrics.responseSize = promauto.NewHistogramVec(prometheus.HistogramOpts{ metrics.httpMetrics.responseSize = promauto.With(registry).NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "response_size_bytes", Name: "response_size_bytes",
Help: "Size of the returned response.", Help: "Size of the returned response.",
Buckets: sizeBuckets, Buckets: sizeBuckets,
}, httpLabels) }, httpLabels)
httpMetrics.responseDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ metrics.httpMetrics.responseDuration = promauto.With(registry).NewHistogramVec(prometheus.HistogramOpts{
Namespace: ns, Namespace: ns,
Subsystem: sub, Subsystem: sub,
Name: "response_duration_seconds", Name: "response_duration_seconds",
@@ -101,14 +113,15 @@ func serverNameFromContext(ctx context.Context) string {
type metricsInstrumentedHandler struct { type metricsInstrumentedHandler struct {
handler string handler string
mh MiddlewareHandler mh MiddlewareHandler
metrics *Metrics
} }
func newMetricsInstrumentedHandler(handler string, mh MiddlewareHandler) *metricsInstrumentedHandler { func newMetricsInstrumentedHandler(ctx caddy.Context, handler string, mh MiddlewareHandler, metrics *Metrics) *metricsInstrumentedHandler {
httpMetrics.init.Do(func() { metrics.init.Do(func() {
initHTTPMetrics() initHTTPMetrics(ctx, metrics)
}) })
return &metricsInstrumentedHandler{handler, mh} return &metricsInstrumentedHandler{handler, mh, metrics}
} }
func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error { func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
@@ -119,7 +132,12 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
// of a panic // of a panic
statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""} statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""}
inFlight := httpMetrics.requestInFlight.With(labels) if h.metrics.PerHost {
labels["host"] = r.Host
statusLabels["host"] = r.Host
}
inFlight := h.metrics.httpMetrics.requestInFlight.With(labels)
inFlight.Inc() inFlight.Inc()
defer inFlight.Dec() defer inFlight.Dec()
@@ -131,13 +149,13 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
writeHeaderRecorder := ShouldBufferFunc(func(status int, header http.Header) bool { writeHeaderRecorder := ShouldBufferFunc(func(status int, header http.Header) bool {
statusLabels["code"] = metrics.SanitizeCode(status) statusLabels["code"] = metrics.SanitizeCode(status)
ttfb := time.Since(start).Seconds() ttfb := time.Since(start).Seconds()
httpMetrics.responseDuration.With(statusLabels).Observe(ttfb) h.metrics.httpMetrics.responseDuration.With(statusLabels).Observe(ttfb)
return false return false
}) })
wrec := NewResponseRecorder(w, nil, writeHeaderRecorder) wrec := NewResponseRecorder(w, nil, writeHeaderRecorder)
err := h.mh.ServeHTTP(wrec, r, next) err := h.mh.ServeHTTP(wrec, r, next)
dur := time.Since(start).Seconds() dur := time.Since(start).Seconds()
httpMetrics.requestCount.With(labels).Inc() h.metrics.httpMetrics.requestCount.With(labels).Inc()
observeRequest := func(status int) { observeRequest := func(status int) {
// If the code hasn't been set yet, and we didn't encounter an error, we're // If the code hasn't been set yet, and we didn't encounter an error, we're
@@ -148,9 +166,9 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
statusLabels["code"] = metrics.SanitizeCode(status) statusLabels["code"] = metrics.SanitizeCode(status)
} }
httpMetrics.requestDuration.With(statusLabels).Observe(dur) h.metrics.httpMetrics.requestDuration.With(statusLabels).Observe(dur)
httpMetrics.requestSize.With(statusLabels).Observe(float64(computeApproximateRequestSize(r))) h.metrics.httpMetrics.requestSize.With(statusLabels).Observe(float64(computeApproximateRequestSize(r)))
httpMetrics.responseSize.With(statusLabels).Observe(float64(wrec.Size())) h.metrics.httpMetrics.responseSize.With(statusLabels).Observe(float64(wrec.Size()))
} }
if err != nil { if err != nil {
@@ -159,7 +177,7 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
observeRequest(handlerErr.StatusCode) observeRequest(handlerErr.StatusCode)
} }
httpMetrics.requestErrors.With(labels).Inc() h.metrics.httpMetrics.requestErrors.With(labels).Inc()
return err return err
} }
+189 -7
View File
@@ -6,9 +6,10 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings" "strings"
"sync"
"testing" "testing"
"github.com/prometheus/client_golang/prometheus" "github.com/caddyserver/caddy/v2"
"github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/client_golang/prometheus/testutil"
) )
@@ -27,10 +28,15 @@ func TestServerNameFromContext(t *testing.T) {
} }
func TestMetricsInstrumentedHandler(t *testing.T) { func TestMetricsInstrumentedHandler(t *testing.T) {
ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
metrics := &Metrics{
init: sync.Once{},
httpMetrics: &httpMetrics{},
}
handlerErr := errors.New("oh noes") handlerErr := errors.New("oh noes")
response := []byte("hello world!") response := []byte("hello world!")
h := HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { h := HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
if actual := testutil.ToFloat64(httpMetrics.requestInFlight); actual != 1.0 { if actual := testutil.ToFloat64(metrics.httpMetrics.requestInFlight); actual != 1.0 {
t.Errorf("Not same: expected %#v, but got %#v", 1.0, actual) t.Errorf("Not same: expected %#v, but got %#v", 1.0, actual)
} }
if handlerErr == nil { if handlerErr == nil {
@@ -43,7 +49,7 @@ func TestMetricsInstrumentedHandler(t *testing.T) {
return h.ServeHTTP(w, r) return h.ServeHTTP(w, r)
}) })
ih := newMetricsInstrumentedHandler("bar", mh) ih := newMetricsInstrumentedHandler(ctx, "bar", mh, metrics)
r := httptest.NewRequest("GET", "/", nil) r := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
@@ -51,7 +57,7 @@ func TestMetricsInstrumentedHandler(t *testing.T) {
if actual := ih.ServeHTTP(w, r, h); actual != handlerErr { if actual := ih.ServeHTTP(w, r, h); actual != handlerErr {
t.Errorf("Not same: expected %#v, but got %#v", handlerErr, actual) t.Errorf("Not same: expected %#v, but got %#v", handlerErr, actual)
} }
if actual := testutil.ToFloat64(httpMetrics.requestInFlight); actual != 0.0 { if actual := testutil.ToFloat64(metrics.httpMetrics.requestInFlight); actual != 0.0 {
t.Errorf("Not same: expected %#v, but got %#v", 0.0, actual) t.Errorf("Not same: expected %#v, but got %#v", 0.0, actual)
} }
@@ -64,7 +70,7 @@ func TestMetricsInstrumentedHandler(t *testing.T) {
mh = middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error { mh = middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error {
return nil return nil
}) })
ih = newMetricsInstrumentedHandler("empty", mh) ih = newMetricsInstrumentedHandler(ctx, "empty", mh, metrics)
r = httptest.NewRequest("GET", "/", nil) r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder() w = httptest.NewRecorder()
@@ -83,7 +89,7 @@ func TestMetricsInstrumentedHandler(t *testing.T) {
return Error(http.StatusTooManyRequests, nil) return Error(http.StatusTooManyRequests, nil)
}) })
ih = newMetricsInstrumentedHandler("foo", mh) ih = newMetricsInstrumentedHandler(ctx, "foo", mh, metrics)
r = httptest.NewRequest("GET", "/", nil) r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder() w = httptest.NewRecorder()
@@ -183,7 +189,183 @@ func TestMetricsInstrumentedHandler(t *testing.T) {
caddy_http_request_errors_total{handler="bar",server="UNKNOWN"} 1 caddy_http_request_errors_total{handler="bar",server="UNKNOWN"} 1
caddy_http_request_errors_total{handler="foo",server="UNKNOWN"} 1 caddy_http_request_errors_total{handler="foo",server="UNKNOWN"} 1
` `
if err := testutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(expected), if err := testutil.GatherAndCompare(ctx.GetMetricsRegistry(), strings.NewReader(expected),
"caddy_http_request_size_bytes",
"caddy_http_response_size_bytes",
// caddy_http_request_duration_seconds_sum will vary based on how long the test took to run,
// so we check just the _bucket and _count metrics
"caddy_http_request_duration_seconds_bucket",
"caddy_http_request_duration_seconds_count",
"caddy_http_request_errors_total",
); err != nil {
t.Errorf("received unexpected error: %s", err)
}
}
func TestMetricsInstrumentedHandlerPerHost(t *testing.T) {
ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
metrics := &Metrics{
PerHost: true,
init: sync.Once{},
httpMetrics: &httpMetrics{},
}
handlerErr := errors.New("oh noes")
response := []byte("hello world!")
h := HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
if actual := testutil.ToFloat64(metrics.httpMetrics.requestInFlight); actual != 1.0 {
t.Errorf("Not same: expected %#v, but got %#v", 1.0, actual)
}
if handlerErr == nil {
w.Write(response)
}
return handlerErr
})
mh := middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error {
return h.ServeHTTP(w, r)
})
ih := newMetricsInstrumentedHandler(ctx, "bar", mh, metrics)
r := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
if actual := ih.ServeHTTP(w, r, h); actual != handlerErr {
t.Errorf("Not same: expected %#v, but got %#v", handlerErr, actual)
}
if actual := testutil.ToFloat64(metrics.httpMetrics.requestInFlight); actual != 0.0 {
t.Errorf("Not same: expected %#v, but got %#v", 0.0, actual)
}
handlerErr = nil
if err := ih.ServeHTTP(w, r, h); err != nil {
t.Errorf("Received unexpected error: %v", err)
}
// an empty handler - no errors, no header written
mh = middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error {
return nil
})
ih = newMetricsInstrumentedHandler(ctx, "empty", mh, metrics)
r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder()
if err := ih.ServeHTTP(w, r, h); err != nil {
t.Errorf("Received unexpected error: %v", err)
}
if actual := w.Result().StatusCode; actual != 200 {
t.Errorf("Not same: expected status code %#v, but got %#v", 200, actual)
}
if actual := w.Result().Header; len(actual) != 0 {
t.Errorf("Not empty: expected headers to be empty, but got %#v", actual)
}
// handler returning an error with an HTTP status
mh = middlewareHandlerFunc(func(w http.ResponseWriter, r *http.Request, h Handler) error {
return Error(http.StatusTooManyRequests, nil)
})
ih = newMetricsInstrumentedHandler(ctx, "foo", mh, metrics)
r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder()
if err := ih.ServeHTTP(w, r, nil); err == nil {
t.Errorf("expected error to be propagated")
}
expected := `
# HELP caddy_http_request_duration_seconds Histogram of round-trip request durations.
# TYPE caddy_http_request_duration_seconds histogram
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.005"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.01"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.025"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.05"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.1"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.25"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="0.5"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="1"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="2.5"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="5"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="10"} 1
caddy_http_request_duration_seconds_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_request_duration_seconds_count{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN"} 1
# HELP caddy_http_request_size_bytes Total size of the request. Includes body
# TYPE caddy_http_request_size_bytes histogram
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_request_size_bytes_sum{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN"} 23
caddy_http_request_size_bytes_count{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_request_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_request_size_bytes_sum{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN"} 23
caddy_http_request_size_bytes_count{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_request_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_request_size_bytes_sum{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN"} 23
caddy_http_request_size_bytes_count{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN"} 1
# HELP caddy_http_response_size_bytes Size of the returned response.
# TYPE caddy_http_response_size_bytes histogram
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_response_size_bytes_sum{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN"} 12
caddy_http_response_size_bytes_count{code="200",handler="bar",host="example.com",method="GET",server="UNKNOWN"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_response_size_bytes_bucket{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_response_size_bytes_sum{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN"} 0
caddy_http_response_size_bytes_count{code="200",handler="empty",host="example.com",method="GET",server="UNKNOWN"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="256"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="1024"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="4096"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="16384"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="65536"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="262144"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="1.048576e+06"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="4.194304e+06"} 1
caddy_http_response_size_bytes_bucket{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN",le="+Inf"} 1
caddy_http_response_size_bytes_sum{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN"} 0
caddy_http_response_size_bytes_count{code="429",handler="foo",host="example.com",method="GET",server="UNKNOWN"} 1
# HELP caddy_http_request_errors_total Number of requests resulting in middleware errors.
# TYPE caddy_http_request_errors_total counter
caddy_http_request_errors_total{handler="bar",host="example.com",server="UNKNOWN"} 1
caddy_http_request_errors_total{handler="foo",host="example.com",server="UNKNOWN"} 1
`
if err := testutil.GatherAndCompare(ctx.GetMetricsRegistry(), strings.NewReader(expected),
"caddy_http_request_size_bytes", "caddy_http_request_size_bytes",
"caddy_http_response_size_bytes", "caddy_http_response_size_bytes",
// caddy_http_request_duration_seconds_sum will vary based on how long the test took to run, // caddy_http_request_duration_seconds_sum will vary based on how long the test took to run,
@@ -25,7 +25,12 @@ import (
) )
// ListenerWrapper provides PROXY protocol support to Caddy by implementing // ListenerWrapper provides PROXY protocol support to Caddy by implementing
// the caddy.ListenerWrapper interface. It must be loaded before the `tls` listener. // the caddy.ListenerWrapper interface. If a connection is received via Unix
// socket, it's trusted. Otherwise, it's checked against the Allow/Deny lists,
// then it's handled by the FallbackPolicy.
//
// It must be loaded before the `tls` listener because the PROXY protocol
// encapsulates the TLS data.
// //
// Credit goes to https://github.com/mastercactapus/caddy2-proxyprotocol for having // Credit goes to https://github.com/mastercactapus/caddy2-proxyprotocol for having
// initially implemented this as a plugin. // initially implemented this as a plugin.
@@ -40,17 +45,44 @@ type ListenerWrapper struct {
Allow []string `json:"allow,omitempty"` Allow []string `json:"allow,omitempty"`
allow []netip.Prefix 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 PROXY headers from.
Deny []string `json:"deny,omitempty"` Deny []string `json:"deny,omitempty"`
deny []netip.Prefix deny []netip.Prefix
// Accepted values are: ignore, use, reject, require, skip // FallbackPolicy specifies the policy to use if the downstream
// default: ignore // IP address is not in the Allow list nor is in the Deny list.
//
// NOTE: The generated docs which describe the value of this
// field is wrong because of how this type unmarshals JSON in a
// custom way. The field expects a string, not a number.
//
// Accepted values are: IGNORE, USE, REJECT, REQUIRE, SKIP
//
// - IGNORE: address from PROXY header, but accept connection
//
// - USE: address from PROXY header
//
// - REJECT: connection when PROXY header is sent
// Note: even though the first read on the connection returns an error if
// a PROXY header is present, subsequent reads do not. It is the task of
// the code using the connection to handle that case properly.
//
// - REQUIRE: connection to send PROXY header, reject if not present
// Note: even though the first read on the connection returns an error if
// a PROXY header is not present, subsequent reads do not. It is the task
// of the code using the connection to handle that case properly.
//
// - SKIP: accepts a connection without requiring the PROXY header.
// Note: an example usage can be found in the SkipProxyHeaderForCIDR
// function.
//
// Default: IGNORE
//
// Policy definitions are here: https://pkg.go.dev/github.com/pires/go-proxyproto@v0.7.0#Policy // Policy definitions are here: https://pkg.go.dev/github.com/pires/go-proxyproto@v0.7.0#Policy
FallbackPolicy Policy `json:"fallback_policy,omitempty"` FallbackPolicy Policy `json:"fallback_policy,omitempty"`
policy goproxy.PolicyFunc policy goproxy.ConnPolicyFunc
} }
// Provision sets up the listener wrapper. // Provision sets up the listener wrapper.
@@ -69,13 +101,14 @@ func (pp *ListenerWrapper) Provision(ctx caddy.Context) error {
} }
pp.deny = append(pp.deny, ipnet) pp.deny = append(pp.deny, ipnet)
} }
pp.policy = func(upstream net.Addr) (goproxy.Policy, error) {
pp.policy = func(options goproxy.ConnPolicyOptions) (goproxy.Policy, error) {
// trust unix sockets // trust unix sockets
if network := upstream.Network(); caddy.IsUnixNetwork(network) { if network := options.Upstream.Network(); caddy.IsUnixNetwork(network) || caddy.IsFdNetwork(network) {
return goproxy.USE, nil return goproxy.USE, nil
} }
ret := pp.FallbackPolicy ret := pp.FallbackPolicy
host, _, err := net.SplitHostPort(upstream.String()) host, _, err := net.SplitHostPort(options.Upstream.String())
if err != nil { if err != nil {
return goproxy.REJECT, err return goproxy.REJECT, err
} }
@@ -106,6 +139,6 @@ func (pp *ListenerWrapper) WrapListener(l net.Listener) net.Listener {
Listener: l, Listener: l,
ReadHeaderTimeout: time.Duration(pp.Timeout), ReadHeaderTimeout: time.Duration(pp.Timeout),
} }
pl.Policy = pp.policy pl.ConnPolicy = pp.policy
return pl return pl
} }
+15 -9
View File
@@ -20,6 +20,7 @@ import (
"strings" "strings"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -92,14 +93,17 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
// push first! // push first!
for _, resource := range h.Resources { for _, resource := range h.Resources {
h.logger.Debug("pushing resource", if c := h.logger.Check(zapcore.DebugLevel, "pushing resource"); c != nil {
zap.String("uri", r.RequestURI), c.Write(
zap.String("push_method", resource.Method), zap.String("uri", r.RequestURI),
zap.String("push_target", resource.Target), zap.String("push_method", resource.Method),
zap.Object("push_headers", caddyhttp.LoggableHTTPHeader{ zap.String("push_target", resource.Target),
Header: hdr, zap.Object("push_headers", caddyhttp.LoggableHTTPHeader{
ShouldLogCredentials: shouldLogCredentials, Header: hdr,
})) ShouldLogCredentials: shouldLogCredentials,
}),
)
}
err := pusher.Push(repl.ReplaceAll(resource.Target, "."), &http.PushOptions{ err := pusher.Push(repl.ReplaceAll(resource.Target, "."), &http.PushOptions{
Method: resource.Method, Method: resource.Method,
Header: hdr, Header: hdr,
@@ -209,7 +213,9 @@ func (lp linkPusher) WriteHeader(statusCode int) {
if links, ok := lp.ResponseWriter.Header()["Link"]; ok { if links, ok := lp.ResponseWriter.Header()["Link"]; ok {
// only initiate these pushes if it hasn't been done yet // only initiate these pushes if it hasn't been done yet
if val := caddyhttp.GetVar(lp.request.Context(), pushedLink); val == nil { if val := caddyhttp.GetVar(lp.request.Context(), pushedLink); val == nil {
lp.handler.logger.Debug("pushing Link resources", zap.Strings("linked", links)) if c := lp.handler.logger.Check(zapcore.DebugLevel, "pushing Link resources"); c != nil {
c.Write(zap.Strings("linked", links))
}
caddyhttp.SetVar(lp.request.Context(), pushedLink, true) caddyhttp.SetVar(lp.request.Context(), pushedLink, true)
lp.handler.servePreloadLinks(lp.pusher, lp.header, links) lp.handler.servePreloadLinks(lp.pusher, lp.header, links)
} }
+8
View File
@@ -142,8 +142,16 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
} }
return port, true return port, true
case "http.request.remote": case "http.request.remote":
if req.TLS != nil && !req.TLS.HandshakeComplete {
// without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed
return nil, true
}
return req.RemoteAddr, true return req.RemoteAddr, true
case "http.request.remote.host": case "http.request.remote.host":
if req.TLS != nil && !req.TLS.HandshakeComplete {
// without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed
return nil, true
}
host, _, err := net.SplitHostPort(req.RemoteAddr) host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil { if err != nil {
// req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path // req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path
+7 -2
View File
@@ -20,6 +20,7 @@ import (
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -69,12 +70,16 @@ func (rb RequestBody) ServeHTTP(w http.ResponseWriter, r *http.Request, next cad
rc := http.NewResponseController(w) rc := http.NewResponseController(w)
if rb.ReadTimeout > 0 { if rb.ReadTimeout > 0 {
if err := rc.SetReadDeadline(time.Now().Add(rb.ReadTimeout)); err != nil { if err := rc.SetReadDeadline(time.Now().Add(rb.ReadTimeout)); err != nil {
rb.logger.Error("could not set read deadline", zap.Error(err)) if c := rb.logger.Check(zapcore.ErrorLevel, "could not set read deadline"); c != nil {
c.Write(zap.Error(err))
}
} }
} }
if rb.WriteTimeout > 0 { if rb.WriteTimeout > 0 {
if err := rc.SetWriteDeadline(time.Now().Add(rb.WriteTimeout)); err != nil { if err := rc.SetWriteDeadline(time.Now().Add(rb.WriteTimeout)); err != nil {
rb.logger.Error("could not set write deadline", zap.Error(err)) if c := rb.logger.Check(zapcore.ErrorLevel, "could not set write deadline"); c != nil {
c.Write(zap.Error(err))
}
} }
} }
} }
+10 -6
View File
@@ -42,9 +42,13 @@ func (rww *ResponseWriterWrapper) Push(target string, opts *http.PushOptions) er
return ErrNotImplemented return ErrNotImplemented
} }
// ReadFrom implements io.ReaderFrom. It simply calls io.Copy, // ReadFrom implements io.ReaderFrom. It retries to use io.ReaderFrom if available,
// which uses io.ReaderFrom if available. // then fallback to io.Copy.
// see: https://github.com/caddyserver/caddy/issues/6546
func (rww *ResponseWriterWrapper) ReadFrom(r io.Reader) (n int64, err error) { func (rww *ResponseWriterWrapper) ReadFrom(r io.Reader) (n int64, err error) {
if rf, ok := rww.ResponseWriter.(io.ReaderFrom); ok {
return rf.ReadFrom(r)
}
return io.Copy(rww.ResponseWriter, r) return io.Copy(rww.ResponseWriter, r)
} }
@@ -219,13 +223,13 @@ func (rr *responseRecorder) Buffered() bool {
} }
func (rr *responseRecorder) WriteResponse() error { func (rr *responseRecorder) WriteResponse() error {
if rr.stream {
return nil
}
if rr.statusCode == 0 { if rr.statusCode == 0 {
// could happen if no handlers actually wrote anything, // could happen if no handlers actually wrote anything,
// and this prevents a panic; status must be > 0 // and this prevents a panic; status must be > 0
rr.statusCode = http.StatusOK rr.WriteHeader(http.StatusOK)
}
if rr.stream {
return nil
} }
rr.ResponseWriterWrapper.WriteHeader(rr.statusCode) rr.ResponseWriterWrapper.WriteHeader(rr.statusCode)
_, err := io.Copy(rr.ResponseWriterWrapper, rr.buf) _, err := io.Copy(rr.ResponseWriterWrapper, rr.buf)
+1 -1
View File
@@ -137,7 +137,7 @@ func parseUpstreamDialAddress(upstreamAddr string) (parsedAddr, error) {
} }
// we can assume a port if only a hostname is specified, but use of a // we can assume a port if only a hostname is specified, but use of a
// placeholder without a port likely means a port will be filled in // placeholder without a port likely means a port will be filled in
if port == "" && !strings.Contains(host, "{") && !caddy.IsUnixNetwork(network) { if port == "" && !strings.Contains(host, "{") && !caddy.IsUnixNetwork(network) && !caddy.IsFdNetwork(network) {
port = "80" port = "80"
} }
} }
+68 -14
View File
@@ -16,6 +16,7 @@ package reverseproxy
import ( import (
"fmt" "fmt"
"net"
"net/http" "net/http"
"reflect" "reflect"
"strconv" "strconv"
@@ -27,6 +28,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"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/internal"
"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/caddyhttp/rewrite" "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
@@ -67,14 +69,16 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// lb_retry_match <request-matcher> // lb_retry_match <request-matcher>
// //
// # active health checking // # active health checking
// health_uri <uri> // health_uri <uri>
// health_port <port> // health_port <port>
// health_interval <interval> // health_interval <interval>
// health_passes <num> // health_passes <num>
// health_fails <num> // health_fails <num>
// health_timeout <duration> // health_timeout <duration>
// health_status <status> // health_status <status>
// health_body <regexp> // health_body <regexp>
// health_method <value>
// health_request_body <value>
// health_follow_redirects // health_follow_redirects
// health_headers { // health_headers {
// <field> [<values...>] // <field> [<values...>]
@@ -89,12 +93,11 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// //
// # streaming // # streaming
// flush_interval <duration> // flush_interval <duration>
// buffer_requests // request_buffers <size>
// buffer_responses // response_buffers <size>
// max_buffer_size <size>
// stream_timeout <duration> // stream_timeout <duration>
// stream_close_delay <duration> // stream_close_delay <duration>
// trace_logs // verbose_logs
// //
// # request manipulation // # request manipulation
// trusted_proxies [private_ranges] <ranges...> // trusted_proxies [private_ranges] <ranges...>
@@ -353,6 +356,26 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
h.HealthChecks.Active.Path = d.Val() h.HealthChecks.Active.Path = d.Val()
caddy.Log().Named("config.adapter.caddyfile").Warn("the 'health_path' subdirective is deprecated, please use 'health_uri' instead!") caddy.Log().Named("config.adapter.caddyfile").Warn("the 'health_path' subdirective is deprecated, please use 'health_uri' instead!")
case "health_upstream":
if !d.NextArg() {
return d.ArgErr()
}
if h.HealthChecks == nil {
h.HealthChecks = new(HealthChecks)
}
if h.HealthChecks.Active == nil {
h.HealthChecks.Active = new(ActiveHealthChecks)
}
_, port, err := net.SplitHostPort(d.Val())
if err != nil {
return d.Errf("health_upstream is malformed '%s': %v", d.Val(), err)
}
_, err = strconv.Atoi(port)
if err != nil {
return d.Errf("bad port number '%s': %v", d.Val(), err)
}
h.HealthChecks.Active.Upstream = d.Val()
case "health_port": case "health_port":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
@@ -363,6 +386,9 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if h.HealthChecks.Active == nil { if h.HealthChecks.Active == nil {
h.HealthChecks.Active = new(ActiveHealthChecks) h.HealthChecks.Active = new(ActiveHealthChecks)
} }
if h.HealthChecks.Active.Upstream != "" {
return d.Errf("the 'health_port' subdirective is ignored if 'health_upstream' is used!")
}
portNum, err := strconv.Atoi(d.Val()) portNum, err := strconv.Atoi(d.Val())
if err != nil { if err != nil {
return d.Errf("bad port number '%s': %v", d.Val(), err) return d.Errf("bad port number '%s': %v", d.Val(), err)
@@ -387,6 +413,30 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
} }
h.HealthChecks.Active.Headers = healthHeaders h.HealthChecks.Active.Headers = healthHeaders
case "health_method":
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.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": case "health_interval":
if !d.NextArg() { if !d.NextArg() {
return d.ArgErr() return d.ArgErr()
@@ -651,7 +701,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
case "trusted_proxies": case "trusted_proxies":
for d.NextArg() { for d.NextArg() {
if d.Val() == "private_ranges" { if d.Val() == "private_ranges" {
h.TrustedProxies = append(h.TrustedProxies, caddyhttp.PrivateRangesCIDR()...) h.TrustedProxies = append(h.TrustedProxies, internal.PrivateRangesCIDR()...)
continue continue
} }
h.TrustedProxies = append(h.TrustedProxies, d.Val()) h.TrustedProxies = append(h.TrustedProxies, d.Val())
@@ -1275,7 +1325,11 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.Err("cannot specify \"tls_trust_pool\" twice in caddyfile") return d.Err("cannot specify \"tls_trust_pool\" twice in caddyfile")
} }
h.TLS.CARaw = caddyconfig.JSONModuleObject(ca, "provider", modStem, nil) h.TLS.CARaw = caddyconfig.JSONModuleObject(ca, "provider", modStem, nil)
case "local_address":
if !d.NextArg() {
return d.ArgErr()
}
h.LocalAddress = d.Val()
default: default:
return d.Errf("unrecognized subdirective %s", d.Val()) return d.Errf("unrecognized subdirective %s", d.Val())
} }
+7 -5
View File
@@ -229,11 +229,13 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
if changeHost { if changeHost {
if handler.Headers == nil { if handler.Headers == nil {
handler.Headers = &headers.Handler{ handler.Headers = new(headers.Handler)
Request: &headers.HeaderOps{ }
Set: http.Header{}, if handler.Headers.Request == nil {
}, handler.Headers.Request = new(headers.HeaderOps)
} }
if handler.Headers.Request.Set == nil {
handler.Headers.Request.Set = http.Header{}
} }
handler.Headers.Request.Set.Set("Host", "{http.reverse_proxy.upstream.hostport}") handler.Headers.Request.Set.Set("Host", "{http.reverse_proxy.upstream.hostport}")
} }
@@ -19,6 +19,8 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/dustin/go-humanize"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"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"
@@ -43,7 +45,10 @@ func init() {
// dial_timeout <duration> // dial_timeout <duration>
// read_timeout <duration> // read_timeout <duration>
// write_timeout <duration> // write_timeout <duration>
// capture_stderr // body_buffer_disabled
// body_buffer_memory_limit <size>
// file_buffer_size_limit <size>
// file_buffer_filepath <path>
// } // }
func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // consume transport name d.Next() // consume transport name
@@ -113,6 +118,35 @@ func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
} }
t.CaptureStderr = true t.CaptureStderr = true
case "body_buffer_disabled":
t.BodyBufferDisabled = true
case "body_buffer_memory_limit":
if !d.NextArg() {
return d.ArgErr()
}
size, err := humanize.ParseBytes(d.Val())
if err != nil {
return d.Errf("bad buffer size %s: %v", d.Val(), err)
}
t.BodyBufferMemoryLimit = int64(size)
case "file_buffer_size_limit":
if !d.NextArg() {
return d.ArgErr()
}
size, err := humanize.ParseBytes(d.Val())
if err != nil {
return d.Errf("bad buffer size %s: %v", d.Val(), err)
}
t.FileBufferSizeLimit = int64(size)
case "file_buffer_filepath":
if !d.NextArg() {
return d.ArgErr()
}
t.FileBufferFilepath = d.Val()
default: default:
return d.Errf("unrecognized subdirective %s", d.Val()) return d.Errf("unrecognized subdirective %s", d.Val())
} }
@@ -294,6 +328,40 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
args := dispenser.RemainingArgs() args := dispenser.RemainingArgs()
dispenser.DeleteN(len(args) + 1) dispenser.DeleteN(len(args) + 1)
fcgiTransport.CaptureStderr = true fcgiTransport.CaptureStderr = true
case "body_buffer_disabled":
args := dispenser.RemainingArgs()
dispenser.DeleteN(len(args) + 1)
fcgiTransport.BodyBufferDisabled = true
case "body_buffer_memory_limit":
if !dispenser.NextArg() {
return nil, dispenser.ArgErr()
}
size, err := humanize.ParseBytes(dispenser.Val())
if err != nil {
return nil, dispenser.Errf("bad buffer size %s: %v", dispenser.Val(), err)
}
fcgiTransport.BodyBufferMemoryLimit = int64(size)
dispenser.DeleteN(2)
case "file_buffer_size_limit":
if !dispenser.NextArg() {
return nil, dispenser.ArgErr()
}
size, err := humanize.ParseBytes(dispenser.Val())
if err != nil {
return nil, dispenser.Errf("bad buffer size %s: %v", dispenser.Val(), err)
}
fcgiTransport.FileBufferSizeLimit = int64(size)
dispenser.DeleteN(2)
case "file_buffer_filepath":
if !dispenser.NextArg() {
return nil, dispenser.ArgErr()
}
fcgiTransport.FileBufferFilepath = dispenser.Val()
dispenser.DeleteN(2)
} }
} }
} }
@@ -40,6 +40,7 @@ import (
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
) )
// FCGIListenSockFileno describes listen socket file number. // FCGIListenSockFileno describes listen socket file number.
@@ -127,9 +128,11 @@ var pad [maxPad]byte
type client struct { type client struct {
rwc net.Conn rwc net.Conn
// keepAlive bool // TODO: implement // keepAlive bool // TODO: implement
reqID uint16 reqID uint16
stderr bool stderr bool
logger *zap.Logger logger *zap.Logger
buffer bool
bufferFunc func(io.Reader) (int64, io.ReadCloser, error)
} }
// Do made the request and returns a io.Reader that translates the data read // Do made the request and returns a io.Reader that translates the data read
@@ -157,6 +160,10 @@ func (c *client) Do(p map[string]string, req io.Reader) (r io.Reader, err error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// body length mismatch
if lr, ok := req.(*io.LimitedReader); ok && lr.N > 0 {
return nil, io.ErrUnexpectedEOF
}
} }
err = writer.FlushStream() err = writer.FlushStream()
if err != nil { if err != nil {
@@ -184,18 +191,80 @@ func (f clientCloser) Close() error {
return f.rwc.Close() return f.rwc.Close()
} }
logLevel := zapcore.WarnLevel
if f.status >= 400 { if f.status >= 400 {
f.logger.Error("stderr", zap.ByteString("body", stderr)) logLevel = zapcore.ErrorLevel
} else { }
f.logger.Warn("stderr", zap.ByteString("body", stderr))
if c := f.logger.Check(logLevel, "stderr"); c != nil {
c.Write(zap.ByteString("body", stderr))
} }
return f.rwc.Close() return f.rwc.Close()
} }
// create a response that describes the error message, it's passed to the client.
// caller should close the connection to fastcgi server
func newErrorResponse(status int) *http.Response {
statusText := http.StatusText(status)
resp := &http.Response{
Status: statusText,
StatusCode: status,
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader(statusText)),
ContentLength: int64(len(statusText)),
}
resp.Header.Set("Content-Length", strconv.FormatInt(resp.ContentLength, 10))
resp.Header.Set("Content-Type", "text/plain; charset=utf-8")
resp.Header.Set("X-Content-Type-Options", "nosniff")
return resp
}
// check for invalid content_length to determine if the request should be buffered
func checkContentLength(p map[string]string) (int64, bool) {
clStr, ok := p["CONTENT_LENGTH"]
if !ok {
return 0, false
}
cl, err := strconv.ParseInt(clStr, 10, 64)
if err != nil || cl < 0 {
return 0, false
}
return cl, true
}
// Request returns a HTTP Response with Header and Body // Request returns a HTTP Response with Header and Body
// from fcgi responder // from fcgi responder
func (c *client) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) { func (c *client) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) {
// defer closing the request body if it's an io.Closer
if closer, ok := req.(io.Closer); ok {
defer closer.Close()
}
// check for content_length and buffer the request if needed
cl, ok := checkContentLength(p)
if !ok {
// buffering disabled
if !c.buffer {
c.rwc.Close()
return newErrorResponse(http.StatusLengthRequired), nil
} else {
// buffer the request
size, rc, err := c.bufferFunc(req)
if err != nil {
if err == errFileBufferExceeded {
c.rwc.Close()
return newErrorResponse(http.StatusRequestEntityTooLarge), nil
}
return nil, err
}
defer rc.Close()
p["CONTENT_LENGTH"] = strconv.FormatInt(size, 10)
}
} else {
req = io.LimitReader(req, cl)
}
r, err := c.Do(p, req) r, err := c.Do(p, req)
if err != nil { if err != nil {
return return
@@ -298,7 +367,7 @@ func (c *client) Post(p map[string]string, method string, bodyType string, body
// PostForm issues a POST to the fcgi responder, with form // PostForm issues a POST to the fcgi responder, with form
// as a string key to a list values (url.Values) // as a string key to a list values (url.Values)
func (c *client) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) { func (c *client) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) {
body := bytes.NewReader([]byte(data.Encode())) body := strings.NewReader(data.Encode())
return c.Post(p, "POST", "application/x-www-form-urlencoded", body, int64(body.Len())) return c.Post(p, "POST", "application/x-www-form-urlencoded", body, int64(body.Len()))
} }
+171 -11
View File
@@ -15,10 +15,14 @@
package fastcgi package fastcgi
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
@@ -80,8 +84,18 @@ type Transport struct {
// be used instead. // be used instead.
CaptureStderr bool `json:"capture_stderr,omitempty"` CaptureStderr bool `json:"capture_stderr,omitempty"`
serverSoftware string // disable buffering of the request body that doesn't have a content length
logger *zap.Logger BodyBufferDisabled bool `json:"body_buffer_disabled,omitempty"`
// memory limit for buffering the request body, the rest will be buffered by temporary files
BodyBufferMemoryLimit int64 `json:"body_buffer_memory_limit,omitempty"`
// total disk storage allowed by the request body buffer
FileBufferSizeLimit int64 `json:"file_buffer_size_limit,omitempty"`
// the path to store the temporary files for the request body buffer
FileBufferFilepath string `json:"file_buffer_filepath,omitempty"`
serverSoftware string
logger *zap.Logger
tempFileLimiter *fileQuotaLimiter
} }
// CaddyModule returns the Caddy module information. // CaddyModule returns the Caddy module information.
@@ -92,6 +106,15 @@ func (Transport) CaddyModule() caddy.ModuleInfo {
} }
} }
const (
defaultDialTimeout = 3 * time.Second
// nginx default for 64bit platforms
// https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size
defaultMemBufferSize = 1 << 14 // 16KB
// nginx doesn't have an option to limit the total file buffer size
defaultFileBufferSize = 100 << 20 // 100MB
)
// 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.logger = ctx.Logger()
@@ -106,12 +129,144 @@ func (t *Transport) Provision(ctx caddy.Context) error {
// Set a relatively short default dial timeout. // Set a relatively short default dial timeout.
// This is helpful to make load-balancer retries more speedy. // This is helpful to make load-balancer retries more speedy.
if t.DialTimeout == 0 { if t.DialTimeout == 0 {
t.DialTimeout = caddy.Duration(3 * time.Second) t.DialTimeout = caddy.Duration(defaultDialTimeout)
}
if !t.BodyBufferDisabled {
if t.FileBufferFilepath == "" {
t.FileBufferFilepath = os.TempDir()
}
// test if temporary file can be created
file, err := os.CreateTemp(t.FileBufferFilepath, "caddy-fastcgi-buffer-")
if err != nil {
return fmt.Errorf("failed to create temporary file: %v", err)
}
file.Close()
os.Remove(file.Name())
if t.BodyBufferMemoryLimit == 0 {
t.BodyBufferMemoryLimit = defaultMemBufferSize
}
if t.FileBufferSizeLimit == 0 {
t.FileBufferSizeLimit = defaultFileBufferSize
}
t.tempFileLimiter = newFileQuotaLimiter(t.FileBufferSizeLimit)
} }
return nil return nil
} }
type bufferedBody struct {
memBuf *bytes.Buffer
fileBuf *os.File
filesize int64
tempFileLimiter *fileQuotaLimiter
}
func (b *bufferedBody) Read(p []byte) (int, error) {
if b.memBuf != nil {
if b.memBuf.Len() != 0 {
return b.memBuf.Read(p)
}
bufPool.Put(b.memBuf)
b.memBuf = nil
}
if b.fileBuf != nil {
n, err := b.fileBuf.Read(p)
if err != nil {
// close the file and remove it
b.fileBuf.Close()
os.Remove(b.fileBuf.Name())
b.tempFileLimiter.release(b.filesize)
b.fileBuf = nil
return n, err
}
}
return 0, io.EOF
}
func (b *bufferedBody) Close() error {
if b.memBuf != nil {
bufPool.Put(b.memBuf)
b.memBuf = nil
}
if b.fileBuf != nil {
b.fileBuf.Close()
os.Remove(b.fileBuf.Name())
b.tempFileLimiter.release(b.filesize)
b.fileBuf = nil
}
return nil
}
var errFileBufferExceeded = errors.New("temporary file buffer limit exceeded")
func (t Transport) bufferBodyToFile(file *os.File, req io.Reader) (int64, error) {
buf := streamingBufPool.Get().(*[]byte)
defer streamingBufPool.Put(buf)
var size int64
for {
reserved := t.tempFileLimiter.acquire(readBufSize)
if !reserved {
return size, errFileBufferExceeded
}
n, er := req.Read(*buf)
if n > 0 {
nw, ew := file.Write((*buf)[:n])
size += int64(nw)
t.tempFileLimiter.release(int64(readBufSize - nw))
if ew != nil {
return size, ew
}
}
if er != nil {
if er == io.EOF {
return size, nil
}
return size, er
}
}
}
func (t Transport) bufferBody(req io.Reader) (int64, io.ReadCloser, error) {
if closer, ok := req.(io.Closer); ok {
defer closer.Close()
}
memBuf := bufPool.Get().(*bytes.Buffer)
memBuf.Reset()
size, err := io.CopyN(memBuf, req, t.BodyBufferMemoryLimit)
var body bufferedBody // should be closed in case buffering fails
body.memBuf = memBuf
body.tempFileLimiter = t.tempFileLimiter
// error while reading the body
if err != nil {
// fully buffered in memory
if err == io.EOF {
return size, &body, nil
}
body.Close()
return 0, nil, err
}
// temporary file is needed here.
fileBuf, err := os.CreateTemp(t.FileBufferFilepath, "caddy-fastcgi-buffer-")
if err != nil {
body.Close()
return 0, nil, err
}
body.fileBuf = fileBuf
// buffer the rest of the body to the file
fSize, err := t.bufferBodyToFile(fileBuf, req)
body.filesize = fSize
if err != nil {
body.Close()
return 0, nil, err
}
return size + fSize, &body, nil
}
// RoundTrip implements http.RoundTripper. // RoundTrip implements http.RoundTripper.
func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) { func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
server := r.Context().Value(caddyhttp.ServerCtxKey).(*caddyhttp.Server) server := r.Context().Value(caddyhttp.ServerCtxKey).(*caddyhttp.Server)
@@ -148,10 +303,13 @@ func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
zap.Object("request", loggableReq), zap.Object("request", loggableReq),
zap.Object("env", loggableEnv), zap.Object("env", loggableEnv),
) )
logger.Debug("roundtrip", if c := t.logger.Check(zapcore.DebugLevel, "roundtrip"); c != nil {
zap.String("dial", address), c.Write(
zap.Object("env", loggableEnv), zap.String("dial", address),
zap.Object("request", loggableReq)) zap.Object("env", loggableEnv),
zap.Object("request", loggableReq),
)
}
// connect to the backend // connect to the backend
dialer := net.Dialer{Timeout: time.Duration(t.DialTimeout)} dialer := net.Dialer{Timeout: time.Duration(t.DialTimeout)}
@@ -168,10 +326,12 @@ func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
// create the client that will facilitate the protocol // create the client that will facilitate the protocol
client := client{ client := client{
rwc: conn, rwc: conn,
reqID: 1, reqID: 1,
logger: logger, logger: logger,
stderr: t.CaptureStderr, stderr: t.CaptureStderr,
buffer: !t.BodyBufferDisabled,
bufferFunc: t.bufferBody,
} }
// read/write timeouts // read/write timeouts
@@ -24,3 +24,16 @@ var bufPool = sync.Pool{
return new(bytes.Buffer) return new(bytes.Buffer)
}, },
} }
const readBufSize = 4096
var streamingBufPool = sync.Pool{
New: func() any {
// The Pool's New function should generally only return pointer
// types, since a pointer can be put into the return interface
// value without an allocation
// - (from the package docs)
b := make([]byte, readBufSize)
return &b
},
}
@@ -0,0 +1,34 @@
package fastcgi
import "sync"
type fileQuotaLimiter struct {
maxUsage int64
currentUsage int64
mu sync.Mutex
}
func newFileQuotaLimiter(maxUsage int64) *fileQuotaLimiter {
return &fileQuotaLimiter{
maxUsage: maxUsage,
}
}
func (l *fileQuotaLimiter) acquire(n int64) bool {
l.mu.Lock()
defer l.mu.Unlock()
if l.currentUsage+n > l.maxUsage {
return false
}
l.currentUsage += n
return true
}
func (l *fileQuotaLimiter) release(n int64) {
l.mu.Lock()
defer l.mu.Unlock()
l.currentUsage -= n
}
+153 -74
View File
@@ -23,10 +23,13 @@ import (
"net/url" "net/url"
"regexp" "regexp"
"runtime/debug" "runtime/debug"
"slices"
"strconv" "strconv"
"strings"
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -75,13 +78,27 @@ type ActiveHealthChecks struct {
// The URI (path and query) to use for health checks // The URI (path and query) to use for health checks
URI string `json:"uri,omitempty"` URI string `json:"uri,omitempty"`
// The host:port to use (if different from the upstream's dial address)
// for health checks. This should be used in tandem with `health_header` and
// `{http.reverse_proxy.active.target_upstream}`. This can be helpful when
// creating an intermediate service to do a more thorough health check.
// If upstream is set, the active health check port is ignored.
Upstream string `json:"upstream,omitempty"`
// The port to use (if different from the upstream's dial // The port to use (if different from the upstream's dial
// address) for health checks. // address) for health checks. If active upstream is set,
// this value is ignored.
Port int `json:"port,omitempty"` Port int `json:"port,omitempty"`
// HTTP headers to set on health check requests. // HTTP headers to set on health check requests.
Headers http.Header `json:"headers,omitempty"` Headers http.Header `json:"headers,omitempty"`
// 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). // Whether to follow HTTP redirects in response to active health checks (default off).
FollowRedirects bool `json:"follow_redirects,omitempty"` FollowRedirects bool `json:"follow_redirects,omitempty"`
@@ -133,6 +150,11 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
} }
a.Headers = cleaned a.Headers = cleaned
// If Method is not set, default to GET
if a.Method == "" {
a.Method = http.MethodGet
}
h.HealthChecks.Active.logger = h.logger.Named("health_checker.active") h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
timeout := time.Duration(a.Timeout) timeout := time.Duration(a.Timeout)
@@ -165,9 +187,14 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
} }
for _, upstream := range h.Upstreams { for _, upstream := range h.Upstreams {
// if there's an alternative port for health-check provided in the config, // if there's an alternative upstream for health-check provided in the config,
// then use it, otherwise use the port of upstream. // then use it, otherwise use the upstream's dial address. if upstream is used,
if a.Port != 0 { // then the port is ignored.
if a.Upstream != "" {
upstream.activeHealthCheckUpstream = a.Upstream
} else if a.Port != 0 {
// if there's an alternative port for health-check provided in the config,
// then use it, otherwise use the port of upstream.
upstream.activeHealthCheckPort = a.Port upstream.activeHealthCheckPort = a.Port
} }
} }
@@ -245,9 +272,12 @@ type CircuitBreaker interface {
func (h *Handler) activeHealthChecker() { func (h *Handler) activeHealthChecker() {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
h.HealthChecks.Active.logger.Error("active health checker panicked", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health checker panicked"); c != nil {
zap.Any("error", err), c.Write(
zap.ByteString("stack", debug.Stack())) zap.Any("error", err),
zap.ByteString("stack", debug.Stack()),
)
}
} }
}() }()
ticker := time.NewTicker(time.Duration(h.HealthChecks.Active.Interval)) ticker := time.NewTicker(time.Duration(h.HealthChecks.Active.Interval))
@@ -270,54 +300,65 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
go func(upstream *Upstream) { go func(upstream *Upstream) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
h.HealthChecks.Active.logger.Error("active health check panicked", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health checker panicked"); c != nil {
zap.Any("error", err), c.Write(
zap.ByteString("stack", debug.Stack())) zap.Any("error", err),
zap.ByteString("stack", debug.Stack()),
)
}
} }
}() }()
networkAddr, err := caddy.NewReplacer().ReplaceOrErr(upstream.Dial, true, true) networkAddr, err := caddy.NewReplacer().ReplaceOrErr(upstream.Dial, true, true)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Error("invalid use of placeholders in dial address for active health checks", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "invalid use of placeholders in dial address for active health checks"); c != nil {
zap.String("address", networkAddr), c.Write(
zap.Error(err), zap.String("address", networkAddr),
) zap.Error(err),
)
}
return return
} }
addr, err := caddy.ParseNetworkAddress(networkAddr) addr, err := caddy.ParseNetworkAddress(networkAddr)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Error("bad network address", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "bad network address"); c != nil {
zap.String("address", networkAddr), c.Write(
zap.Error(err), zap.String("address", networkAddr),
) zap.Error(err),
)
}
return return
} }
if hcp := uint(upstream.activeHealthCheckPort); hcp != 0 { if hcp := uint(upstream.activeHealthCheckPort); hcp != 0 {
if addr.IsUnixNetwork() { if addr.IsUnixNetwork() || addr.IsFdNetwork() {
addr.Network = "tcp" // I guess we just assume TCP since we are using a port?? addr.Network = "tcp" // I guess we just assume TCP since we are using a port??
} }
addr.StartPort, addr.EndPort = hcp, hcp addr.StartPort, addr.EndPort = hcp, hcp
} }
if addr.PortRangeSize() != 1 { if addr.PortRangeSize() != 1 {
h.HealthChecks.Active.logger.Error("multiple addresses (upstream must map to only one address)", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "multiple addresses (upstream must map to only one address)"); c != nil {
zap.String("address", networkAddr), c.Write(
) zap.String("address", networkAddr),
)
}
return return
} }
hostAddr := addr.JoinHostPort(0) hostAddr := addr.JoinHostPort(0)
dialAddr := hostAddr dialAddr := hostAddr
if addr.IsUnixNetwork() { if addr.IsUnixNetwork() || addr.IsFdNetwork() {
// this will be used as the Host portion of a http.Request URL, and // this will be used as the Host portion of a http.Request URL, and
// paths to socket files would produce an error when creating URL, // paths to socket files would produce an error when creating URL,
// so use a fake Host value instead; unix sockets are usually local // so use a fake Host value instead; unix sockets are usually local
hostAddr = "localhost" hostAddr = "localhost"
} }
err = h.doActiveHealthCheck(DialInfo{Network: addr.Network, Address: dialAddr}, hostAddr, upstream) err = h.doActiveHealthCheck(DialInfo{Network: addr.Network, Address: dialAddr}, hostAddr, networkAddr, upstream)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Error("active health check failed", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health check failed"); c != nil {
zap.String("address", hostAddr), c.Write(
zap.Error(err), zap.String("address", hostAddr),
) zap.Error(err),
)
}
} }
}(upstream) }(upstream)
} }
@@ -330,7 +371,7 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
// according to whether it passes the health check. An error is // according to whether it passes the health check. An error is
// returned only if the health check fails to occur or if marking // returned only if the health check fails to occur or if marking
// the host's health status fails. // the host's health status fails.
func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstream *Upstream) error { func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networkAddr string, upstream *Upstream) error {
// create the URL for the request that acts as a health check // create the URL for the request that acts as a health check
u := &url.URL{ u := &url.URL{
Scheme: "http", Scheme: "http",
@@ -342,7 +383,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
if err != nil { if err != nil {
host = hostAddr host = hostAddr
} }
if h.HealthChecks.Active.Port != 0 {
// ignore active health check port if active upstream is provided as the
// active upstream already contains the replacement port
if h.HealthChecks.Active.Upstream != "" {
u.Host = h.HealthChecks.Active.Upstream
} else if h.HealthChecks.Active.Port != 0 {
port := strconv.Itoa(h.HealthChecks.Active.Port) port := strconv.Itoa(h.HealthChecks.Active.Port)
u.Host = net.JoinHostPort(host, port) u.Host = net.JoinHostPort(host, port)
} }
@@ -352,12 +398,8 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
u.Scheme = "https" u.Scheme = "https"
// if the port is in the except list, flip back to HTTP // if the port is in the except list, flip back to HTTP
if ht, ok := h.Transport.(*HTTPTransport); ok { if ht, ok := h.Transport.(*HTTPTransport); ok && slices.Contains(ht.TLS.ExceptPorts, port) {
for _, exceptPort := range ht.TLS.ExceptPorts { u.Scheme = "http"
if exceptPort == port {
u.Scheme = "http"
}
}
} }
} }
@@ -370,6 +412,16 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
u.Path = h.HealthChecks.Active.Path 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 // attach dialing information to this request, as well as context values that
// may be expected by handlers of this request // may be expected by handlers of this request
ctx := h.ctx.Context ctx := h.ctx.Context
@@ -377,15 +429,15 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{ ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{
dialInfoVarKey: dialInfo, dialInfoVarKey: dialInfo,
}) })
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), requestBody)
if err != nil { if err != nil {
return fmt.Errorf("making request: %v", err) return fmt.Errorf("making request: %v", err)
} }
ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req) ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
req = req.WithContext(ctx) req = req.WithContext(ctx)
// set headers, using a replacer with only globals (env vars, system info, etc.) // set headers, using replacer
repl := caddy.NewReplacer() repl.Set("http.reverse_proxy.active.target_upstream", networkAddr)
for key, vals := range h.HealthChecks.Active.Headers { for key, vals := range h.HealthChecks.Active.Headers {
key = repl.ReplaceAll(key, "") key = repl.ReplaceAll(key, "")
if key == "Host" { if key == "Host" {
@@ -401,9 +453,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
// increment failures and then check if it has reached the threshold to mark unhealthy // increment failures and then check if it has reached the threshold to mark unhealthy
err := upstream.Host.countHealthFail(1) err := upstream.Host.countHealthFail(1)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Error("could not count active health failure", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count active health failure"); c != nil {
zap.String("host", upstream.Dial), c.Write(
zap.Error(err)) zap.String("host", upstream.Dial),
zap.Error(err),
)
}
return return
} }
if upstream.Host.activeHealthFails() >= h.HealthChecks.Active.Fails { if upstream.Host.activeHealthFails() >= h.HealthChecks.Active.Fails {
@@ -419,13 +474,19 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
// increment passes and then check if it has reached the threshold to be healthy // increment passes and then check if it has reached the threshold to be healthy
err := upstream.Host.countHealthPass(1) err := upstream.Host.countHealthPass(1)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Error("could not count active health pass", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count active health pass"); c != nil {
zap.String("host", upstream.Dial), c.Write(
zap.Error(err)) zap.String("host", upstream.Dial),
zap.Error(err),
)
}
return return
} }
if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes { if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes {
if upstream.setHealthy(true) { if upstream.setHealthy(true) {
if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "host is up"); c != nil {
c.Write(zap.String("host", hostAddr))
}
h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr}) h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr})
upstream.Host.resetHealth() upstream.Host.resetHealth()
} }
@@ -435,10 +496,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
// do the request, being careful to tame the response body // do the request, being careful to tame the response body
resp, err := h.HealthChecks.Active.httpClient.Do(req) resp, err := h.HealthChecks.Active.httpClient.Do(req)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Info("HTTP request failed", if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "HTTP request failed"); c != nil {
zap.String("host", hostAddr), c.Write(
zap.Error(err), zap.String("host", hostAddr),
) zap.Error(err),
)
}
markUnhealthy() markUnhealthy()
return nil return nil
} }
@@ -455,18 +518,22 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
// if status code is outside criteria, mark down // if status code is outside criteria, mark down
if h.HealthChecks.Active.ExpectStatus > 0 { if h.HealthChecks.Active.ExpectStatus > 0 {
if !caddyhttp.StatusCodeMatches(resp.StatusCode, h.HealthChecks.Active.ExpectStatus) { if !caddyhttp.StatusCodeMatches(resp.StatusCode, h.HealthChecks.Active.ExpectStatus) {
h.HealthChecks.Active.logger.Info("unexpected status code", if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "unexpected status code"); c != nil {
zap.Int("status_code", resp.StatusCode), c.Write(
zap.String("host", hostAddr), zap.Int("status_code", resp.StatusCode),
) zap.String("host", hostAddr),
)
}
markUnhealthy() markUnhealthy()
return nil return nil
} }
} else if resp.StatusCode < 200 || resp.StatusCode >= 300 { } else if resp.StatusCode < 200 || resp.StatusCode >= 300 {
h.HealthChecks.Active.logger.Info("status code out of tolerances", if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "status code out of tolerances"); c != nil {
zap.Int("status_code", resp.StatusCode), c.Write(
zap.String("host", hostAddr), zap.Int("status_code", resp.StatusCode),
) zap.String("host", hostAddr),
)
}
markUnhealthy() markUnhealthy()
return nil return nil
} }
@@ -475,24 +542,27 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
if h.HealthChecks.Active.bodyRegexp != nil { if h.HealthChecks.Active.bodyRegexp != nil {
bodyBytes, err := io.ReadAll(body) bodyBytes, err := io.ReadAll(body)
if err != nil { if err != nil {
h.HealthChecks.Active.logger.Info("failed to read response body", if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "failed to read response body"); c != nil {
zap.String("host", hostAddr), c.Write(
zap.Error(err), zap.String("host", hostAddr),
) zap.Error(err),
)
}
markUnhealthy() markUnhealthy()
return nil return nil
} }
if !h.HealthChecks.Active.bodyRegexp.Match(bodyBytes) { if !h.HealthChecks.Active.bodyRegexp.Match(bodyBytes) {
h.HealthChecks.Active.logger.Info("response body failed expectations", if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "response body failed expectations"); c != nil {
zap.String("host", hostAddr), c.Write(
) zap.String("host", hostAddr),
)
}
markUnhealthy() markUnhealthy()
return nil return nil
} }
} }
// passed health check parameters, so mark as healthy // passed health check parameters, so mark as healthy
h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
markHealthy() markHealthy()
return nil return nil
@@ -516,9 +586,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
// count failure immediately // count failure immediately
err := upstream.Host.countFail(1) err := upstream.Host.countFail(1)
if err != nil { if err != nil {
h.HealthChecks.Passive.logger.Error("could not count failure", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count failure"); c != nil {
zap.String("host", upstream.Dial), c.Write(
zap.Error(err)) zap.String("host", upstream.Dial),
zap.Error(err),
)
}
return return
} }
@@ -526,9 +599,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
go func(host *Host, failDuration time.Duration) { go func(host *Host, failDuration time.Duration) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
h.HealthChecks.Passive.logger.Error("passive health check failure forgetter panicked", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "passive health check failure forgetter panicked"); c != nil {
zap.Any("error", err), c.Write(
zap.ByteString("stack", debug.Stack())) zap.Any("error", err),
zap.ByteString("stack", debug.Stack()),
)
}
} }
}() }()
timer := time.NewTimer(failDuration) timer := time.NewTimer(failDuration)
@@ -541,9 +617,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
} }
err := host.countFail(-1) err := host.countFail(-1)
if err != nil { if err != nil {
h.HealthChecks.Passive.logger.Error("could not forget failure", if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not forget failure"); c != nil {
zap.String("host", upstream.Dial), c.Write(
zap.Error(err)) zap.String("host", upstream.Dial),
zap.Error(err),
)
}
} }
}(upstream.Host, failDuration) }(upstream.Host, failDuration)
} }
+5 -4
View File
@@ -57,10 +57,11 @@ type Upstream struct {
// HeaderAffinity string // HeaderAffinity string
// IPAffinity string // IPAffinity string
activeHealthCheckPort int activeHealthCheckPort int
healthCheckPolicy *PassiveHealthChecks activeHealthCheckUpstream string
cb CircuitBreaker healthCheckPolicy *PassiveHealthChecks
unhealthy int32 // accessed atomically; status from active health checker cb CircuitBreaker
unhealthy int32 // accessed atomically; status from active health checker
} }
// (pointer receiver necessary to avoid a race condition, since // (pointer receiver necessary to avoid a race condition, since

Some files were not shown because too many files have changed in this diff Show More