@@ -246,6 +246,112 @@ func TestRouteRedirectTrailingSlash(t *testing.T) {
246246 assert .Equal (t , http .StatusNotFound , w .Code )
247247}
248248
249+ func TestRouteTrailingSlashInsensitivity (t * testing.T ) {
250+ router := New ()
251+ router .RedirectTrailingSlash = false
252+ router .TrailingSlashInsensitivity = true
253+ router .GET ("/path" , func (c * Context ) { c .String (http .StatusOK , "path" ) })
254+ router .GET ("/path2/" , func (c * Context ) { c .String (http .StatusOK , "path2" ) })
255+ router .GET ("/:id" , func (c * Context ) { c .String (http .StatusOK , c .Param ("id" )) })
256+
257+ // Test that trailing slash insensitivity works.
258+ w := PerformRequest (router , http .MethodGet , "/a/" )
259+ assert .Equal (t , http .StatusOK , w .Code )
260+ assert .Equal (t , "a" , w .Body .String ())
261+
262+ w = PerformRequest (router , http .MethodGet , "/path/" )
263+ assert .Equal (t , http .StatusOK , w .Code )
264+ assert .Equal (t , "path" , w .Body .String ())
265+
266+ w = PerformRequest (router , http .MethodGet , "/path" )
267+ assert .Equal (t , http .StatusOK , w .Code )
268+ assert .Equal (t , "path" , w .Body .String ())
269+
270+ w = PerformRequest (router , http .MethodGet , "/path2/" )
271+ assert .Equal (t , http .StatusOK , w .Code )
272+ assert .Equal (t , "path2" , w .Body .String ())
273+
274+ w = PerformRequest (router , http .MethodGet , "/path2" )
275+ assert .Equal (t , http .StatusOK , w .Code )
276+ assert .Equal (t , "path2" , w .Body .String ())
277+
278+ // If handlers for `/path` and `/path/` are different, the request should not be redirected.
279+ router .GET ("/path3" , func (c * Context ) { c .String (http .StatusOK , "path3" ) })
280+ router .GET ("/path3/" , func (c * Context ) { c .String (http .StatusOK , "path3/" ) })
281+
282+ w = PerformRequest (router , http .MethodGet , "/path3" )
283+ assert .Equal (t , http .StatusOK , w .Code )
284+ assert .Equal (t , "path3" , w .Body .String ())
285+
286+ w = PerformRequest (router , http .MethodGet , "/path3/" )
287+ assert .Equal (t , http .StatusOK , w .Code )
288+ assert .Equal (t , "path3/" , w .Body .String ())
289+
290+ // Should no longer match.
291+ router .TrailingSlashInsensitivity = false
292+
293+ w = PerformRequest (router , http .MethodGet , "/path2" )
294+ assert .Equal (t , http .StatusNotFound , w .Code )
295+
296+ w = PerformRequest (router , http .MethodGet , "/path/" )
297+ assert .Equal (t , http .StatusNotFound , w .Code )
298+ }
299+
300+ func BenchmarkRouteTrailingSlashInsensitivity (b * testing.B ) {
301+ b .Run ("Insensitive" , func (b * testing.B ) {
302+ router := New ()
303+ router .RedirectTrailingSlash = false
304+ router .TrailingSlashInsensitivity = true
305+ router .GET ("/path" , func (c * Context ) { c .String (http .StatusOK , "path" ) })
306+
307+ b .ResetTimer ()
308+ b .ReportAllocs ()
309+
310+ for i := 0 ; i < b .N ; i ++ {
311+ // Cause an insensitive match. Test if the retry logic is causing
312+ // slowdowns.
313+ w := PerformRequest (router , http .MethodGet , "/path/" )
314+ if w .Code != http .StatusOK || w .Body .String () != "path" {
315+ b .Fatalf ("Expected status %d, got %d" , http .StatusOK , w .Code )
316+ }
317+ }
318+ })
319+
320+ b .Run ("Exact" , func (b * testing.B ) {
321+ router := New ()
322+ router .RedirectTrailingSlash = false
323+ router .TrailingSlashInsensitivity = false
324+ router .GET ("/path" , func (c * Context ) { c .String (http .StatusOK , "path" ) })
325+
326+ b .ResetTimer ()
327+ b .ReportAllocs ()
328+
329+ for i := 0 ; i < b .N ; i ++ {
330+ w := PerformRequest (router , http .MethodGet , "/path" ) // Exact match.
331+ if w .Code != http .StatusOK || w .Body .String () != "path" {
332+ b .Fatalf ("Expected status %d, got %d" , http .StatusOK , w .Code )
333+ }
334+ }
335+ })
336+
337+ b .Run ("Redirect" , func (b * testing.B ) {
338+ router := New ()
339+ router .RedirectTrailingSlash = true
340+ router .TrailingSlashInsensitivity = false
341+ router .GET ("/path" , func (c * Context ) { c .String (http .StatusOK , "path" ) })
342+
343+ b .ResetTimer ()
344+ b .ReportAllocs ()
345+
346+ for i := 0 ; i < b .N ; i ++ {
347+ w := PerformRequest (router , http .MethodGet , "/path/" ) // Redirect.
348+ if w .Code != http .StatusMovedPermanently {
349+ b .Fatalf ("Expected status %d, got %d" , http .StatusMovedPermanently , w .Code )
350+ }
351+ }
352+ })
353+ }
354+
249355func TestRouteRedirectFixedPath (t * testing.T ) {
250356 router := New ()
251357 router .RedirectFixedPath = true
0 commit comments