mirror of
https://github.com/caddyserver/caddy.git
synced 2025-02-02 06:07:21 +01:00
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
This commit is contained in:
parent
33c70f418f
commit
907e2d8d3a
3 changed files with 39 additions and 1 deletions
|
@ -19,6 +19,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -60,7 +61,11 @@ type MatchFile struct {
|
||||||
// directories are treated distinctly, so to match
|
// directories are treated distinctly, so to match
|
||||||
// a directory, the filepath MUST end in a forward
|
// a directory, the filepath MUST end in a forward
|
||||||
// slash `/`. To match a regular file, there must
|
// 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"`
|
TryFiles []string `json:"try_files,omitempty"`
|
||||||
|
|
||||||
// How to choose a file in TryFiles. Can be:
|
// 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 {
|
switch m.TryPolicy {
|
||||||
case "", tryPolicyFirstExist:
|
case "", tryPolicyFirstExist:
|
||||||
for _, f := range m.TryFiles {
|
for _, f := range m.TryFiles {
|
||||||
|
if err := parseErrorCode(f); err != nil {
|
||||||
|
caddyhttp.SetVar(r.Context(), caddyhttp.MatcherErrorVarKey, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
suffix, fullpath, remainder := prepareFilePath(f)
|
suffix, fullpath, remainder := prepareFilePath(f)
|
||||||
if info, exists := strictFileExists(fullpath); exists {
|
if info, exists := strictFileExists(fullpath); exists {
|
||||||
setPlaceholders(info, suffix, fullpath, remainder)
|
setPlaceholders(info, suffix, fullpath, remainder)
|
||||||
|
@ -274,6 +283,20 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
|
||||||
return
|
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
|
// strictFileExists returns true if file exists
|
||||||
// and matches the convention of the given file
|
// and matches the convention of the given file
|
||||||
// path. If the path ends in a forward slash,
|
// path. If the path ends in a forward slash,
|
||||||
|
|
|
@ -987,6 +987,12 @@ var wordRE = regexp.MustCompile(`\w+`)
|
||||||
|
|
||||||
const regexpPlaceholderPrefix = "http.regexp"
|
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
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ RequestMatcher = (*MatchHost)(nil)
|
_ RequestMatcher = (*MatchHost)(nil)
|
||||||
|
|
|
@ -200,6 +200,15 @@ func wrapRoute(route Route) Middleware {
|
||||||
|
|
||||||
// route must match at least one of the matcher sets
|
// route must match at least one of the matcher sets
|
||||||
if !route.MatcherSets.AnyMatch(req) {
|
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)
|
return nextCopy.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue