...

Source file src/github.com/gin-gonic/gin/routes_test.go

Documentation: github.com/gin-gonic/gin

     1  // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
     2  // Use of this source code is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gin
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  type header struct {
    19  	Key   string
    20  	Value string
    21  }
    22  
    23  // PerformRequest for testing gin router.
    24  func PerformRequest(r http.Handler, method, path string, headers ...header) *httptest.ResponseRecorder {
    25  	req := httptest.NewRequest(method, path, nil)
    26  	for _, h := range headers {
    27  		req.Header.Add(h.Key, h.Value)
    28  	}
    29  	w := httptest.NewRecorder()
    30  	r.ServeHTTP(w, req)
    31  	return w
    32  }
    33  
    34  func testRouteOK(method string, t *testing.T) {
    35  	passed := false
    36  	passedAny := false
    37  	r := New()
    38  	r.Any("/test2", func(c *Context) {
    39  		passedAny = true
    40  	})
    41  	r.Handle(method, "/test", func(c *Context) {
    42  		passed = true
    43  	})
    44  
    45  	w := PerformRequest(r, method, "/test")
    46  	assert.True(t, passed)
    47  	assert.Equal(t, http.StatusOK, w.Code)
    48  
    49  	PerformRequest(r, method, "/test2")
    50  	assert.True(t, passedAny)
    51  }
    52  
    53  // TestSingleRouteOK tests that POST route is correctly invoked.
    54  func testRouteNotOK(method string, t *testing.T) {
    55  	passed := false
    56  	router := New()
    57  	router.Handle(method, "/test_2", func(c *Context) {
    58  		passed = true
    59  	})
    60  
    61  	w := PerformRequest(router, method, "/test")
    62  
    63  	assert.False(t, passed)
    64  	assert.Equal(t, http.StatusNotFound, w.Code)
    65  }
    66  
    67  // TestSingleRouteOK tests that POST route is correctly invoked.
    68  func testRouteNotOK2(method string, t *testing.T) {
    69  	passed := false
    70  	router := New()
    71  	router.HandleMethodNotAllowed = true
    72  	var methodRoute string
    73  	if method == http.MethodPost {
    74  		methodRoute = http.MethodGet
    75  	} else {
    76  		methodRoute = http.MethodPost
    77  	}
    78  	router.Handle(methodRoute, "/test", func(c *Context) {
    79  		passed = true
    80  	})
    81  
    82  	w := PerformRequest(router, method, "/test")
    83  
    84  	assert.False(t, passed)
    85  	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
    86  }
    87  
    88  func TestRouterMethod(t *testing.T) {
    89  	router := New()
    90  	router.PUT("/hey2", func(c *Context) {
    91  		c.String(http.StatusOK, "sup2")
    92  	})
    93  
    94  	router.PUT("/hey", func(c *Context) {
    95  		c.String(http.StatusOK, "called")
    96  	})
    97  
    98  	router.PUT("/hey3", func(c *Context) {
    99  		c.String(http.StatusOK, "sup3")
   100  	})
   101  
   102  	w := PerformRequest(router, http.MethodPut, "/hey")
   103  
   104  	assert.Equal(t, http.StatusOK, w.Code)
   105  	assert.Equal(t, "called", w.Body.String())
   106  }
   107  
   108  func TestRouterGroupRouteOK(t *testing.T) {
   109  	testRouteOK(http.MethodGet, t)
   110  	testRouteOK(http.MethodPost, t)
   111  	testRouteOK(http.MethodPut, t)
   112  	testRouteOK(http.MethodPatch, t)
   113  	testRouteOK(http.MethodHead, t)
   114  	testRouteOK(http.MethodOptions, t)
   115  	testRouteOK(http.MethodDelete, t)
   116  	testRouteOK(http.MethodConnect, t)
   117  	testRouteOK(http.MethodTrace, t)
   118  }
   119  
   120  func TestRouteNotOK(t *testing.T) {
   121  	testRouteNotOK(http.MethodGet, t)
   122  	testRouteNotOK(http.MethodPost, t)
   123  	testRouteNotOK(http.MethodPut, t)
   124  	testRouteNotOK(http.MethodPatch, t)
   125  	testRouteNotOK(http.MethodHead, t)
   126  	testRouteNotOK(http.MethodOptions, t)
   127  	testRouteNotOK(http.MethodDelete, t)
   128  	testRouteNotOK(http.MethodConnect, t)
   129  	testRouteNotOK(http.MethodTrace, t)
   130  }
   131  
   132  func TestRouteNotOK2(t *testing.T) {
   133  	testRouteNotOK2(http.MethodGet, t)
   134  	testRouteNotOK2(http.MethodPost, t)
   135  	testRouteNotOK2(http.MethodPut, t)
   136  	testRouteNotOK2(http.MethodPatch, t)
   137  	testRouteNotOK2(http.MethodHead, t)
   138  	testRouteNotOK2(http.MethodOptions, t)
   139  	testRouteNotOK2(http.MethodDelete, t)
   140  	testRouteNotOK2(http.MethodConnect, t)
   141  	testRouteNotOK2(http.MethodTrace, t)
   142  }
   143  
   144  func TestRouteRedirectTrailingSlash(t *testing.T) {
   145  	router := New()
   146  	router.RedirectFixedPath = false
   147  	router.RedirectTrailingSlash = true
   148  	router.GET("/path", func(c *Context) {})
   149  	router.GET("/path2/", func(c *Context) {})
   150  	router.POST("/path3", func(c *Context) {})
   151  	router.PUT("/path4/", func(c *Context) {})
   152  
   153  	w := PerformRequest(router, http.MethodGet, "/path/")
   154  	assert.Equal(t, "/path", w.Header().Get("Location"))
   155  	assert.Equal(t, http.StatusMovedPermanently, w.Code)
   156  
   157  	w = PerformRequest(router, http.MethodGet, "/path2")
   158  	assert.Equal(t, "/path2/", w.Header().Get("Location"))
   159  	assert.Equal(t, http.StatusMovedPermanently, w.Code)
   160  
   161  	w = PerformRequest(router, http.MethodPost, "/path3/")
   162  	assert.Equal(t, "/path3", w.Header().Get("Location"))
   163  	assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
   164  
   165  	w = PerformRequest(router, http.MethodPut, "/path4")
   166  	assert.Equal(t, "/path4/", w.Header().Get("Location"))
   167  	assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
   168  
   169  	w = PerformRequest(router, http.MethodGet, "/path")
   170  	assert.Equal(t, http.StatusOK, w.Code)
   171  
   172  	w = PerformRequest(router, http.MethodGet, "/path2/")
   173  	assert.Equal(t, http.StatusOK, w.Code)
   174  
   175  	w = PerformRequest(router, http.MethodPost, "/path3")
   176  	assert.Equal(t, http.StatusOK, w.Code)
   177  
   178  	w = PerformRequest(router, http.MethodPut, "/path4/")
   179  	assert.Equal(t, http.StatusOK, w.Code)
   180  
   181  	w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"})
   182  	assert.Equal(t, "/api/path2/", w.Header().Get("Location"))
   183  	assert.Equal(t, 301, w.Code)
   184  
   185  	w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"})
   186  	assert.Equal(t, 200, w.Code)
   187  
   188  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api#?"})
   189  	assert.Equal(t, "/api/path", w.Header().Get("Location"))
   190  	assert.Equal(t, 301, w.Code)
   191  
   192  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api"})
   193  	assert.Equal(t, "/api/path", w.Header().Get("Location"))
   194  	assert.Equal(t, 301, w.Code)
   195  
   196  	w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../api"})
   197  	assert.Equal(t, "/api/path2/", w.Header().Get("Location"))
   198  	assert.Equal(t, 301, w.Code)
   199  
   200  	w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../api"})
   201  	assert.Equal(t, "/api/path2/", w.Header().Get("Location"))
   202  	assert.Equal(t, 301, w.Code)
   203  
   204  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../"})
   205  	assert.Equal(t, "//path", w.Header().Get("Location"))
   206  	assert.Equal(t, 301, w.Code)
   207  
   208  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../../"})
   209  	assert.Equal(t, "/path", w.Header().Get("Location"))
   210  	assert.Equal(t, 301, w.Code)
   211  
   212  	w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../gin-gonic.com"})
   213  	assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location"))
   214  	assert.Equal(t, 301, w.Code)
   215  
   216  	w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../gin-gonic.com"})
   217  	assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location"))
   218  	assert.Equal(t, 301, w.Code)
   219  
   220  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "https://gin-gonic.com/#"})
   221  	assert.Equal(t, "https/gin-goniccom/https/gin-goniccom/path", w.Header().Get("Location"))
   222  	assert.Equal(t, 301, w.Code)
   223  
   224  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#api"})
   225  	assert.Equal(t, "api/api/path", w.Header().Get("Location"))
   226  	assert.Equal(t, 301, w.Code)
   227  
   228  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/#?a=1"})
   229  	assert.Equal(t, "/nor-mal/a1/path", w.Header().Get("Location"))
   230  	assert.Equal(t, 301, w.Code)
   231  
   232  	w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/%2e%2e/"})
   233  	assert.Equal(t, "/nor-mal/2e2e/path", w.Header().Get("Location"))
   234  	assert.Equal(t, 301, w.Code)
   235  
   236  	router.RedirectTrailingSlash = false
   237  
   238  	w = PerformRequest(router, http.MethodGet, "/path/")
   239  	assert.Equal(t, http.StatusNotFound, w.Code)
   240  	w = PerformRequest(router, http.MethodGet, "/path2")
   241  	assert.Equal(t, http.StatusNotFound, w.Code)
   242  	w = PerformRequest(router, http.MethodPost, "/path3/")
   243  	assert.Equal(t, http.StatusNotFound, w.Code)
   244  	w = PerformRequest(router, http.MethodPut, "/path4")
   245  	assert.Equal(t, http.StatusNotFound, w.Code)
   246  }
   247  
   248  func TestRouteRedirectFixedPath(t *testing.T) {
   249  	router := New()
   250  	router.RedirectFixedPath = true
   251  	router.RedirectTrailingSlash = false
   252  
   253  	router.GET("/path", func(c *Context) {})
   254  	router.GET("/Path2", func(c *Context) {})
   255  	router.POST("/PATH3", func(c *Context) {})
   256  	router.POST("/Path4/", func(c *Context) {})
   257  
   258  	w := PerformRequest(router, http.MethodGet, "/PATH")
   259  	assert.Equal(t, "/path", w.Header().Get("Location"))
   260  	assert.Equal(t, http.StatusMovedPermanently, w.Code)
   261  
   262  	w = PerformRequest(router, http.MethodGet, "/path2")
   263  	assert.Equal(t, "/Path2", w.Header().Get("Location"))
   264  	assert.Equal(t, http.StatusMovedPermanently, w.Code)
   265  
   266  	w = PerformRequest(router, http.MethodPost, "/path3")
   267  	assert.Equal(t, "/PATH3", w.Header().Get("Location"))
   268  	assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
   269  
   270  	w = PerformRequest(router, http.MethodPost, "/path4")
   271  	assert.Equal(t, "/Path4/", w.Header().Get("Location"))
   272  	assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
   273  }
   274  
   275  // TestContextParamsGet tests that a parameter can be parsed from the URL.
   276  func TestRouteParamsByName(t *testing.T) {
   277  	name := ""
   278  	lastName := ""
   279  	wild := ""
   280  	router := New()
   281  	router.GET("/test/:name/:last_name/*wild", func(c *Context) {
   282  		name = c.Params.ByName("name")
   283  		lastName = c.Params.ByName("last_name")
   284  		var ok bool
   285  		wild, ok = c.Params.Get("wild")
   286  
   287  		assert.True(t, ok)
   288  		assert.Equal(t, name, c.Param("name"))
   289  		assert.Equal(t, lastName, c.Param("last_name"))
   290  
   291  		assert.Empty(t, c.Param("wtf"))
   292  		assert.Empty(t, c.Params.ByName("wtf"))
   293  
   294  		wtf, ok := c.Params.Get("wtf")
   295  		assert.Empty(t, wtf)
   296  		assert.False(t, ok)
   297  	})
   298  
   299  	w := PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great")
   300  
   301  	assert.Equal(t, http.StatusOK, w.Code)
   302  	assert.Equal(t, "john", name)
   303  	assert.Equal(t, "smith", lastName)
   304  	assert.Equal(t, "/is/super/great", wild)
   305  }
   306  
   307  // TestContextParamsGet tests that a parameter can be parsed from the URL even with extra slashes.
   308  func TestRouteParamsByNameWithExtraSlash(t *testing.T) {
   309  	name := ""
   310  	lastName := ""
   311  	wild := ""
   312  	router := New()
   313  	router.RemoveExtraSlash = true
   314  	router.GET("/test/:name/:last_name/*wild", func(c *Context) {
   315  		name = c.Params.ByName("name")
   316  		lastName = c.Params.ByName("last_name")
   317  		var ok bool
   318  		wild, ok = c.Params.Get("wild")
   319  
   320  		assert.True(t, ok)
   321  		assert.Equal(t, name, c.Param("name"))
   322  		assert.Equal(t, lastName, c.Param("last_name"))
   323  
   324  		assert.Empty(t, c.Param("wtf"))
   325  		assert.Empty(t, c.Params.ByName("wtf"))
   326  
   327  		wtf, ok := c.Params.Get("wtf")
   328  		assert.Empty(t, wtf)
   329  		assert.False(t, ok)
   330  	})
   331  
   332  	w := PerformRequest(router, http.MethodGet, "//test//john//smith//is//super//great")
   333  
   334  	assert.Equal(t, http.StatusOK, w.Code)
   335  	assert.Equal(t, "john", name)
   336  	assert.Equal(t, "smith", lastName)
   337  	assert.Equal(t, "/is/super/great", wild)
   338  }
   339  
   340  // TestHandleStaticFile - ensure the static file handles properly
   341  func TestRouteStaticFile(t *testing.T) {
   342  	// SETUP file
   343  	testRoot, _ := os.Getwd()
   344  	f, err := os.CreateTemp(testRoot, "")
   345  	if err != nil {
   346  		t.Error(err)
   347  	}
   348  	defer os.Remove(f.Name())
   349  	_, err = f.WriteString("Gin Web Framework")
   350  	assert.NoError(t, err)
   351  	f.Close()
   352  
   353  	dir, filename := filepath.Split(f.Name())
   354  
   355  	// SETUP gin
   356  	router := New()
   357  	router.Static("/using_static", dir)
   358  	router.StaticFile("/result", f.Name())
   359  
   360  	w := PerformRequest(router, http.MethodGet, "/using_static/"+filename)
   361  	w2 := PerformRequest(router, http.MethodGet, "/result")
   362  
   363  	assert.Equal(t, w, w2)
   364  	assert.Equal(t, http.StatusOK, w.Code)
   365  	assert.Equal(t, "Gin Web Framework", w.Body.String())
   366  	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
   367  
   368  	w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename)
   369  	w4 := PerformRequest(router, http.MethodHead, "/result")
   370  
   371  	assert.Equal(t, w3, w4)
   372  	assert.Equal(t, http.StatusOK, w3.Code)
   373  }
   374  
   375  // TestHandleStaticFile - ensure the static file handles properly
   376  func TestRouteStaticFileFS(t *testing.T) {
   377  	// SETUP file
   378  	testRoot, _ := os.Getwd()
   379  	f, err := os.CreateTemp(testRoot, "")
   380  	if err != nil {
   381  		t.Error(err)
   382  	}
   383  	defer os.Remove(f.Name())
   384  	_, err = f.WriteString("Gin Web Framework")
   385  	assert.NoError(t, err)
   386  	f.Close()
   387  
   388  	dir, filename := filepath.Split(f.Name())
   389  	// SETUP gin
   390  	router := New()
   391  	router.Static("/using_static", dir)
   392  	router.StaticFileFS("/result_fs", filename, Dir(dir, false))
   393  
   394  	w := PerformRequest(router, http.MethodGet, "/using_static/"+filename)
   395  	w2 := PerformRequest(router, http.MethodGet, "/result_fs")
   396  
   397  	assert.Equal(t, w, w2)
   398  	assert.Equal(t, http.StatusOK, w.Code)
   399  	assert.Equal(t, "Gin Web Framework", w.Body.String())
   400  	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
   401  
   402  	w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename)
   403  	w4 := PerformRequest(router, http.MethodHead, "/result_fs")
   404  
   405  	assert.Equal(t, w3, w4)
   406  	assert.Equal(t, http.StatusOK, w3.Code)
   407  }
   408  
   409  // TestHandleStaticDir - ensure the root/sub dir handles properly
   410  func TestRouteStaticListingDir(t *testing.T) {
   411  	router := New()
   412  	router.StaticFS("/", Dir("./", true))
   413  
   414  	w := PerformRequest(router, http.MethodGet, "/")
   415  
   416  	assert.Equal(t, http.StatusOK, w.Code)
   417  	assert.Contains(t, w.Body.String(), "gin.go")
   418  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   419  }
   420  
   421  // TestHandleHeadToDir - ensure the root/sub dir handles properly
   422  func TestRouteStaticNoListing(t *testing.T) {
   423  	router := New()
   424  	router.Static("/", "./")
   425  
   426  	w := PerformRequest(router, http.MethodGet, "/")
   427  
   428  	assert.Equal(t, http.StatusNotFound, w.Code)
   429  	assert.NotContains(t, w.Body.String(), "gin.go")
   430  }
   431  
   432  func TestRouterMiddlewareAndStatic(t *testing.T) {
   433  	router := New()
   434  	static := router.Group("/", func(c *Context) {
   435  		c.Writer.Header().Add("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST")
   436  		c.Writer.Header().Add("Expires", "Mon, 02 Jan 2006 15:04:05 MST")
   437  		c.Writer.Header().Add("X-GIN", "Gin Framework")
   438  	})
   439  	static.Static("/", "./")
   440  
   441  	w := PerformRequest(router, http.MethodGet, "/gin.go")
   442  
   443  	assert.Equal(t, http.StatusOK, w.Code)
   444  	assert.Contains(t, w.Body.String(), "package gin")
   445  	// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
   446  	// else, Content-Type='text/x-go; charset=utf-8'
   447  	assert.NotEqual(t, "", w.Header().Get("Content-Type"))
   448  	assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
   449  	assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
   450  	assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
   451  }
   452  
   453  func TestRouteNotAllowedEnabled(t *testing.T) {
   454  	router := New()
   455  	router.HandleMethodNotAllowed = true
   456  	router.POST("/path", func(c *Context) {})
   457  	w := PerformRequest(router, http.MethodGet, "/path")
   458  	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
   459  
   460  	router.NoMethod(func(c *Context) {
   461  		c.String(http.StatusTeapot, "responseText")
   462  	})
   463  	w = PerformRequest(router, http.MethodGet, "/path")
   464  	assert.Equal(t, "responseText", w.Body.String())
   465  	assert.Equal(t, http.StatusTeapot, w.Code)
   466  }
   467  
   468  func TestRouteNotAllowedEnabled2(t *testing.T) {
   469  	router := New()
   470  	router.HandleMethodNotAllowed = true
   471  	// add one methodTree to trees
   472  	router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}})
   473  	router.GET("/path2", func(c *Context) {})
   474  	w := PerformRequest(router, http.MethodPost, "/path2")
   475  	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
   476  }
   477  
   478  func TestRouteNotAllowedDisabled(t *testing.T) {
   479  	router := New()
   480  	router.HandleMethodNotAllowed = false
   481  	router.POST("/path", func(c *Context) {})
   482  	w := PerformRequest(router, http.MethodGet, "/path")
   483  	assert.Equal(t, http.StatusNotFound, w.Code)
   484  
   485  	router.NoMethod(func(c *Context) {
   486  		c.String(http.StatusTeapot, "responseText")
   487  	})
   488  	w = PerformRequest(router, http.MethodGet, "/path")
   489  	assert.Equal(t, "404 page not found", w.Body.String())
   490  	assert.Equal(t, http.StatusNotFound, w.Code)
   491  }
   492  
   493  func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) {
   494  	router := New()
   495  	router.RemoveExtraSlash = true
   496  	router.GET("/path", func(c *Context) {})
   497  	router.GET("/", func(c *Context) {})
   498  
   499  	testRoutes := []struct {
   500  		route    string
   501  		code     int
   502  		location string
   503  	}{
   504  		{"/../path", http.StatusOK, ""},    // CleanPath
   505  		{"/nope", http.StatusNotFound, ""}, // NotFound
   506  	}
   507  	for _, tr := range testRoutes {
   508  		w := PerformRequest(router, "GET", tr.route)
   509  		assert.Equal(t, tr.code, w.Code)
   510  		if w.Code != http.StatusNotFound {
   511  			assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
   512  		}
   513  	}
   514  }
   515  
   516  func TestRouterNotFound(t *testing.T) {
   517  	router := New()
   518  	router.RedirectFixedPath = true
   519  	router.GET("/path", func(c *Context) {})
   520  	router.GET("/dir/", func(c *Context) {})
   521  	router.GET("/", func(c *Context) {})
   522  
   523  	testRoutes := []struct {
   524  		route    string
   525  		code     int
   526  		location string
   527  	}{
   528  		{"/path/", http.StatusMovedPermanently, "/path"},   // TSR -/
   529  		{"/dir", http.StatusMovedPermanently, "/dir/"},     // TSR +/
   530  		{"/PATH", http.StatusMovedPermanently, "/path"},    // Fixed Case
   531  		{"/DIR/", http.StatusMovedPermanently, "/dir/"},    // Fixed Case
   532  		{"/PATH/", http.StatusMovedPermanently, "/path"},   // Fixed Case -/
   533  		{"/DIR", http.StatusMovedPermanently, "/dir/"},     // Fixed Case +/
   534  		{"/../path", http.StatusMovedPermanently, "/path"}, // Without CleanPath
   535  		{"/nope", http.StatusNotFound, ""},                 // NotFound
   536  	}
   537  	for _, tr := range testRoutes {
   538  		w := PerformRequest(router, http.MethodGet, tr.route)
   539  		assert.Equal(t, tr.code, w.Code)
   540  		if w.Code != http.StatusNotFound {
   541  			assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
   542  		}
   543  	}
   544  
   545  	// Test custom not found handler
   546  	var notFound bool
   547  	router.NoRoute(func(c *Context) {
   548  		c.AbortWithStatus(http.StatusNotFound)
   549  		notFound = true
   550  	})
   551  	w := PerformRequest(router, http.MethodGet, "/nope")
   552  	assert.Equal(t, http.StatusNotFound, w.Code)
   553  	assert.True(t, notFound)
   554  
   555  	// Test other method than GET (want 307 instead of 301)
   556  	router.PATCH("/path", func(c *Context) {})
   557  	w = PerformRequest(router, http.MethodPatch, "/path/")
   558  	assert.Equal(t, http.StatusTemporaryRedirect, w.Code)
   559  	assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header()))
   560  
   561  	// Test special case where no node for the prefix "/" exists
   562  	router = New()
   563  	router.GET("/a", func(c *Context) {})
   564  	w = PerformRequest(router, http.MethodGet, "/")
   565  	assert.Equal(t, http.StatusNotFound, w.Code)
   566  
   567  	// Reproduction test for the bug of issue #2843
   568  	router = New()
   569  	router.NoRoute(func(c *Context) {
   570  		if c.Request.RequestURI == "/login" {
   571  			c.String(200, "login")
   572  		}
   573  	})
   574  	router.GET("/logout", func(c *Context) {
   575  		c.String(200, "logout")
   576  	})
   577  	w = PerformRequest(router, http.MethodGet, "/login")
   578  	assert.Equal(t, "login", w.Body.String())
   579  	w = PerformRequest(router, http.MethodGet, "/logout")
   580  	assert.Equal(t, "logout", w.Body.String())
   581  }
   582  
   583  func TestRouterStaticFSNotFound(t *testing.T) {
   584  	router := New()
   585  	router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/")))
   586  	router.NoRoute(func(c *Context) {
   587  		c.String(404, "non existent")
   588  	})
   589  
   590  	w := PerformRequest(router, http.MethodGet, "/nonexistent")
   591  	assert.Equal(t, "non existent", w.Body.String())
   592  
   593  	w = PerformRequest(router, http.MethodHead, "/nonexistent")
   594  	assert.Equal(t, "non existent", w.Body.String())
   595  }
   596  
   597  func TestRouterStaticFSFileNotFound(t *testing.T) {
   598  	router := New()
   599  
   600  	router.StaticFS("/", http.FileSystem(http.Dir(".")))
   601  
   602  	assert.NotPanics(t, func() {
   603  		PerformRequest(router, http.MethodGet, "/nonexistent")
   604  	})
   605  }
   606  
   607  // Reproduction test for the bug of issue #1805
   608  func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) {
   609  	router := New()
   610  
   611  	// Middleware must be called just only once by per request.
   612  	middlewareCalledNum := 0
   613  	router.Use(func(c *Context) {
   614  		middlewareCalledNum++
   615  	})
   616  
   617  	router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/")))
   618  
   619  	// First access
   620  	PerformRequest(router, http.MethodGet, "/nonexistent")
   621  	assert.Equal(t, 1, middlewareCalledNum)
   622  
   623  	// Second access
   624  	PerformRequest(router, http.MethodHead, "/nonexistent")
   625  	assert.Equal(t, 2, middlewareCalledNum)
   626  }
   627  
   628  func TestRouteRawPath(t *testing.T) {
   629  	route := New()
   630  	route.UseRawPath = true
   631  
   632  	route.POST("/project/:name/build/:num", func(c *Context) {
   633  		name := c.Params.ByName("name")
   634  		num := c.Params.ByName("num")
   635  
   636  		assert.Equal(t, name, c.Param("name"))
   637  		assert.Equal(t, num, c.Param("num"))
   638  
   639  		assert.Equal(t, "Some/Other/Project", name)
   640  		assert.Equal(t, "222", num)
   641  	})
   642  
   643  	w := PerformRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/222")
   644  	assert.Equal(t, http.StatusOK, w.Code)
   645  }
   646  
   647  func TestRouteRawPathNoUnescape(t *testing.T) {
   648  	route := New()
   649  	route.UseRawPath = true
   650  	route.UnescapePathValues = false
   651  
   652  	route.POST("/project/:name/build/:num", func(c *Context) {
   653  		name := c.Params.ByName("name")
   654  		num := c.Params.ByName("num")
   655  
   656  		assert.Equal(t, name, c.Param("name"))
   657  		assert.Equal(t, num, c.Param("num"))
   658  
   659  		assert.Equal(t, "Some%2FOther%2FProject", name)
   660  		assert.Equal(t, "333", num)
   661  	})
   662  
   663  	w := PerformRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/333")
   664  	assert.Equal(t, http.StatusOK, w.Code)
   665  }
   666  
   667  func TestRouteServeErrorWithWriteHeader(t *testing.T) {
   668  	route := New()
   669  	route.Use(func(c *Context) {
   670  		c.Status(421)
   671  		c.Next()
   672  	})
   673  
   674  	w := PerformRequest(route, http.MethodGet, "/NotFound")
   675  	assert.Equal(t, 421, w.Code)
   676  	assert.Equal(t, 0, w.Body.Len())
   677  }
   678  
   679  func TestRouteContextHoldsFullPath(t *testing.T) {
   680  	router := New()
   681  
   682  	// Test routes
   683  	routes := []string{
   684  		"/simple",
   685  		"/project/:name",
   686  		"/",
   687  		"/news/home",
   688  		"/news",
   689  		"/simple-two/one",
   690  		"/simple-two/one-two",
   691  		"/project/:name/build/*params",
   692  		"/project/:name/bui",
   693  		"/user/:id/status",
   694  		"/user/:id",
   695  		"/user/:id/profile",
   696  	}
   697  
   698  	for _, route := range routes {
   699  		actualRoute := route
   700  		router.GET(route, func(c *Context) {
   701  			// For each defined route context should contain its full path
   702  			assert.Equal(t, actualRoute, c.FullPath())
   703  			c.AbortWithStatus(http.StatusOK)
   704  		})
   705  	}
   706  
   707  	for _, route := range routes {
   708  		w := PerformRequest(router, http.MethodGet, route)
   709  		assert.Equal(t, http.StatusOK, w.Code)
   710  	}
   711  
   712  	// Test not found
   713  	router.Use(func(c *Context) {
   714  		// For not found routes full path is empty
   715  		assert.Equal(t, "", c.FullPath())
   716  	})
   717  
   718  	w := PerformRequest(router, http.MethodGet, "/not-found")
   719  	assert.Equal(t, http.StatusNotFound, w.Code)
   720  }
   721  
   722  func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) {
   723  	r := New()
   724  	r.HandleMethodNotAllowed = true
   725  
   726  	base := r.Group("base")
   727  	base.GET("/metrics", handlerTest1)
   728  
   729  	v1 := base.Group("v1")
   730  
   731  	v1.GET("/:id/devices", handlerTest1)
   732  	v1.GET("/user/:id/groups", handlerTest1)
   733  
   734  	v1.GET("/orgs/:id", handlerTest1)
   735  	v1.DELETE("/orgs/:id", handlerTest1)
   736  
   737  	w := PerformRequest(r, "GET", "/base/v1/user/groups")
   738  	assert.Equal(t, http.StatusNotFound, w.Code)
   739  }
   740  

View as plain text