From b51def0bb82322f8993582b2edb1a94b6429c4bb Mon Sep 17 00:00:00 2001 From: ReneWerner87 <rene@gofiber.io> Date: Wed, 6 Jan 2021 13:21:54 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20UnescapePath=20not=20working=20#?= =?UTF-8?q?1102?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fenny <fenny@gofiber.io> --- app_test.go | 22 ++++++++++++++++ ctx.go | 73 +++++++++++++++++++++++++++++------------------------ helpers.go | 2 +- path.go | 16 ++++++------ router.go | 26 +++++++++---------- 5 files changed, 84 insertions(+), 55 deletions(-) diff --git a/app_test.go b/app_test.go index bf1c1999..5fb3ca4a 100644 --- a/app_test.go +++ b/app_test.go @@ -314,6 +314,28 @@ func Test_App_Use_Params(t *testing.T) { }) } +func Test_App_Use_UnescapedPath(t *testing.T) { + app := New(Config{UnescapePath: true, CaseSensitive: true}) + + app.Use("/cRéeR/:param", func(c *Ctx) error { + return c.SendString(c.Params("param")) + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/cR%C3%A9eR/%D9%85%D8%AD%D9%85%D8%AF", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + body, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err, "app.Test(req)") + // check the param result + utils.AssertEqual(t, "محمد", getString(body)) + + // with lowercase letters + resp, err = app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er/%D9%85%D8%AD%D9%85%D8%AF", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") +} + func Test_App_Add_Method_Test(t *testing.T) { app := New() defer func() { diff --git a/ctx.go b/ctx.go index 961e0b7e..649e1b80 100644 --- a/ctx.go +++ b/ctx.go @@ -33,20 +33,22 @@ const maxParams = 30 // Ctx represents the Context which hold the HTTP request and response. // It has methods for the request query string, parameters, body, HTTP headers and so on. type Ctx struct { - app *App // Reference to *App - route *Route // Reference to *Route - indexRoute int // Index of the current route - indexHandler int // Index of the current handler - method string // HTTP method - methodINT int // HTTP method INT equivalent - baseURI string // HTTP base uri - path string // Prettified HTTP path -> string copy from pathBuffer - pathBuffer []byte // Prettified HTTP path buffer - treePath string // Path for the search in the tree - pathOriginal string // Original HTTP path - values [maxParams]string // Route parameter values - fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx - matched bool // Non use route matched + app *App // Reference to *App + route *Route // Reference to *Route + indexRoute int // Index of the current route + indexHandler int // Index of the current handler + method string // HTTP method + methodINT int // HTTP method INT equivalent + baseURI string // HTTP base uri + path string // Prettified HTTP path for the user -> string copy from pathBuffer + pathBuffer []byte // HTTP path buffer + detectionPath string // Route detection path -> string copy from pathBuffer + detectionPathBuffer []byte // HTTP path detectionPath + treePath string // Path for the search in the tree + pathOriginal string // Original HTTP path + values [maxParams]string // Route parameter values + fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx + matched bool // Non use route matched } // Range data for c.Range @@ -88,7 +90,6 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { // Reset matched flag c.matched = false // Set paths - c.pathBuffer = append(c.pathBuffer[0:0], fctx.URI().PathOriginal()...) c.pathOriginal = getString(fctx.URI().PathOriginal()) // Set method c.method = getString(fctx.Request.Header.Method()) @@ -98,7 +99,7 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { // reset base uri c.baseURI = "" // Prettify path - c.prettifyPath() + c.configDependentPaths() return c } @@ -674,15 +675,12 @@ func (c *Ctx) Params(key string, defaultValue ...string) string { func (c *Ctx) Path(override ...string) string { if len(override) != 0 && c.path != override[0] { // Set new path to context - c.pathBuffer = append(c.pathBuffer[0:0], override[0]...) c.pathOriginal = override[0] - // c.path = override[0] - // c.pathOriginal = c.path // Set new path to request context c.fasthttp.Request.URI().SetPath(c.pathOriginal) // Prettify path - c.prettifyPath() + c.configDependentPaths() } return c.pathOriginal } @@ -1105,24 +1103,33 @@ func (c *Ctx) XHR() bool { return utils.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest")) } -// prettifyPath ... -func (c *Ctx) prettifyPath() { - // If UnescapePath enabled, we decode the path +// configDependentPaths set paths for route recognition and prepared paths for the user, +// here the features for caseSensitive, decoded paths, strict paths are evaluated +func (c *Ctx) configDependentPaths() { + c.pathBuffer = append(c.pathBuffer[0:0], c.pathOriginal...) + // If UnescapePath enabled, we decode the path and save it for the framework user if c.app.config.UnescapePath { c.pathBuffer = fasthttp.AppendUnquotedArg(c.pathBuffer[:0], c.pathBuffer) } - // If CaseSensitive is disabled, we lowercase the original path - if !c.app.config.CaseSensitive { - c.pathBuffer = utils.ToLowerBytes(c.pathBuffer) - } - // If StrictRouting is disabled, we strip all trailing slashes - if !c.app.config.StrictRouting && len(c.pathBuffer) > 1 && c.pathBuffer[len(c.pathBuffer)-1] == '/' { - c.pathBuffer = utils.TrimRightBytes(c.pathBuffer, '/') - } c.path = getString(c.pathBuffer) + // another path is specified which is for routing recognition only + // use the path that was changed by the previous configuration flags + c.detectionPathBuffer = append(c.detectionPathBuffer[0:0], c.pathBuffer...) + // If CaseSensitive is disabled, we lowercase the original path + if !c.app.config.CaseSensitive { + c.detectionPathBuffer = utils.ToLowerBytes(c.detectionPathBuffer) + } + // If StrictRouting is disabled, we strip all trailing slashes + if !c.app.config.StrictRouting && len(c.detectionPathBuffer) > 1 && c.detectionPathBuffer[len(c.detectionPathBuffer)-1] == '/' { + c.detectionPathBuffer = utils.TrimRightBytes(c.detectionPathBuffer, '/') + } + c.detectionPath = getString(c.detectionPathBuffer) + + // Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed, + // since the first three characters area select a list of routes c.treePath = c.treePath[0:0] - if len(c.path) >= 3 { - c.treePath = c.path[:3] + if len(c.detectionPath) >= 3 { + c.treePath = c.detectionPath[:3] } } diff --git a/helpers.go b/helpers.go index ee973f36..392f9e6b 100644 --- a/helpers.go +++ b/helpers.go @@ -120,7 +120,7 @@ func methodExist(ctx *Ctx) (exist bool) { continue } // Check if it matches the request path - match := route.match(ctx.path, ctx.pathOriginal, &ctx.values) + match := route.match(ctx.detectionPath, ctx.path, &ctx.values) // No match, next route if match { // We matched diff --git a/path.go b/path.go index c44f058a..3a5fdd46 100644 --- a/path.go +++ b/path.go @@ -227,37 +227,37 @@ func findNextCharsetPosition(search string, charset []byte) int { } // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions -func (routeParser *routeParser) getMatch(s, original string, params *[maxParams]string, partialCheck bool) bool { +func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { var i, paramsIterator, partLen int for _, segment := range routeParser.segs { - partLen = len(s) + partLen = len(detectionPath) // check const segment if !segment.IsParam { i = segment.Length // is optional part or the const part must match with the given string // check if the end of the segment is a optional slash - if segment.HasOptionalSlash && partLen == i-1 && s == segment.Const[:i-1] { + if segment.HasOptionalSlash && partLen == i-1 && detectionPath == segment.Const[:i-1] { i-- - } else if !(i <= partLen && s[:i] == segment.Const) { + } else if !(i <= partLen && detectionPath[:i] == segment.Const) { return false } } else { // determine parameter length - i = findParamLen(s, segment) + i = findParamLen(detectionPath, segment) if !segment.IsOptional && i == 0 { return false } // take over the params positions - params[paramsIterator] = original[:i] + params[paramsIterator] = path[:i] paramsIterator++ } // reduce founded part from the string if partLen > 0 { - s, original = s[i:], original[i:] + detectionPath, path = detectionPath[i:], path[i:] } } - if len(s) != 0 && !partialCheck { + if len(detectionPath) != 0 && !partialCheck { return false } diff --git a/router.go b/router.go index 5b7614f2..a1f2ad1b 100644 --- a/router.go +++ b/router.go @@ -55,14 +55,14 @@ type Route struct { Handlers []Handler `json:"-"` // Ctx handlers } -func (r *Route) match(path, original string, params *[maxParams]string) (match bool) { - // root path check - if r.root && path == "/" { +func (r *Route) match(detectionPath, path string, params *[maxParams]string) (match bool) { + // root detectionPath check + if r.root && detectionPath == "/" { return true - // '*' wildcard matches any path + // '*' wildcard matches any detectionPath } else if r.star { - if len(original) > 1 { - params[0] = original[1:] + if len(path) > 1 { + params[0] = path[1:] } else { params[0] = "" } @@ -71,19 +71,19 @@ func (r *Route) match(path, original string, params *[maxParams]string) (match b // Does this route have parameters if len(r.Params) > 0 { // Match params - if match := r.routeParser.getMatch(path, original, params, r.use); match { - // Get params from the original path + if match := r.routeParser.getMatch(detectionPath, path, params, r.use); match { + // Get params from the path detectionPath return match } } // Is this route a Middleware? if r.use { - // Single slash will match or path prefix - if r.root || strings.HasPrefix(path, r.path) { + // Single slash will match or detectionPath prefix + if r.root || strings.HasPrefix(detectionPath, r.path) { return true } - // Check for a simple path match - } else if len(r.path) == len(path) && r.path == path { + // Check for a simple detectionPath match + } else if len(r.path) == len(detectionPath) && r.path == detectionPath { return true } // No match @@ -107,7 +107,7 @@ func (app *App) next(c *Ctx) (match bool, err error) { route := tree[c.indexRoute] // Check if it matches the request path - match = route.match(c.path, c.pathOriginal, &c.values) + match = route.match(c.detectionPath, c.path, &c.values) // No match, next route if !match {