@marcel91200 / caddy Mirror · read-only / commits / 0e8eb41b
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 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