From 7f6597f435a9db32d3c69c4dfd3278b91d048efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dario=20Casta=C3=B1=C3=A9?= Date: Wed, 8 May 2024 16:13:34 +0200 Subject: [PATCH] contrib/dimfeld/httptreemux.v5: fix route and name for 30X redirects (#2685) Co-authored-by: De Ville Weppenaar --- .github/workflows/unit-integration-tests.yml | 2 +- contrib/dimfeld/httptreemux.v5/httptreemux.go | 39 +- .../httptreemux.v5/httptreemux_test.go | 600 +++++++++++++++++- docker-compose.yaml | 2 +- 4 files changed, 616 insertions(+), 27 deletions(-) diff --git a/.github/workflows/unit-integration-tests.yml b/.github/workflows/unit-integration-tests.yml index 5642870623..3d88429d45 100644 --- a/.github/workflows/unit-integration-tests.yml +++ b/.github/workflows/unit-integration-tests.yml @@ -166,7 +166,7 @@ jobs: ports: - 2181:2181 kafka: - image: wurstmeister/kafka:2.13-2.8.1 + image: darccio/kafka:2.13-2.8.1 env: KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 diff --git a/contrib/dimfeld/httptreemux.v5/httptreemux.go b/contrib/dimfeld/httptreemux.v5/httptreemux.go index 6169ac4b7e..d9ae06e91d 100644 --- a/contrib/dimfeld/httptreemux.v5/httptreemux.go +++ b/contrib/dimfeld/httptreemux.v5/httptreemux.go @@ -112,18 +112,26 @@ func getRoute(router *httptreemux.TreeMux, w http.ResponseWriter, req *http.Requ if !found { return "", false } + routeLen := len(route) + trailingSlash := route[routeLen-1] == '/' && routeLen > 1 - // Check for redirecting route due to trailing slash for parameters. - // The redirecting behaviour originates from httptreemux router. - if lr.StatusCode == http.StatusMovedPermanently && strings.HasSuffix(route, "/") { + // Retry the population of lookup result parameters. + // If the initial attempt to populate the parameters fails, clone the request and modify the URI and URL Path. + // Depending on whether the route has a trailing slash or not, it will either add or remove the trailing slash and retry the lookup. + if routerRedirectEnabled(router) && isSupportedRedirectStatus(lr.StatusCode) && lr.Params == nil { rReq := req.Clone(req.Context()) - rReq.RequestURI = strings.TrimSuffix(rReq.RequestURI, "/") - rReq.URL.Path = strings.TrimSuffix(rReq.URL.Path, "/") - - lr, found = router.Lookup(w, rReq) - if !found { - return "", false + if trailingSlash { + // if the route has a trailing slash, remove it + rReq.RequestURI = strings.TrimSuffix(rReq.RequestURI, "/") + rReq.URL.Path = strings.TrimSuffix(rReq.URL.Path, "/") + } else { + // if the route does not have a trailing slash, add one + rReq.RequestURI = rReq.RequestURI + "/" + rReq.URL.Path = rReq.URL.Path + "/" } + // no need to check found again + // we already matched a route and we are only trying to populate the lookup result params + lr, _ = router.Lookup(w, rReq) } for k, v := range lr.Params { @@ -141,3 +149,16 @@ func getRoute(router *httptreemux.TreeMux, w http.ResponseWriter, req *http.Requ } return route, true } + +// isSupportedRedirectStatus checks if the given HTTP status code is a supported redirect status. +func isSupportedRedirectStatus(status int) bool { + return status == http.StatusMovedPermanently || + status == http.StatusTemporaryRedirect || + status == http.StatusPermanentRedirect +} + +// routerRedirectEnabled checks if the redirection is enabled on the router. +func routerRedirectEnabled(router *httptreemux.TreeMux) bool { + return (router.RedirectCleanPath || router.RedirectTrailingSlash) && + router.RedirectBehavior != httptreemux.UseHandler +} diff --git a/contrib/dimfeld/httptreemux.v5/httptreemux_test.go b/contrib/dimfeld/httptreemux.v5/httptreemux_test.go index 017f2feedd..6bf900217c 100644 --- a/contrib/dimfeld/httptreemux.v5/httptreemux_test.go +++ b/contrib/dimfeld/httptreemux.v5/httptreemux_test.go @@ -238,18 +238,27 @@ func TestNamingSchema(t *testing.T) { namingschematest.NewHTTPServerTest(genSpans, "http.router")(t) } -func TestTrailingSlashRoutes(t *testing.T) { - t.Run("unknown if no handler matches", func(t *testing.T) { +func TestTrailingSlashRoutesWithBehaviorRedirect301(t *testing.T) { + t.Run("GET unknown", func(t *testing.T) { assert := assert.New(t) mt := mocktracer.Start() defer mt.Stop() - url := "/unknown/" - r := httptest.NewRequest(http.MethodGet, url, nil) + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.RedirectBehavior = httptreemux.Redirect301 // default + + // Note that the router has no handlers since we expect a 404 + + url := "/api/paramvalue" + r := httptest.NewRequest("GET", url, nil) w := httptest.NewRecorder() - router().ServeHTTP(w, r) - assert.Equal(404, w.Code) - assert.Equal("404 page not found\n", w.Body.String()) + router.ServeHTTP(w, r) + + assert.Equal(http.StatusNotFound, w.Code) + assert.Contains(w.Body.String(), "404 page not found") spans := mt.FinishedSpans() assert.Equal(1, len(spans)) @@ -260,22 +269,30 @@ func TestTrailingSlashRoutes(t *testing.T) { assert.Equal("GET unknown", s.Tag(ext.ResourceName)) assert.Equal("404", s.Tag(ext.HTTPCode)) assert.Equal("GET", s.Tag(ext.HTTPMethod)) - assert.Equal("http://example.com/unknown/", s.Tag(ext.HTTPURL)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) assert.Equal("testvalue", s.Tag("testkey")) assert.Nil(s.Tag(ext.Error)) assert.NotContains(s.Tags(), ext.HTTPRoute) }) - t.Run("parametrizes URL with trailing slash", func(t *testing.T) { + t.Run("GET /api/:parameter", func(t *testing.T) { assert := assert.New(t) mt := mocktracer.Start() defer mt.Stop() - url := "/api/paramvalue/" + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter", handler200) // without trailing slash + router.RedirectBehavior = httptreemux.Redirect301 // default + + url := "/api/paramvalue/" // with trailing slash r := httptest.NewRequest("GET", url, nil) w := httptest.NewRecorder() - router().ServeHTTP(w, r) - assert.Equal(301, w.Code) + router.ServeHTTP(w, r) + + assert.Equal(http.StatusMovedPermanently, w.Code) assert.Contains(w.Body.String(), "Moved Permanently") spans := mt.FinishedSpans() @@ -292,6 +309,558 @@ func TestTrailingSlashRoutes(t *testing.T) { assert.Nil(s.Tag(ext.Error)) assert.Contains(s.Tags(), ext.HTTPRoute) }) + + t.Run("GET /api/:parameter/", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter/", handler200) // with trailing slash + router.RedirectBehavior = httptreemux.Redirect301 // default + + url := "/api/paramvalue" // without trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusMovedPermanently, w.Code) + assert.Contains(w.Body.String(), "Moved Permanently") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter", s.Tag(ext.ResourceName)) + assert.Equal("301", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) +} + +func TestTrailingSlashRoutesWithBehaviorRedirect307(t *testing.T) { + t.Run("GET unknown", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.RedirectBehavior = httptreemux.Redirect307 + + // Note that the router has no handlers since we expect a 404 + + url := "/api/paramvalue" + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusNotFound, w.Code) + assert.Contains(w.Body.String(), "404 page not found") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET unknown", s.Tag(ext.ResourceName)) + assert.Equal("404", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.NotContains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter", handler200) // without trailing slash + router.RedirectBehavior = httptreemux.Redirect307 + + url := "/api/paramvalue/" // with trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusTemporaryRedirect, w.Code) + assert.Contains(w.Body.String(), "Temporary Redirect") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter/", s.Tag(ext.ResourceName)) + assert.Equal("307", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue/", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter/", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter/", handler200) // with trailing slash + router.RedirectBehavior = httptreemux.Redirect307 + + url := "/api/paramvalue" // without trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusTemporaryRedirect, w.Code) + assert.Contains(w.Body.String(), "Temporary Redirect") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter", s.Tag(ext.ResourceName)) + assert.Equal("307", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) +} + +func TestTrailingSlashRoutesWithBehaviorRedirect308(t *testing.T) { + t.Run("GET unknown", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.RedirectBehavior = httptreemux.Redirect308 + + // Note that the router has no handlers since we expect a 404 + + url := "/api/paramvalue" + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusNotFound, w.Code) + assert.Contains(w.Body.String(), "404 page not found") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET unknown", s.Tag(ext.ResourceName)) + assert.Equal("404", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.NotContains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter", handler200) // without trailing slash + router.RedirectBehavior = httptreemux.Redirect308 + + url := "/api/paramvalue/" // with trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusPermanentRedirect, w.Code) + assert.Contains(w.Body.String(), "Permanent Redirect") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter/", s.Tag(ext.ResourceName)) + assert.Equal("308", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue/", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter/", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter/", handler200) // with trailing slash + router.RedirectBehavior = httptreemux.Redirect308 + + url := "/api/paramvalue" // without trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusPermanentRedirect, w.Code) + assert.Contains(w.Body.String(), "Permanent Redirect") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter", s.Tag(ext.ResourceName)) + assert.Equal("308", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) +} + +func TestTrailingSlashRoutesWithBehaviorUseHandler(t *testing.T) { + t.Run("GET unknown", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.RedirectBehavior = httptreemux.UseHandler + + // Note that the router has no handlers since we expect a 404 + + url := "/api/paramvalue" + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusNotFound, w.Code) + assert.Contains(w.Body.String(), "404 page not found") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET unknown", s.Tag(ext.ResourceName)) + assert.Equal("404", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.NotContains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter", handler200) // without trailing slash + router.RedirectBehavior = httptreemux.UseHandler + + url := "/api/paramvalue/" // with trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusOK, w.Code) + assert.Contains(w.Body.String(), "OK\n") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter/", s.Tag(ext.ResourceName)) + assert.Equal("200", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue/", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) + + t.Run("GET /api/:parameter/", func(t *testing.T) { + assert := assert.New(t) + mt := mocktracer.Start() + defer mt.Stop() + + router := New( + WithServiceName("my-service"), + WithSpanOptions(tracer.Tag("testkey", "testvalue")), + ) + router.GET("/api/:parameter/", handler200) // with trailing slash + router.RedirectBehavior = httptreemux.UseHandler + + url := "/api/paramvalue" // without trailing slash + r := httptest.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, r) + + assert.Equal(http.StatusOK, w.Code) + assert.Contains(w.Body.String(), "OK\n") + + spans := mt.FinishedSpans() + assert.Equal(1, len(spans)) + + s := spans[0] + assert.Equal("http.request", s.OperationName()) + assert.Equal("my-service", s.Tag(ext.ServiceName)) + assert.Equal("GET /api/:parameter", s.Tag(ext.ResourceName)) + assert.Equal("200", s.Tag(ext.HTTPCode)) + assert.Equal("GET", s.Tag(ext.HTTPMethod)) + assert.Equal("http://example.com/api/paramvalue", s.Tag(ext.HTTPURL)) + assert.Equal("testvalue", s.Tag("testkey")) + assert.Nil(s.Tag(ext.Error)) + assert.Contains(s.Tags(), ext.HTTPRoute) + }) +} + +func TestIsSupportedRedirectStatus(t *testing.T) { + tests := []struct { + name string + status int + want bool + }{ + { + name: "Test with status 301", + status: 301, + want: true, + }, + { + name: "Test with status 302", + status: 302, + want: false, + }, + { + name: "Test with status 303", + status: 303, + want: false, + }, + { + name: "Test with status 307", + status: 307, + want: true, + }, + { + name: "Test with status 308", + status: 308, + want: true, + }, + { + name: "Test with status 400", + status: 400, + want: false, + }, + { + name: "Test with status 0", + status: 0, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isSupportedRedirectStatus(tt.status); got != tt.want { + t.Errorf("isSupportedRedirectStatus() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRouterRedirectEnabled(t *testing.T) { + tests := []struct { + name string + cleanPath bool + trailingSlash bool + redirectBehaviour httptreemux.RedirectBehavior + + want bool + }{ + // httptreemux.Redirect301 + { + name: "Test Redirect301 with clean path and trailing slash", + cleanPath: true, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect301, + want: true, + }, + { + name: "Test Redirect301 with clean path and no trailing slash", + cleanPath: true, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect301, + want: true, + }, + { + name: "Test Redirect301 with no clean path and trailing slash", + cleanPath: false, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect301, + want: true, + }, + { + name: "Test Redirect301 with no clean path and no trailing slash", + cleanPath: false, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect301, + want: false, + }, + // httptreemux.Redirect307 + { + name: "Test Redirect307 with clean path and trailing slash", + cleanPath: true, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect307, + want: true, + }, + { + name: "Test Redirect307 with clean path and no trailing slash", + cleanPath: true, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect307, + want: true, + }, + { + name: "Test Redirect307 with no clean path and trailing slash", + cleanPath: false, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect307, + want: true, + }, + { + name: "Test Redirect307 with no clean path and no trailing slash", + cleanPath: false, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect307, + want: false, + }, + // httptreemux.Redirect308 + { + name: "Test Redirect308 with clean path and trailing slash", + cleanPath: true, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect308, + want: true, + }, + { + name: "Test Redirect308 with clean path and no trailing slash", + cleanPath: true, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect308, + want: true, + }, + { + name: "Test Redirect308 with no clean path and trailing slash", + cleanPath: false, + trailingSlash: true, + redirectBehaviour: httptreemux.Redirect308, + want: true, + }, + { + name: "Test Redirect308 with no clean path and no trailing slash", + cleanPath: false, + trailingSlash: false, + redirectBehaviour: httptreemux.Redirect308, + want: false, + }, + // httptreemux.UseHandler + { + name: "Test UseHandler with clean path and trailing slash", + cleanPath: true, + trailingSlash: true, + redirectBehaviour: httptreemux.UseHandler, + want: false, + }, + { + name: "Test UseHandler with clean path and no trailing slash", + cleanPath: true, + trailingSlash: false, + redirectBehaviour: httptreemux.UseHandler, + want: false, + }, + { + name: "Test UseHandler with no clean path and trailing slash", + cleanPath: false, + trailingSlash: true, + redirectBehaviour: httptreemux.UseHandler, + want: false, + }, + { + name: "Test UseHandler with no clean path and no trailing slash", + cleanPath: false, + trailingSlash: false, + redirectBehaviour: httptreemux.UseHandler, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + router := httptreemux.New() + router.RedirectCleanPath = tt.cleanPath + router.RedirectTrailingSlash = tt.trailingSlash + router.RedirectBehavior = tt.redirectBehaviour + + if got := routerRedirectEnabled(router); got != tt.want { + t.Errorf("routerRedirectEnabled() = %v, want %v", got, tt.want) + } + }) + } } func router() http.Handler { @@ -302,7 +871,9 @@ func router() http.Handler { router.GET("/200", handler200) router.GET("/500", handler500) - router.GET("/api/:parameter", handlerDummy) + + router.GET("/api/:parameter", handler200) + router.GET("/api/:param1/:param2/:param3", handler200) return router } @@ -314,6 +885,3 @@ func handler200(w http.ResponseWriter, _ *http.Request, _ map[string]string) { func handler500(w http.ResponseWriter, _ *http.Request, _ map[string]string) { http.Error(w, "500!", http.StatusInternalServerError) } -func handlerDummy(w http.ResponseWriter, _ *http.Request, _ map[string]string) { - w.WriteHeader(http.StatusAccepted) -} diff --git a/docker-compose.yaml b/docker-compose.yaml index 2927629b6a..1657755709 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -117,7 +117,7 @@ services: ports: - "2181:2181" kafka2: - image: wurstmeister/kafka:2.13-2.8.1 + image: darccio/kafka:2.13-2.8.1 environment: KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092