mirror of
https://github.com/caddyserver/caddy.git
synced 2025-03-09 07:29:03 -04:00
Merge branch 'master' into filesystem-changes
This commit is contained in:
commit
e94663b866
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -157,7 +157,7 @@ jobs:
|
||||
|
||||
# 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"
|
||||
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 -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 -p 1 -tags nobadger -v ./..."
|
||||
test_result=$?
|
||||
|
||||
# There's no need leaving the files around
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,17 +18,23 @@ func TestIntercept(t *testing.T) {
|
||||
|
||||
localhost:9080 {
|
||||
respond /intercept "I'm a teapot" 408
|
||||
header /intercept To-Intercept ok
|
||||
respond /no-intercept "I'm not a teapot"
|
||||
|
||||
intercept {
|
||||
@teapot status 408
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
`, "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")
|
||||
}
|
||||
|
@ -60,8 +60,6 @@ type NetworkAddress struct {
|
||||
// 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.)
|
||||
// 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) {
|
||||
var listeners []any
|
||||
var err error
|
||||
@ -130,8 +128,6 @@ func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig)
|
||||
// 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
|
||||
// unlinked upon a graceful exit (or when a new config does not use that socket).
|
||||
//
|
||||
// TODO: Experimental API: subject to change or removal.
|
||||
func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) {
|
||||
if na.IsUnixNetwork() {
|
||||
unixSocketsMu.Lock()
|
||||
@ -221,8 +217,6 @@ func (na NetworkAddress) JoinHostPort(offset uint) string {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
size := na.PortRangeSize()
|
||||
addrs := make([]NetworkAddress, size)
|
||||
|
@ -112,7 +112,8 @@ func (enc *Encode) Provision(ctx caddy.Context) error {
|
||||
"application/x-ttf*",
|
||||
"application/xhtml+xml*",
|
||||
"application/xml*",
|
||||
"font/*",
|
||||
"font/ttf*",
|
||||
"font/otf*",
|
||||
"image/svg+xml*",
|
||||
"image/vnd.microsoft.icon*",
|
||||
"image/x-icon*",
|
||||
|
@ -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"}}
|
||||
{{- if .IsDir}}
|
||||
{{- 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">
|
||||
<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 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>
|
||||
{{- 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">
|
||||
@ -303,7 +310,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
<style {{ $nonceAttribute }}>
|
||||
* { padding: 0; margin: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
@ -342,6 +349,10 @@ svg,
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#layout-list, #layout-grid {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
max-width: 1200px;
|
||||
margin-left: auto;
|
||||
@ -768,10 +779,10 @@ footer {
|
||||
|
||||
</style>
|
||||
{{- 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}}
|
||||
</head>
|
||||
<body onload="initPage()">
|
||||
<body>
|
||||
<header>
|
||||
<div class="wrapper">
|
||||
<div class="breadcrumbs">Folder Path</div>
|
||||
@ -799,7 +810,7 @@ footer {
|
||||
</span>
|
||||
{{- end}}
|
||||
</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">
|
||||
<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"/>
|
||||
@ -807,7 +818,7 @@ footer {
|
||||
</svg>
|
||||
List
|
||||
</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">
|
||||
<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"/>
|
||||
@ -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="M21 21l-6 -6"/>
|
||||
</svg>
|
||||
<input type="search" placeholder="Search" id="filter" onkeyup='filter()'>
|
||||
<input type="search" placeholder="Search" id="filter">
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
@ -980,7 +991,7 @@ footer {
|
||||
<div class="sizebar">
|
||||
<div class="sizebar-bar"></div>
|
||||
<div class="sizebar-text">
|
||||
{{.HumanSize}}
|
||||
{{if .IsSymlink}}↱ {{end}}{{.HumanSize}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -1000,70 +1011,70 @@ footer {
|
||||
<footer>
|
||||
Served with
|
||||
<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.16548,0,0,1.10195,1823.12,393.466)">
|
||||
<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>
|
||||
<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 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 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 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 id="Padlock" transform="matrix(3.11426,0,0,3.11426,3938.31,1737.25)">
|
||||
<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="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="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" fill="none" fill-rule="nonzero" stroke="rgb(34,182,56)" stroke-width="1.89px"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<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 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 id="Text">
|
||||
<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)">
|
||||
<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 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)">
|
||||
<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 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 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 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 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(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 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>
|
||||
@ -1074,7 +1085,7 @@ footer {
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
<script {{ $nonceAttribute }}>
|
||||
const filterEl = document.getElementById('filter');
|
||||
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) {
|
||||
const qs = new URLSearchParams(window.location.search);
|
||||
if (!v) {
|
||||
|
@ -80,6 +80,13 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
|
||||
}
|
||||
|
||||
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)
|
||||
symlinkPath := ""
|
||||
if fileIsSymlink {
|
||||
@ -103,7 +110,8 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
|
||||
}
|
||||
|
||||
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
|
||||
@ -150,9 +158,15 @@ type browseTemplateContext struct {
|
||||
// The number of files (items that aren't directories) in the listing.
|
||||
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"`
|
||||
|
||||
// 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 string `json:"sort,omitempty"`
|
||||
|
||||
@ -288,6 +302,12 @@ func (btc browseTemplateContext) HumanTotalFileSize() string {
|
||||
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
|
||||
// as a human-readable string given by format.
|
||||
func (fi fileInfo) HumanModTime(format string) string {
|
||||
|
@ -50,7 +50,6 @@ type Intercept struct {
|
||||
//
|
||||
// Three new placeholders are available in this handler chain:
|
||||
// - `{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
|
||||
HandleResponse []caddyhttp.ResponseHandler `json:"handle_response,omitempty"`
|
||||
|
||||
@ -161,7 +160,7 @@ 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
|
||||
// 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.status_code", rec.Status())
|
||||
|
@ -143,6 +143,9 @@ func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
|
||||
|
||||
// Match returns true if r matches m.
|
||||
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
|
||||
clientIP, zoneID, err := parseIPZoneFromString(address)
|
||||
if err != nil {
|
||||
@ -228,6 +231,9 @@ func (m *MatchClientIP) Provision(ctx caddy.Context) error {
|
||||
|
||||
// Match returns true if r matches m.
|
||||
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)
|
||||
clientIP, zoneID, err := parseIPZoneFromString(address)
|
||||
if err != nil {
|
||||
|
@ -178,6 +178,22 @@ type (
|
||||
// "http/2", "http/3", or minimum versions: "http/2+", etc.
|
||||
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
|
||||
// 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
|
||||
@ -213,6 +229,7 @@ func init() {
|
||||
caddy.RegisterModule(MatchHeader{})
|
||||
caddy.RegisterModule(MatchHeaderRE{})
|
||||
caddy.RegisterModule(new(MatchProtocol))
|
||||
caddy.RegisterModule(MatchTLS{})
|
||||
caddy.RegisterModule(MatchNot{})
|
||||
}
|
||||
|
||||
@ -1236,6 +1253,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.
|
||||
func (MatchNot) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
|
@ -142,8 +142,16 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
|
||||
}
|
||||
return port, true
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
// req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path
|
||||
|
@ -16,6 +16,7 @@ package reverseproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@ -79,6 +80,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
||||
// health_headers {
|
||||
// <field> [<values...>]
|
||||
// }
|
||||
// health_method <value>
|
||||
//
|
||||
// # passive health checking
|
||||
// fail_duration <duration>
|
||||
@ -353,6 +355,26 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
h.HealthChecks.Active.Path = d.Val()
|
||||
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":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
@ -363,6 +385,9 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
if h.HealthChecks.Active == nil {
|
||||
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())
|
||||
if err != nil {
|
||||
return d.Errf("bad port number '%s': %v", d.Val(), err)
|
||||
@ -387,6 +412,18 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
}
|
||||
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_interval":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
|
@ -229,11 +229,13 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
|
||||
|
||||
if changeHost {
|
||||
if handler.Headers == nil {
|
||||
handler.Headers = &headers.Handler{
|
||||
Request: &headers.HeaderOps{
|
||||
Set: http.Header{},
|
||||
},
|
||||
}
|
||||
handler.Headers = new(headers.Handler)
|
||||
}
|
||||
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}")
|
||||
}
|
||||
|
@ -75,13 +75,24 @@ type ActiveHealthChecks struct {
|
||||
// The URI (path and query) to use for health checks
|
||||
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
|
||||
// address) for health checks.
|
||||
// address) for health checks. If active upstream is set,
|
||||
// this value is ignored.
|
||||
Port int `json:"port,omitempty"`
|
||||
|
||||
// HTTP headers to set on health check requests.
|
||||
Headers http.Header `json:"headers,omitempty"`
|
||||
|
||||
// The HTTP method to use for health checks (default "GET").
|
||||
Method string `json:"method,omitempty"`
|
||||
|
||||
// Whether to follow HTTP redirects in response to active health checks (default off).
|
||||
FollowRedirects bool `json:"follow_redirects,omitempty"`
|
||||
|
||||
@ -133,6 +144,11 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
|
||||
}
|
||||
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")
|
||||
|
||||
timeout := time.Duration(a.Timeout)
|
||||
@ -165,9 +181,14 @@ func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
|
||||
}
|
||||
|
||||
for _, upstream := range h.Upstreams {
|
||||
// if there's an alternative port for health-check provided in the config,
|
||||
// then use it, otherwise use the port of upstream.
|
||||
if a.Port != 0 {
|
||||
// if there's an alternative upstream for health-check provided in the config,
|
||||
// then use it, otherwise use the upstream's dial address. if upstream is used,
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@ -312,7 +333,7 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
|
||||
// so use a fake Host value instead; unix sockets are usually local
|
||||
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 {
|
||||
h.HealthChecks.Active.logger.Error("active health check failed",
|
||||
zap.String("address", hostAddr),
|
||||
@ -330,7 +351,7 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
|
||||
// according to whether it passes the health check. An error is
|
||||
// returned only if the health check fails to occur or if marking
|
||||
// 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
|
||||
u := &url.URL{
|
||||
Scheme: "http",
|
||||
@ -342,7 +363,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
if err != nil {
|
||||
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)
|
||||
u.Host = net.JoinHostPort(host, port)
|
||||
}
|
||||
@ -377,7 +403,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{
|
||||
dialInfoVarKey: dialInfo,
|
||||
})
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making request: %v", err)
|
||||
}
|
||||
@ -386,6 +412,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
|
||||
// set headers, using a replacer with only globals (env vars, system info, etc.)
|
||||
repl := caddy.NewReplacer()
|
||||
repl.Set("http.reverse_proxy.active.target_upstream", networkAddr)
|
||||
for key, vals := range h.HealthChecks.Active.Headers {
|
||||
key = repl.ReplaceAll(key, "")
|
||||
if key == "Host" {
|
||||
@ -426,6 +453,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
}
|
||||
if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes {
|
||||
if upstream.setHealthy(true) {
|
||||
h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
|
||||
h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr})
|
||||
upstream.Host.resetHealth()
|
||||
}
|
||||
@ -492,7 +520,6 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
|
||||
}
|
||||
|
||||
// passed health check parameters, so mark as healthy
|
||||
h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
|
||||
markHealthy()
|
||||
|
||||
return nil
|
||||
|
@ -57,10 +57,11 @@ type Upstream struct {
|
||||
// HeaderAffinity string
|
||||
// IPAffinity string
|
||||
|
||||
activeHealthCheckPort int
|
||||
healthCheckPolicy *PassiveHealthChecks
|
||||
cb CircuitBreaker
|
||||
unhealthy int32 // accessed atomically; status from active health checker
|
||||
activeHealthCheckPort int
|
||||
activeHealthCheckUpstream string
|
||||
healthCheckPolicy *PassiveHealthChecks
|
||||
cb CircuitBreaker
|
||||
unhealthy int32 // accessed atomically; status from active health checker
|
||||
}
|
||||
|
||||
// (pointer receiver necessary to avoid a race condition, since
|
||||
|
@ -363,6 +363,13 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
||||
// site owners control the backends), so it must be exclusive
|
||||
if len(h.Versions) == 1 && h.Versions[0] == "3" {
|
||||
h.h3Transport = new(http3.RoundTripper)
|
||||
if h.TLS != nil {
|
||||
var err error
|
||||
h.h3Transport.TLSClientConfig, err = h.TLS.MakeTLSClientConfig(caddyCtx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err)
|
||||
}
|
||||
}
|
||||
} else if len(h.Versions) > 1 && sliceContains(h.Versions, "3") {
|
||||
return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported")
|
||||
}
|
||||
|
@ -605,6 +605,18 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http.
|
||||
req.Header.Set("User-Agent", "")
|
||||
}
|
||||
|
||||
// Indicate if request has been conveyed in early data.
|
||||
// RFC 8470: "An intermediary that forwards a request prior to the
|
||||
// completion of the TLS handshake with its client MUST send it with
|
||||
// the Early-Data header field set to “1” (i.e., it adds it if not
|
||||
// present in the request). An intermediary MUST use the Early-Data
|
||||
// header field if the request might have been subject to a replay and
|
||||
// might already have been forwarded by it or another instance
|
||||
// (see Section 6.2)."
|
||||
if req.TLS != nil && !req.TLS.HandshakeComplete {
|
||||
req.Header.Set("Early-Data", "1")
|
||||
}
|
||||
|
||||
reqUpType := upgradeType(req.Header)
|
||||
removeConnectionHeaders(req.Header)
|
||||
|
||||
|
@ -231,6 +231,19 @@ type IPVersions struct {
|
||||
IPv6 *bool `json:"ipv6,omitempty"`
|
||||
}
|
||||
|
||||
func resolveIpVersion(versions *IPVersions) string {
|
||||
resolveIpv4 := versions == nil || (versions.IPv4 == nil && versions.IPv6 == nil) || (versions.IPv4 != nil && *versions.IPv4)
|
||||
resolveIpv6 := versions == nil || (versions.IPv6 == nil && versions.IPv4 == nil) || (versions.IPv6 != nil && *versions.IPv6)
|
||||
switch {
|
||||
case resolveIpv4 && !resolveIpv6:
|
||||
return "ip4"
|
||||
case !resolveIpv4 && resolveIpv6:
|
||||
return "ip6"
|
||||
default:
|
||||
return "ip"
|
||||
}
|
||||
}
|
||||
|
||||
// AUpstreams provides upstreams from A/AAAA lookups.
|
||||
// Results are cached and refreshed at the configured
|
||||
// refresh interval.
|
||||
@ -313,9 +326,6 @@ func (au *AUpstreams) Provision(ctx caddy.Context) error {
|
||||
func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
|
||||
resolveIpv4 := au.Versions == nil || au.Versions.IPv4 == nil || *au.Versions.IPv4
|
||||
resolveIpv6 := au.Versions == nil || au.Versions.IPv6 == nil || *au.Versions.IPv6
|
||||
|
||||
// Map ipVersion early, so we can use it as part of the cache-key.
|
||||
// This should be fairly inexpensive and comes and the upside of
|
||||
// allowing the same dynamic upstream (name + port combination)
|
||||
@ -324,15 +334,7 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||
// It also forced a cache-miss if a previously cached dynamic
|
||||
// upstream changes its ip version, e.g. after a config reload,
|
||||
// while keeping the cache-invalidation as simple as it currently is.
|
||||
var ipVersion string
|
||||
switch {
|
||||
case resolveIpv4 && !resolveIpv6:
|
||||
ipVersion = "ip4"
|
||||
case !resolveIpv4 && resolveIpv6:
|
||||
ipVersion = "ip6"
|
||||
default:
|
||||
ipVersion = "ip"
|
||||
}
|
||||
ipVersion := resolveIpVersion(au.Versions)
|
||||
|
||||
auStr := repl.ReplaceAll(au.String()+ipVersion, "")
|
||||
|
||||
|
56
modules/caddyhttp/reverseproxy/upstreams_test.go
Normal file
56
modules/caddyhttp/reverseproxy/upstreams_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package reverseproxy
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestResolveIpVersion(t *testing.T) {
|
||||
falseBool := false
|
||||
trueBool := true
|
||||
tests := []struct {
|
||||
Versions *IPVersions
|
||||
expectedIpVersion string
|
||||
}{
|
||||
{
|
||||
Versions: &IPVersions{IPv4: &trueBool},
|
||||
expectedIpVersion: "ip4",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv4: &falseBool},
|
||||
expectedIpVersion: "ip",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv4: &trueBool, IPv6: &falseBool},
|
||||
expectedIpVersion: "ip4",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv6: &trueBool},
|
||||
expectedIpVersion: "ip6",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv6: &falseBool},
|
||||
expectedIpVersion: "ip",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv6: &trueBool, IPv4: &falseBool},
|
||||
expectedIpVersion: "ip6",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{},
|
||||
expectedIpVersion: "ip",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv4: &trueBool, IPv6: &trueBool},
|
||||
expectedIpVersion: "ip",
|
||||
},
|
||||
{
|
||||
Versions: &IPVersions{IPv4: &falseBool, IPv6: &falseBool},
|
||||
expectedIpVersion: "ip",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
ipVersion := resolveIpVersion(test.Versions)
|
||||
if ipVersion != test.expectedIpVersion {
|
||||
t.Errorf("resolveIpVersion(): Expected %s got %s", test.expectedIpVersion, ipVersion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user