🐛 UnescapePath not working #1102

Co-authored-by: Fenny <fenny@gofiber.io>
pull/1104/head
ReneWerner87 2021-01-06 13:21:54 +01:00
parent 383d037a95
commit b51def0bb8
5 changed files with 84 additions and 55 deletions

View File

@ -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) { func Test_App_Add_Method_Test(t *testing.T) {
app := New() app := New()
defer func() { defer func() {

73
ctx.go
View File

@ -33,20 +33,22 @@ const maxParams = 30
// Ctx represents the Context which hold the HTTP request and response. // 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. // It has methods for the request query string, parameters, body, HTTP headers and so on.
type Ctx struct { type Ctx struct {
app *App // Reference to *App app *App // Reference to *App
route *Route // Reference to *Route route *Route // Reference to *Route
indexRoute int // Index of the current route indexRoute int // Index of the current route
indexHandler int // Index of the current handler indexHandler int // Index of the current handler
method string // HTTP method method string // HTTP method
methodINT int // HTTP method INT equivalent methodINT int // HTTP method INT equivalent
baseURI string // HTTP base uri baseURI string // HTTP base uri
path string // Prettified HTTP path -> string copy from pathBuffer path string // Prettified HTTP path for the user -> string copy from pathBuffer
pathBuffer []byte // Prettified HTTP path buffer pathBuffer []byte // HTTP path buffer
treePath string // Path for the search in the tree detectionPath string // Route detection path -> string copy from pathBuffer
pathOriginal string // Original HTTP path detectionPathBuffer []byte // HTTP path detectionPath
values [maxParams]string // Route parameter values treePath string // Path for the search in the tree
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx pathOriginal string // Original HTTP path
matched bool // Non use route matched values [maxParams]string // Route parameter values
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
matched bool // Non use route matched
} }
// Range data for c.Range // Range data for c.Range
@ -88,7 +90,6 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
// Reset matched flag // Reset matched flag
c.matched = false c.matched = false
// Set paths // Set paths
c.pathBuffer = append(c.pathBuffer[0:0], fctx.URI().PathOriginal()...)
c.pathOriginal = getString(fctx.URI().PathOriginal()) c.pathOriginal = getString(fctx.URI().PathOriginal())
// Set method // Set method
c.method = getString(fctx.Request.Header.Method()) c.method = getString(fctx.Request.Header.Method())
@ -98,7 +99,7 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
// reset base uri // reset base uri
c.baseURI = "" c.baseURI = ""
// Prettify path // Prettify path
c.prettifyPath() c.configDependentPaths()
return c return c
} }
@ -674,15 +675,12 @@ func (c *Ctx) Params(key string, defaultValue ...string) string {
func (c *Ctx) Path(override ...string) string { func (c *Ctx) Path(override ...string) string {
if len(override) != 0 && c.path != override[0] { if len(override) != 0 && c.path != override[0] {
// Set new path to context // Set new path to context
c.pathBuffer = append(c.pathBuffer[0:0], override[0]...)
c.pathOriginal = override[0] c.pathOriginal = override[0]
// c.path = override[0]
// c.pathOriginal = c.path
// Set new path to request context // Set new path to request context
c.fasthttp.Request.URI().SetPath(c.pathOriginal) c.fasthttp.Request.URI().SetPath(c.pathOriginal)
// Prettify path // Prettify path
c.prettifyPath() c.configDependentPaths()
} }
return c.pathOriginal return c.pathOriginal
} }
@ -1105,24 +1103,33 @@ func (c *Ctx) XHR() bool {
return utils.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest")) return utils.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest"))
} }
// prettifyPath ... // configDependentPaths set paths for route recognition and prepared paths for the user,
func (c *Ctx) prettifyPath() { // here the features for caseSensitive, decoded paths, strict paths are evaluated
// If UnescapePath enabled, we decode the path 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 { if c.app.config.UnescapePath {
c.pathBuffer = fasthttp.AppendUnquotedArg(c.pathBuffer[:0], c.pathBuffer) 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) 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] c.treePath = c.treePath[0:0]
if len(c.path) >= 3 { if len(c.detectionPath) >= 3 {
c.treePath = c.path[:3] c.treePath = c.detectionPath[:3]
} }
} }

View File

@ -120,7 +120,7 @@ func methodExist(ctx *Ctx) (exist bool) {
continue continue
} }
// Check if it matches the request path // 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 // No match, next route
if match { if match {
// We matched // We matched

16
path.go
View File

@ -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 // 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 var i, paramsIterator, partLen int
for _, segment := range routeParser.segs { for _, segment := range routeParser.segs {
partLen = len(s) partLen = len(detectionPath)
// check const segment // check const segment
if !segment.IsParam { if !segment.IsParam {
i = segment.Length i = segment.Length
// is optional part or the const part must match with the given string // is optional part or the const part must match with the given string
// check if the end of the segment is a optional slash // 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-- i--
} else if !(i <= partLen && s[:i] == segment.Const) { } else if !(i <= partLen && detectionPath[:i] == segment.Const) {
return false return false
} }
} else { } else {
// determine parameter length // determine parameter length
i = findParamLen(s, segment) i = findParamLen(detectionPath, segment)
if !segment.IsOptional && i == 0 { if !segment.IsOptional && i == 0 {
return false return false
} }
// take over the params positions // take over the params positions
params[paramsIterator] = original[:i] params[paramsIterator] = path[:i]
paramsIterator++ paramsIterator++
} }
// reduce founded part from the string // reduce founded part from the string
if partLen > 0 { 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 return false
} }

View File

@ -55,14 +55,14 @@ type Route struct {
Handlers []Handler `json:"-"` // Ctx handlers Handlers []Handler `json:"-"` // Ctx handlers
} }
func (r *Route) match(path, original string, params *[maxParams]string) (match bool) { func (r *Route) match(detectionPath, path string, params *[maxParams]string) (match bool) {
// root path check // root detectionPath check
if r.root && path == "/" { if r.root && detectionPath == "/" {
return true return true
// '*' wildcard matches any path // '*' wildcard matches any detectionPath
} else if r.star { } else if r.star {
if len(original) > 1 { if len(path) > 1 {
params[0] = original[1:] params[0] = path[1:]
} else { } else {
params[0] = "" params[0] = ""
} }
@ -71,19 +71,19 @@ func (r *Route) match(path, original string, params *[maxParams]string) (match b
// Does this route have parameters // Does this route have parameters
if len(r.Params) > 0 { if len(r.Params) > 0 {
// Match params // Match params
if match := r.routeParser.getMatch(path, original, params, r.use); match { if match := r.routeParser.getMatch(detectionPath, path, params, r.use); match {
// Get params from the original path // Get params from the path detectionPath
return match return match
} }
} }
// Is this route a Middleware? // Is this route a Middleware?
if r.use { if r.use {
// Single slash will match or path prefix // Single slash will match or detectionPath prefix
if r.root || strings.HasPrefix(path, r.path) { if r.root || strings.HasPrefix(detectionPath, r.path) {
return true return true
} }
// Check for a simple path match // Check for a simple detectionPath match
} else if len(r.path) == len(path) && r.path == path { } else if len(r.path) == len(detectionPath) && r.path == detectionPath {
return true return true
} }
// No match // No match
@ -107,7 +107,7 @@ func (app *App) next(c *Ctx) (match bool, err error) {
route := tree[c.indexRoute] route := tree[c.indexRoute]
// Check if it matches the request path // 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 // No match, next route
if !match { if !match {