mirror of https://github.com/gin-gonic/gin.git
fix: case-insensitive RawPath comparison to prevent infinite redirect
This commit is contained in:
parent
c8a3adc657
commit
01c7c95fc1
49
gin.go
49
gin.go
|
@ -9,6 +9,7 @@ import (
|
|||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
|
@ -643,11 +644,57 @@ func (engine *Engine) HandleContext(c *Context) {
|
|||
c.index = oldIndexValue
|
||||
}
|
||||
|
||||
// compareRawWithEscaped compares the RawPath with the EscapedPath of a URL.
|
||||
// It returns true if they are equal, false otherwise.
|
||||
func compareRawWithEscaped(url *url.URL) bool {
|
||||
rawPath := url.RawPath
|
||||
escapedPath := url.EscapedPath()
|
||||
|
||||
if len(rawPath) != len(escapedPath) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(rawPath); i++ {
|
||||
if rawPath[i] != escapedPath[i] {
|
||||
return false
|
||||
}
|
||||
|
||||
if rawPath[i] == '%' && i+2 < len(rawPath) {
|
||||
if rawPath[i+1] == '2' && (rawPath[i+2] == 'F' || rawPath[i+2] == 'f') {
|
||||
return false
|
||||
}
|
||||
|
||||
var diff byte
|
||||
if rawPath[i+1] > escapedPath[i+1] {
|
||||
diff = rawPath[i+1] - escapedPath[i+1]
|
||||
} else {
|
||||
diff = escapedPath[i+1] - rawPath[i+1]
|
||||
}
|
||||
if diff != 0 && diff != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
if rawPath[i+2] > escapedPath[i+2] {
|
||||
diff = rawPath[i+2] - escapedPath[i+2]
|
||||
} else {
|
||||
diff = escapedPath[i+2] - rawPath[i+2]
|
||||
}
|
||||
if diff != 0 && diff != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
i += 2
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||
httpMethod := c.Request.Method
|
||||
rPath := c.Request.URL.Path
|
||||
unescape := false
|
||||
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
||||
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 && !compareRawWithEscaped(c.Request.URL) {
|
||||
rPath = c.Request.URL.RawPath
|
||||
unescape = engine.UnescapePathValues
|
||||
}
|
||||
|
|
|
@ -789,3 +789,19 @@ func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) {
|
|||
w := PerformRequest(r, "GET", "/base/v1/user/groups")
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
// Test the fix for https://github.com/gin-gonic/gin/issues/4034
|
||||
func TestLowercasePercentEncodePath(t *testing.T) {
|
||||
route := Default()
|
||||
route.UnescapePathValues = false
|
||||
route.UseRawPath = true
|
||||
route.RedirectFixedPath = true
|
||||
route.GET("/핫", func(ctx *Context) {
|
||||
ctx.JSON(200, H{})
|
||||
})
|
||||
req := httptest.NewRequest("GET", "/%ed%95%ab", nil)
|
||||
w := httptest.NewRecorder()
|
||||
route.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "{}", w.Body.String())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue