mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
fileserver: Only redirect if filename not rewritten (fix #4205)
This is the more correct implementation of23dadc0d86
(#4179)... I think. This commit effectively undoes the revert in8848df9c5d
, but with corrections to the logic. We *do* need to use the original request path (the path the browser knows) for redirects, since they are external, and rewrites are only internal. However, if the path was rewritten to a non-canonical path, we should not redirect to canonicalize that, since rewrites are intentional by the site owner. Canonicalizing the path involves modifying only the suffix (base element, or filename) of the path. Thus, if a rewrite involves only the prefix (like how handle_path strips a path prefix), then we can (hopefully!) safely redirect using the original URI since the filename was not rewritten. So basically, if rewrites modify the filename, we should not canonicalize those requests. If rewrites only modify another part of the path (commonly a prefix), we should be OK to redirect.
This commit is contained in:
parent
238914d70b
commit
fbd6560976
2 changed files with 42 additions and 14 deletions
|
@ -42,15 +42,28 @@ func (fsrv *FileServer) serveBrowse(root, dirPath string, w http.ResponseWriter,
|
||||||
zap.String("path", dirPath),
|
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 like
|
// URL doesn't end in a trailing slash because hrefs to
|
||||||
// "/b/c" on a path like "/a" end up going to "/b/c" instead
|
// "b/c" at path "/a" end up going to "/b/c" instead
|
||||||
// of "/a/b/c" - so we have to redirect in this case
|
// of "/a/b/c" - so we have to redirect in this case
|
||||||
if !strings.HasSuffix(r.URL.Path, "/") {
|
// so that the path is "/a/" and the client constructs
|
||||||
fsrv.logger.Debug("redirecting to trailing slash to preserve hrefs", zap.String("request_path", r.URL.Path))
|
// relative hrefs "b/c" to be "/a/b/c".
|
||||||
r.URL.Path += "/"
|
//
|
||||||
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
|
// Only redirect if the last element of the path (the filename) was not
|
||||||
return nil
|
// rewritten; if the admin wanted to rewrite to the canonical path, they
|
||||||
|
// would have, and we have to be very careful not to introduce unwanted
|
||||||
|
// redirects and especially redirect loops! (Redirecting using the
|
||||||
|
// original URI is necessary because that's the URI the browser knows,
|
||||||
|
// we don't want to redirect from internally-rewritten URIs.)
|
||||||
|
// See https://github.com/caddyserver/caddy/issues/4205.
|
||||||
|
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
|
||||||
|
if path.Base(origReq.URL.Path) == path.Base(r.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))
|
||||||
|
origReq.URL.Path += "/"
|
||||||
|
http.Redirect(w, r, origReq.URL.String(), http.StatusMovedPermanently)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := fsrv.openFile(dirPath, w)
|
dir, err := fsrv.openFile(dirPath, w)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -240,12 +241,26 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
|
||||||
// trailing slash - not enforcing this can break relative hrefs
|
// trailing slash - not enforcing this can break relative hrefs
|
||||||
// in HTML (see https://github.com/caddyserver/caddy/issues/2741)
|
// in HTML (see https://github.com/caddyserver/caddy/issues/2741)
|
||||||
if fsrv.CanonicalURIs == nil || *fsrv.CanonicalURIs {
|
if fsrv.CanonicalURIs == nil || *fsrv.CanonicalURIs {
|
||||||
if implicitIndexFile && !strings.HasSuffix(r.URL.Path, "/") {
|
// Only redirect if the last element of the path (the filename) was not
|
||||||
fsrv.logger.Debug("redirecting to canonical URI (adding trailing slash for directory)", zap.String("path", r.URL.Path))
|
// rewritten; if the admin wanted to rewrite to the canonical path, they
|
||||||
return redirect(w, r, r.URL.Path+"/")
|
// would have, and we have to be very careful not to introduce unwanted
|
||||||
} else if !implicitIndexFile && strings.HasSuffix(r.URL.Path, "/") {
|
// redirects and especially redirect loops!
|
||||||
fsrv.logger.Debug("redirecting to canonical URI (removing trailing slash for file)", zap.String("path", r.URL.Path))
|
// See https://github.com/caddyserver/caddy/issues/4205.
|
||||||
return redirect(w, r, r.URL.Path[:len(r.URL.Path)-1])
|
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
|
||||||
|
if path.Base(origReq.URL.Path) == path.Base(r.URL.Path) {
|
||||||
|
if implicitIndexFile && !strings.HasSuffix(origReq.URL.Path, "/") {
|
||||||
|
to := origReq.URL.Path + "/"
|
||||||
|
fsrv.logger.Debug("redirecting to canonical URI (adding trailing slash for directory)",
|
||||||
|
zap.String("from_path", origReq.URL.Path),
|
||||||
|
zap.String("to_path", to))
|
||||||
|
return redirect(w, r, to)
|
||||||
|
} else if !implicitIndexFile && strings.HasSuffix(origReq.URL.Path, "/") {
|
||||||
|
to := origReq.URL.Path[:len(origReq.URL.Path)-1]
|
||||||
|
fsrv.logger.Debug("redirecting to canonical URI (removing trailing slash for file)",
|
||||||
|
zap.String("from_path", origReq.URL.Path),
|
||||||
|
zap.String("to_path", to))
|
||||||
|
return redirect(w, r, to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue