Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 04:00 UTC
caddyhttp: Add support for triggering errors from `try_files` (#4346)
* caddyhttp: Add support for triggering errors from `try_files` * caddyhttp: Use vars instead of placeholders/replacer for matcher errors * caddyhttp: Add comment for matcher error var key
Francis Lavoie committed
Sep 17, 2021 at 02:52 UTC
907e2d8d3a8ebaf14aa99939c493e56645bc2089
3 files changed
+39
-1
modules/caddyhttp/fileserver/matcher.go
+24
-1
@@ -19,6 +19,7 @@ import (
19
"net/http"
20
"os"
21
"path"
22
+ "strconv"
23
"strings"
24
"time"
25
@@ -60,7 +61,11 @@ type MatchFile struct {
61
// directories are treated distinctly, so to match
62
// a directory, the filepath MUST end in a forward
63
// slash `/`. To match a regular file, there must
63
- // be no trailing slash. Accepts placeholders.
64
+ // be no trailing slash. Accepts placeholders. If
65
+ // the policy is "first_exist", then an error may
66
+ // be triggered as a fallback by configuring "="
67
+ // followed by a status code number,
68
+ // for example "=404".
69
TryFiles []string `json:"try_files,omitempty"`
70
71
// How to choose a file in TryFiles. Can be:
@@ -205,6 +210,10 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
210
switch m.TryPolicy {
211
case "", tryPolicyFirstExist:
212
for _, f := range m.TryFiles {
213
+ if err := parseErrorCode(f); err != nil {
214
+ caddyhttp.SetVar(r.Context(), caddyhttp.MatcherErrorVarKey, err)
215
+ return
216
+ }
217
suffix, fullpath, remainder := prepareFilePath(f)
218
if info, exists := strictFileExists(fullpath); exists {
219
setPlaceholders(info, suffix, fullpath, remainder)
@@ -274,6 +283,20 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
283
return
284
}
285
286
+// parseErrorCode checks if the input is a status
287
+// code number, prefixed by "=", and returns an
288
+// error if so.
289
+func parseErrorCode(input string) error {
290
+ if len(input) > 1 && input[0] == '=' {
291
+ code, err := strconv.Atoi(input[1:])
292
+ if err != nil || code < 100 || code > 999 {
293
+ return nil
294
+ }
295
+ return caddyhttp.Error(code, fmt.Errorf("%s", input[1:]))
296
+ }
297
+ return nil
298
+}
299
+
300
// strictFileExists returns true if file exists
301
// and matches the convention of the given file
302
// path. If the path ends in a forward slash,
modules/caddyhttp/matchers.go
+6
@@ -987,6 +987,12 @@ var wordRE = regexp.MustCompile(`\w+`)
987
988
const regexpPlaceholderPrefix = "http.regexp"
989
990
+// MatcherErrorVarKey is the key used for the variable that
991
+// holds an optional error emitted from a request matcher,
992
+// to short-circuit the handler chain, since matchers cannot
993
+// return errors via the RequestMatcher interface.
994
+const MatcherErrorVarKey = "matchers.error"
995
+
996
// Interface guards
997
var (
998
_ RequestMatcher = (*MatchHost)(nil)
modules/caddyhttp/routes.go
+9
@@ -200,6 +200,15 @@ func wrapRoute(route Route) Middleware {
200
201
// route must match at least one of the matcher sets
202
if !route.MatcherSets.AnyMatch(req) {
203
+ // allow matchers the opportunity to short circuit
204
+ // the request and trigger the error handling chain
205
+ err, ok := GetVar(req.Context(), MatcherErrorVarKey).(error)
206
+ if ok {
207
+ return err
208
+ }
209
+
210
+ // call the next handler, and skip this one,
211
+ // since the matcher didn't match
212
return nextCopy.ServeHTTP(rw, req)
213
}
214