From cfd8d8d26fd7fe6b8572778d7dbe5fcf9c66cfe3 Mon Sep 17 00:00:00 2001 From: Loren Hale Date: Thu, 27 Jul 2017 11:22:55 -0700 Subject: [PATCH] add UseRawPath option --- router.go | 12 ++++++++++++ router_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ tree.go | 9 ++++++++- tree_test.go | 2 ++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index bfd4db04..0efd0f6e 100644 --- a/router.go +++ b/router.go @@ -159,6 +159,13 @@ type Router struct { // The handler can be used to keep your server from crashing because of // unrecovered panics. PanicHandler func(http.ResponseWriter, *http.Request, interface{}) + + // Go 1.5 introduced the RawPath field in net/url to hold the encoded form of Path. + // The Parse function sets both Path and RawPath in the URL it returns, + // and URL's String method uses RawPath if it is a valid encoding of Path, + // by calling the EncodedPath method. + // This tells the router to use the request.URL.RawPath when parsing the path. + UseRawPath bool } // Make sure the Router conforms with the http.Handler interface @@ -172,6 +179,7 @@ func New() *Router { RedirectFixedPath: true, HandleMethodNotAllowed: true, HandleOPTIONS: true, + UseRawPath: false, } } @@ -329,6 +337,10 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { path := req.URL.Path + if r.UseRawPath && req.URL.RawPath != "" { + path = req.URL.RawPath + } + if root := r.trees[req.Method]; root != nil { if handle, ps, tsr := root.getValue(path); handle != nil { handle(w, req, ps) diff --git a/router_test.go b/router_test.go index b774a4ea..51b6a93d 100644 --- a/router_test.go +++ b/router_test.go @@ -528,3 +528,48 @@ func TestRouterServeFiles(t *testing.T) { t.Error("serving file failed") } } + +func TestRouterUseRawPathSuccess(t *testing.T) { + router := New() + router.UseRawPath = true + + routed := false + router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) { + routed = true + want := Params{Param{"name", "abc/123"}} + if !reflect.DeepEqual(ps, want) { + t.Fatalf("wrong wildcard values: want %v, got %v", want, ps) + } + }) + + w := new(mockResponseWriter) + + req, _ := http.NewRequest("GET", "/user/abc%2F123", nil) + router.ServeHTTP(w, req) + + if !routed { + t.Fatal("routing failed") + } +} + +func TestRouterUseRawPathFailure(t *testing.T) { + router := New() + + routed := false + router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) { + routed = true + want := Params{Param{"name", "abc/123"}} + if !reflect.DeepEqual(ps, want) { + t.Fatalf("wrong wildcard values: want %v, got %v", want, ps) + } + }) + + w := new(mockResponseWriter) + + req, _ := http.NewRequest("GET", "/user/abc%2F123", nil) + router.ServeHTTP(w, req) + + if routed { + t.Fatal("routing unexpectedly succeeded") + } +} diff --git a/tree.go b/tree.go index a8fa98b0..b9c45509 100644 --- a/tree.go +++ b/tree.go @@ -5,6 +5,7 @@ package httprouter import ( + "net/url" "strings" "unicode" "unicode/utf8" @@ -371,7 +372,13 @@ walk: // outer loop for walking the tree i := len(p) p = p[:i+1] // expand slice within preallocated capacity p[i].Key = n.path[1:] - p[i].Value = path[:end] + + value, err := url.PathUnescape(path[:end]) + if err != nil { + p[i].Value = path[:end] + } else { + p[i].Value = value + } // we need to go deeper! if end < len(path) { diff --git a/tree_test.go b/tree_test.go index e89d9452..b6e9105b 100644 --- a/tree_test.go +++ b/tree_test.go @@ -193,6 +193,8 @@ func TestTreeWildcard(t *testing.T) { {"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}}, {"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}}, {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}}, + {"/search/something%2Fencoded", false, "/search/:query", Params{Param{"query", "something/encoded"}}}, + {"/search/invalid%encoding", false, "/search/:query", Params{Param{"query", "invalid%encoding"}}}, }) checkPriorities(t, tree)