mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
caddyfile: Populate regexp matcher names by default (#6145)
* caddyfile: Populate regexp matcher names by default * Some lint cleanup that my VSCode complained about * Pass down matcher name through expression matcher * Compat with #6113: fix adapt test, set both styles in replacer
This commit is contained in:
parent
e0daa39cd3
commit
9cd472c031
14 changed files with 183 additions and 27 deletions
|
@ -30,6 +30,10 @@ type Dispenser struct {
|
||||||
tokens []Token
|
tokens []Token
|
||||||
cursor int
|
cursor int
|
||||||
nesting int
|
nesting int
|
||||||
|
|
||||||
|
// A map of arbitrary context data that can be used
|
||||||
|
// to pass through some information to unmarshalers.
|
||||||
|
context map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDispenser returns a Dispenser filled with the given tokens.
|
// NewDispenser returns a Dispenser filled with the given tokens.
|
||||||
|
@ -454,6 +458,34 @@ func (d *Dispenser) DeleteN(amount int) []Token {
|
||||||
return d.tokens
|
return d.tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetContext sets a key-value pair in the context map.
|
||||||
|
func (d *Dispenser) SetContext(key string, value any) {
|
||||||
|
if d.context == nil {
|
||||||
|
d.context = make(map[string]any)
|
||||||
|
}
|
||||||
|
d.context[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext gets the value of a key in the context map.
|
||||||
|
func (d *Dispenser) GetContext(key string) any {
|
||||||
|
if d.context == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.context[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextString gets the value of a key in the context map
|
||||||
|
// as a string, or an empty string if the key does not exist.
|
||||||
|
func (d *Dispenser) GetContextString(key string) string {
|
||||||
|
if d.context == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if val, ok := d.context[key].(string); ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// isNewLine determines whether the current token is on a different
|
// isNewLine determines whether the current token is on a different
|
||||||
// line (higher line number) than the previous token. It handles imported
|
// line (higher line number) than the previous token. It handles imported
|
||||||
// tokens correctly. If there isn't a previous token, it returns true.
|
// tokens correctly. If there isn't a previous token, it returns true.
|
||||||
|
@ -485,3 +517,5 @@ func (d *Dispenser) isNextOnNewLine() bool {
|
||||||
next := d.tokens[d.cursor+1]
|
next := d.tokens[d.cursor+1]
|
||||||
return isNextOnNewLine(curr, next)
|
return isNextOnNewLine(curr, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MatcherNameCtxKey = "matcher_name"
|
||||||
|
|
|
@ -1397,6 +1397,14 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
|
||||||
// given a matcher name and the tokens following it, parse
|
// given a matcher name and the tokens following it, parse
|
||||||
// the tokens as a matcher module and record it
|
// the tokens as a matcher module and record it
|
||||||
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
|
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
|
||||||
|
// create a new dispenser from the tokens
|
||||||
|
dispenser := caddyfile.NewDispenser(tokens)
|
||||||
|
|
||||||
|
// set the matcher name (without @) in the dispenser context so
|
||||||
|
// that matcher modules can access it to use it as their name
|
||||||
|
// (e.g. regexp matchers which use the name for capture groups)
|
||||||
|
dispenser.SetContext(caddyfile.MatcherNameCtxKey, definitionName[1:])
|
||||||
|
|
||||||
mod, err := caddy.GetModule("http.matchers." + matcherName)
|
mod, err := caddy.GetModule("http.matchers." + matcherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
|
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
|
||||||
|
@ -1405,7 +1413,7 @@ func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.M
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
|
return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
|
||||||
}
|
}
|
||||||
err = unm.UnmarshalCaddyfile(caddyfile.NewDispenser(tokens))
|
err = unm.UnmarshalCaddyfile(dispenser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,7 +291,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
||||||
func applyServerOptions(
|
func applyServerOptions(
|
||||||
servers map[string]*caddyhttp.Server,
|
servers map[string]*caddyhttp.Server,
|
||||||
options map[string]any,
|
options map[string]any,
|
||||||
warnings *[]caddyconfig.Warning,
|
_ *[]caddyconfig.Warning,
|
||||||
) error {
|
) error {
|
||||||
serverOpts, ok := options["servers"].([]serverOptions)
|
serverOpts, ok := options["servers"].([]serverOptions)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -487,7 +487,11 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
|
||||||
// for any other automation policies. A nil policy (and no error) will be
|
// for any other automation policies. A nil policy (and no error) will be
|
||||||
// returned if there are no default/global options. However, if always is
|
// returned if there are no default/global options. However, if always is
|
||||||
// true, a non-nil value will always be returned (unless there is an error).
|
// true, a non-nil value will always be returned (unless there is an error).
|
||||||
func newBaseAutomationPolicy(options map[string]any, warnings []caddyconfig.Warning, always bool) (*caddytls.AutomationPolicy, error) {
|
func newBaseAutomationPolicy(
|
||||||
|
options map[string]any,
|
||||||
|
_ []caddyconfig.Warning,
|
||||||
|
always bool,
|
||||||
|
) (*caddytls.AutomationPolicy, error) {
|
||||||
issuers, hasIssuers := options["cert_issuer"]
|
issuers, hasIssuers := options["cert_issuer"]
|
||||||
_, hasLocalCerts := options["local_certs"]
|
_, hasLocalCerts := options["local_certs"]
|
||||||
keyType, hasKeyType := options["key_type"]
|
keyType, hasKeyType := options["key_type"]
|
||||||
|
|
|
@ -84,7 +84,10 @@ abort @e
|
||||||
],
|
],
|
||||||
"match": [
|
"match": [
|
||||||
{
|
{
|
||||||
"expression": "{http.error.status_code} == 403"
|
"expression": {
|
||||||
|
"expr": "{http.error.status_code} == 403",
|
||||||
|
"name": "d"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -97,7 +100,10 @@ abort @e
|
||||||
],
|
],
|
||||||
"match": [
|
"match": [
|
||||||
{
|
{
|
||||||
"expression": "{http.error.status_code} == 404"
|
"expression": {
|
||||||
|
"expr": "{http.error.status_code} == 404",
|
||||||
|
"name": "e"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@
|
||||||
{
|
{
|
||||||
"vars_regexp": {
|
"vars_regexp": {
|
||||||
"{http.request.uri}": {
|
"{http.request.uri}": {
|
||||||
|
"name": "matcher6",
|
||||||
"pattern": "\\.([a-f0-9]{6})\\.(css|js)$"
|
"pattern": "\\.([a-f0-9]{6})\\.(css|js)$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +162,10 @@
|
||||||
{
|
{
|
||||||
"match": [
|
"match": [
|
||||||
{
|
{
|
||||||
"expression": "path('/foo*') \u0026\u0026 method('GET')"
|
"expression": {
|
||||||
|
"expr": "path('/foo*') \u0026\u0026 method('GET')",
|
||||||
|
"name": "matcher7"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"handle": [
|
"handle": [
|
||||||
|
|
|
@ -36,6 +36,7 @@ respond @match "{re.1}"
|
||||||
"match": [
|
"match": [
|
||||||
{
|
{
|
||||||
"path_regexp": {
|
"path_regexp": {
|
||||||
|
"name": "match",
|
||||||
"pattern": "^/foo(.*)$"
|
"pattern": "^/foo(.*)$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
context.go
12
context.go
|
@ -556,3 +556,15 @@ func (ctx Context) Module() Module {
|
||||||
}
|
}
|
||||||
return ctx.ancestry[len(ctx.ancestry)-1]
|
return ctx.ancestry[len(ctx.ancestry)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithValue returns a new context with the given key-value pair.
|
||||||
|
func (ctx *Context) WithValue(key, value any) Context {
|
||||||
|
return Context{
|
||||||
|
Context: context.WithValue(ctx.Context, key, value),
|
||||||
|
moduleInstances: ctx.moduleInstances,
|
||||||
|
cfg: ctx.cfg,
|
||||||
|
ancestry: ctx.ancestry,
|
||||||
|
cleanupFuncs: ctx.cleanupFuncs,
|
||||||
|
exitFuncs: ctx.exitFuncs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -62,7 +62,12 @@ type MatchExpression struct {
|
||||||
// The CEL expression to evaluate. Any Caddy placeholders
|
// The CEL expression to evaluate. Any Caddy placeholders
|
||||||
// will be expanded and situated into proper CEL function
|
// will be expanded and situated into proper CEL function
|
||||||
// calls before evaluating.
|
// calls before evaluating.
|
||||||
Expr string
|
Expr string `json:"expr,omitempty"`
|
||||||
|
|
||||||
|
// Name is an optional name for this matcher.
|
||||||
|
// This is used to populate the name for regexp
|
||||||
|
// matchers that appear in the expression.
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
expandedExpr string
|
expandedExpr string
|
||||||
prg cel.Program
|
prg cel.Program
|
||||||
|
@ -81,13 +86,37 @@ func (MatchExpression) CaddyModule() caddy.ModuleInfo {
|
||||||
|
|
||||||
// MarshalJSON marshals m's expression.
|
// MarshalJSON marshals m's expression.
|
||||||
func (m MatchExpression) MarshalJSON() ([]byte, error) {
|
func (m MatchExpression) MarshalJSON() ([]byte, error) {
|
||||||
|
// if the name is empty, then we can marshal just the expression string
|
||||||
|
if m.Name == "" {
|
||||||
return json.Marshal(m.Expr)
|
return json.Marshal(m.Expr)
|
||||||
}
|
}
|
||||||
|
// otherwise, we need to marshal the full object, using an
|
||||||
|
// anonymous struct to avoid infinite recursion
|
||||||
|
return json.Marshal(struct {
|
||||||
|
Expr string `json:"expr"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{
|
||||||
|
Expr: m.Expr,
|
||||||
|
Name: m.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals m's expression.
|
// UnmarshalJSON unmarshals m's expression.
|
||||||
func (m *MatchExpression) UnmarshalJSON(data []byte) error {
|
func (m *MatchExpression) UnmarshalJSON(data []byte) error {
|
||||||
|
// if the data is a string, then it's just the expression
|
||||||
|
if data[0] == '"' {
|
||||||
return json.Unmarshal(data, &m.Expr)
|
return json.Unmarshal(data, &m.Expr)
|
||||||
}
|
}
|
||||||
|
// otherwise, it's a full object, so unmarshal it,
|
||||||
|
// using an temp map to avoid infinite recursion
|
||||||
|
var tmpJson map[string]any
|
||||||
|
err := json.Unmarshal(data, &tmpJson)
|
||||||
|
*m = MatchExpression{
|
||||||
|
Expr: tmpJson["expr"].(string),
|
||||||
|
Name: tmpJson["name"].(string),
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Provision sets ups m.
|
// Provision sets ups m.
|
||||||
func (m *MatchExpression) Provision(ctx caddy.Context) error {
|
func (m *MatchExpression) Provision(ctx caddy.Context) error {
|
||||||
|
@ -109,6 +138,11 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
|
||||||
matcherLibProducers = append(matcherLibProducers, p)
|
matcherLibProducers = append(matcherLibProducers, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add the matcher name to the context so that the matcher name
|
||||||
|
// can be used by regexp matchers being provisioned
|
||||||
|
ctx = ctx.WithValue(MatcherNameCtxKey, m.Name)
|
||||||
|
|
||||||
// Assemble the compilation and program options from the different library
|
// Assemble the compilation and program options from the different library
|
||||||
// producers into a single cel.Library implementation.
|
// producers into a single cel.Library implementation.
|
||||||
matcherEnvOpts := []cel.EnvOption{}
|
matcherEnvOpts := []cel.EnvOption{}
|
||||||
|
@ -197,6 +231,11 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
// quoted string; commonly quotes are used in Caddyfile to
|
// quoted string; commonly quotes are used in Caddyfile to
|
||||||
// define the expression
|
// define the expression
|
||||||
m.Expr = d.Val()
|
m.Expr = d.Val()
|
||||||
|
|
||||||
|
// use the named matcher's name, to fill regexp
|
||||||
|
// matchers names by default
|
||||||
|
m.Name = d.GetContextString(caddyfile.MatcherNameCtxKey)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,6 +712,8 @@ var httpRequestObjectType = cel.ObjectType("http.Request")
|
||||||
// The name of the CEL function which accesses Replacer values.
|
// The name of the CEL function which accesses Replacer values.
|
||||||
const placeholderFuncName = "caddyPlaceholder"
|
const placeholderFuncName = "caddyPlaceholder"
|
||||||
|
|
||||||
|
const MatcherNameCtxKey = "matcher_name"
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ caddy.Provisioner = (*MatchExpression)(nil)
|
_ caddy.Provisioner = (*MatchExpression)(nil)
|
||||||
|
|
|
@ -380,7 +380,9 @@ func TestMatchExpressionMatch(t *testing.T) {
|
||||||
for _, tst := range matcherTests {
|
for _, tst := range matcherTests {
|
||||||
tc := tst
|
tc := tst
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err := tc.expression.Provision(caddy.Context{})
|
caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||||
|
defer cancel()
|
||||||
|
err := tc.expression.Provision(caddyCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !tc.wantErr {
|
if !tc.wantErr {
|
||||||
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr)
|
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr)
|
||||||
|
@ -482,7 +484,9 @@ func TestMatchExpressionProvision(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if err := tt.expression.Provision(caddy.Context{}); (err != nil) != tt.wantErr {
|
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||||
|
defer cancel()
|
||||||
|
if err := tt.expression.Provision(ctx); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -360,7 +360,9 @@ func TestMatchExpressionMatch(t *testing.T) {
|
||||||
for _, tst := range expressionTests {
|
for _, tst := range expressionTests {
|
||||||
tc := tst
|
tc := tst
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err := tc.expression.Provision(caddy.Context{})
|
caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||||
|
defer cancel()
|
||||||
|
err := tc.expression.Provision(caddyCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !tc.wantErr {
|
if !tc.wantErr {
|
||||||
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr)
|
t.Errorf("MatchExpression.Provision() error = %v, wantErr %v", err, tc.wantErr)
|
||||||
|
|
|
@ -675,7 +675,10 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
||||||
[]*cel.Type{cel.StringType},
|
[]*cel.Type{cel.StringType},
|
||||||
func(data ref.Val) (RequestMatcher, error) {
|
func(data ref.Val) (RequestMatcher, error) {
|
||||||
pattern := data.(types.String)
|
pattern := data.(types.String)
|
||||||
matcher := MatchPathRE{MatchRegexp{Pattern: string(pattern)}}
|
matcher := MatchPathRE{MatchRegexp{
|
||||||
|
Name: ctx.Value(MatcherNameCtxKey).(string),
|
||||||
|
Pattern: string(pattern),
|
||||||
|
}}
|
||||||
err := matcher.Provision(ctx)
|
err := matcher.Provision(ctx)
|
||||||
return matcher, err
|
return matcher, err
|
||||||
},
|
},
|
||||||
|
@ -694,7 +697,14 @@ func (MatchPathRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
strParams := params.([]string)
|
strParams := params.([]string)
|
||||||
matcher := MatchPathRE{MatchRegexp{Name: strParams[0], Pattern: strParams[1]}}
|
name := strParams[0]
|
||||||
|
if name == "" {
|
||||||
|
name = ctx.Value(MatcherNameCtxKey).(string)
|
||||||
|
}
|
||||||
|
matcher := MatchPathRE{MatchRegexp{
|
||||||
|
Name: name,
|
||||||
|
Pattern: strParams[1],
|
||||||
|
}}
|
||||||
err = matcher.Provision(ctx)
|
err = matcher.Provision(ctx)
|
||||||
return matcher, err
|
return matcher, err
|
||||||
},
|
},
|
||||||
|
@ -1023,6 +1033,11 @@ func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
val = second
|
val = second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default to the named matcher's name, if no regexp name is provided
|
||||||
|
if name == "" {
|
||||||
|
name = d.GetContextString(caddyfile.MatcherNameCtxKey)
|
||||||
|
}
|
||||||
|
|
||||||
// If there's already a pattern for this field
|
// If there's already a pattern for this field
|
||||||
// then we would end up overwriting the old one
|
// then we would end up overwriting the old one
|
||||||
if (*m)[field] != nil {
|
if (*m)[field] != nil {
|
||||||
|
@ -1099,7 +1114,10 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
||||||
}
|
}
|
||||||
strParams := params.([]string)
|
strParams := params.([]string)
|
||||||
matcher := MatchHeaderRE{}
|
matcher := MatchHeaderRE{}
|
||||||
matcher[strParams[0]] = &MatchRegexp{Pattern: strParams[1], Name: ""}
|
matcher[strParams[0]] = &MatchRegexp{
|
||||||
|
Pattern: strParams[1],
|
||||||
|
Name: ctx.Value(MatcherNameCtxKey).(string),
|
||||||
|
}
|
||||||
err = matcher.Provision(ctx)
|
err = matcher.Provision(ctx)
|
||||||
return matcher, err
|
return matcher, err
|
||||||
},
|
},
|
||||||
|
@ -1118,8 +1136,15 @@ func (MatchHeaderRE) CELLibrary(ctx caddy.Context) (cel.Library, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
strParams := params.([]string)
|
strParams := params.([]string)
|
||||||
|
name := strParams[0]
|
||||||
|
if name == "" {
|
||||||
|
name = ctx.Value(MatcherNameCtxKey).(string)
|
||||||
|
}
|
||||||
matcher := MatchHeaderRE{}
|
matcher := MatchHeaderRE{}
|
||||||
matcher[strParams[1]] = &MatchRegexp{Pattern: strParams[2], Name: strParams[0]}
|
matcher[strParams[1]] = &MatchRegexp{
|
||||||
|
Pattern: strParams[2],
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
err = matcher.Provision(ctx)
|
err = matcher.Provision(ctx)
|
||||||
return matcher, err
|
return matcher, err
|
||||||
},
|
},
|
||||||
|
@ -1284,7 +1309,6 @@ type MatchRegexp struct {
|
||||||
Pattern string `json:"pattern"`
|
Pattern string `json:"pattern"`
|
||||||
|
|
||||||
compiled *regexp.Regexp
|
compiled *regexp.Regexp
|
||||||
phPrefix string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision compiles the regular expression.
|
// Provision compiles the regular expression.
|
||||||
|
@ -1294,10 +1318,6 @@ func (mre *MatchRegexp) Provision(caddy.Context) error {
|
||||||
return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err)
|
return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err)
|
||||||
}
|
}
|
||||||
mre.compiled = re
|
mre.compiled = re
|
||||||
mre.phPrefix = regexpPlaceholderPrefix
|
|
||||||
if mre.Name != "" {
|
|
||||||
mre.phPrefix += "." + mre.Name
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1321,16 +1341,25 @@ func (mre *MatchRegexp) Match(input string, repl *caddy.Replacer) bool {
|
||||||
|
|
||||||
// save all capture groups, first by index
|
// save all capture groups, first by index
|
||||||
for i, match := range matches {
|
for i, match := range matches {
|
||||||
key := mre.phPrefix + "." + strconv.Itoa(i)
|
keySuffix := "." + strconv.Itoa(i)
|
||||||
repl.Set(key, match)
|
if mre.Name != "" {
|
||||||
|
repl.Set(regexpPlaceholderPrefix+"."+mre.Name+keySuffix, match)
|
||||||
|
}
|
||||||
|
repl.Set(regexpPlaceholderPrefix+keySuffix, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
// then by name
|
// then by name
|
||||||
for i, name := range mre.compiled.SubexpNames() {
|
for i, name := range mre.compiled.SubexpNames() {
|
||||||
if i != 0 && name != "" {
|
// skip the first element (the full match), and empty names
|
||||||
key := mre.phPrefix + "." + name
|
if i == 0 || name == "" {
|
||||||
repl.Set(key, matches[i])
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keySuffix := "." + name
|
||||||
|
if mre.Name != "" {
|
||||||
|
repl.Set(regexpPlaceholderPrefix+"."+mre.Name+keySuffix, matches[i])
|
||||||
|
}
|
||||||
|
repl.Set(regexpPlaceholderPrefix+keySuffix, matches[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -1357,6 +1386,12 @@ func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
default:
|
default:
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default to the named matcher's name, if no regexp name is provided
|
||||||
|
if mre.Name == "" {
|
||||||
|
mre.Name = d.GetContextString(caddyfile.MatcherNameCtxKey)
|
||||||
|
}
|
||||||
|
|
||||||
if d.NextBlock(0) {
|
if d.NextBlock(0) {
|
||||||
return d.Err("malformed path_regexp matcher: blocks are not supported")
|
return d.Err("malformed path_regexp matcher: blocks are not supported")
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ func wrapRoute(route Route) Middleware {
|
||||||
// we need to pull this particular MiddlewareHandler
|
// we need to pull this particular MiddlewareHandler
|
||||||
// pointer into its own stack frame to preserve it so it
|
// pointer into its own stack frame to preserve it so it
|
||||||
// won't be overwritten in future loop iterations.
|
// won't be overwritten in future loop iterations.
|
||||||
func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler, metrics *Metrics) Middleware {
|
func wrapMiddleware(_ caddy.Context, mh MiddlewareHandler, metrics *Metrics) Middleware {
|
||||||
handlerToUse := mh
|
handlerToUse := mh
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
// wrap the middleware with metrics instrumentation
|
// wrap the middleware with metrics instrumentation
|
||||||
|
|
|
@ -242,6 +242,11 @@ func (m *MatchVarsRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
val = second
|
val = second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default to the named matcher's name, if no regexp name is provided
|
||||||
|
if name == "" {
|
||||||
|
name = d.GetContextString(caddyfile.MatcherNameCtxKey)
|
||||||
|
}
|
||||||
|
|
||||||
(*m)[field] = &MatchRegexp{Pattern: val, Name: name}
|
(*m)[field] = &MatchRegexp{Pattern: val, Name: name}
|
||||||
if d.NextBlock(0) {
|
if d.NextBlock(0) {
|
||||||
return d.Err("malformed vars_regexp matcher: blocks are not supported")
|
return d.Err("malformed vars_regexp matcher: blocks are not supported")
|
||||||
|
|
Loading…
Reference in a new issue