From fa18c35a9a3e16b94dff8e86cf8c3c5243ca94f3 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Mon, 26 May 2025 17:12:08 +0200 Subject: [PATCH] Refactor new muxer to have only one parser instance --- cmd/traefik/traefik.go | 5 +- integration/simple_test.go | 8 +-- pkg/muxer/http/matcher.go | 2 +- pkg/muxer/http/matcher_test.go | 44 +++++++++---- pkg/muxer/http/matcher_v2.go | 2 +- pkg/muxer/http/matcher_v2_test.go | 40 +++++++++--- pkg/muxer/http/mux.go | 72 ++++----------------- pkg/muxer/http/mux_test.go | 12 +++- pkg/muxer/http/parser.go | 103 ++++++++++++++++++++++++++++++ pkg/server/router/router.go | 9 ++- pkg/server/router/router_test.go | 22 +++++-- pkg/server/routerfactory.go | 16 ++++- pkg/server/routerfactory_test.go | 10 ++- 13 files changed, 240 insertions(+), 105 deletions(-) create mode 100644 pkg/muxer/http/parser.go diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 61f8e8217..77930754b 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -301,7 +301,10 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err // Router factory - routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager) + routerFactory, err := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager) + if err != nil { + return nil, fmt.Errorf("creating router factory: %w", err) + } // Watcher diff --git a/integration/simple_test.go b/integration/simple_test.go index b2c3166c6..4a70bd79b 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -711,11 +711,11 @@ func (s *SimpleSuite) TestWithDefaultRuleSyntax() { require.NoError(s.T(), err) // router2 has an error because it uses the wrong rule syntax (v3 instead of v2) - err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp")) + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp")) require.NoError(s.T(), err) // router3 has an error because it uses the wrong rule syntax (v2 instead of v3) - err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]")) + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]")) require.NoError(s.T(), err) } @@ -741,11 +741,11 @@ func (s *SimpleSuite) TestWithoutDefaultRuleSyntax() { require.NoError(s.T(), err) // router2 has an error because it uses the wrong rule syntax (v3 instead of v2) - err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]")) + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]")) require.NoError(s.T(), err) // router2 has an error because it uses the wrong rule syntax (v2 instead of v3) - err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp")) + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp")) require.NoError(s.T(), err) } diff --git a/pkg/muxer/http/matcher.go b/pkg/muxer/http/matcher.go index 200f3fc5d..98ebc8553 100644 --- a/pkg/muxer/http/matcher.go +++ b/pkg/muxer/http/matcher.go @@ -13,7 +13,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator" ) -var httpFuncs = map[string]func(*matchersTree, ...string) error{ +var httpFuncs = matcherBuilderFuncs{ "ClientIP": expectNParameters(clientIP, 1), "Method": expectNParameters(method, 1), "Host": expectNParameters(host, 1), diff --git a/pkg/muxer/http/matcher_test.go b/pkg/muxer/http/matcher_test.go index f3a8f262c..35550ecaf 100644 --- a/pkg/muxer/http/matcher_test.go +++ b/pkg/muxer/http/matcher_test.go @@ -69,9 +69,11 @@ func TestClientIPMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -142,9 +144,11 @@ func TestMethodMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -259,9 +263,11 @@ func TestHostMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -358,9 +364,11 @@ func TestHostRegexpMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -431,9 +439,11 @@ func TestPathMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -523,9 +533,11 @@ func TestPathRegexpMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -594,9 +606,11 @@ func TestPathPrefixMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -680,9 +694,11 @@ func TestHeaderMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -787,9 +803,11 @@ func TestHeaderRegexpMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -875,9 +893,11 @@ func TestQueryMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) @@ -988,9 +1008,11 @@ func TestQueryRegexpMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { require.Error(t, err) diff --git a/pkg/muxer/http/matcher_v2.go b/pkg/muxer/http/matcher_v2.go index d87b8e41d..b6635220f 100644 --- a/pkg/muxer/http/matcher_v2.go +++ b/pkg/muxer/http/matcher_v2.go @@ -11,7 +11,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator" ) -var httpFuncsV2 = map[string]func(*matchersTree, ...string) error{ +var httpFuncsV2 = matcherBuilderFuncs{ "Host": hostV2, "HostHeader": hostV2, "HostRegexp": hostRegexpV2, diff --git a/pkg/muxer/http/matcher_v2_test.go b/pkg/muxer/http/matcher_v2_test.go index 6799fb7da..d05e57e75 100644 --- a/pkg/muxer/http/matcher_v2_test.go +++ b/pkg/muxer/http/matcher_v2_test.go @@ -73,9 +73,11 @@ func TestClientIPV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -149,9 +151,11 @@ func TestMethodV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -273,9 +277,11 @@ func TestHostV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -375,9 +381,11 @@ func TestHostRegexpV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -469,9 +477,11 @@ func TestPathV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -561,9 +571,11 @@ func TestPathPrefixV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -647,9 +659,11 @@ func TestHeadersMatcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -754,9 +768,11 @@ func TestHeaderRegexpV2Matcher(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) @@ -846,9 +862,11 @@ func TestHostRegexp(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.hostExp, "v2", 0, handler) require.NoError(t, err) @@ -1513,9 +1531,11 @@ func Test_addRoute(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "v2", 0, handler) if test.expectedError { require.Error(t, err) diff --git a/pkg/muxer/http/mux.go b/pkg/muxer/http/mux.go index 1d84a1433..2adad60bc 100644 --- a/pkg/muxer/http/mux.go +++ b/pkg/muxer/http/mux.go @@ -7,44 +7,27 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/rules" - "github.com/vulcand/predicate" ) +type matcherBuilderFuncs map[string]matcherBuilderFunc + +type matcherBuilderFunc func(*matchersTree, ...string) error + +type MatcherFunc func(*http.Request) bool + // Muxer handles routing with rules. type Muxer struct { routes routes - parser predicate.Parser - parserV2 predicate.Parser + parser SyntaxParser defaultHandler http.Handler } // NewMuxer returns a new muxer instance. -func NewMuxer() (*Muxer, error) { - var matchers []string - for matcher := range httpFuncs { - matchers = append(matchers, matcher) - } - - parser, err := rules.NewParser(matchers) - if err != nil { - return nil, fmt.Errorf("error while creating parser: %w", err) - } - - var matchersV2 []string - for matcher := range httpFuncsV2 { - matchersV2 = append(matchersV2, matcher) - } - - parserV2, err := rules.NewParser(matchersV2) - if err != nil { - return nil, fmt.Errorf("error while creating v2 parser: %w", err) - } - +func NewMuxer(parser SyntaxParser) *Muxer { return &Muxer{ parser: parser, - parserV2: parserV2, defaultHandler: http.NotFoundHandler(), - }, nil + } } // ServeHTTP forwards the connection to the matching HTTP handler. @@ -73,36 +56,9 @@ func GetRulePriority(rule string) int { // AddRoute add a new route to the router. func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler http.Handler) error { - var parse interface{} - var err error - var matcherFuncs map[string]func(*matchersTree, ...string) error - - switch syntax { - case "v2": - parse, err = m.parserV2.Parse(rule) - if err != nil { - return fmt.Errorf("error while parsing rule %s: %w", rule, err) - } - - matcherFuncs = httpFuncsV2 - default: - parse, err = m.parser.Parse(rule) - if err != nil { - return fmt.Errorf("error while parsing rule %s: %w", rule, err) - } - - matcherFuncs = httpFuncs - } - - buildTree, ok := parse.(rules.TreeBuilder) - if !ok { - return fmt.Errorf("error while parsing rule %s", rule) - } - - var matchers matchersTree - err = matchers.addRule(buildTree(), matcherFuncs) + matchers, err := m.parser.parse(syntax, rule) if err != nil { - return fmt.Errorf("error while adding rule %s: %w", rule, err) + return fmt.Errorf("error while parsing rule %s: %w", rule, err) } m.routes = append(m.routes, &route{ @@ -173,7 +129,7 @@ type matchersTree struct { // matcher is a matcher func used to match HTTP request properties. // If matcher is not nil, it means that this matcherTree is a leaf of the tree. // It is therefore mutually exclusive with left and right. - matcher func(*http.Request) bool + matcher MatcherFunc // operator to combine the evaluation of left and right leaves. operator string // Mutually exclusive with matcher. @@ -204,9 +160,7 @@ func (m *matchersTree) match(req *http.Request) bool { } } -type matcherFuncs map[string]func(*matchersTree, ...string) error - -func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error { +func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherBuilderFuncs) error { switch rule.Matcher { case "and", "or": m.operator = rule.Matcher diff --git a/pkg/muxer/http/mux_test.go b/pkg/muxer/http/mux_test.go index 710df72b7..d0b4a08bf 100644 --- a/pkg/muxer/http/mux_test.go +++ b/pkg/muxer/http/mux_test.go @@ -225,9 +225,11 @@ func TestMuxer(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) err = muxer.AddRoute(test.rule, "", 0, handler) if test.expectedError { @@ -378,9 +380,11 @@ func Test_addRoutePriority(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { t.Parallel() - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + for _, route := range test.cases { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-From", route.xFrom) @@ -510,9 +514,11 @@ func TestEmptyHost(t *testing.T) { t.Parallel() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - muxer, err := NewMuxer() + parser, err := NewSyntaxParser() require.NoError(t, err) + muxer := NewMuxer(parser) + err = muxer.AddRoute(test.rule, "", 0, handler) require.NoError(t, err) diff --git a/pkg/muxer/http/parser.go b/pkg/muxer/http/parser.go new file mode 100644 index 000000000..dc473b19e --- /dev/null +++ b/pkg/muxer/http/parser.go @@ -0,0 +1,103 @@ +package http + +import ( + "errors" + "fmt" + "maps" + "slices" + "strings" + + "github.com/traefik/traefik/v3/pkg/rules" + "github.com/vulcand/predicate" +) + +type SyntaxParser struct { + parsers map[string]*parser +} + +type Options func(map[string]matcherBuilderFuncs) + +func WithMatcher(syntax, matcherName string, builderFunc func(params ...string) (MatcherFunc, error)) Options { + return func(syntaxFuncs map[string]matcherBuilderFuncs) { + syntax = strings.ToLower(syntax) + + syntaxFuncs[syntax][matcherName] = func(tree *matchersTree, s ...string) error { + matcher, err := builderFunc(s...) + if err != nil { + return fmt.Errorf("building matcher: %w", err) + } + + tree.matcher = matcher + return nil + } + } +} + +func NewSyntaxParser(opts ...Options) (SyntaxParser, error) { + syntaxFuncs := map[string]matcherBuilderFuncs{ + "v2": httpFuncsV2, + "v3": httpFuncs, + } + + for _, opt := range opts { + opt(syntaxFuncs) + } + + parsers := map[string]*parser{} + for syntax, funcs := range syntaxFuncs { + var err error + parsers[syntax], err = newParser(funcs) + if err != nil { + return SyntaxParser{}, err + } + } + + return SyntaxParser{ + parsers: parsers, + }, nil +} + +func (s SyntaxParser) parse(syntax string, rule string) (matchersTree, error) { + parser, ok := s.parsers[syntax] + if !ok { + parser = s.parsers["v3"] + } + + return parser.parse(rule) +} + +func newParser(funcs matcherBuilderFuncs) (*parser, error) { + p, err := rules.NewParser(slices.Collect(maps.Keys(funcs))) + if err != nil { + return nil, err + } + + return &parser{ + parser: p, + matcherFuncs: funcs, + }, nil +} + +type parser struct { + parser predicate.Parser + matcherFuncs matcherBuilderFuncs +} + +func (p *parser) parse(rule string) (matchersTree, error) { + parse, err := p.parser.Parse(rule) + if err != nil { + return matchersTree{}, fmt.Errorf("parsing rule %s: %w", rule, err) + } + buildTree, ok := parse.(rules.TreeBuilder) + if !ok { + return matchersTree{}, errors.New("obtaining build tree") + } + + var matchers matchersTree + err = matchers.addRule(buildTree(), p.matcherFuncs) + if err != nil { + return matchersTree{}, fmt.Errorf("adding rule %s: %w", rule, err) + } + + return matchers, nil +} diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index fcab90a94..3abd239e5 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -42,10 +42,11 @@ type Manager struct { middlewaresBuilder middlewareBuilder conf *runtime.Configuration tlsManager *tls.Manager + parser httpmuxer.SyntaxParser } // NewManager creates a new Manager. -func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, observabilityMgr *middleware.ObservabilityMgr, tlsManager *tls.Manager) *Manager { +func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, observabilityMgr *middleware.ObservabilityMgr, tlsManager *tls.Manager, parser httpmuxer.SyntaxParser) *Manager { return &Manager{ routerHandlers: make(map[string]http.Handler), serviceManager: serviceManager, @@ -53,6 +54,7 @@ func NewManager(conf *runtime.Configuration, serviceManager serviceManager, midd middlewaresBuilder: middlewaresBuilder, conf: conf, tlsManager: tlsManager, + parser: parser, } } @@ -103,10 +105,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t } func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName string, configs map[string]*runtime.RouterInfo) (http.Handler, error) { - muxer, err := httpmuxer.NewMuxer() - if err != nil { - return nil, err - } + muxer := httpmuxer.NewMuxer(m.parser) defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "", nil).Then(http.NotFoundHandler()) if err != nil { diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 35a58bfb8..b73f3df5a 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -13,10 +13,12 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator" + httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" "github.com/traefik/traefik/v3/pkg/server/middleware" "github.com/traefik/traefik/v3/pkg/server/service" "github.com/traefik/traefik/v3/pkg/testhelpers" @@ -326,7 +328,10 @@ func TestRouterManager_Get(t *testing.T) { middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) tlsManager := traefiktls.NewManager() - routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager) + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false) @@ -711,7 +716,10 @@ func TestRuntimeConfiguration(t *testing.T) { tlsManager := traefiktls.NewManager() tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil) - routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager) + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) _ = routerManager.BuildHandlers(context.Background(), entryPoints, false) _ = routerManager.BuildHandlers(context.Background(), entryPoints, true) @@ -789,7 +797,10 @@ func TestProviderOnMiddlewares(t *testing.T) { middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) tlsManager := traefiktls.NewManager() - routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager) + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) _ = routerManager.BuildHandlers(context.Background(), entryPoints, false) @@ -865,7 +876,10 @@ func BenchmarkRouterServe(b *testing.B) { middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) tlsManager := traefiktls.NewManager() - routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager) + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(b, err) + + routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false) diff --git a/pkg/server/routerfactory.go b/pkg/server/routerfactory.go index b0700849c..f4e0a1102 100644 --- a/pkg/server/routerfactory.go +++ b/pkg/server/routerfactory.go @@ -2,10 +2,12 @@ package server import ( "context" + "fmt" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" + httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" "github.com/traefik/traefik/v3/pkg/server/middleware" tcpmiddleware "github.com/traefik/traefik/v3/pkg/server/middleware/tcp" "github.com/traefik/traefik/v3/pkg/server/router" @@ -35,12 +37,14 @@ type RouterFactory struct { dialerManager *tcp.DialerManager cancelPrevState func() + + parser httpmuxer.SyntaxParser } // NewRouterFactory creates a new RouterFactory. func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *service.ManagerFactory, tlsManager *tls.Manager, observabilityMgr *middleware.ObservabilityMgr, pluginBuilder middleware.PluginsBuilder, dialerManager *tcp.DialerManager, -) *RouterFactory { +) (*RouterFactory, error) { handlesTLSChallenge := false for _, resolver := range staticConfiguration.CertificatesResolvers { if resolver.ACME != nil && resolver.ACME.TLSChallenge != nil { @@ -67,6 +71,11 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory * } } + parser, err := httpmuxer.NewSyntaxParser() + if err != nil { + return nil, fmt.Errorf("creating parser: %w", err) + } + return &RouterFactory{ entryPointsTCP: entryPointsTCP, entryPointsUDP: entryPointsUDP, @@ -76,7 +85,8 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory * pluginBuilder: pluginBuilder, dialerManager: dialerManager, allowACMEByPass: allowACMEByPass, - } + parser: parser, + }, nil } // CreateRouters creates new TCPRouters and UDPRouters. @@ -93,7 +103,7 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder) - routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.observabilityMgr, f.tlsManager) + routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.observabilityMgr, f.tlsManager, f.parser) handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false) handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true) diff --git a/pkg/server/routerfactory_test.go b/pkg/server/routerfactory_test.go index 2b3d6fe1f..648b24e8f 100644 --- a/pkg/server/routerfactory_test.go +++ b/pkg/server/routerfactory_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" @@ -58,7 +59,8 @@ func TestReuseService(t *testing.T) { dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) - factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager) + factory, err := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager) + require.NoError(t, err) entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs})) @@ -196,7 +198,8 @@ func TestServerResponseEmptyBackend(t *testing.T) { dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) observabiltyMgr := middleware.NewObservabilityMgr(staticConfig, nil, nil, nil, nil, nil) - factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, observabiltyMgr, nil, dialerManager) + factory, err := NewRouterFactory(staticConfig, managerFactory, tlsManager, observabiltyMgr, nil, dialerManager) + require.NoError(t, err) entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)})) @@ -240,7 +243,8 @@ func TestInternalServices(t *testing.T) { dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) - factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager) + factory, err := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager) + require.NoError(t, err) entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))