summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/caddyhttp/fileserver/matcher.go25
-rw-r--r--modules/caddyhttp/matchers.go6
-rw-r--r--modules/caddyhttp/routes.go9
3 files changed, 39 insertions, 1 deletions
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index 739b9e0..f8e9ce0 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -19,6 +19,7 @@ import (
"net/http"
"os"
"path"
+ "strconv"
"strings"
"time"
@@ -60,7 +61,11 @@ type MatchFile struct {
// directories are treated distinctly, so to match
// a directory, the filepath MUST end in a forward
// slash `/`. To match a regular file, there must
- // be no trailing slash. Accepts placeholders.
+ // be no trailing slash. Accepts placeholders. If
+ // the policy is "first_exist", then an error may
+ // be triggered as a fallback by configuring "="
+ // followed by a status code number,
+ // for example "=404".
TryFiles []string `json:"try_files,omitempty"`
// How to choose a file in TryFiles. Can be:
@@ -205,6 +210,10 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
switch m.TryPolicy {
case "", tryPolicyFirstExist:
for _, f := range m.TryFiles {
+ if err := parseErrorCode(f); err != nil {
+ caddyhttp.SetVar(r.Context(), caddyhttp.MatcherErrorVarKey, err)
+ return
+ }
suffix, fullpath, remainder := prepareFilePath(f)
if info, exists := strictFileExists(fullpath); exists {
setPlaceholders(info, suffix, fullpath, remainder)
@@ -274,6 +283,20 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
return
}
+// parseErrorCode checks if the input is a status
+// code number, prefixed by "=", and returns an
+// error if so.
+func parseErrorCode(input string) error {
+ if len(input) > 1 && input[0] == '=' {
+ code, err := strconv.Atoi(input[1:])
+ if err != nil || code < 100 || code > 999 {
+ return nil
+ }
+ return caddyhttp.Error(code, fmt.Errorf("%s", input[1:]))
+ }
+ return nil
+}
+
// strictFileExists returns true if file exists
// and matches the convention of the given file
// path. If the path ends in a forward slash,
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index b452d48..a22a3f1 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -987,6 +987,12 @@ var wordRE = regexp.MustCompile(`\w+`)
const regexpPlaceholderPrefix = "http.regexp"
+// MatcherErrorVarKey is the key used for the variable that
+// holds an optional error emitted from a request matcher,
+// to short-circuit the handler chain, since matchers cannot
+// return errors via the RequestMatcher interface.
+const MatcherErrorVarKey = "matchers.error"
+
// Interface guards
var (
_ RequestMatcher = (*MatchHost)(nil)
diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go
index ebd763c..7b2871f 100644
--- a/modules/caddyhttp/routes.go
+++ b/modules/caddyhttp/routes.go
@@ -200,6 +200,15 @@ func wrapRoute(route Route) Middleware {
// route must match at least one of the matcher sets
if !route.MatcherSets.AnyMatch(req) {
+ // allow matchers the opportunity to short circuit
+ // the request and trigger the error handling chain
+ err, ok := GetVar(req.Context(), MatcherErrorVarKey).(error)
+ if ok {
+ return err
+ }
+
+ // call the next handler, and skip this one,
+ // since the matcher didn't match
return nextCopy.ServeHTTP(rw, req)
}