mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-11 19:47:00 +08:00
Merge branch 'master' into err-abort-handler
This commit is contained in:
commit
2bce0acff9
16
gin.go
16
gin.go
@ -135,10 +135,16 @@ type Engine struct {
|
|||||||
AppEngine bool
|
AppEngine bool
|
||||||
|
|
||||||
// UseRawPath if enabled, the url.RawPath will be used to find parameters.
|
// UseRawPath if enabled, the url.RawPath will be used to find parameters.
|
||||||
|
// The RawPath is only a hint, EscapedPath() should be use instead. (https://pkg.go.dev/net/url@master#URL)
|
||||||
|
// Only use RawPath if you know what you are doing.
|
||||||
UseRawPath bool
|
UseRawPath bool
|
||||||
|
|
||||||
|
// UseEscapedPath if enable, the url.EscapedPath() will be used to find parameters
|
||||||
|
// It overrides UseRawPath
|
||||||
|
UseEscapedPath bool
|
||||||
|
|
||||||
// UnescapePathValues if true, the path value will be unescaped.
|
// UnescapePathValues if true, the path value will be unescaped.
|
||||||
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
|
// If UseRawPath and UseEscapedPath are false (by default), the UnescapePathValues effectively is true,
|
||||||
// as url.Path gonna be used, which is already unescaped.
|
// as url.Path gonna be used, which is already unescaped.
|
||||||
UnescapePathValues bool
|
UnescapePathValues bool
|
||||||
|
|
||||||
@ -191,6 +197,7 @@ var _ IRouter = (*Engine)(nil)
|
|||||||
// - HandleMethodNotAllowed: false
|
// - HandleMethodNotAllowed: false
|
||||||
// - ForwardedByClientIP: true
|
// - ForwardedByClientIP: true
|
||||||
// - UseRawPath: false
|
// - UseRawPath: false
|
||||||
|
// - UseEscapedPath: false
|
||||||
// - UnescapePathValues: true
|
// - UnescapePathValues: true
|
||||||
func New(opts ...OptionFunc) *Engine {
|
func New(opts ...OptionFunc) *Engine {
|
||||||
debugPrintWARNINGNew()
|
debugPrintWARNINGNew()
|
||||||
@ -208,6 +215,7 @@ func New(opts ...OptionFunc) *Engine {
|
|||||||
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
|
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
|
||||||
TrustedPlatform: defaultPlatform,
|
TrustedPlatform: defaultPlatform,
|
||||||
UseRawPath: false,
|
UseRawPath: false,
|
||||||
|
UseEscapedPath: false,
|
||||||
RemoveExtraSlash: false,
|
RemoveExtraSlash: false,
|
||||||
UnescapePathValues: true,
|
UnescapePathValues: true,
|
||||||
MaxMultipartMemory: defaultMultipartMemory,
|
MaxMultipartMemory: defaultMultipartMemory,
|
||||||
@ -683,7 +691,11 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
|
|||||||
httpMethod := c.Request.Method
|
httpMethod := c.Request.Method
|
||||||
rPath := c.Request.URL.Path
|
rPath := c.Request.URL.Path
|
||||||
unescape := false
|
unescape := false
|
||||||
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
|
||||||
|
if engine.UseEscapedPath {
|
||||||
|
rPath = c.Request.URL.EscapedPath()
|
||||||
|
unescape = engine.UnescapePathValues
|
||||||
|
} else if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
||||||
rPath = c.Request.URL.RawPath
|
rPath = c.Request.URL.RawPath
|
||||||
unescape = engine.UnescapePathValues
|
unescape = engine.UnescapePathValues
|
||||||
}
|
}
|
||||||
|
|||||||
246
ginS/gins_test.go
Normal file
246
ginS/gins_test.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Copyright 2025 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ginS
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGET(t *testing.T) {
|
||||||
|
GET("/test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "test")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "test", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPOST(t *testing.T) {
|
||||||
|
POST("/post", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusCreated, "created")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/post", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
|
assert.Equal(t, "created", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPUT(t *testing.T) {
|
||||||
|
PUT("/put", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "updated")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPut, "/put", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "updated", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDELETE(t *testing.T) {
|
||||||
|
DELETE("/delete", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "deleted")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodDelete, "/delete", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "deleted", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPATCH(t *testing.T) {
|
||||||
|
PATCH("/patch", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "patched")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPatch, "/patch", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "patched", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOPTIONS(t *testing.T) {
|
||||||
|
OPTIONS("/options", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "options")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodOptions, "/options", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "options", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHEAD(t *testing.T) {
|
||||||
|
HEAD("/head", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "head")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodHead, "/head", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
Any("/any", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "any")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/any", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "any", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle(t *testing.T) {
|
||||||
|
Handle(http.MethodGet, "/handle", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "handle")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/handle", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "handle", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroup(t *testing.T) {
|
||||||
|
group := Group("/group")
|
||||||
|
group.GET("/test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "group test")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/group/test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "group test", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUse(t *testing.T) {
|
||||||
|
var middlewareExecuted bool
|
||||||
|
Use(func(c *gin.Context) {
|
||||||
|
middlewareExecuted = true
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
|
||||||
|
GET("/middleware-test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/middleware-test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.True(t, middlewareExecuted)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoRoute(t *testing.T) {
|
||||||
|
NoRoute(func(c *gin.Context) {
|
||||||
|
c.String(http.StatusNotFound, "custom 404")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/nonexistent", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
assert.Equal(t, "custom 404", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoMethod(t *testing.T) {
|
||||||
|
NoMethod(func(c *gin.Context) {
|
||||||
|
c.String(http.StatusMethodNotAllowed, "method not allowed")
|
||||||
|
})
|
||||||
|
|
||||||
|
// This just verifies that NoMethod is callable
|
||||||
|
// Testing the actual behavior would require a separate engine instance
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoutes(t *testing.T) {
|
||||||
|
GET("/routes-test", func(c *gin.Context) {})
|
||||||
|
|
||||||
|
routes := Routes()
|
||||||
|
assert.NotEmpty(t, routes)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, route := range routes {
|
||||||
|
if route.Path == "/routes-test" && route.Method == http.MethodGet {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetHTMLTemplate(t *testing.T) {
|
||||||
|
tmpl := template.Must(template.New("test").Parse("Hello {{.}}"))
|
||||||
|
SetHTMLTemplate(tmpl)
|
||||||
|
|
||||||
|
// Verify engine has template set
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticFile(t *testing.T) {
|
||||||
|
StaticFile("/static-file", "../testdata/test_file.txt")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-file", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatic(t *testing.T) {
|
||||||
|
Static("/static-dir", "../testdata")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-dir/test_file.txt", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticFS(t *testing.T) {
|
||||||
|
fs := http.Dir("../testdata")
|
||||||
|
StaticFS("/static-fs", fs)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-fs/test_file.txt", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
49
gin_test.go
49
gin_test.go
@ -720,6 +720,55 @@ func TestEngineHandleContextPreventsMiddlewareReEntry(t *testing.T) {
|
|||||||
assert.Equal(t, int64(1), handlerCounterV2)
|
assert.Equal(t, int64(1), handlerCounterV2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEngineHandleContextUseEscapedPathPercentEncoded(t *testing.T) {
|
||||||
|
r := New()
|
||||||
|
r.UseEscapedPath = true
|
||||||
|
r.UnescapePathValues = false
|
||||||
|
|
||||||
|
r.GET("/v1/:path", func(c *Context) {
|
||||||
|
// Path is Escaped, the %25 is not interpreted as %
|
||||||
|
assert.Equal(t, "foo%252Fbar", c.Param("path"))
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/v1/foo%252Fbar", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
r.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEngineHandleContextUseRawPathPercentEncoded(t *testing.T) {
|
||||||
|
r := New()
|
||||||
|
r.UseRawPath = true
|
||||||
|
r.UnescapePathValues = false
|
||||||
|
|
||||||
|
r.GET("/v1/:path", func(c *Context) {
|
||||||
|
// Path is used, the %25 is interpreted as %
|
||||||
|
assert.Equal(t, "foo%2Fbar", c.Param("path"))
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/v1/foo%252Fbar", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
r.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEngineHandleContextUseEscapedPathOverride(t *testing.T) {
|
||||||
|
r := New()
|
||||||
|
r.UseEscapedPath = true
|
||||||
|
r.UseRawPath = true
|
||||||
|
r.UnescapePathValues = false
|
||||||
|
|
||||||
|
r.GET("/v1/:path", func(c *Context) {
|
||||||
|
assert.Equal(t, "foo%25bar", c.Param("path"))
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NotPanics(t, func() {
|
||||||
|
w := PerformRequest(r, http.MethodGet, "/v1/foo%25bar")
|
||||||
|
assert.Equal(t, 200, w.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user