🐛 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) {
app := New()
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.
// 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]
}
}

View File

@ -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

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
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
}

View File

@ -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 {