mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-14 12:12:12 +08:00
Merge branch 'gin-gonic:master' into master
This commit is contained in:
commit
c57a166902
5
.github/workflows/goreleaser.yml
vendored
5
.github/workflows/goreleaser.yml
vendored
@ -29,3 +29,8 @@ jobs:
|
|||||||
args: release --clean
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Trigger Go module reindex (pkg.go.dev)
|
||||||
|
run: |
|
||||||
|
echo "Triggering Go module reindex at proxy.golang.org"
|
||||||
|
curl -sSf "https://proxy.golang.org/github.com/${GITHUB_REPOSITORY,,}/@v/${GITHUB_REF_NAME}.info"
|
||||||
|
@ -175,7 +175,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
|
|||||||
|
|
||||||
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
||||||
type BindUnmarshaler interface {
|
type BindUnmarshaler interface {
|
||||||
// UnmarshalParam decodes and assigns a value from an form or query param.
|
// UnmarshalParam decodes and assigns a value from a form or query param.
|
||||||
UnmarshalParam(param string) error
|
UnmarshalParam(param string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
context.go
21
context.go
@ -216,7 +216,7 @@ func (c *Context) AbortWithStatus(code int) {
|
|||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortWithStatusJSON calls `Abort()` and then `PureJSON` internally.
|
// AbortWithStatusPureJSON calls `Abort()` and then `PureJSON` internally.
|
||||||
// This method stops the chain, writes the status code and return a JSON body without escaping.
|
// This method stops the chain, writes the status code and return a JSON body without escaping.
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
func (c *Context) AbortWithStatusPureJSON(code int, jsonObj any) {
|
func (c *Context) AbortWithStatusPureJSON(code int, jsonObj any) {
|
||||||
@ -573,7 +573,7 @@ func (c *Context) QueryMap(key string) (dicts map[string]string) {
|
|||||||
// whether at least one value exists for the given key.
|
// whether at least one value exists for the given key.
|
||||||
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
|
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
|
||||||
c.initQueryCache()
|
c.initQueryCache()
|
||||||
return c.get(c.queryCache, key)
|
return getMapFromFormData(c.queryCache, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
||||||
@ -646,22 +646,23 @@ func (c *Context) PostFormMap(key string) (dicts map[string]string) {
|
|||||||
// whether at least one value exists for the given key.
|
// whether at least one value exists for the given key.
|
||||||
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
|
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
|
||||||
c.initFormCache()
|
c.initFormCache()
|
||||||
return c.get(c.formCache, key)
|
return getMapFromFormData(c.formCache, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get is an internal method and returns a map which satisfies conditions.
|
// getMapFromFormData return a map which satisfies conditions.
|
||||||
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
|
// It parses from data with bracket notation like "key[subkey]=value" into a map.
|
||||||
dicts := make(map[string]string)
|
func getMapFromFormData(m map[string][]string, key string) (map[string]string, bool) {
|
||||||
exist := false
|
d := make(map[string]string)
|
||||||
|
found := false
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
|
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
|
||||||
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
|
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
|
||||||
exist = true
|
found = true
|
||||||
dicts[k[i+1:][:j]] = v[0]
|
d[k[i+1:][:j]] = v[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dicts, exist
|
return d, found
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormFile returns the first file for the provided form key.
|
// FormFile returns the first file for the provided form key.
|
||||||
|
35
context_file_test.go
Normal file
35
context_file_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestContextFileSimple tests the Context.File() method with a simple case
|
||||||
|
func TestContextFileSimple(t *testing.T) {
|
||||||
|
// Test serving an existing file
|
||||||
|
testFile := "testdata/test_file.txt"
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
|
||||||
|
c.File(testFile)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "This is a test file")
|
||||||
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestContextFileNotFound tests serving a non-existent file
|
||||||
|
func TestContextFileNotFound(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
|
||||||
|
c.File("non_existent_file.txt")
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
204
context_test.go
204
context_test.go
@ -76,6 +76,79 @@ func must(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestContextFile tests the Context.File() method
|
||||||
|
func TestContextFile(t *testing.T) {
|
||||||
|
// Test serving an existing file
|
||||||
|
t.Run("serve existing file", func(t *testing.T) {
|
||||||
|
// Create a temporary test file
|
||||||
|
testFile := "testdata/test_file.txt"
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
|
||||||
|
c.File(testFile)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "This is a test file")
|
||||||
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test serving a non-existent file
|
||||||
|
t.Run("serve non-existent file", func(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
|
||||||
|
c.File("non_existent_file.txt")
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test serving a directory (should return 200 with directory listing or 403 Forbidden)
|
||||||
|
t.Run("serve directory", func(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
|
||||||
|
c.File(".")
|
||||||
|
|
||||||
|
// Directory serving can return either 200 (with listing) or 403 (forbidden)
|
||||||
|
assert.True(t, w.Code == http.StatusOK || w.Code == http.StatusForbidden)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test with HEAD request
|
||||||
|
t.Run("HEAD request", func(t *testing.T) {
|
||||||
|
testFile := "testdata/test_file.txt"
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodHead, "/test", nil)
|
||||||
|
|
||||||
|
c.File(testFile)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Empty(t, w.Body.String()) // HEAD request should not return body
|
||||||
|
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test with Range request
|
||||||
|
t.Run("Range request", func(t *testing.T) {
|
||||||
|
testFile := "testdata/test_file.txt"
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
c.Request.Header.Set("Range", "bytes=0-10")
|
||||||
|
|
||||||
|
c.File(testFile)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusPartialContent, w.Code)
|
||||||
|
assert.Equal(t, "bytes", w.Header().Get("Accept-Ranges"))
|
||||||
|
assert.Contains(t, w.Header().Get("Content-Range"), "bytes 0-10")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextFormFile(t *testing.T) {
|
func TestContextFormFile(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
mw := multipart.NewWriter(buf)
|
mw := multipart.NewWriter(buf)
|
||||||
@ -3470,3 +3543,134 @@ func TestContextSetCookieData(t *testing.T) {
|
|||||||
assert.Contains(t, setCookie, "SameSite=Strict")
|
assert.Contains(t, setCookie, "SameSite=Strict")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMapFromFormData(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data map[string][]string
|
||||||
|
key string
|
||||||
|
expected map[string]string
|
||||||
|
found bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Basic bracket notation",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
"ids[b]": {"3.14"},
|
||||||
|
},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{
|
||||||
|
"a": "hi",
|
||||||
|
"b": "3.14",
|
||||||
|
},
|
||||||
|
found: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed data with bracket notation",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
"ids[b]": {"3.14"},
|
||||||
|
"names[a]": {"mike"},
|
||||||
|
"names[b]": {"maria"},
|
||||||
|
"other[key]": {"value"},
|
||||||
|
"simple": {"data"},
|
||||||
|
},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{
|
||||||
|
"a": "hi",
|
||||||
|
"b": "3.14",
|
||||||
|
},
|
||||||
|
found: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Names key",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
"ids[b]": {"3.14"},
|
||||||
|
"names[a]": {"mike"},
|
||||||
|
"names[b]": {"maria"},
|
||||||
|
"other[key]": {"value"},
|
||||||
|
},
|
||||||
|
key: "names",
|
||||||
|
expected: map[string]string{
|
||||||
|
"a": "mike",
|
||||||
|
"b": "maria",
|
||||||
|
},
|
||||||
|
found: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Key not found",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
"names[b]": {"maria"},
|
||||||
|
},
|
||||||
|
key: "notfound",
|
||||||
|
expected: map[string]string{},
|
||||||
|
found: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty data",
|
||||||
|
data: map[string][]string{},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{},
|
||||||
|
found: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Malformed bracket notation",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a": {"hi"}, // Missing closing bracket
|
||||||
|
"ids]b": {"3.14"}, // Missing opening bracket
|
||||||
|
"idsab": {"value"}, // No brackets
|
||||||
|
},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{},
|
||||||
|
found: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nested bracket notation",
|
||||||
|
data: map[string][]string{
|
||||||
|
"ids[a][b]": {"nested"},
|
||||||
|
"ids[c]": {"simple"},
|
||||||
|
},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{
|
||||||
|
"a": "nested",
|
||||||
|
"c": "simple",
|
||||||
|
},
|
||||||
|
found: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Simple key without brackets",
|
||||||
|
data: map[string][]string{
|
||||||
|
"simple": {"data"},
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
},
|
||||||
|
key: "simple",
|
||||||
|
expected: map[string]string{},
|
||||||
|
found: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed simple and bracket keys",
|
||||||
|
data: map[string][]string{
|
||||||
|
"simple": {"data"},
|
||||||
|
"ids[a]": {"hi"},
|
||||||
|
"ids[b]": {"3.14"},
|
||||||
|
"other": {"value"},
|
||||||
|
},
|
||||||
|
key: "ids",
|
||||||
|
expected: map[string]string{
|
||||||
|
"a": "hi",
|
||||||
|
"b": "3.14",
|
||||||
|
},
|
||||||
|
found: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result, found := getMapFromFormData(tc.data, tc.key)
|
||||||
|
assert.Equal(t, tc.expected, result, "result mismatch")
|
||||||
|
assert.Equal(t, tc.found, found, "found mismatch")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
12
go.mod
12
go.mod
@ -15,7 +15,7 @@ require (
|
|||||||
github.com/quic-go/quic-go v0.53.0
|
github.com/quic-go/quic-go v0.53.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/ugorji/go/codec v1.3.0
|
github.com/ugorji/go/codec v1.3.0
|
||||||
golang.org/x/net v0.41.0
|
golang.org/x/net v0.42.0
|
||||||
google.golang.org/protobuf v1.36.6
|
google.golang.org/protobuf v1.36.6
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,11 +34,11 @@ 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.39.0 // indirect
|
golang.org/x/crypto v0.40.0 // indirect
|
||||||
golang.org/x/mod v0.25.0 // indirect
|
golang.org/x/mod v0.25.0 // indirect
|
||||||
golang.org/x/sync v0.15.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/tools v0.33.0 // indirect
|
golang.org/x/tools v0.34.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
24
go.sum
24
go.sum
@ -67,21 +67,21 @@ 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.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
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=
|
||||||
|
@ -44,7 +44,7 @@ type LoggerConfig struct {
|
|||||||
// Optional. Default value is gin.DefaultWriter.
|
// Optional. Default value is gin.DefaultWriter.
|
||||||
Output io.Writer
|
Output io.Writer
|
||||||
|
|
||||||
// SkipPaths is an url path array which logs are not written.
|
// SkipPaths is a URL path array which logs are not written.
|
||||||
// Optional.
|
// Optional.
|
||||||
SkipPaths []string
|
SkipPaths []string
|
||||||
|
|
||||||
|
2
mode.go
2
mode.go
@ -65,7 +65,7 @@ func SetMode(value string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch value {
|
switch value {
|
||||||
case DebugMode, "":
|
case DebugMode:
|
||||||
atomic.StoreInt32(&ginMode, debugCode)
|
atomic.StoreInt32(&ginMode, debugCode)
|
||||||
case ReleaseMode:
|
case ReleaseMode:
|
||||||
atomic.StoreInt32(&ginMode, releaseCode)
|
atomic.StoreInt32(&ginMode, releaseCode)
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var MaxHandlers = 32
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
SetMode(TestMode)
|
SetMode(TestMode)
|
||||||
}
|
}
|
||||||
@ -193,3 +195,25 @@ func testRoutesInterface(t *testing.T, r IRoutes) {
|
|||||||
assert.Equal(t, r, r.Static("/static", "."))
|
assert.Equal(t, r, r.Static("/static", "."))
|
||||||
assert.Equal(t, r, r.StaticFS("/static2", Dir(".", false)))
|
assert.Equal(t, r, r.StaticFS("/static2", Dir(".", false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouterGroupCombineHandlersTooManyHandlers(t *testing.T) {
|
||||||
|
group := &RouterGroup{
|
||||||
|
Handlers: make(HandlersChain, MaxHandlers), // Assume group already has MaxHandlers middleware
|
||||||
|
}
|
||||||
|
tooManyHandlers := make(HandlersChain, MaxHandlers) // Add MaxHandlers more, total 2 * MaxHandlers
|
||||||
|
|
||||||
|
// This should trigger panic
|
||||||
|
assert.Panics(t, func() {
|
||||||
|
group.combineHandlers(tooManyHandlers)
|
||||||
|
}, "should panic due to too many handlers")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterGroupCombineHandlersEmptySliceNotNil(t *testing.T) {
|
||||||
|
group := &RouterGroup{
|
||||||
|
Handlers: HandlersChain{},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := group.combineHandlers(HandlersChain{})
|
||||||
|
assert.NotNil(t, result, "result should not be nil even with empty handlers")
|
||||||
|
assert.Empty(t, result, "empty handlers should return empty chain")
|
||||||
|
}
|
||||||
|
2
testdata/test_file.txt
vendored
Normal file
2
testdata/test_file.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
This is a test file for Context.File() method testing.
|
||||||
|
It contains some sample content to verify file serving functionality.
|
Loading…
x
Reference in New Issue
Block a user