for WebRedirect, don't "match" when the destination URL has the same scheme,host,path, for doing http -> https redirects without loops

you can already get most http to https redirects through DontRedirectPlainHTTP
in WebHandler, but that needs handlers for all paths.

now you can just set up a redirect for a domain and all its path to baseurl
https://domain (leaving other webdirect fields empty). when the request comes
in with plain http, the redirect to https is done. that next request will also
evaluate the same redirect rule. but it will not cause a match because it would
redirect to the same scheme,host,path. so next webhandlers get a chance to
serve.

also clarify in webhandlers docs that also account & admin built-in handlers
run first.

related to issue #16
This commit is contained in:
Mechiel Lukkien
2023-03-08 23:29:44 +01:00
parent a9ef0f2aea
commit 8b0706e02d
7 changed files with 63 additions and 8 deletions

View File

@ -1754,7 +1754,7 @@ const webserver = async () => {
dom.td('Type'),
dom.td(
'BaseURL',
attr({title: 'Base URL to redirect to. The path must be empty and will be replaced, either by the request URL path, or by OrigPathRegexp/ReplacePath. Scheme, host, port and fragment stay intact, and query strings are combined. If empty, the response redirects to a different path through OrigPathRegexp and ReplacePath, which must then be set. Use a URL without scheme to redirect without changing the protocol, e.g. //newdomain/.'}),
attr({title: 'Base URL to redirect to. The path must be empty and will be replaced, either by the request URL path, or by OrigPathRegexp/ReplacePath. Scheme, host, port and fragment stay intact, and query strings are combined. If empty, the response redirects to a different path through OrigPathRegexp and ReplacePath, which must then be set. Use a URL without scheme to redirect without changing the protocol, e.g. //newdomain/. If a redirect would send a request to a URL with the same scheme, host and path, the WebRedirect does not match so a next WebHandler can be tried. This can be used to redirect all plain http traffic to https.'}),
),
dom.td(
'OrigPathRegexp',

View File

@ -341,6 +341,19 @@ func HandleRedirect(h *config.WebRedirect, w http.ResponseWriter, r *http.Reques
if h.StatusCode != 0 {
code = h.StatusCode
}
// If we would be redirecting to the same scheme,host,path, we would get here again
// causing a redirect loop. Instead, this causes this redirect to not match,
// allowing to try the next WebHandler. This can be used to redirect all plain http
// requests to https.
reqscheme := "http"
if r.TLS != nil {
reqscheme = "https"
}
if reqscheme == u.Scheme && r.Host == u.Host && r.URL.Path == u.Path {
return false
}
http.Redirect(w, r, u.String(), code)
return true
}

View File

@ -50,6 +50,10 @@ func TestWebserver(t *testing.T) {
test("GET", "http://redir.mox.example", nil, http.StatusPermanentRedirect, "", map[string]string{"Location": "https://mox.example/"})
// http to https redirect, and stay on https afterwards without redirect loop.
test("GET", "http://schemeredir.example", nil, http.StatusPermanentRedirect, "", map[string]string{"Location": "https://schemeredir.example/"})
test("GET", "https://schemeredir.example", nil, http.StatusNotFound, "", nil)
test("GET", "http://mox.example/static/", nil, http.StatusOK, "", map[string]string{"X-Test": "mox"}) // index.html
test("GET", "http://mox.example/static/dir/", nil, http.StatusOK, "", map[string]string{"X-Test": "mox"}) // listing
test("GET", "http://mox.example/static/dir", nil, http.StatusTemporaryRedirect, "", map[string]string{"Location": "/static/dir/"}) // redirect to dir