|
|
@ -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. ¯\_(ツ)_/¯
|
|
|
|