Skip to content

Commit

Permalink
Merge pull request #84 from Russman12/feature/case-insensitive-routing
Browse files Browse the repository at this point in the history
Feature/case insensitive routing
  • Loading branch information
dimfeld authored Oct 7, 2021
2 parents b61bfc4 + cff09fa commit ce8677c
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ _testmain.go

*.exe
*.test

.idea/
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ These are the values accepted for RedirectBehavior. You may also add these value
* Redirect308 - RFC7538 Permanent Redirect
* UseHandler - Don't redirect to the canonical path. Just call the handler instead.

### Case Insensitive Routing

You can optionally allow case-insensitive routing by setting the _CaseInsensitive_ property on the router to true.
This allows you to make all routes case-insensitive. For example:
```go
router := httptreemux.New()
router.CaseInsensitive
router.GET("/My-RoUtE", pageHandler)
```
In this example, performing a GET request to /my-route will match the route and execute the _pageHandler_ functionality.
It's important to note that when using case-insensitive routing, the CaseInsensitive property must be set before routes are defined or there may be unexpected side effects.

#### Rationale/Usage
On a POST request, most browsers that receive a 301 will submit a GET request to the redirected URL, meaning that any data will likely be lost. If you want to handle and avoid this behavior, you may use Redirect307, which causes most browsers to resubmit the request using the original method and request body.

Expand Down
4 changes: 4 additions & 0 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ func (g *Group) Handle(method string, path string, handler HandlerFunc) {
func (g *Group) addFullStackHandler(method string, path string, handler HandlerFunc) {
addSlash := false
addOne := func(thePath string) {
if g.mux.CaseInsensitive {
thePath = strings.ToLower(thePath)
}

node := g.mux.root.addPath(thePath[1:], nil, false)
if addSlash {
node.addSlash = true
Expand Down
15 changes: 15 additions & 0 deletions group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ func TestSubGroupEmptyMapping(t *testing.T) {
}
}

func TestGroupCaseInsensitiveRouting(t *testing.T) {
r := New()
r.CaseInsensitive = true
r.NewGroup("/MY-path").GET("", func(w http.ResponseWriter, _ *http.Request, _ map[string]string) {
w.WriteHeader(200)
})

req, _ := http.NewRequest("GET", "/MY-PATH", nil)
recorder := httptest.NewRecorder()
r.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK {
t.Errorf("expected 200 response for case-insensitive request. Received: %d", recorder.Code)
}
}

func TestGroupMethods(t *testing.T) {
for _, scenario := range scenarios {
t.Log(scenario.description)
Expand Down
5 changes: 5 additions & 0 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
)

// The params argument contains the parameters parsed from wildcards and catch-alls in the URL.
Expand Down Expand Up @@ -125,6 +126,10 @@ func (t *TreeMux) lookup(w http.ResponseWriter, r *http.Request) (result LookupR
path = r.URL.Path
pathLen = len(path)
}
if t.CaseInsensitive {
path = strings.ToLower(path)
unescapedPath = strings.ToLower(unescapedPath)
}

trailingSlash := path[pathLen-1] == '/' && pathLen > 1
if trailingSlash && t.RedirectTrailingSlash {
Expand Down
18 changes: 18 additions & 0 deletions router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,24 @@ func testMethods(t *testing.T, newRequest RequestCreator, headCanUseGet bool, us
testMethod("HEAD", "HEAD")
}


func TestCaseInsensitiveRouting(t *testing.T) {
router := New()
// create case-insensitive route
router.CaseInsensitive = true
router.GET("/MY-path", simpleHandler)

w := httptest.NewRecorder()
r, _ := newRequest("GET", "/MY-PATH", nil)
router.ServeHTTP(w, r)

w = httptest.NewRecorder()
router.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Errorf("expected 200 response for case-insensitive request. Received: %d", w.Code)
}
}

func TestNotFound(t *testing.T) {
calledNotFound := false

Expand Down
3 changes: 3 additions & 0 deletions treemux_17.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ type TreeMux struct {
// if you are going to add routes after the router has already begun serving requests. There is a potential
// performance penalty at high load.
SafeAddRoutesWhileRunning bool

// CaseInsensitive determines if routes should be treated as case-insensitive.
CaseInsensitive bool
}

func (t *TreeMux) setDefaultRequestContext(r *http.Request) *http.Request {
Expand Down

0 comments on commit ce8677c

Please sign in to comment.