1package router
2
3import (
4	"context"
5	"io"
6	"net/http"
7	"net/http/httptest"
8	"strings"
9	"testing"
10
11	"github.com/stretchr/testify/assert"
12	"github.com/stretchr/testify/require"
13	"github.com/traefik/traefik/v2/pkg/config/dynamic"
14	"github.com/traefik/traefik/v2/pkg/config/runtime"
15	"github.com/traefik/traefik/v2/pkg/config/static"
16	"github.com/traefik/traefik/v2/pkg/metrics"
17	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
18	"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
19	"github.com/traefik/traefik/v2/pkg/server/middleware"
20	"github.com/traefik/traefik/v2/pkg/server/service"
21	"github.com/traefik/traefik/v2/pkg/testhelpers"
22	"github.com/traefik/traefik/v2/pkg/types"
23)
24
25func TestRouterManager_Get(t *testing.T) {
26	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
27
28	t.Cleanup(func() { server.Close() })
29
30	type expectedResult struct {
31		StatusCode     int
32		RequestHeaders map[string]string
33	}
34
35	testCases := []struct {
36		desc              string
37		routersConfig     map[string]*dynamic.Router
38		serviceConfig     map[string]*dynamic.Service
39		middlewaresConfig map[string]*dynamic.Middleware
40		entryPoints       []string
41		expected          expectedResult
42	}{
43		{
44			desc: "no middleware",
45			routersConfig: map[string]*dynamic.Router{
46				"foo": {
47					EntryPoints: []string{"web"},
48					Service:     "foo-service",
49					Rule:        "Host(`foo.bar`)",
50				},
51			},
52			serviceConfig: map[string]*dynamic.Service{
53				"foo-service": {
54					LoadBalancer: &dynamic.ServersLoadBalancer{
55						Servers: []dynamic.Server{
56							{
57								URL: server.URL,
58							},
59						},
60					},
61				},
62			},
63			entryPoints: []string{"web"},
64			expected:    expectedResult{StatusCode: http.StatusOK},
65		},
66		{
67			desc: "empty host",
68			routersConfig: map[string]*dynamic.Router{
69				"foo": {
70					EntryPoints: []string{"web"},
71					Service:     "foo-service",
72					Rule:        "Host(``)",
73				},
74			},
75			serviceConfig: map[string]*dynamic.Service{
76				"foo-service": {
77					LoadBalancer: &dynamic.ServersLoadBalancer{
78						Servers: []dynamic.Server{
79							{
80								URL: server.URL,
81							},
82						},
83					},
84				},
85			},
86			entryPoints: []string{"web"},
87			expected:    expectedResult{StatusCode: http.StatusNotFound},
88		},
89		{
90			desc: "no load balancer",
91			routersConfig: map[string]*dynamic.Router{
92				"foo": {
93					EntryPoints: []string{"web"},
94					Service:     "foo-service",
95					Rule:        "Host(`foo.bar`)",
96				},
97			},
98			serviceConfig: map[string]*dynamic.Service{
99				"foo-service": {},
100			},
101			entryPoints: []string{"web"},
102			expected:    expectedResult{StatusCode: http.StatusNotFound},
103		},
104		{
105			desc: "no middleware, no matching",
106			routersConfig: map[string]*dynamic.Router{
107				"foo": {
108					EntryPoints: []string{"web"},
109					Service:     "foo-service",
110					Rule:        "Host(`bar.bar`)",
111				},
112			},
113			serviceConfig: map[string]*dynamic.Service{
114				"foo-service": {
115					LoadBalancer: &dynamic.ServersLoadBalancer{
116						Servers: []dynamic.Server{
117							{
118								URL: server.URL,
119							},
120						},
121					},
122				},
123			},
124			entryPoints: []string{"web"},
125			expected:    expectedResult{StatusCode: http.StatusNotFound},
126		},
127		{
128			desc: "middleware: headers > auth",
129			routersConfig: map[string]*dynamic.Router{
130				"foo": {
131					EntryPoints: []string{"web"},
132					Middlewares: []string{"headers-middle", "auth-middle"},
133					Service:     "foo-service",
134					Rule:        "Host(`foo.bar`)",
135				},
136			},
137			serviceConfig: map[string]*dynamic.Service{
138				"foo-service": {
139					LoadBalancer: &dynamic.ServersLoadBalancer{
140						Servers: []dynamic.Server{
141							{
142								URL: server.URL,
143							},
144						},
145					},
146				},
147			},
148			middlewaresConfig: map[string]*dynamic.Middleware{
149				"auth-middle": {
150					BasicAuth: &dynamic.BasicAuth{
151						Users: []string{"toto:titi"},
152					},
153				},
154				"headers-middle": {
155					Headers: &dynamic.Headers{
156						CustomRequestHeaders: map[string]string{"X-Apero": "beer"},
157					},
158				},
159			},
160			entryPoints: []string{"web"},
161			expected: expectedResult{
162				StatusCode: http.StatusUnauthorized,
163				RequestHeaders: map[string]string{
164					"X-Apero": "beer",
165				},
166			},
167		},
168		{
169			desc: "middleware: auth > header",
170			routersConfig: map[string]*dynamic.Router{
171				"foo": {
172					EntryPoints: []string{"web"},
173					Middlewares: []string{"auth-middle", "headers-middle"},
174					Service:     "foo-service",
175					Rule:        "Host(`foo.bar`)",
176				},
177			},
178			serviceConfig: map[string]*dynamic.Service{
179				"foo-service": {
180					LoadBalancer: &dynamic.ServersLoadBalancer{
181						Servers: []dynamic.Server{
182							{
183								URL: server.URL,
184							},
185						},
186					},
187				},
188			},
189			middlewaresConfig: map[string]*dynamic.Middleware{
190				"auth-middle": {
191					BasicAuth: &dynamic.BasicAuth{
192						Users: []string{"toto:titi"},
193					},
194				},
195				"headers-middle": {
196					Headers: &dynamic.Headers{
197						CustomRequestHeaders: map[string]string{"X-Apero": "beer"},
198					},
199				},
200			},
201			entryPoints: []string{"web"},
202			expected: expectedResult{
203				StatusCode: http.StatusUnauthorized,
204				RequestHeaders: map[string]string{
205					"X-Apero": "",
206				},
207			},
208		},
209		{
210			desc: "no middleware with provider name",
211			routersConfig: map[string]*dynamic.Router{
212				"foo@provider-1": {
213					EntryPoints: []string{"web"},
214					Service:     "foo-service",
215					Rule:        "Host(`foo.bar`)",
216				},
217			},
218			serviceConfig: map[string]*dynamic.Service{
219				"foo-service@provider-1": {
220					LoadBalancer: &dynamic.ServersLoadBalancer{
221						Servers: []dynamic.Server{
222							{
223								URL: server.URL,
224							},
225						},
226					},
227				},
228			},
229			entryPoints: []string{"web"},
230			expected:    expectedResult{StatusCode: http.StatusOK},
231		},
232		{
233			desc: "no middleware with specified provider name",
234			routersConfig: map[string]*dynamic.Router{
235				"foo@provider-1": {
236					EntryPoints: []string{"web"},
237					Service:     "foo-service@provider-2",
238					Rule:        "Host(`foo.bar`)",
239				},
240			},
241			serviceConfig: map[string]*dynamic.Service{
242				"foo-service@provider-2": {
243					LoadBalancer: &dynamic.ServersLoadBalancer{
244						Servers: []dynamic.Server{
245							{
246								URL: server.URL,
247							},
248						},
249					},
250				},
251			},
252			entryPoints: []string{"web"},
253			expected:    expectedResult{StatusCode: http.StatusOK},
254		},
255		{
256			desc: "middleware: chain with provider name",
257			routersConfig: map[string]*dynamic.Router{
258				"foo@provider-1": {
259					EntryPoints: []string{"web"},
260					Middlewares: []string{"chain-middle@provider-2", "headers-middle"},
261					Service:     "foo-service",
262					Rule:        "Host(`foo.bar`)",
263				},
264			},
265			serviceConfig: map[string]*dynamic.Service{
266				"foo-service@provider-1": {
267					LoadBalancer: &dynamic.ServersLoadBalancer{
268						Servers: []dynamic.Server{
269							{
270								URL: server.URL,
271							},
272						},
273					},
274				},
275			},
276			middlewaresConfig: map[string]*dynamic.Middleware{
277				"chain-middle@provider-2": {
278					Chain: &dynamic.Chain{Middlewares: []string{"auth-middle"}},
279				},
280				"auth-middle@provider-2": {
281					BasicAuth: &dynamic.BasicAuth{
282						Users: []string{"toto:titi"},
283					},
284				},
285				"headers-middle@provider-1": {
286					Headers: &dynamic.Headers{
287						CustomRequestHeaders: map[string]string{"X-Apero": "beer"},
288					},
289				},
290			},
291			entryPoints: []string{"web"},
292			expected: expectedResult{
293				StatusCode: http.StatusUnauthorized,
294				RequestHeaders: map[string]string{
295					"X-Apero": "",
296				},
297			},
298		},
299	}
300
301	for _, test := range testCases {
302		test := test
303		t.Run(test.desc, func(t *testing.T) {
304			t.Parallel()
305
306			rtConf := runtime.NewConfig(dynamic.Configuration{
307				HTTP: &dynamic.HTTPConfiguration{
308					Services:    test.serviceConfig,
309					Routers:     test.routersConfig,
310					Middlewares: test.middlewaresConfig,
311				},
312			})
313
314			roundTripperManager := service.NewRoundTripperManager()
315			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
316			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
317			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
318			chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
319
320			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
321
322			handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
323
324			w := httptest.NewRecorder()
325			req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
326
327			reqHost := requestdecorator.New(nil)
328			reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
329
330			assert.Equal(t, test.expected.StatusCode, w.Code)
331
332			for key, value := range test.expected.RequestHeaders {
333				assert.Equal(t, value, req.Header.Get(key))
334			}
335		})
336	}
337}
338
339func TestAccessLog(t *testing.T) {
340	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
341
342	t.Cleanup(func() { server.Close() })
343
344	testCases := []struct {
345		desc              string
346		routersConfig     map[string]*dynamic.Router
347		serviceConfig     map[string]*dynamic.Service
348		middlewaresConfig map[string]*dynamic.Middleware
349		entryPoints       []string
350		expected          string
351	}{
352		{
353			desc: "apply routerName in accesslog (first match)",
354			routersConfig: map[string]*dynamic.Router{
355				"foo": {
356					EntryPoints: []string{"web"},
357					Service:     "foo-service",
358					Rule:        "Host(`foo.bar`)",
359				},
360				"bar": {
361					EntryPoints: []string{"web"},
362					Service:     "foo-service",
363					Rule:        "Host(`bar.foo`)",
364				},
365			},
366			serviceConfig: map[string]*dynamic.Service{
367				"foo-service": {
368					LoadBalancer: &dynamic.ServersLoadBalancer{
369						Servers: []dynamic.Server{
370							{
371								URL: server.URL,
372							},
373						},
374					},
375				},
376			},
377			entryPoints: []string{"web"},
378			expected:    "foo",
379		},
380		{
381			desc: "apply routerName in accesslog (second match)",
382			routersConfig: map[string]*dynamic.Router{
383				"foo": {
384					EntryPoints: []string{"web"},
385					Service:     "foo-service",
386					Rule:        "Host(`bar.foo`)",
387				},
388				"bar": {
389					EntryPoints: []string{"web"},
390					Service:     "foo-service",
391					Rule:        "Host(`foo.bar`)",
392				},
393			},
394			serviceConfig: map[string]*dynamic.Service{
395				"foo-service": {
396					LoadBalancer: &dynamic.ServersLoadBalancer{
397						Servers: []dynamic.Server{
398							{
399								URL: server.URL,
400							},
401						},
402					},
403				},
404			},
405			entryPoints: []string{"web"},
406			expected:    "bar",
407		},
408	}
409
410	for _, test := range testCases {
411		t.Run(test.desc, func(t *testing.T) {
412			rtConf := runtime.NewConfig(dynamic.Configuration{
413				HTTP: &dynamic.HTTPConfiguration{
414					Services:    test.serviceConfig,
415					Routers:     test.routersConfig,
416					Middlewares: test.middlewaresConfig,
417				},
418			})
419
420			roundTripperManager := service.NewRoundTripperManager()
421			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
422			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
423			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
424			chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
425
426			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
427
428			handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
429
430			w := httptest.NewRecorder()
431			req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
432
433			accesslogger, err := accesslog.NewHandler(&types.AccessLog{
434				Format: "json",
435			})
436			require.NoError(t, err)
437
438			reqHost := requestdecorator.New(nil)
439
440			accesslogger.ServeHTTP(w, req, http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
441				reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
442
443				data := accesslog.GetLogData(req)
444				require.NotNil(t, data)
445
446				assert.Equal(t, test.expected, data.Core[accesslog.RouterName])
447			}))
448		})
449	}
450}
451
452func TestRuntimeConfiguration(t *testing.T) {
453	testCases := []struct {
454		desc             string
455		serviceConfig    map[string]*dynamic.Service
456		routerConfig     map[string]*dynamic.Router
457		middlewareConfig map[string]*dynamic.Middleware
458		expectedError    int
459	}{
460		{
461			desc: "No error",
462			serviceConfig: map[string]*dynamic.Service{
463				"foo-service": {
464					LoadBalancer: &dynamic.ServersLoadBalancer{
465						Servers: []dynamic.Server{
466							{
467								URL: "http://127.0.0.1:8085",
468							},
469							{
470								URL: "http://127.0.0.1:8086",
471							},
472						},
473						HealthCheck: &dynamic.ServerHealthCheck{
474							Interval: "500ms",
475							Path:     "/health",
476						},
477					},
478				},
479			},
480			routerConfig: map[string]*dynamic.Router{
481				"foo": {
482					EntryPoints: []string{"web"},
483					Service:     "foo-service",
484					Rule:        "Host(`bar.foo`)",
485				},
486				"bar": {
487					EntryPoints: []string{"web"},
488					Service:     "foo-service",
489					Rule:        "Host(`foo.bar`)",
490				},
491			},
492			expectedError: 0,
493		},
494		{
495			desc: "One router with wrong rule",
496			serviceConfig: map[string]*dynamic.Service{
497				"foo-service": {
498					LoadBalancer: &dynamic.ServersLoadBalancer{
499						Servers: []dynamic.Server{
500							{
501								URL: "http://127.0.0.1",
502							},
503						},
504					},
505				},
506			},
507			routerConfig: map[string]*dynamic.Router{
508				"foo": {
509					EntryPoints: []string{"web"},
510					Service:     "foo-service",
511					Rule:        "WrongRule(`bar.foo`)",
512				},
513				"bar": {
514					EntryPoints: []string{"web"},
515					Service:     "foo-service",
516					Rule:        "Host(`foo.bar`)",
517				},
518			},
519			expectedError: 1,
520		},
521		{
522			desc: "All router with wrong rule",
523			serviceConfig: map[string]*dynamic.Service{
524				"foo-service": {
525					LoadBalancer: &dynamic.ServersLoadBalancer{
526						Servers: []dynamic.Server{
527							{
528								URL: "http://127.0.0.1",
529							},
530						},
531					},
532				},
533			},
534			routerConfig: map[string]*dynamic.Router{
535				"foo": {
536					EntryPoints: []string{"web"},
537					Service:     "foo-service",
538					Rule:        "WrongRule(`bar.foo`)",
539				},
540				"bar": {
541					EntryPoints: []string{"web"},
542					Service:     "foo-service",
543					Rule:        "WrongRule(`foo.bar`)",
544				},
545			},
546			expectedError: 2,
547		},
548		{
549			desc: "Router with unknown service",
550			serviceConfig: map[string]*dynamic.Service{
551				"foo-service": {
552					LoadBalancer: &dynamic.ServersLoadBalancer{
553						Servers: []dynamic.Server{
554							{
555								URL: "http://127.0.0.1",
556							},
557						},
558					},
559				},
560			},
561			routerConfig: map[string]*dynamic.Router{
562				"foo": {
563					EntryPoints: []string{"web"},
564					Service:     "wrong-service",
565					Rule:        "Host(`bar.foo`)",
566				},
567				"bar": {
568					EntryPoints: []string{"web"},
569					Service:     "foo-service",
570					Rule:        "Host(`foo.bar`)",
571				},
572			},
573			expectedError: 1,
574		},
575		{
576			desc: "Router with broken service",
577			serviceConfig: map[string]*dynamic.Service{
578				"foo-service": {
579					LoadBalancer: nil,
580				},
581			},
582			routerConfig: map[string]*dynamic.Router{
583				"bar": {
584					EntryPoints: []string{"web"},
585					Service:     "foo-service",
586					Rule:        "Host(`foo.bar`)",
587				},
588			},
589			expectedError: 2,
590		},
591		{
592			desc: "Router with middleware",
593			serviceConfig: map[string]*dynamic.Service{
594				"foo-service": {
595					LoadBalancer: &dynamic.ServersLoadBalancer{
596						Servers: []dynamic.Server{
597							{
598								URL: "http://127.0.0.1",
599							},
600						},
601					},
602				},
603			},
604			middlewareConfig: map[string]*dynamic.Middleware{
605				"auth": {
606					BasicAuth: &dynamic.BasicAuth{
607						Users: []string{"admin:admin"},
608					},
609				},
610				"addPrefixTest": {
611					AddPrefix: &dynamic.AddPrefix{
612						Prefix: "/toto",
613					},
614				},
615			},
616			routerConfig: map[string]*dynamic.Router{
617				"bar": {
618					EntryPoints: []string{"web"},
619					Service:     "foo-service",
620					Rule:        "Host(`foo.bar`)",
621					Middlewares: []string{"auth", "addPrefixTest"},
622				},
623				"test": {
624					EntryPoints: []string{"web"},
625					Service:     "foo-service",
626					Rule:        "Host(`foo.bar.other`)",
627					Middlewares: []string{"addPrefixTest", "auth"},
628				},
629			},
630		},
631		{
632			desc: "Router with unknown middleware",
633			serviceConfig: map[string]*dynamic.Service{
634				"foo-service": {
635					LoadBalancer: &dynamic.ServersLoadBalancer{
636						Servers: []dynamic.Server{
637							{
638								URL: "http://127.0.0.1",
639							},
640						},
641					},
642				},
643			},
644			middlewareConfig: map[string]*dynamic.Middleware{
645				"auth": {
646					BasicAuth: &dynamic.BasicAuth{
647						Users: []string{"admin:admin"},
648					},
649				},
650			},
651			routerConfig: map[string]*dynamic.Router{
652				"bar": {
653					EntryPoints: []string{"web"},
654					Service:     "foo-service",
655					Rule:        "Host(`foo.bar`)",
656					Middlewares: []string{"unknown"},
657				},
658			},
659			expectedError: 1,
660		},
661
662		{
663			desc: "Router with broken middleware",
664			serviceConfig: map[string]*dynamic.Service{
665				"foo-service": {
666					LoadBalancer: &dynamic.ServersLoadBalancer{
667						Servers: []dynamic.Server{
668							{
669								URL: "http://127.0.0.1",
670							},
671						},
672					},
673				},
674			},
675			middlewareConfig: map[string]*dynamic.Middleware{
676				"auth": {
677					BasicAuth: &dynamic.BasicAuth{
678						Users: []string{"foo"},
679					},
680				},
681			},
682			routerConfig: map[string]*dynamic.Router{
683				"bar": {
684					EntryPoints: []string{"web"},
685					Service:     "foo-service",
686					Rule:        "Host(`foo.bar`)",
687					Middlewares: []string{"auth"},
688				},
689			},
690			expectedError: 2,
691		},
692	}
693
694	for _, test := range testCases {
695		test := test
696		t.Run(test.desc, func(t *testing.T) {
697			t.Parallel()
698
699			entryPoints := []string{"web"}
700
701			rtConf := runtime.NewConfig(dynamic.Configuration{
702				HTTP: &dynamic.HTTPConfiguration{
703					Services:    test.serviceConfig,
704					Routers:     test.routerConfig,
705					Middlewares: test.middlewareConfig,
706				},
707			})
708
709			roundTripperManager := service.NewRoundTripperManager()
710			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
711			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
712			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
713			chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
714
715			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
716
717			_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
718
719			// even though rtConf was passed by argument to the manager builders above,
720			// it's ok to use it as the result we check, because everything worth checking
721			// can be accessed by pointers in it.
722			var allErrors int
723			for _, v := range rtConf.Services {
724				if v.Err != nil {
725					allErrors++
726				}
727			}
728			for _, v := range rtConf.Routers {
729				if len(v.Err) > 0 {
730					allErrors++
731				}
732			}
733			for _, v := range rtConf.Middlewares {
734				if v.Err != nil {
735					allErrors++
736				}
737			}
738			assert.Equal(t, test.expectedError, allErrors)
739		})
740	}
741}
742
743func TestProviderOnMiddlewares(t *testing.T) {
744	entryPoints := []string{"web"}
745
746	staticCfg := static.Configuration{
747		EntryPoints: map[string]*static.EntryPoint{
748			"web": {
749				Address: ":80",
750			},
751		},
752	}
753
754	rtConf := runtime.NewConfig(dynamic.Configuration{
755		HTTP: &dynamic.HTTPConfiguration{
756			Services: map[string]*dynamic.Service{
757				"test@file": {
758					LoadBalancer: &dynamic.ServersLoadBalancer{
759						Servers: []dynamic.Server{},
760					},
761				},
762			},
763			Routers: map[string]*dynamic.Router{
764				"router@file": {
765					EntryPoints: []string{"web"},
766					Rule:        "Host(`test`)",
767					Service:     "test@file",
768					Middlewares: []string{"chain@file", "m1"},
769				},
770				"router@docker": {
771					EntryPoints: []string{"web"},
772					Rule:        "Host(`test`)",
773					Service:     "test@file",
774					Middlewares: []string{"chain", "m1@file"},
775				},
776			},
777			Middlewares: map[string]*dynamic.Middleware{
778				"chain@file": {
779					Chain: &dynamic.Chain{Middlewares: []string{"m1", "m2", "m1@file"}},
780				},
781				"chain@docker": {
782					Chain: &dynamic.Chain{Middlewares: []string{"m1", "m2", "m1@file"}},
783				},
784				"m1@file":   {AddPrefix: &dynamic.AddPrefix{Prefix: "/m1"}},
785				"m2@file":   {AddPrefix: &dynamic.AddPrefix{Prefix: "/m2"}},
786				"m1@docker": {AddPrefix: &dynamic.AddPrefix{Prefix: "/m1"}},
787				"m2@docker": {AddPrefix: &dynamic.AddPrefix{Prefix: "/m2"}},
788			},
789		},
790	})
791
792	roundTripperManager := service.NewRoundTripperManager()
793	roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
794	serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
795	middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
796	chainBuilder := middleware.NewChainBuilder(staticCfg, nil, nil)
797
798	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
799
800	_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
801
802	assert.Equal(t, []string{"chain@file", "m1@file"}, rtConf.Routers["router@file"].Middlewares)
803	assert.Equal(t, []string{"m1@file", "m2@file", "m1@file"}, rtConf.Middlewares["chain@file"].Chain.Middlewares)
804	assert.Equal(t, []string{"chain@docker", "m1@file"}, rtConf.Routers["router@docker"].Middlewares)
805	assert.Equal(t, []string{"m1@docker", "m2@docker", "m1@file"}, rtConf.Middlewares["chain@docker"].Chain.Middlewares)
806}
807
808type staticRoundTripperGetter struct {
809	res *http.Response
810}
811
812func (s staticRoundTripperGetter) Get(name string) (http.RoundTripper, error) {
813	return &staticTransport{res: s.res}, nil
814}
815
816type staticTransport struct {
817	res *http.Response
818}
819
820func (t *staticTransport) RoundTrip(_ *http.Request) (*http.Response, error) {
821	return t.res, nil
822}
823
824func BenchmarkRouterServe(b *testing.B) {
825	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
826
827	b.Cleanup(func() { server.Close() })
828
829	res := &http.Response{
830		StatusCode: 200,
831		Body:       io.NopCloser(strings.NewReader("")),
832	}
833
834	routersConfig := map[string]*dynamic.Router{
835		"foo": {
836			EntryPoints: []string{"web"},
837			Service:     "foo-service",
838			Rule:        "Host(`foo.bar`) && Path(`/`)",
839		},
840	}
841	serviceConfig := map[string]*dynamic.Service{
842		"foo-service": {
843			LoadBalancer: &dynamic.ServersLoadBalancer{
844				Servers: []dynamic.Server{
845					{
846						URL: server.URL,
847					},
848				},
849			},
850		},
851	}
852	entryPoints := []string{"web"}
853
854	rtConf := runtime.NewConfig(dynamic.Configuration{
855		HTTP: &dynamic.HTTPConfiguration{
856			Services:    serviceConfig,
857			Routers:     routersConfig,
858			Middlewares: map[string]*dynamic.Middleware{},
859		},
860	})
861
862	serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
863	middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
864	chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
865
866	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
867
868	handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
869
870	w := httptest.NewRecorder()
871	req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
872
873	reqHost := requestdecorator.New(nil)
874	b.ReportAllocs()
875	for i := 0; i < b.N; i++ {
876		reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
877	}
878}
879
880func BenchmarkService(b *testing.B) {
881	res := &http.Response{
882		StatusCode: 200,
883		Body:       io.NopCloser(strings.NewReader("")),
884	}
885
886	serviceConfig := map[string]*dynamic.Service{
887		"foo-service": {
888			LoadBalancer: &dynamic.ServersLoadBalancer{
889				Servers: []dynamic.Server{
890					{
891						URL: "tchouck",
892					},
893				},
894			},
895		},
896	}
897
898	rtConf := runtime.NewConfig(dynamic.Configuration{
899		HTTP: &dynamic.HTTPConfiguration{
900			Services: serviceConfig,
901		},
902	})
903
904	serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
905	w := httptest.NewRecorder()
906	req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
907
908	handler, _ := serviceManager.BuildHTTP(context.Background(), "foo-service")
909	b.ReportAllocs()
910	for i := 0; i < b.N; i++ {
911		handler.ServeHTTP(w, req)
912	}
913}
914