@marcel91200 / caddy Mirror · read-only / commits / 907e2d8d
Fork 0

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