Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 09:45 UTC
httpcaddyfile: fix incorrect error message on duplicate matchers (#7780)
Parse each matcher segment individually using NewDispenser(segment) instead of DispenseDirective(dir), which coalesced all same-name segments into one token stream. This caused the second definition name to be misinterpreted as a matcher module name, producing 'module not registered: http.matchers.@name' instead of the correct 'matcher is defined more than once' error. By parsing segments individually, the existing duplicate check in parseMatcherDefinitions naturally catches the duplicate on the second pass. Signed-off-by: Brunotlps <brunoteixlps@gmail.com>
Bruno Teixeira Lopes committed
May 29, 2026 at 18:05 UTC
0e8eb41b87ab60803d48cb2a183face3f4e4248e
2 files changed
+40
-3
caddyconfig/httpcaddyfile/httptype.go
+1
-1
@@ -108,7 +108,7 @@ func (st ServerType) Setup(
108
matcherDefs := make(map[string]caddy.ModuleMap)
109
for _, segment := range sb.block.Segments {
110
if dir := segment.Directive(); strings.HasPrefix(dir, matcherPrefix) {
111
- d := sb.block.DispenseDirective(dir)
111
+ d := caddyfile.NewDispenser(segment)
112
err := parseMatcherDefinitions(d, matcherDefs)
113
if err != nil {
114
return nil, warnings, err
caddyconfig/httpcaddyfile/httptype_test.go
+39
-2
@@ -2,6 +2,7 @@ package httpcaddyfile
2
3
import (
4
"encoding/json"
5
+ "strings"
6
"testing"
7
8
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -10,8 +11,9 @@ import (
11
12
func TestMatcherSyntax(t *testing.T) {
13
for i, tc := range []struct {
13
- input string
14
- expectError bool
14
+ input string
15
+ expectError bool
16
+ expectContains string
17
}{
18
{
19
input: `http://localhost
@@ -53,6 +55,34 @@ func TestMatcherSyntax(t *testing.T) {
55
`,
56
expectError: false,
57
},
58
+ {
59
+ input: `http://localhost {
60
+ @test {
61
+ path /test
62
+ }
63
+ @test {
64
+ path /other
65
+ }
66
+ respond @test "hello"
67
+ }
68
+ `,
69
+ expectError: true,
70
+ expectContains: "is defined more than once",
71
+ },
72
+ {
73
+ input: `(snippet) {
74
+ @{args[0]} {
75
+ path /{args[0]}
76
+ }
77
+ respond @{args[0]} "hello"
78
+ }
79
+ http://localhost {
80
+ import snippet foo
81
+ import snippet bar
82
+ }
83
+ `,
84
+ expectError: false,
85
+ },
86
{
87
input: `@matcher {
88
path /matcher-not-allowed/outside-of-site-block/*
@@ -73,6 +103,13 @@ func TestMatcherSyntax(t *testing.T) {
103
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
104
continue
105
}
106
+
107
+ if err != nil && tc.expectContains != "" {
108
+ if !strings.Contains(err.Error(), tc.expectContains) {
109
+ t.Errorf("Test %d error message mismatch: expected to contain %q, got %q",
110
+ i, tc.expectContains, err.Error())
111
+ }
112
+ }
113
}
114
}
115