From b43e986a526c9de83c04c29bc530f1d711a40997 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 15 Nov 2019 17:32:13 -0700 Subject: file_server: Optional pass_thru mode If enabled, will call the next handler in the chain instead of returning a 404. --- modules/caddyhttp/fileserver/browse.go | 4 ++-- modules/caddyhttp/fileserver/staticfiles.go | 27 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'modules/caddyhttp/fileserver') diff --git a/modules/caddyhttp/fileserver/browse.go b/modules/caddyhttp/fileserver/browse.go index 91ebcd5..48f2cc7 100644 --- a/modules/caddyhttp/fileserver/browse.go +++ b/modules/caddyhttp/fileserver/browse.go @@ -34,7 +34,7 @@ type Browse struct { template *template.Template } -func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *http.Request) error { +func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { // navigation on the client-side gets messed up if the // URL doesn't end in a trailing slash because hrefs like // "/b/c" on a path like "/a" end up going to "/b/c" instead @@ -59,7 +59,7 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht case os.IsPermission(err): return caddyhttp.Error(http.StatusForbidden, err) case os.IsNotExist(err): - return caddyhttp.Error(http.StatusNotFound, err) + return fsrv.notFound(w, r, next) case err != nil: return caddyhttp.Error(http.StatusInternalServerError, err) } diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index 26c0efe..d1791db 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -47,6 +47,7 @@ type FileServer struct { IndexNames []string `json:"index_names,omitempty"` Browse *Browse `json:"browse,omitempty"` CanonicalURIs *bool `json:"canonical_uris,omitempty"` + PassThru bool `json:"pass_thru,omitempty"` // if 404, call next handler instead } // CaddyModule returns the Caddy module information. @@ -87,7 +88,7 @@ func (fsrv *FileServer) Provision(ctx caddy.Context) error { return nil } -func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error { +func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer) filesToHide := fsrv.transformHidePaths(repl) @@ -101,7 +102,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd if err != nil { err = mapDirOpenError(err, filename) if os.IsNotExist(err) { - return caddyhttp.Error(http.StatusNotFound, err) + return fsrv.notFound(w, r, next) } else if os.IsPermission(err) { return caddyhttp.Error(http.StatusForbidden, err) } @@ -144,17 +145,20 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd // to browse or return an error if info.IsDir() { if fsrv.Browse != nil && !fileHidden(filename, filesToHide) { - return fsrv.serveBrowse(filename, w, r) + return fsrv.serveBrowse(filename, w, r, next) } - return caddyhttp.Error(http.StatusNotFound, nil) + return fsrv.notFound(w, r, next) } + // TODO: maybe there should be a way to serve the next handler + // instead of returning 404 if a file is not found? + // TODO: content negotiation (brotli sidecar files, etc...) // one last check to ensure the file isn't hidden (we might // have changed the filename from when we last checked) if fileHidden(filename, filesToHide) { - return caddyhttp.Error(http.StatusNotFound, nil) + return fsrv.notFound(w, r, next) } // if URL canonicalization is enabled, we need to enforce trailing @@ -172,6 +176,10 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd // open the file file, err := fsrv.openFile(filename, w) if err != nil { + if herr, ok := err.(caddyhttp.HandlerError); ok && + herr.StatusCode == http.StatusNotFound { + return fsrv.notFound(w, r, next) + } return err // error is already structured } defer file.Close() @@ -336,6 +344,15 @@ func fileHidden(filename string, hide []string) bool { return false } +// notFound returns a 404 error or, if pass-thru is enabled, +// it calls the next handler in the chain. +func (fsrv *FileServer) notFound(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { + if fsrv.PassThru { + return next.ServeHTTP(w, r) + } + return caddyhttp.Error(http.StatusNotFound, nil) +} + // calculateEtag produces a strong etag by default, although, for // efficiency reasons, it does not actually consume the contents // of the file to make a hash of all the bytes. ¯\_(ツ)_/¯ -- cgit v1.2.3