mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-14 20:22:20 +08:00
Merge branch 'master' of https://github.com/gin-gonic/gin into bind-request-entity-too-large-2
This commit is contained in:
commit
666545f787
6
.github/workflows/gin.yml
vendored
6
.github/workflows/gin.yml
vendored
@ -24,9 +24,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: "^1"
|
go-version: "^1"
|
||||||
- name: Setup golangci-lint
|
- name: Setup golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: v1.61.0
|
version: v2.1.6
|
||||||
args: --verbose
|
args: --verbose
|
||||||
test:
|
test:
|
||||||
needs: lint
|
needs: lint
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"-tags nomsgpack",
|
"-tags nomsgpack",
|
||||||
'--ldflags="-checklinkname=0" -tags "sonic avx"',
|
'--ldflags="-checklinkname=0" -tags sonic',
|
||||||
"-tags go_json",
|
"-tags go_json",
|
||||||
"-race",
|
"-race",
|
||||||
]
|
]
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
run:
|
version: "2"
|
||||||
timeout: 5m
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
- asciicheck
|
- asciicheck
|
||||||
|
- copyloopvar
|
||||||
- dogsled
|
- dogsled
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- errcheck
|
|
||||||
- errorlint
|
- errorlint
|
||||||
- copyloopvar
|
|
||||||
- gci
|
|
||||||
- gofmt
|
|
||||||
- goimports
|
|
||||||
- gosec
|
- gosec
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
@ -21,12 +16,8 @@ linters:
|
|||||||
- testifylint
|
- testifylint
|
||||||
- usestdlibvars
|
- usestdlibvars
|
||||||
- wastedassign
|
- wastedassign
|
||||||
|
settings:
|
||||||
linters-settings:
|
|
||||||
gosec:
|
gosec:
|
||||||
# To select a subset of rules to run.
|
|
||||||
# Available rules: https://github.com/securego/gosec#available-rules
|
|
||||||
# Default: [] - means include all rules
|
|
||||||
includes:
|
includes:
|
||||||
- G102
|
- G102
|
||||||
- G106
|
- G106
|
||||||
@ -37,35 +28,54 @@ linters-settings:
|
|||||||
- G201
|
- G201
|
||||||
- G203
|
- G203
|
||||||
perfsprint:
|
perfsprint:
|
||||||
|
int-conversion: true
|
||||||
err-error: true
|
err-error: true
|
||||||
errorf: true
|
errorf: true
|
||||||
int-conversion: true
|
|
||||||
sprintf1: true
|
sprintf1: true
|
||||||
strconcat: true
|
strconcat: true
|
||||||
testifylint:
|
testifylint:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
|
exclusions:
|
||||||
issues:
|
generated: lax
|
||||||
exclude-rules:
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
rules:
|
||||||
- linters:
|
- linters:
|
||||||
- structcheck
|
- structcheck
|
||||||
- unused
|
- unused
|
||||||
text: "`data` is unused"
|
text: '`data` is unused'
|
||||||
- linters:
|
- linters:
|
||||||
- staticcheck
|
- staticcheck
|
||||||
text: "SA1019:"
|
text: 'SA1019:'
|
||||||
- linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
text: "var-naming:"
|
text: 'var-naming:'
|
||||||
- linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
text: "exported:"
|
text: 'exported:'
|
||||||
- path: _test\.go
|
- linters:
|
||||||
linters:
|
- gosec
|
||||||
- gosec # security is not make sense in tests
|
path: _test\.go
|
||||||
- linters:
|
- linters:
|
||||||
- revive
|
- revive
|
||||||
path: _test\.go
|
path: _test\.go
|
||||||
- path: gin.go
|
paths:
|
||||||
linters:
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
- gci
|
- gci
|
||||||
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
- goimports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
- gin.go
|
||||||
|
@ -153,7 +153,7 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr
|
|||||||
|
|
||||||
## Middleware
|
## Middleware
|
||||||
|
|
||||||
You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib).
|
You can find many useful Gin middlewares at [gin-contrib](https://github.com/gin-contrib) and [gin-gonic/contrib](https://github.com/gin-gonic/contrib).
|
||||||
|
|
||||||
## Uses
|
## Uses
|
||||||
|
|
||||||
|
@ -51,8 +51,6 @@ type FooBarFileStruct struct {
|
|||||||
type FooBarFileFailStruct struct {
|
type FooBarFileFailStruct struct {
|
||||||
FooBarStruct
|
FooBarStruct
|
||||||
File *multipart.FileHeader `invalid_name:"file" binding:"required"`
|
File *multipart.FileHeader `invalid_name:"file" binding:"required"`
|
||||||
// for unexport test
|
|
||||||
data *multipart.FileHeader `form:"data" binding:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooDefaultBarStruct struct {
|
type FooDefaultBarStruct struct {
|
||||||
@ -1063,7 +1061,7 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
|
|||||||
}
|
}
|
||||||
err := b.Bind(req, &obj)
|
err := b.Bind(req, &obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "", obj.TestName)
|
assert.Empty(t, obj.TestName)
|
||||||
|
|
||||||
obj = InvalidNameType{}
|
obj = InvalidNameType{}
|
||||||
req = requestWithBody(method, badPath, badBody)
|
req = requestWithBody(method, badPath, badBody)
|
||||||
@ -1318,7 +1316,7 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad
|
|||||||
req := requestWithBody(http.MethodPost, path, body)
|
req := requestWithBody(http.MethodPost, path, body)
|
||||||
err := b.Bind(req, &obj)
|
err := b.Bind(req, &obj)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Equal(t, "", obj.Foo)
|
assert.Empty(t, obj.Foo)
|
||||||
|
|
||||||
obj = FooStruct{}
|
obj = FooStruct{}
|
||||||
req = requestWithBody(http.MethodPost, badPath, badBody)
|
req = requestWithBody(http.MethodPost, badPath, badBody)
|
||||||
|
@ -18,14 +18,16 @@ func TestSliceValidationError(t *testing.T) {
|
|||||||
{"has nil elements", SliceValidationError{errors.New("test error"), nil}, "[0]: test error"},
|
{"has nil elements", SliceValidationError{errors.New("test error"), nil}, "[0]: test error"},
|
||||||
{"has zero elements", SliceValidationError{}, ""},
|
{"has zero elements", SliceValidationError{}, ""},
|
||||||
{"has one element", SliceValidationError{errors.New("test one error")}, "[0]: test one error"},
|
{"has one element", SliceValidationError{errors.New("test one error")}, "[0]: test one error"},
|
||||||
{"has two elements",
|
{
|
||||||
|
"has two elements",
|
||||||
SliceValidationError{
|
SliceValidationError{
|
||||||
errors.New("first error"),
|
errors.New("first error"),
|
||||||
errors.New("second error"),
|
errors.New("second error"),
|
||||||
},
|
},
|
||||||
"[0]: first error\n[1]: second error",
|
"[0]: first error\n[1]: second error",
|
||||||
},
|
},
|
||||||
{"has many elements",
|
{
|
||||||
|
"has many elements",
|
||||||
SliceValidationError{
|
SliceValidationError{
|
||||||
errors.New("first error"),
|
errors.New("first error"),
|
||||||
errors.New("second error"),
|
errors.New("second error"),
|
||||||
|
@ -11,9 +11,11 @@ import (
|
|||||||
|
|
||||||
const defaultMemory = 32 << 20
|
const defaultMemory = 32 << 20
|
||||||
|
|
||||||
type formBinding struct{}
|
type (
|
||||||
type formPostBinding struct{}
|
formBinding struct{}
|
||||||
type formMultipartBinding struct{}
|
formPostBinding struct{}
|
||||||
|
formMultipartBinding struct{}
|
||||||
|
)
|
||||||
|
|
||||||
func (formBinding) Name() string {
|
func (formBinding) Name() string {
|
||||||
return "form"
|
return "form"
|
||||||
|
@ -509,9 +509,9 @@ func TestMappingCustomStructTypeWithFormTag(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
|
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "file", s.FileData.Protocol)
|
assert.Equal(t, "file", s.FileData.Protocol)
|
||||||
assert.EqualValues(t, "/foo", s.FileData.Path)
|
assert.Equal(t, "/foo", s.FileData.Path)
|
||||||
assert.EqualValues(t, "happiness", s.FileData.Name)
|
assert.Equal(t, "happiness", s.FileData.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappingCustomStructTypeWithURITag(t *testing.T) {
|
func TestMappingCustomStructTypeWithURITag(t *testing.T) {
|
||||||
@ -521,9 +521,9 @@ func TestMappingCustomStructTypeWithURITag(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
|
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "file", s.FileData.Protocol)
|
assert.Equal(t, "file", s.FileData.Protocol)
|
||||||
assert.EqualValues(t, "/foo", s.FileData.Path)
|
assert.Equal(t, "/foo", s.FileData.Path)
|
||||||
assert.EqualValues(t, "happiness", s.FileData.Name)
|
assert.Equal(t, "happiness", s.FileData.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
|
func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
|
||||||
@ -533,9 +533,9 @@ func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
|
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "file", s.FileData.Protocol)
|
assert.Equal(t, "file", s.FileData.Protocol)
|
||||||
assert.EqualValues(t, "/foo", s.FileData.Path)
|
assert.Equal(t, "/foo", s.FileData.Path)
|
||||||
assert.EqualValues(t, "happiness", s.FileData.Name)
|
assert.Equal(t, "happiness", s.FileData.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
|
func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
|
||||||
@ -545,9 +545,9 @@ func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
|
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "file", s.FileData.Protocol)
|
assert.Equal(t, "file", s.FileData.Protocol)
|
||||||
assert.EqualValues(t, "/foo", s.FileData.Path)
|
assert.Equal(t, "/foo", s.FileData.Path)
|
||||||
assert.EqualValues(t, "happiness", s.FileData.Name)
|
assert.Equal(t, "happiness", s.FileData.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
type customPath []string
|
type customPath []string
|
||||||
@ -570,8 +570,8 @@ func TestMappingCustomSliceUri(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri")
|
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "bar", s.FileData[0])
|
assert.Equal(t, "bar", s.FileData[0])
|
||||||
assert.EqualValues(t, "foo", s.FileData[1])
|
assert.Equal(t, "foo", s.FileData[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappingCustomSliceForm(t *testing.T) {
|
func TestMappingCustomSliceForm(t *testing.T) {
|
||||||
@ -581,8 +581,8 @@ func TestMappingCustomSliceForm(t *testing.T) {
|
|||||||
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form")
|
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, "bar", s.FileData[0])
|
assert.Equal(t, "bar", s.FileData[0])
|
||||||
assert.EqualValues(t, "foo", s.FileData[1])
|
assert.Equal(t, "foo", s.FileData[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
type objectID [12]byte
|
type objectID [12]byte
|
||||||
@ -621,7 +621,7 @@ func TestMappingCustomArrayUri(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected, _ := convertTo(val)
|
expected, _ := convertTo(val)
|
||||||
assert.EqualValues(t, expected, s.FileData)
|
assert.Equal(t, expected, s.FileData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappingCustomArrayForm(t *testing.T) {
|
func TestMappingCustomArrayForm(t *testing.T) {
|
||||||
@ -633,5 +633,5 @@ func TestMappingCustomArrayForm(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected, _ := convertTo(val)
|
expected, _ := convertTo(val)
|
||||||
assert.EqualValues(t, expected, s.FileData)
|
assert.Equal(t, expected, s.FileData)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ func (headerBinding) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (headerBinding) Bind(req *http.Request, obj any) error {
|
func (headerBinding) Bind(req *http.Request, obj any) error {
|
||||||
|
|
||||||
if err := mapHeader(obj, req.Header); err != nil {
|
if err := mapHeader(obj, req.Header); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -158,16 +158,16 @@ type structNoValidationPointer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateNoValidationPointers(t *testing.T) {
|
func TestValidateNoValidationPointers(t *testing.T) {
|
||||||
//origin := createNoValidation_values()
|
// origin := createNoValidation_values()
|
||||||
//test := createNoValidation_values()
|
// test := createNoValidation_values()
|
||||||
empty := structNoValidationPointer{}
|
empty := structNoValidationPointer{}
|
||||||
|
|
||||||
//assert.Nil(t, validate(test))
|
// assert.Nil(t, validate(test))
|
||||||
//assert.Nil(t, validate(&test))
|
// assert.Nil(t, validate(&test))
|
||||||
require.NoError(t, validate(empty))
|
require.NoError(t, validate(empty))
|
||||||
require.NoError(t, validate(&empty))
|
require.NoError(t, validate(&empty))
|
||||||
|
|
||||||
//assert.Equal(t, origin, test)
|
// assert.Equal(t, origin, test)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Object map[string]any
|
type Object map[string]any
|
||||||
@ -198,7 +198,7 @@ type structModifyValidation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toZero(sl validator.StructLevel) {
|
func toZero(sl validator.StructLevel) {
|
||||||
var s *structModifyValidation = sl.Top().Interface().(*structModifyValidation)
|
s := sl.Top().Interface().(*structModifyValidation)
|
||||||
s.Integer = 0
|
s.Integer = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,5 +249,5 @@ func TestValidatorEngine(t *testing.T) {
|
|||||||
// Check that we got back non-nil errs
|
// Check that we got back non-nil errs
|
||||||
require.Error(t, errs)
|
require.Error(t, errs)
|
||||||
// Check that the error matches expectation
|
// Check that the error matches expectation
|
||||||
require.Error(t, errs, "", "", "notone")
|
require.Error(t, errs, "notone")
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ func (xmlBinding) Bind(req *http.Request, obj any) error {
|
|||||||
func (xmlBinding) BindBody(body []byte, obj any) error {
|
func (xmlBinding) BindBody(body []byte, obj any) error {
|
||||||
return decodeXML(bytes.NewReader(body), obj)
|
return decodeXML(bytes.NewReader(body), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeXML(r io.Reader, obj any) error {
|
func decodeXML(r io.Reader, obj any) error {
|
||||||
decoder := xml.NewDecoder(r)
|
decoder := xml.NewDecoder(r)
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
13
context.go
13
context.go
@ -1034,6 +1034,19 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCookieData adds a Set-Cookie header to the ResponseWriter's headers.
|
||||||
|
// It accepts a pointer to http.Cookie structure for more flexibility in setting cookie attributes.
|
||||||
|
// The provided cookie must have a valid Name. Invalid cookies may be silently dropped.
|
||||||
|
func (c *Context) SetCookieData(cookie *http.Cookie) {
|
||||||
|
if cookie.Path == "" {
|
||||||
|
cookie.Path = "/"
|
||||||
|
}
|
||||||
|
if cookie.SameSite == http.SameSiteDefaultMode {
|
||||||
|
cookie.SameSite = c.sameSite
|
||||||
|
}
|
||||||
|
http.SetCookie(c.Writer, cookie)
|
||||||
|
}
|
||||||
|
|
||||||
// Cookie returns the named cookie provided in the request or
|
// Cookie returns the named cookie provided in the request or
|
||||||
// ErrNoCookie if not found. And return the named cookie is unescaped.
|
// ErrNoCookie if not found. And return the named cookie is unescaped.
|
||||||
// If multiple cookies match the given name, only one cookie will
|
// If multiple cookies match the given name, only one cookie will
|
||||||
|
176
context_test.go
176
context_test.go
@ -885,10 +885,10 @@ func TestContextGetCookie(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestContextBodyAllowedForStatus(t *testing.T) {
|
func TestContextBodyAllowedForStatus(t *testing.T) {
|
||||||
assert.False(t, false, bodyAllowedForStatus(http.StatusProcessing))
|
assert.False(t, bodyAllowedForStatus(http.StatusProcessing))
|
||||||
assert.False(t, false, bodyAllowedForStatus(http.StatusNoContent))
|
assert.False(t, bodyAllowedForStatus(http.StatusNoContent))
|
||||||
assert.False(t, false, bodyAllowedForStatus(http.StatusNotModified))
|
assert.False(t, bodyAllowedForStatus(http.StatusNotModified))
|
||||||
assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError))
|
assert.True(t, bodyAllowedForStatus(http.StatusInternalServerError))
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestRender struct{}
|
type TestRender struct{}
|
||||||
@ -918,7 +918,7 @@ func TestContextRenderJSON(t *testing.T) {
|
|||||||
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -946,7 +946,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) {
|
|||||||
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
c.JSONP(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
assert.JSONEq(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,7 +972,7 @@ func TestContextRenderAPIJSON(t *testing.T) {
|
|||||||
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
assert.JSONEq(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -998,7 +998,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
|
|||||||
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String())
|
assert.JSONEq(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1059,7 +1059,7 @@ func TestContextRenderPureJSON(t *testing.T) {
|
|||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1142,7 +1142,7 @@ func TestContextRenderNoContentXML(t *testing.T) {
|
|||||||
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContextString tests that the response is returned
|
// TestContextRenderString tests that the response is returned
|
||||||
// with Content-Type set to text/plain
|
// with Content-Type set to text/plain
|
||||||
func TestContextRenderString(t *testing.T) {
|
func TestContextRenderString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@ -1167,7 +1167,7 @@ func TestContextRenderNoContentString(t *testing.T) {
|
|||||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContextString tests that the response is returned
|
// TestContextRenderHTMLString tests that the response is returned
|
||||||
// with Content-Type set to text/html
|
// with Content-Type set to text/html
|
||||||
func TestContextRenderHTMLString(t *testing.T) {
|
func TestContextRenderHTMLString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@ -1233,7 +1233,7 @@ func TestContextRenderSSE(t *testing.T) {
|
|||||||
"bar": "foo",
|
"bar": "foo",
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(t, strings.Replace(w.Body.String(), " ", "", -1), strings.Replace("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", "", -1))
|
assert.Equal(t, strings.ReplaceAll(w.Body.String(), " ", ""), strings.ReplaceAll("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextRenderFile(t *testing.T) {
|
func TestContextRenderFile(t *testing.T) {
|
||||||
@ -1247,7 +1247,7 @@ func TestContextRenderFile(t *testing.T) {
|
|||||||
assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
|
assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
|
||||||
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
||||||
// else, Content-Type='text/x-go; charset=utf-8'
|
// else, Content-Type='text/x-go; charset=utf-8'
|
||||||
assert.NotEqual(t, "", w.Header().Get("Content-Type"))
|
assert.NotEmpty(t, w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextRenderFileFromFS(t *testing.T) {
|
func TestContextRenderFileFromFS(t *testing.T) {
|
||||||
@ -1261,7 +1261,7 @@ func TestContextRenderFileFromFS(t *testing.T) {
|
|||||||
assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
|
assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
|
||||||
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
||||||
// else, Content-Type='text/x-go; charset=utf-8'
|
// else, Content-Type='text/x-go; charset=utf-8'
|
||||||
assert.NotEqual(t, "", w.Header().Get("Content-Type"))
|
assert.NotEmpty(t, w.Header().Get("Content-Type"))
|
||||||
assert.Equal(t, "/some/path", c.Request.URL.Path)
|
assert.Equal(t, "/some/path", c.Request.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1432,7 +1432,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Equal(t, `{"foo":"bar"}`, w.Body.String())
|
assert.JSONEq(t, `{"foo":"bar"}`, w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1518,7 +1518,7 @@ func TestContextNegotiationFormat(t *testing.T) {
|
|||||||
c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
|
c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
|
||||||
|
|
||||||
assert.Panics(t, func() { c.NegotiateFormat() })
|
assert.Panics(t, func() { c.NegotiateFormat() })
|
||||||
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML)) //nolint:testifylint
|
||||||
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
|
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1540,7 +1540,7 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
|
|||||||
assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
|
assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
|
||||||
assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
|
assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
|
||||||
assert.Equal(t, "application/*", c.NegotiateFormat("application/*"))
|
assert.Equal(t, "application/*", c.NegotiateFormat("application/*"))
|
||||||
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON)) //nolint:testifylint
|
||||||
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML))
|
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML))
|
||||||
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
|
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
|
||||||
|
|
||||||
@ -1550,9 +1550,9 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
|
assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
|
||||||
assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
|
assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
|
||||||
assert.Equal(t, "", c.NegotiateFormat("application/*"))
|
assert.Empty(t, c.NegotiateFormat("application/*"))
|
||||||
assert.Equal(t, "", c.NegotiateFormat(MIMEJSON))
|
assert.Empty(t, c.NegotiateFormat(MIMEJSON))
|
||||||
assert.Equal(t, "", c.NegotiateFormat(MIMEXML))
|
assert.Empty(t, c.NegotiateFormat(MIMEXML))
|
||||||
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
|
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1564,9 +1564,9 @@ func TestContextNegotiationFormatCustom(t *testing.T) {
|
|||||||
c.Accepted = nil
|
c.Accepted = nil
|
||||||
c.SetAccepted(MIMEJSON, MIMEXML)
|
c.SetAccepted(MIMEJSON, MIMEXML)
|
||||||
|
|
||||||
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML)) //nolint:testifylint
|
||||||
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
|
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
|
||||||
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON)) //nolint:testifylint
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextNegotiationFormat2(t *testing.T) {
|
func TestContextNegotiationFormat2(t *testing.T) {
|
||||||
@ -1574,7 +1574,7 @@ func TestContextNegotiationFormat2(t *testing.T) {
|
|||||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
||||||
c.Request.Header.Add("Accept", "image/tiff-fx")
|
c.Request.Header.Add("Accept", "image/tiff-fx")
|
||||||
|
|
||||||
assert.Equal(t, "", c.NegotiateFormat("image/tiff"))
|
assert.Empty(t, c.NegotiateFormat("image/tiff"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextIsAborted(t *testing.T) {
|
func TestContextIsAborted(t *testing.T) {
|
||||||
@ -1634,7 +1634,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) {
|
|||||||
_, err := buf.ReadFrom(w.Body)
|
_, err := buf.ReadFrom(w.Body)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
jsonStringBody := buf.String()
|
jsonStringBody := buf.String()
|
||||||
assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody)
|
assert.JSONEq(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextError(t *testing.T) {
|
func TestContextError(t *testing.T) {
|
||||||
@ -3096,7 +3096,7 @@ func TestInterceptedHeader(t *testing.T) {
|
|||||||
// Compared to this time, this is when the response headers will be flushed
|
// Compared to this time, this is when the response headers will be flushed
|
||||||
// As response is flushed on c.String, the Header cannot be set by the first
|
// As response is flushed on c.String, the Header cannot be set by the first
|
||||||
// middleware. Assert this
|
// middleware. Assert this
|
||||||
assert.Equal(t, "", w.Result().Header.Get("X-Test"))
|
assert.Empty(t, w.Result().Header.Get("X-Test"))
|
||||||
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
|
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3143,3 +3143,129 @@ func TestContextNext(t *testing.T) {
|
|||||||
assert.True(t, exists)
|
assert.True(t, exists)
|
||||||
assert.Equal(t, "value3", value)
|
assert.Equal(t, "value3", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextSetCookieData(t *testing.T) {
|
||||||
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
c.SetSameSite(http.SameSiteLaxMode)
|
||||||
|
var setCookie string
|
||||||
|
|
||||||
|
// Basic cookie settings
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
MaxAge: 1,
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
setCookie = c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "user=gin")
|
||||||
|
assert.Contains(t, setCookie, "Path=/")
|
||||||
|
assert.Contains(t, setCookie, "Domain=localhost")
|
||||||
|
assert.Contains(t, setCookie, "Max-Age=1")
|
||||||
|
assert.Contains(t, setCookie, "HttpOnly")
|
||||||
|
assert.Contains(t, setCookie, "Secure")
|
||||||
|
// SameSite=Lax might be omitted in Go 1.23+ as it's the default
|
||||||
|
// assert.Contains(t, setCookie, "SameSite=Lax")
|
||||||
|
|
||||||
|
// Test that when Path is empty, "/" is automatically set
|
||||||
|
cookie = &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
MaxAge: 1,
|
||||||
|
Path: "",
|
||||||
|
Domain: "localhost",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
setCookie = c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "user=gin")
|
||||||
|
assert.Contains(t, setCookie, "Path=/")
|
||||||
|
assert.Contains(t, setCookie, "Domain=localhost")
|
||||||
|
assert.Contains(t, setCookie, "Max-Age=1")
|
||||||
|
assert.Contains(t, setCookie, "HttpOnly")
|
||||||
|
assert.Contains(t, setCookie, "Secure")
|
||||||
|
// SameSite=Lax might be omitted in Go 1.23+ as it's the default
|
||||||
|
// assert.Contains(t, setCookie, "SameSite=Lax")
|
||||||
|
|
||||||
|
// Test additional cookie attributes (Expires)
|
||||||
|
expireTime := time.Now().Add(24 * time.Hour)
|
||||||
|
cookie = &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
Expires: expireTime,
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
|
||||||
|
// Since the Expires value varies by time, partially verify with Contains
|
||||||
|
setCookie = c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "user=gin")
|
||||||
|
assert.Contains(t, setCookie, "Path=/")
|
||||||
|
assert.Contains(t, setCookie, "Domain=localhost")
|
||||||
|
assert.Contains(t, setCookie, "HttpOnly")
|
||||||
|
assert.Contains(t, setCookie, "Secure")
|
||||||
|
// SameSite=Lax might be omitted in Go 1.23+ as it's the default
|
||||||
|
// assert.Contains(t, setCookie, "SameSite=Lax")
|
||||||
|
|
||||||
|
// Test for Partitioned attribute (Go 1.18+)
|
||||||
|
cookie = &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
Partitioned: true,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
setCookie = c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "user=gin")
|
||||||
|
assert.Contains(t, setCookie, "Path=/")
|
||||||
|
assert.Contains(t, setCookie, "Domain=localhost")
|
||||||
|
assert.Contains(t, setCookie, "HttpOnly")
|
||||||
|
assert.Contains(t, setCookie, "Secure")
|
||||||
|
// SameSite=Lax might be omitted in Go 1.23+ as it's the default
|
||||||
|
// assert.Contains(t, setCookie, "SameSite=Lax")
|
||||||
|
// Not testing for Partitioned attribute as it may not be supported in all Go versions
|
||||||
|
|
||||||
|
// Test that SameSiteStrictMode is explicitly included in the header
|
||||||
|
t.Run("SameSite=Strict is included", func(t *testing.T) {
|
||||||
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteStrictMode,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
setCookie := c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "SameSite=Strict")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test that SameSiteNoneMode is explicitly included in the header
|
||||||
|
t.Run("SameSite=None is included", func(t *testing.T) {
|
||||||
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "user",
|
||||||
|
Value: "gin",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteNoneMode,
|
||||||
|
}
|
||||||
|
c.SetCookieData(cookie)
|
||||||
|
setCookie := c.Writer.Header().Get("Set-Cookie")
|
||||||
|
assert.Contains(t, setCookie, "SameSite=None")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
60
docs/doc.md
60
docs/doc.md
@ -84,10 +84,10 @@ go build -tags=jsoniter .
|
|||||||
go build -tags=go_json .
|
go build -tags=go_json .
|
||||||
```
|
```
|
||||||
|
|
||||||
[sonic](https://github.com/bytedance/sonic) (you have to ensure that your cpu supports avx instruction.)
|
[sonic](https://github.com/bytedance/sonic)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ go build -tags="sonic avx" .
|
$ go build -tags=sonic .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build without `MsgPack` rendering feature
|
### Build without `MsgPack` rendering feature
|
||||||
@ -2304,12 +2304,64 @@ func main() {
|
|||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.GET("/cookie", func(c *gin.Context) {
|
router.GET("/cookie", func(c *gin.Context) {
|
||||||
|
|
||||||
cookie, err := c.Cookie("gin_cookie")
|
cookie, err := c.Cookie("gin_cookie")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cookie = "NotSet"
|
cookie = "NotSet"
|
||||||
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
|
// Using http.Cookie struct for more control
|
||||||
|
c.SetCookieData(&http.Cookie{
|
||||||
|
Name: "gin_cookie",
|
||||||
|
Value: "test",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
MaxAge: 3600,
|
||||||
|
Secure: false,
|
||||||
|
HttpOnly: true,
|
||||||
|
// Additional fields available in http.Cookie
|
||||||
|
Expires: time.Now().Add(24 * time.Hour),
|
||||||
|
// Partitioned: true, // Available in newer Go versions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Cookie value: %s \n", cookie)
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Run()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the `SetCookieData` method, which accepts a `*http.Cookie` directly for more flexibility:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
router.GET("/cookie", func(c *gin.Context) {
|
||||||
|
cookie, err := c.Cookie("gin_cookie")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
cookie = "NotSet"
|
||||||
|
// Using http.Cookie struct for more control
|
||||||
|
c.SetCookieData(&http.Cookie{
|
||||||
|
Name: "gin_cookie",
|
||||||
|
Value: "test",
|
||||||
|
Path: "/",
|
||||||
|
Domain: "localhost",
|
||||||
|
MaxAge: 3600,
|
||||||
|
Secure: false,
|
||||||
|
HttpOnly: true,
|
||||||
|
// Additional fields available in http.Cookie
|
||||||
|
Expires: time.Now().Add(24 * time.Hour),
|
||||||
|
// Partitioned: true, // Available in newer Go versions
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Cookie value: %s \n", cookie)
|
fmt.Printf("Cookie value: %s \n", cookie)
|
||||||
|
@ -91,7 +91,7 @@ func (msg *Error) IsType(flags ErrorType) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap()
|
// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap()
|
||||||
func (msg *Error) Unwrap() error {
|
func (msg Error) Unwrap() error {
|
||||||
return msg.Err
|
return msg.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ func TestError(t *testing.T) {
|
|||||||
}, err.JSON())
|
}, err.JSON())
|
||||||
|
|
||||||
jsonBytes, _ := json.Marshal(err)
|
jsonBytes, _ := json.Marshal(err)
|
||||||
assert.Equal(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes))
|
assert.JSONEq(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes))
|
||||||
|
|
||||||
err.SetMeta(H{ //nolint: errcheck
|
err.SetMeta(H{ //nolint: errcheck
|
||||||
"status": "200",
|
"status": "200",
|
||||||
@ -93,13 +93,13 @@ Error #03: third
|
|||||||
H{"error": "third", "status": "400"},
|
H{"error": "third", "status": "400"},
|
||||||
}, errs.JSON())
|
}, errs.JSON())
|
||||||
jsonBytes, _ := json.Marshal(errs)
|
jsonBytes, _ := json.Marshal(errs)
|
||||||
assert.Equal(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", string(jsonBytes))
|
assert.JSONEq(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", string(jsonBytes))
|
||||||
errs = errorMsgs{
|
errs = errorMsgs{
|
||||||
{Err: errors.New("first"), Type: ErrorTypePrivate},
|
{Err: errors.New("first"), Type: ErrorTypePrivate},
|
||||||
}
|
}
|
||||||
assert.Equal(t, H{"error": "first"}, errs.JSON())
|
assert.Equal(t, H{"error": "first"}, errs.JSON())
|
||||||
jsonBytes, _ = json.Marshal(errs)
|
jsonBytes, _ = json.Marshal(errs)
|
||||||
assert.Equal(t, "{\"error\":\"first\"}", string(jsonBytes))
|
assert.JSONEq(t, "{\"error\":\"first\"}", string(jsonBytes))
|
||||||
|
|
||||||
errs = errorMsgs{}
|
errs = errorMsgs{}
|
||||||
assert.Nil(t, errs.Last())
|
assert.Nil(t, errs.Last())
|
||||||
@ -126,4 +126,15 @@ func TestErrorUnwrap(t *testing.T) {
|
|||||||
require.ErrorIs(t, err, innerErr)
|
require.ErrorIs(t, err, innerErr)
|
||||||
var testErr TestErr
|
var testErr TestErr
|
||||||
require.ErrorAs(t, err, &testErr)
|
require.ErrorAs(t, err, &testErr)
|
||||||
|
|
||||||
|
// Test non-pointer usage of gin.Error
|
||||||
|
errNonPointer := Error{
|
||||||
|
Err: innerErr,
|
||||||
|
Type: ErrorTypeAny,
|
||||||
|
}
|
||||||
|
wrappedErr := fmt.Errorf("wrapped: %w", errNonPointer)
|
||||||
|
// Check that 'errors.Is()' and 'errors.As()' behave as expected for non-pointer usage
|
||||||
|
require.ErrorIs(t, wrappedErr, innerErr)
|
||||||
|
var testErrNonPointer TestErr
|
||||||
|
require.ErrorAs(t, wrappedErr, &testErrNonPointer)
|
||||||
}
|
}
|
||||||
|
1
fs.go
1
fs.go
@ -17,7 +17,6 @@ type OnlyFilesFS struct {
|
|||||||
// Open passes `Open` to the upstream implementation without `Readdir` functionality.
|
// Open passes `Open` to the upstream implementation without `Readdir` functionality.
|
||||||
func (o OnlyFilesFS) Open(name string) (http.File, error) {
|
func (o OnlyFilesFS) Open(name string) (http.File, error) {
|
||||||
f, err := o.FileSystem.Open(name)
|
f, err := o.FileSystem.Open(name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
3
gin.go
3
gin.go
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||||
filesystem "github.com/gin-gonic/gin/internal/fs"
|
filesystem "github.com/gin-gonic/gin/internal/fs"
|
||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
|
|
||||||
"github.com/quic-go/quic-go/http3"
|
"github.com/quic-go/quic-go/http3"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
@ -216,7 +215,7 @@ func New(opts ...OptionFunc) *Engine {
|
|||||||
trustedProxies: []string{"0.0.0.0/0", "::/0"},
|
trustedProxies: []string{"0.0.0.0/0", "::/0"},
|
||||||
trustedCIDRs: defaultTrustedCIDRs,
|
trustedCIDRs: defaultTrustedCIDRs,
|
||||||
}
|
}
|
||||||
engine.RouterGroup.engine = engine
|
engine.engine = engine
|
||||||
engine.pool.New = func() any {
|
engine.pool.New = func() any {
|
||||||
return engine.allocateContext(engine.maxParams)
|
return engine.allocateContext(engine.maxParams)
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var once sync.Once
|
var (
|
||||||
var internalEngine *gin.Engine
|
once sync.Once
|
||||||
|
internalEngine *gin.Engine
|
||||||
|
)
|
||||||
|
|
||||||
func engine() *gin.Engine {
|
func engine() *gin.Engine {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
// params[1]=response status (custom compare status) default:"200 OK"
|
// params[1]=response status (custom compare status) default:"200 OK"
|
||||||
// params[2]=response body (custom compare content) default:"it worked"
|
// params[2]=response body (custom compare content) default:"it worked"
|
||||||
func testRequest(t *testing.T, params ...string) {
|
func testRequest(t *testing.T, params ...string) {
|
||||||
|
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
t.Fatal("url cannot be empty")
|
t.Fatal("url cannot be empty")
|
||||||
}
|
}
|
||||||
@ -47,12 +46,12 @@ func testRequest(t *testing.T, params ...string) {
|
|||||||
body, ioerr := io.ReadAll(resp.Body)
|
body, ioerr := io.ReadAll(resp.Body)
|
||||||
require.NoError(t, ioerr)
|
require.NoError(t, ioerr)
|
||||||
|
|
||||||
var responseStatus = "200 OK"
|
responseStatus := "200 OK"
|
||||||
if len(params) > 1 && params[1] != "" {
|
if len(params) > 1 && params[1] != "" {
|
||||||
responseStatus = params[1]
|
responseStatus = params[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseBody = "it worked"
|
responseBody := "it worked"
|
||||||
if len(params) > 2 && params[2] != "" {
|
if len(params) > 2 && params[2] != "" {
|
||||||
responseBody = params[2]
|
responseBody = params[2]
|
||||||
}
|
}
|
||||||
@ -170,7 +169,7 @@ func TestRunTLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPusher(t *testing.T) {
|
func TestPusher(t *testing.T) {
|
||||||
var html = template.Must(template.New("https").Parse(`
|
html := template.Must(template.New("https").Parse(`
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Https Test</title>
|
<title>Https Test</title>
|
||||||
|
@ -46,7 +46,7 @@ func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine
|
|||||||
})
|
})
|
||||||
router.GET("/raw", func(c *Context) {
|
router.GET("/raw", func(c *Context) {
|
||||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]any{
|
c.HTML(http.StatusOK, "raw.tmpl", map[string]any{
|
||||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), //nolint:gofumpt
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -847,7 +847,7 @@ func handlerTest1(c *Context) {}
|
|||||||
func handlerTest2(c *Context) {}
|
func handlerTest2(c *Context) {}
|
||||||
|
|
||||||
func TestNewOptionFunc(t *testing.T) {
|
func TestNewOptionFunc(t *testing.T) {
|
||||||
var fc = func(e *Engine) {
|
fc := func(e *Engine) {
|
||||||
e.GET("/test1", handlerTest1)
|
e.GET("/test1", handlerTest1)
|
||||||
e.GET("/test2", handlerTest2)
|
e.GET("/test2", handlerTest2)
|
||||||
|
|
||||||
@ -883,7 +883,7 @@ func TestWithOptionFunc(t *testing.T) {
|
|||||||
type Birthday string
|
type Birthday string
|
||||||
|
|
||||||
func (b *Birthday) UnmarshalParam(param string) error {
|
func (b *Birthday) UnmarshalParam(param string) error {
|
||||||
*b = Birthday(strings.Replace(param, "-", "/", -1))
|
*b = Birthday(strings.ReplaceAll(param, "-", "/"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,8 +298,8 @@ func TestShouldBindUri(t *testing.T) {
|
|||||||
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
|
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
|
||||||
var person Person
|
var person Person
|
||||||
require.NoError(t, c.ShouldBindUri(&person))
|
require.NoError(t, c.ShouldBindUri(&person))
|
||||||
assert.NotEqual(t, "", person.Name)
|
assert.NotEmpty(t, person.Name)
|
||||||
assert.NotEqual(t, "", person.ID)
|
assert.NotEmpty(t, person.ID)
|
||||||
c.String(http.StatusOK, "ShouldBindUri test OK")
|
c.String(http.StatusOK, "ShouldBindUri test OK")
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -320,8 +320,8 @@ func TestBindUri(t *testing.T) {
|
|||||||
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
|
router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
|
||||||
var person Person
|
var person Person
|
||||||
require.NoError(t, c.BindUri(&person))
|
require.NoError(t, c.BindUri(&person))
|
||||||
assert.NotEqual(t, "", person.Name)
|
assert.NotEmpty(t, person.Name)
|
||||||
assert.NotEqual(t, "", person.ID)
|
assert.NotEmpty(t, person.ID)
|
||||||
c.String(http.StatusOK, "BindUri test OK")
|
c.String(http.StatusOK, "BindUri test OK")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
16
go.mod
16
go.mod
@ -3,18 +3,18 @@ module github.com/gin-gonic/gin
|
|||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.13.1
|
github.com/bytedance/sonic v1.13.2
|
||||||
github.com/gin-contrib/sse v1.1.0
|
github.com/gin-contrib/sse v1.1.0
|
||||||
github.com/go-playground/validator/v10 v10.26.0
|
github.com/go-playground/validator/v10 v10.26.0
|
||||||
github.com/goccy/go-json v0.10.2
|
github.com/goccy/go-json v0.10.2
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/quic-go/quic-go v0.51.0
|
github.com/quic-go/quic-go v0.51.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/ugorji/go/codec v1.2.12
|
github.com/ugorji/go/codec v1.2.12
|
||||||
golang.org/x/net v0.38.0
|
golang.org/x/net v0.40.0
|
||||||
google.golang.org/protobuf v1.34.1
|
google.golang.org/protobuf v1.36.6
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,10 +37,10 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/crypto v0.36.0 // indirect
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
golang.org/x/sync v0.12.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
)
|
)
|
||||||
|
35
go.sum
35
go.sum
@ -1,5 +1,5 @@
|
|||||||
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
|
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||||
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
@ -55,8 +55,8 @@ github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
|||||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
@ -66,15 +66,12 @@ github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
@ -85,24 +82,24 @@ go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
|||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -12,8 +12,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
|
var (
|
||||||
var testBytes = []byte(testString)
|
testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
|
||||||
|
testBytes = []byte(testString)
|
||||||
|
)
|
||||||
|
|
||||||
func rawBytesToStr(b []byte) string {
|
func rawBytesToStr(b []byte) string {
|
||||||
return string(b)
|
return string(b)
|
||||||
|
@ -13,7 +13,6 @@ type FileSystem struct {
|
|||||||
// Open passes `Open` to the upstream implementation and return an [fs.File].
|
// Open passes `Open` to the upstream implementation and return an [fs.File].
|
||||||
func (o FileSystem) Open(name string) (fs.File, error) {
|
func (o FileSystem) Open(name string) (fs.File, error) {
|
||||||
f, err := o.FileSystem.Open(name)
|
f, err := o.FileSystem.Open(name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ func (m *mockFileSystem) Open(name string) (http.File, error) {
|
|||||||
return m.open(name)
|
return m.open(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TesFileSystem_Open(t *testing.T) {
|
func TestFileSystem_Open(t *testing.T) {
|
||||||
var testFile *os.File
|
var testFile *os.File
|
||||||
mockFS := &mockFileSystem{
|
mockFS := &mockFileSystem{
|
||||||
open: func(name string) (http.File, error) {
|
open: func(name string) (http.File, error) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
|
//go:build !jsoniter && !go_json && !(sonic && (linux || windows || darwin))
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build sonic && avx && (linux || windows || darwin) && amd64
|
//go:build sonic && (linux || windows || darwin)
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -371,11 +371,11 @@ func TestErrorLogger(t *testing.T) {
|
|||||||
|
|
||||||
w := PerformRequest(router, http.MethodGet, "/error")
|
w := PerformRequest(router, http.MethodGet, "/error")
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String())
|
assert.JSONEq(t, "{\"error\":\"this is an error\"}", w.Body.String())
|
||||||
|
|
||||||
w = PerformRequest(router, http.MethodGet, "/abort")
|
w = PerformRequest(router, http.MethodGet, "/abort")
|
||||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||||
assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String())
|
assert.JSONEq(t, "{\"error\":\"no authorized\"}", w.Body.String())
|
||||||
|
|
||||||
w = PerformRequest(router, http.MethodGet, "/print")
|
w = PerformRequest(router, http.MethodGet, "/print")
|
||||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||||
|
@ -203,7 +203,7 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) {
|
|||||||
assert.Equal(t, "ACB", signature)
|
assert.Equal(t, "ACB", signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFailHandlersChain - ensure that Fail interrupt used middleware in fifo order as
|
// TestMiddlewareFailHandlersChain - ensure that Fail interrupt used middleware in fifo order as
|
||||||
// as well as Abort
|
// as well as Abort
|
||||||
func TestMiddlewareFailHandlersChain(t *testing.T) {
|
func TestMiddlewareFailHandlersChain(t *testing.T) {
|
||||||
// SETUP
|
// SETUP
|
||||||
@ -249,5 +249,5 @@ func TestMiddlewareWrite(t *testing.T) {
|
|||||||
w := PerformRequest(router, http.MethodGet, "/")
|
w := PerformRequest(router, http.MethodGet, "/")
|
||||||
|
|
||||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||||
assert.Equal(t, strings.Replace("hola\n<map><foo>bar</foo></map>{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1))
|
assert.Equal(t, strings.ReplaceAll("hola\n<map><foo>bar</foo></map>{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", ""), strings.ReplaceAll(w.Body.String(), " ", ""))
|
||||||
}
|
}
|
||||||
|
6
mode.go
6
mode.go
@ -44,8 +44,10 @@ var DefaultWriter io.Writer = os.Stdout
|
|||||||
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
|
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
|
||||||
var DefaultErrorWriter io.Writer = os.Stderr
|
var DefaultErrorWriter io.Writer = os.Stderr
|
||||||
|
|
||||||
var ginMode int32 = debugCode
|
var (
|
||||||
var modeName atomic.Value
|
ginMode int32 = debugCode
|
||||||
|
modeName atomic.Value
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
mode := os.Getenv(EnvGinMode)
|
mode := os.Getenv(EnvGinMode)
|
||||||
|
@ -74,9 +74,9 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
|||||||
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
||||||
headers := strings.Split(string(httpRequest), "\r\n")
|
headers := strings.Split(string(httpRequest), "\r\n")
|
||||||
for idx, header := range headers {
|
for idx, header := range headers {
|
||||||
current := strings.Split(header, ":")
|
key, _, _ := strings.Cut(header, ":")
|
||||||
if current[0] == "Authorization" {
|
if key == "Authorization" {
|
||||||
headers[idx] = current[0] + ": *"
|
headers[idx] = key + ": *"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headersToStr := strings.Join(headers, "\r\n")
|
headersToStr := strings.Join(headers, "\r\n")
|
||||||
|
@ -67,6 +67,7 @@ func (r HTMLDebug) Instance(name string, data any) Render {
|
|||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r HTMLDebug) loadTemplate() *template.Template {
|
func (r HTMLDebug) loadTemplate() *template.Template {
|
||||||
if r.FuncMap == nil {
|
if r.FuncMap == nil {
|
||||||
r.FuncMap = template.FuncMap{}
|
r.FuncMap = template.FuncMap{}
|
||||||
|
@ -38,7 +38,7 @@ func TestRenderJSON(t *testing.T) {
|
|||||||
err := (JSON{data}).Render(w)
|
err := (JSON{data}).Render(w)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ func TestRenderIndentedJSON(t *testing.T) {
|
|||||||
err := (IndentedJSON{data}).Render(w)
|
err := (IndentedJSON{data}).Render(w)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
|
assert.JSONEq(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func TestRenderSecureJSON(t *testing.T) {
|
|||||||
err1 := (SecureJSON{"while(1);", data}).Render(w1)
|
err1 := (SecureJSON{"while(1);", data}).Render(w1)
|
||||||
|
|
||||||
require.NoError(t, err1)
|
require.NoError(t, err1)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\"}", w1.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
|
||||||
|
|
||||||
w2 := httptest.NewRecorder()
|
w2 := httptest.NewRecorder()
|
||||||
@ -194,7 +194,7 @@ func TestRenderJsonpJSONError2(t *testing.T) {
|
|||||||
e := (JsonpJSON{"", data}).Render(w)
|
e := (JsonpJSON{"", data}).Render(w)
|
||||||
require.NoError(t, e)
|
require.NoError(t, e)
|
||||||
|
|
||||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\"}", w.Body.String())
|
||||||
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ func TestRenderAsciiJSON(t *testing.T) {
|
|||||||
err := (AsciiJSON{data1}).Render(w1)
|
err := (AsciiJSON{data1}).Render(w1)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
|
assert.JSONEq(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
|
||||||
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
|
||||||
|
|
||||||
w2 := httptest.NewRecorder()
|
w2 := httptest.NewRecorder()
|
||||||
@ -244,7 +244,7 @@ func TestRenderPureJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err := (PureJSON{data}).Render(w)
|
err := (PureJSON{data}).Render(w)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
||||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +484,7 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
|
|||||||
assert.Contains(t, w.Body.String(), "package gin")
|
assert.Contains(t, w.Body.String(), "package gin")
|
||||||
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
|
||||||
// else, Content-Type='text/x-go; charset=utf-8'
|
// else, Content-Type='text/x-go; charset=utf-8'
|
||||||
assert.NotEqual(t, "", w.Header().Get("Content-Type"))
|
assert.NotEmpty(t, w.Header().Get("Content-Type"))
|
||||||
assert.NotEqual(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Last-Modified"))
|
assert.NotEqual(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Last-Modified"))
|
||||||
assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
|
assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
|
||||||
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
||||||
@ -764,7 +764,7 @@ func TestRouteContextHoldsFullPath(t *testing.T) {
|
|||||||
// Test not found
|
// Test not found
|
||||||
router.Use(func(c *Context) {
|
router.Use(func(c *Context) {
|
||||||
// For not found routes full path is empty
|
// For not found routes full path is empty
|
||||||
assert.Equal(t, "", c.FullPath())
|
assert.Empty(t, c.FullPath())
|
||||||
})
|
})
|
||||||
|
|
||||||
w := PerformRequest(router, http.MethodGet, "/not-found")
|
w := PerformRequest(router, http.MethodGet, "/not-found")
|
||||||
|
4
tree.go
4
tree.go
@ -234,7 +234,7 @@ walk:
|
|||||||
// Wildcard conflict
|
// Wildcard conflict
|
||||||
pathSeg := path
|
pathSeg := path
|
||||||
if n.nType != catchAll {
|
if n.nType != catchAll {
|
||||||
pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
|
pathSeg, _, _ = strings.Cut(pathSeg, "/")
|
||||||
}
|
}
|
||||||
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
|
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
|
||||||
panic("'" + pathSeg +
|
panic("'" + pathSeg +
|
||||||
@ -358,7 +358,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
|
|||||||
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
||||||
pathSeg := ""
|
pathSeg := ""
|
||||||
if len(n.children) != 0 {
|
if len(n.children) != 0 {
|
||||||
pathSeg = strings.SplitN(n.children[0].path, "/", 2)[0]
|
pathSeg, _, _ = strings.Cut(n.children[0].path, "/")
|
||||||
}
|
}
|
||||||
panic("catch-all wildcard '" + path +
|
panic("catch-all wildcard '" + path +
|
||||||
"' in new path '" + fullPath +
|
"' in new path '" + fullPath +
|
||||||
|
@ -481,7 +481,7 @@ func TestTreeDuplicatePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
// printChildren(tree, "")
|
||||||
|
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
@ -532,7 +532,7 @@ func TestTreeCatchAllConflictRoot(t *testing.T) {
|
|||||||
|
|
||||||
func TestTreeCatchMaxParams(t *testing.T) {
|
func TestTreeCatchMaxParams(t *testing.T) {
|
||||||
tree := &node{}
|
tree := &node{}
|
||||||
var route = "/cmd/*filepath"
|
route := "/cmd/*filepath"
|
||||||
tree.addRoute(route, fakeHandler(route))
|
tree.addRoute(route, fakeHandler(route))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,7 +692,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRedirectTrailingSlash(t *testing.T) {
|
func TestRedirectTrailingSlash(t *testing.T) {
|
||||||
var data = []struct {
|
data := []struct {
|
||||||
path string
|
path string
|
||||||
}{
|
}{
|
||||||
{"/hello/:name"},
|
{"/hello/:name"},
|
||||||
|
@ -94,7 +94,7 @@ func somefunction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJoinPaths(t *testing.T) {
|
func TestJoinPaths(t *testing.T) {
|
||||||
assert.Equal(t, "", joinPaths("", ""))
|
assert.Empty(t, joinPaths("", ""))
|
||||||
assert.Equal(t, "/", joinPaths("", "/"))
|
assert.Equal(t, "/", joinPaths("", "/"))
|
||||||
assert.Equal(t, "/a", joinPaths("/a", ""))
|
assert.Equal(t, "/a", joinPaths("/a", ""))
|
||||||
assert.Equal(t, "/a/", joinPaths("/a/", ""))
|
assert.Equal(t, "/a/", joinPaths("/a/", ""))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user