Compare commits

...

13 Commits

Author SHA1 Message Date
Ludovico Cavedon e7039cb64b
Merge 929794596f into e46bd52185 2024-11-20 15:24:19 +03:00
haesuo566 e46bd52185
refactor(context): add an optional permission parameter to the SaveUploadedFile method (#4068) (#4088)
Co-authored-by: hso <hso@trinitysoft.co.kr>
2024-11-15 23:54:06 +08:00
Matthieu MOREL e8d34d053f
ci(lint): enable usestdlibvars linter (#4091)
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2024-11-15 23:52:16 +08:00
Matthieu MOREL 02c1144f31
ci(lint): enable perfsprint linter (#4090)
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2024-11-15 23:51:12 +08:00
Bo-Yi Wu f875d87283
chore(context): test context initialization and handler logic (#4087)
* enhance code imported by #3413

if it needs to check if the handler is nil, tie c.index shall
always ++

* test: refactor test context initialization and handler logic

- Remove an empty line in `TestContextInitQueryCache`
- Add `TestContextNext` function with tests for `Next` method behavior with no handlers, one handler, and multiple handlers

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: zjj <zhong2plus@gmail.com>
2024-11-15 23:49:08 +08:00
Konovalov Maxim c8a3adc657
refactor(context): simplify "GetType()" functions (#4080)
This PR introduces a generic function, getTyped[T any], to simplify value retrieval in the Context struct. It replaces repetitive type assertions in the GetString  GetBool etc. methods.

Co-authored-by: Maksim Konovalov <maksim.konovalov@vk.team>
2024-10-29 23:24:53 +08:00
Xinyu Kuo ea53388e6e
fix(tree): Keep panic infos consistent when wildcard type build faild (#4077) 2024-10-26 08:28:59 +08:00
Oskar Karpiński 9d11234efe
docs(gin): Replace broken link to documentation with valid (#4064) 2024-10-26 08:26:25 +08:00
Xinyu Kuo 647311aba2
refactor(context): refactor context handling and improve test robustness (#4066)
Use assert.InDelta for float comparison with tolerance in TestContextGetFloat32
Remove unnecessary blank line in TestContextInitQueryCache
Replace anonymous struct with named contextKey type in TestContextWithFallbackValueFromRequestContext
Update context key handling in TestContextWithFallbackValueFromRequestContext to use contextKey type
2024-10-25 09:33:31 +08:00
tsukasa-ino 299c6f30e3
docs: trimmed some white spaces (#4070) 2024-10-25 09:16:40 +08:00
Enzo Lanzellotti b080116a7f
docs(readme): add Portuguese documentation. (#4078) 2024-10-25 09:08:11 +08:00
wangjingcun ad740d508f
docs(context): fix some function names in comment (#4079) 2024-10-25 09:07:03 +08:00
Ludovico Cavedon 929794596f Do not QueryEscape cookies (#1717)
Cookies values are already sanitized by the Go http library, so there is
no need to invoke QueryEscape() on them.
Furthermore, QueryEscape() has the undesirable effect of replacing
spaces wiith "+" characters.
2023-08-02 00:13:28 +02:00
25 changed files with 551 additions and 490 deletions

View File

@ -26,7 +26,7 @@ jobs:
- name: Setup golangci-lint - name: Setup golangci-lint
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: v1.58.1 version: v1.61.0
args: --verbose args: --verbose
test: test:
needs: lint needs: lint

View File

@ -16,8 +16,10 @@ linters:
- nakedret - nakedret
- nilerr - nilerr
- nolintlint - nolintlint
- perfsprint
- revive - revive
- testifylint - testifylint
- usestdlibvars
- wastedassign - wastedassign
linters-settings: linters-settings:
@ -34,6 +36,13 @@ linters-settings:
- G112 - G112
- G201 - G201
- G203 - G203
perfsprint:
err-error: true
errorf: true
fiximports: true
int-conversion: true
sprintf1: true
strconcat: true
testifylint: testifylint:
enable-all: true enable-all: true

View File

@ -102,6 +102,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in
- [한국어](https://gin-gonic.com/ko-kr/docs/) - [한국어](https://gin-gonic.com/ko-kr/docs/)
- [Turkish](https://gin-gonic.com/tr/docs/) - [Turkish](https://gin-gonic.com/tr/docs/)
- [Persian](https://gin-gonic.com/fa/docs/) - [Persian](https://gin-gonic.com/fa/docs/)
- [Português](https://gin-gonic.com/pt/docs/)
### Articles ### Articles

View File

@ -90,7 +90,7 @@ func TestBasicAuthSucceed(t *testing.T) {
}) })
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/login", nil) req, _ := http.NewRequest(http.MethodGet, "/login", nil)
req.Header.Set("Authorization", authorizationHeader("admin", "password")) req.Header.Set("Authorization", authorizationHeader("admin", "password"))
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
@ -109,7 +109,7 @@ func TestBasicAuth401(t *testing.T) {
}) })
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/login", nil) req, _ := http.NewRequest(http.MethodGet, "/login", nil)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
@ -129,7 +129,7 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) {
}) })
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/login", nil) req, _ := http.NewRequest(http.MethodGet, "/login", nil)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
@ -147,7 +147,7 @@ func TestBasicAuthForProxySucceed(t *testing.T) {
}) })
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil) req, _ := http.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password")) req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password"))
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
@ -166,7 +166,7 @@ func TestBasicAuthForProxy407(t *testing.T) {
}) })
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil) req, _ := http.NewRequest(http.MethodGet, "/test", nil)
req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password"))) req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
router.ServeHTTP(w, req) router.ServeHTTP(w, req)

View File

@ -14,21 +14,21 @@ import (
func BenchmarkOneRoute(B *testing.B) { func BenchmarkOneRoute(B *testing.B) {
router := New() router := New()
router.GET("/ping", func(c *Context) {}) router.GET("/ping", func(c *Context) {})
runRequest(B, router, "GET", "/ping") runRequest(B, router, http.MethodGet, "/ping")
} }
func BenchmarkRecoveryMiddleware(B *testing.B) { func BenchmarkRecoveryMiddleware(B *testing.B) {
router := New() router := New()
router.Use(Recovery()) router.Use(Recovery())
router.GET("/", func(c *Context) {}) router.GET("/", func(c *Context) {})
runRequest(B, router, "GET", "/") runRequest(B, router, http.MethodGet, "/")
} }
func BenchmarkLoggerMiddleware(B *testing.B) { func BenchmarkLoggerMiddleware(B *testing.B) {
router := New() router := New()
router.Use(LoggerWithWriter(newMockWriter())) router.Use(LoggerWithWriter(newMockWriter()))
router.GET("/", func(c *Context) {}) router.GET("/", func(c *Context) {})
runRequest(B, router, "GET", "/") runRequest(B, router, http.MethodGet, "/")
} }
func BenchmarkManyHandlers(B *testing.B) { func BenchmarkManyHandlers(B *testing.B) {
@ -37,7 +37,7 @@ func BenchmarkManyHandlers(B *testing.B) {
router.Use(func(c *Context) {}) router.Use(func(c *Context) {})
router.Use(func(c *Context) {}) router.Use(func(c *Context) {})
router.GET("/ping", func(c *Context) {}) router.GET("/ping", func(c *Context) {})
runRequest(B, router, "GET", "/ping") runRequest(B, router, http.MethodGet, "/ping")
} }
func Benchmark5Params(B *testing.B) { func Benchmark5Params(B *testing.B) {
@ -45,7 +45,7 @@ func Benchmark5Params(B *testing.B) {
router := New() router := New()
router.Use(func(c *Context) {}) router.Use(func(c *Context) {})
router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *Context) {}) router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *Context) {})
runRequest(B, router, "GET", "/param/path/to/parameter/john/12345") runRequest(B, router, http.MethodGet, "/param/path/to/parameter/john/12345")
} }
func BenchmarkOneRouteJSON(B *testing.B) { func BenchmarkOneRouteJSON(B *testing.B) {
@ -56,7 +56,7 @@ func BenchmarkOneRouteJSON(B *testing.B) {
router.GET("/json", func(c *Context) { router.GET("/json", func(c *Context) {
c.JSON(http.StatusOK, data) c.JSON(http.StatusOK, data)
}) })
runRequest(B, router, "GET", "/json") runRequest(B, router, http.MethodGet, "/json")
} }
func BenchmarkOneRouteHTML(B *testing.B) { func BenchmarkOneRouteHTML(B *testing.B) {
@ -68,7 +68,7 @@ func BenchmarkOneRouteHTML(B *testing.B) {
router.GET("/html", func(c *Context) { router.GET("/html", func(c *Context) {
c.HTML(http.StatusOK, "index", "hola") c.HTML(http.StatusOK, "index", "hola")
}) })
runRequest(B, router, "GET", "/html") runRequest(B, router, http.MethodGet, "/html")
} }
func BenchmarkOneRouteSet(B *testing.B) { func BenchmarkOneRouteSet(B *testing.B) {
@ -76,7 +76,7 @@ func BenchmarkOneRouteSet(B *testing.B) {
router.GET("/ping", func(c *Context) { router.GET("/ping", func(c *Context) {
c.Set("key", "value") c.Set("key", "value")
}) })
runRequest(B, router, "GET", "/ping") runRequest(B, router, http.MethodGet, "/ping")
} }
func BenchmarkOneRouteString(B *testing.B) { func BenchmarkOneRouteString(B *testing.B) {
@ -84,13 +84,13 @@ func BenchmarkOneRouteString(B *testing.B) {
router.GET("/text", func(c *Context) { router.GET("/text", func(c *Context) {
c.String(http.StatusOK, "this is a plain text") c.String(http.StatusOK, "this is a plain text")
}) })
runRequest(B, router, "GET", "/text") runRequest(B, router, http.MethodGet, "/text")
} }
func BenchmarkManyRoutesFist(B *testing.B) { func BenchmarkManyRoutesFist(B *testing.B) {
router := New() router := New()
router.Any("/ping", func(c *Context) {}) router.Any("/ping", func(c *Context) {})
runRequest(B, router, "GET", "/ping") runRequest(B, router, http.MethodGet, "/ping")
} }
func BenchmarkManyRoutesLast(B *testing.B) { func BenchmarkManyRoutesLast(B *testing.B) {
@ -103,7 +103,7 @@ func Benchmark404(B *testing.B) {
router := New() router := New()
router.Any("/something", func(c *Context) {}) router.Any("/something", func(c *Context) {})
router.NoRoute(func(c *Context) {}) router.NoRoute(func(c *Context) {})
runRequest(B, router, "GET", "/ping") runRequest(B, router, http.MethodGet, "/ping")
} }
func Benchmark404Many(B *testing.B) { func Benchmark404Many(B *testing.B) {
@ -118,7 +118,7 @@ func Benchmark404Many(B *testing.B) {
router.GET("/user/:id/:mode", func(c *Context) {}) router.GET("/user/:id/:mode", func(c *Context) {})
router.NoRoute(func(c *Context) {}) router.NoRoute(func(c *Context) {})
runRequest(B, router, "GET", "/viewfake") runRequest(B, router, http.MethodGet, "/viewfake")
} }
type mockWriter struct { type mockWriter struct {

View File

@ -8,6 +8,7 @@ package binding
import ( import (
"bytes" "bytes"
"net/http"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -39,20 +40,20 @@ func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body,
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := FooStruct{} obj := FooStruct{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
req.Header.Add("Content-Type", MIMEMSGPACK) req.Header.Add("Content-Type", MIMEMSGPACK)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{} obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEMSGPACK) req.Header.Add("Content-Type", MIMEMSGPACK)
err = MsgPack.Bind(req, &obj) err = MsgPack.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
func TestBindingDefaultMsgPack(t *testing.T) { func TestBindingDefaultMsgPack(t *testing.T) {
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK)) assert.Equal(t, MsgPack, Default(http.MethodPost, MIMEMSGPACK))
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2)) assert.Equal(t, MsgPack, Default(http.MethodPut, MIMEMSGPACK2))
} }

View File

@ -145,31 +145,31 @@ type FooStructForMapPtrType struct {
} }
func TestBindingDefault(t *testing.T) { func TestBindingDefault(t *testing.T) {
assert.Equal(t, Form, Default("GET", "")) assert.Equal(t, Form, Default(http.MethodGet, ""))
assert.Equal(t, Form, Default("GET", MIMEJSON)) assert.Equal(t, Form, Default(http.MethodGet, MIMEJSON))
assert.Equal(t, JSON, Default("POST", MIMEJSON)) assert.Equal(t, JSON, Default(http.MethodPost, MIMEJSON))
assert.Equal(t, JSON, Default("PUT", MIMEJSON)) assert.Equal(t, JSON, Default(http.MethodPut, MIMEJSON))
assert.Equal(t, XML, Default("POST", MIMEXML)) assert.Equal(t, XML, Default(http.MethodPost, MIMEXML))
assert.Equal(t, XML, Default("PUT", MIMEXML2)) assert.Equal(t, XML, Default(http.MethodPut, MIMEXML2))
assert.Equal(t, Form, Default("POST", MIMEPOSTForm)) assert.Equal(t, Form, Default(http.MethodPost, MIMEPOSTForm))
assert.Equal(t, Form, Default("PUT", MIMEPOSTForm)) assert.Equal(t, Form, Default(http.MethodPut, MIMEPOSTForm))
assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm)) assert.Equal(t, FormMultipart, Default(http.MethodPost, MIMEMultipartPOSTForm))
assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm)) assert.Equal(t, FormMultipart, Default(http.MethodPut, MIMEMultipartPOSTForm))
assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF)) assert.Equal(t, ProtoBuf, Default(http.MethodPost, MIMEPROTOBUF))
assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF)) assert.Equal(t, ProtoBuf, Default(http.MethodPut, MIMEPROTOBUF))
assert.Equal(t, YAML, Default("POST", MIMEYAML)) assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML))
assert.Equal(t, YAML, Default("PUT", MIMEYAML)) assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML))
assert.Equal(t, YAML, Default("POST", MIMEYAML2)) assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML2))
assert.Equal(t, YAML, Default("PUT", MIMEYAML2)) assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML2))
assert.Equal(t, TOML, Default("POST", MIMETOML)) assert.Equal(t, TOML, Default(http.MethodPost, MIMETOML))
assert.Equal(t, TOML, Default("PUT", MIMETOML)) assert.Equal(t, TOML, Default(http.MethodPut, MIMETOML))
} }
func TestBindingJSONNilBody(t *testing.T) { func TestBindingJSONNilBody(t *testing.T) {
@ -227,137 +227,137 @@ func TestBindingJSONStringMap(t *testing.T) {
} }
func TestBindingForm(t *testing.T) { func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST", testFormBinding(t, http.MethodPost,
"/", "/", "/", "/",
"foo=bar&bar=foo", "bar2=foo") "foo=bar&bar=foo", "bar2=foo")
} }
func TestBindingForm2(t *testing.T) { func TestBindingForm2(t *testing.T) {
testFormBinding(t, "GET", testFormBinding(t, http.MethodGet,
"/?foo=bar&bar=foo", "/?bar2=foo", "/?foo=bar&bar=foo", "/?bar2=foo",
"", "") "", "")
} }
func TestBindingFormEmbeddedStruct(t *testing.T) { func TestBindingFormEmbeddedStruct(t *testing.T) {
testFormBindingEmbeddedStruct(t, "POST", testFormBindingEmbeddedStruct(t, http.MethodPost,
"/", "/", "/", "/",
"page=1&size=2&appkey=test-appkey", "bar2=foo") "page=1&size=2&appkey=test-appkey", "bar2=foo")
} }
func TestBindingFormEmbeddedStruct2(t *testing.T) { func TestBindingFormEmbeddedStruct2(t *testing.T) {
testFormBindingEmbeddedStruct(t, "GET", testFormBindingEmbeddedStruct(t, http.MethodGet,
"/?page=1&size=2&appkey=test-appkey", "/?bar2=foo", "/?page=1&size=2&appkey=test-appkey", "/?bar2=foo",
"", "") "", "")
} }
func TestBindingFormDefaultValue(t *testing.T) { func TestBindingFormDefaultValue(t *testing.T) {
testFormBindingDefaultValue(t, "POST", testFormBindingDefaultValue(t, http.MethodPost,
"/", "/", "/", "/",
"foo=bar", "bar2=foo") "foo=bar", "bar2=foo")
} }
func TestBindingFormDefaultValue2(t *testing.T) { func TestBindingFormDefaultValue2(t *testing.T) {
testFormBindingDefaultValue(t, "GET", testFormBindingDefaultValue(t, http.MethodGet,
"/?foo=bar", "/?bar2=foo", "/?foo=bar", "/?bar2=foo",
"", "") "", "")
} }
func TestBindingFormForTime(t *testing.T) { func TestBindingFormForTime(t *testing.T) {
testFormBindingForTime(t, "POST", testFormBindingForTime(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo") "time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo")
testFormBindingForTimeNotUnixFormat(t, "POST", testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, "POST", testFormBindingForTimeNotFormat(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15", "bar2=foo") "time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailFormat(t, "POST", testFormBindingForTimeFailFormat(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15", "bar2=foo") "time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailLocation(t, "POST", testFormBindingForTimeFailLocation(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15", "bar2=foo") "time_foo=2017-11-15", "bar2=foo")
} }
func TestBindingFormForTime2(t *testing.T) { func TestBindingFormForTime2(t *testing.T) {
testFormBindingForTime(t, "GET", testFormBindingForTime(t, http.MethodGet,
"/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo", "/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo",
"", "") "", "")
testFormBindingForTimeNotUnixFormat(t, "POST", testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
"/", "/", "/", "/",
"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo") "time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, "GET", testFormBindingForTimeNotFormat(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo", "/?time_foo=2017-11-15", "/?bar2=foo",
"", "") "", "")
testFormBindingForTimeFailFormat(t, "GET", testFormBindingForTimeFailFormat(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo", "/?time_foo=2017-11-15", "/?bar2=foo",
"", "") "", "")
testFormBindingForTimeFailLocation(t, "GET", testFormBindingForTimeFailLocation(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo", "/?time_foo=2017-11-15", "/?bar2=foo",
"", "") "", "")
} }
func TestFormBindingIgnoreField(t *testing.T) { func TestFormBindingIgnoreField(t *testing.T) {
testFormBindingIgnoreField(t, "POST", testFormBindingIgnoreField(t, http.MethodPost,
"/", "/", "/", "/",
"-=bar", "") "-=bar", "")
} }
func TestBindingFormInvalidName(t *testing.T) { func TestBindingFormInvalidName(t *testing.T) {
testFormBindingInvalidName(t, "POST", testFormBindingInvalidName(t, http.MethodPost,
"/", "/", "/", "/",
"test_name=bar", "bar2=foo") "test_name=bar", "bar2=foo")
} }
func TestBindingFormInvalidName2(t *testing.T) { func TestBindingFormInvalidName2(t *testing.T) {
testFormBindingInvalidName2(t, "POST", testFormBindingInvalidName2(t, http.MethodPost,
"/", "/", "/", "/",
"map_foo=bar", "bar2=foo") "map_foo=bar", "bar2=foo")
} }
func TestBindingFormForType(t *testing.T) { func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"map_foo={\"bar\":123}", "map_foo=1", "Map") "map_foo={\"bar\":123}", "map_foo=1", "Map")
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice") "slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
testFormBindingForType(t, "GET", testFormBindingForType(t, http.MethodGet,
"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2", "/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
"", "", "Slice") "", "", "Slice")
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap") "slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
testFormBindingForType(t, "GET", testFormBindingForType(t, http.MethodGet,
"/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2", "/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2",
"", "", "SliceMap") "", "", "SliceMap")
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"ptr_bar=test", "bar2=test", "Ptr") "ptr_bar=test", "bar2=test", "Ptr")
testFormBindingForType(t, "GET", testFormBindingForType(t, http.MethodGet,
"/?ptr_bar=test", "/?bar2=test", "/?ptr_bar=test", "/?bar2=test",
"", "", "Ptr") "", "", "Ptr")
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"idx=123", "id1=1", "Struct") "idx=123", "id1=1", "Struct")
testFormBindingForType(t, "GET", testFormBindingForType(t, http.MethodGet,
"/?idx=123", "/?id1=1", "/?idx=123", "/?id1=1",
"", "", "Struct") "", "", "Struct")
testFormBindingForType(t, "POST", testFormBindingForType(t, http.MethodPost,
"/", "/", "/", "/",
"name=thinkerou", "name1=ou", "StructPointer") "name=thinkerou", "name1=ou", "StructPointer")
testFormBindingForType(t, "GET", testFormBindingForType(t, http.MethodGet,
"/?name=thinkerou", "/?name1=ou", "/?name=thinkerou", "/?name1=ou",
"", "", "StructPointer") "", "", "StructPointer")
} }
@ -374,7 +374,7 @@ func TestBindingFormStringMap(t *testing.T) {
func TestBindingFormStringSliceMap(t *testing.T) { func TestBindingFormStringSliceMap(t *testing.T) {
obj := make(map[string][]string) obj := make(map[string][]string)
req := requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") req := requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
err := Form.Bind(req, &obj) err := Form.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
@ -387,38 +387,38 @@ func TestBindingFormStringSliceMap(t *testing.T) {
assert.True(t, reflect.DeepEqual(obj, target)) assert.True(t, reflect.DeepEqual(obj, target))
objInvalid := make(map[string][]int) objInvalid := make(map[string][]int)
req = requestWithBody("POST", "/", "foo=something&foo=bar&hello=world") req = requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
err = Form.Bind(req, &objInvalid) err = Form.Bind(req, &objInvalid)
require.Error(t, err) require.Error(t, err)
} }
func TestBindingQuery(t *testing.T) { func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST", testQueryBinding(t, http.MethodPost,
"/?foo=bar&bar=foo", "/", "/?foo=bar&bar=foo", "/",
"foo=unused", "bar2=foo") "foo=unused", "bar2=foo")
} }
func TestBindingQuery2(t *testing.T) { func TestBindingQuery2(t *testing.T) {
testQueryBinding(t, "GET", testQueryBinding(t, http.MethodGet,
"/?foo=bar&bar=foo", "/?bar2=foo", "/?foo=bar&bar=foo", "/?bar2=foo",
"foo=unused", "") "foo=unused", "")
} }
func TestBindingQueryFail(t *testing.T) { func TestBindingQueryFail(t *testing.T) {
testQueryBindingFail(t, "POST", testQueryBindingFail(t, http.MethodPost,
"/?map_foo=", "/", "/?map_foo=", "/",
"map_foo=unused", "bar2=foo") "map_foo=unused", "bar2=foo")
} }
func TestBindingQueryFail2(t *testing.T) { func TestBindingQueryFail2(t *testing.T) {
testQueryBindingFail(t, "GET", testQueryBindingFail(t, http.MethodGet,
"/?map_foo=", "/?bar2=foo", "/?map_foo=", "/?bar2=foo",
"map_foo=unused", "") "map_foo=unused", "")
} }
func TestBindingQueryBoolFail(t *testing.T) { func TestBindingQueryBoolFail(t *testing.T) {
testQueryBindingBoolFail(t, "GET", testQueryBindingBoolFail(t, http.MethodGet,
"/?bool_foo=fasl", "/?bar2=foo", "/?bool_foo=fasl", "/?bar2=foo",
"bool_foo=unused", "") "bool_foo=unused", "")
} }
@ -427,7 +427,7 @@ func TestBindingQueryStringMap(t *testing.T) {
b := Query b := Query
obj := make(map[string]string) obj := make(map[string]string)
req := requestWithBody("GET", "/?foo=bar&hello=world", "") req := requestWithBody(http.MethodGet, "/?foo=bar&hello=world", "")
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, obj) assert.NotNil(t, obj)
@ -436,7 +436,7 @@ func TestBindingQueryStringMap(t *testing.T) {
assert.Equal(t, "world", obj["hello"]) assert.Equal(t, "world", obj["hello"])
obj = make(map[string]string) obj = make(map[string]string)
req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last req = requestWithBody(http.MethodGet, "/?foo=bar&foo=2&hello=world", "") // should pick last
err = b.Bind(req, &obj) err = b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, obj) assert.NotNil(t, obj)
@ -495,28 +495,28 @@ func TestBindingYAMLFail(t *testing.T) {
} }
func createFormPostRequest(t *testing.T) *http.Request { func createFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo")) req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm) req.Header.Set("Content-Type", MIMEPOSTForm)
return req return req
} }
func createDefaultFormPostRequest(t *testing.T) *http.Request { func createDefaultFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar")) req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm) req.Header.Set("Content-Type", MIMEPOSTForm)
return req return req
} }
func createFormPostRequestForMap(t *testing.T) *http.Request { func createFormPostRequestForMap(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}")) req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm) req.Header.Set("Content-Type", MIMEPOSTForm)
return req return req
} }
func createFormPostRequestForMapFail(t *testing.T) *http.Request { func createFormPostRequestForMapFail(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello")) req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm) req.Header.Set("Content-Type", MIMEPOSTForm)
return req return req
@ -540,7 +540,7 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request {
_, err = io.Copy(fw, f) _, err = io.Copy(fw, f)
require.NoError(t, err) require.NoError(t, err)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err2) require.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
@ -565,7 +565,7 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request {
_, err = io.Copy(fw, f) _, err = io.Copy(fw, f)
require.NoError(t, err) require.NoError(t, err)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err2) require.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
@ -581,7 +581,7 @@ func createFormMultipartRequest(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("foo", "bar")) require.NoError(t, mw.WriteField("foo", "bar"))
require.NoError(t, mw.WriteField("bar", "foo")) require.NoError(t, mw.WriteField("bar", "foo"))
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body) req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req return req
@ -595,7 +595,7 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}")) require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req return req
@ -609,7 +609,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary)) require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("map_foo", "3.14")) require.NoError(t, mw.WriteField("map_foo", "3.14"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body) req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req return req
@ -731,7 +731,7 @@ func TestBindingProtoBufFail(t *testing.T) {
func TestValidationFails(t *testing.T) { func TestValidationFails(t *testing.T) {
var obj FooStruct var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`) req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj) err := JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -742,7 +742,7 @@ func TestValidationDisabled(t *testing.T) {
defer func() { Validator = backup }() defer func() { Validator = backup }()
var obj FooStruct var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`) req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj) err := JSON.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
} }
@ -753,7 +753,7 @@ func TestRequiredSucceeds(t *testing.T) {
} }
var obj HogeStruct var obj HogeStruct
req := requestWithBody("POST", "/", `{"hoge": 0}`) req := requestWithBody(http.MethodPost, "/", `{"hoge": 0}`)
err := JSON.Bind(req, &obj) err := JSON.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
} }
@ -764,7 +764,7 @@ func TestRequiredFails(t *testing.T) {
} }
var obj HogeStruct var obj HogeStruct
req := requestWithBody("POST", "/", `{"boen": 0}`) req := requestWithBody(http.MethodPost, "/", `{"boen": 0}`)
err := JSON.Bind(req, &obj) err := JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -778,12 +778,12 @@ func TestHeaderBinding(t *testing.T) {
} }
var theader tHeader var theader tHeader
req := requestWithBody("GET", "/", "") req := requestWithBody(http.MethodGet, "/", "")
req.Header.Add("limit", "1000") req.Header.Add("limit", "1000")
require.NoError(t, h.Bind(req, &theader)) require.NoError(t, h.Bind(req, &theader))
assert.Equal(t, 1000, theader.Limit) assert.Equal(t, 1000, theader.Limit)
req = requestWithBody("GET", "/", "") req = requestWithBody(http.MethodGet, "/", "")
req.Header.Add("fail", `{fail:fail}`) req.Header.Add("fail", `{fail:fail}`)
type failStruct struct { type failStruct struct {
@ -843,7 +843,7 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba
obj := QueryTest{} obj := QueryTest{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -859,7 +859,7 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{} obj := FooBarStruct{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -879,7 +879,7 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB
obj := FooDefaultBarStruct{} obj := FooDefaultBarStruct{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -898,14 +898,14 @@ func TestFormBindingFail(t *testing.T) {
assert.Equal(t, "form", b.Name()) assert.Equal(t, "form", b.Name())
obj := FooBarStruct{} obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil) req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
func TestFormBindingMultipartFail(t *testing.T) { func TestFormBindingMultipartFail(t *testing.T) {
obj := FooBarStruct{} obj := FooBarStruct{}
req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar")) req, err := http.NewRequest(http.MethodPost, "/", strings.NewReader("foo=bar"))
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary") req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary")
_, err = req.MultipartReader() _, err = req.MultipartReader()
@ -919,7 +919,7 @@ func TestFormPostBindingFail(t *testing.T) {
assert.Equal(t, "form-urlencoded", b.Name()) assert.Equal(t, "form-urlencoded", b.Name())
obj := FooBarStruct{} obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil) req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -929,7 +929,7 @@ func TestFormMultipartBindingFail(t *testing.T) {
assert.Equal(t, "multipart/form-data", b.Name()) assert.Equal(t, "multipart/form-data", b.Name())
obj := FooBarStruct{} obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil) req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -940,7 +940,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
obj := FooBarStructForTimeType{} obj := FooBarStructForTimeType{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -965,7 +965,7 @@ func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, bo
obj := FooStructForTimeTypeNotUnixFormat{} obj := FooStructForTimeTypeNotUnixFormat{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -983,7 +983,7 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeNotFormat{} obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1001,7 +1001,7 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeFailFormat{} obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1019,7 +1019,7 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod
obj := FooStructForTimeTypeFailLocation{} obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1037,7 +1037,7 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo
obj := FooStructForIgnoreFormTag{} obj := FooStructForIgnoreFormTag{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1052,7 +1052,7 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
obj := InvalidNameType{} obj := InvalidNameType{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1071,7 +1071,7 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB
obj := InvalidNameMapType{} obj := InvalidNameMapType{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1088,7 +1088,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
assert.Equal(t, "form", b.Name()) assert.Equal(t, "form", b.Name())
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
switch typ { switch typ {
@ -1159,7 +1159,7 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{} obj := FooBarStruct{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1174,7 +1174,7 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str
obj := FooStructForMapType{} obj := FooStructForMapType{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1187,7 +1187,7 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody
obj := FooStructForBoolType{} obj := FooStructForBoolType{}
req := requestWithBody(method, path, body) req := requestWithBody(method, path, body)
if method == "POST" { if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
@ -1198,13 +1198,13 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := FooStruct{} obj := FooStruct{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{} obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -1213,19 +1213,19 @@ func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
var obj1 []FooStruct var obj1 []FooStruct
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj1) err := b.Bind(req, &obj1)
require.NoError(t, err) require.NoError(t, err)
var obj2 []FooStruct var obj2 []FooStruct
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj2) err = JSON.Bind(req, &obj2)
require.Error(t, err) require.Error(t, err)
} }
func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) { func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
obj := make(map[string]string) obj := make(map[string]string)
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
if b.Name() == "form" { if b.Name() == "form" {
req.Header.Add("Content-Type", MIMEPOSTForm) req.Header.Add("Content-Type", MIMEPOSTForm)
} }
@ -1238,13 +1238,13 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB
if badPath != "" && badBody != "" { if badPath != "" && badBody != "" {
obj = make(map[string]string) obj = make(map[string]string)
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = b.Bind(req, &obj) err = b.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
objInt := make(map[string]int) objInt := make(map[string]int)
req = requestWithBody("POST", path, body) req = requestWithBody(http.MethodPost, path, body)
err = b.Bind(req, &objInt) err = b.Bind(req, &objInt)
require.Error(t, err) require.Error(t, err)
} }
@ -1253,7 +1253,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{} obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
EnableDecoderUseNumber = true EnableDecoderUseNumber = true
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
@ -1263,7 +1263,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, int64(123), v) assert.Equal(t, int64(123), v)
obj = FooStructUseNumber{} obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -1272,7 +1272,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{} obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
EnableDecoderUseNumber = false EnableDecoderUseNumber = false
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
@ -1281,7 +1281,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod
assert.InDelta(t, float64(123), obj.Foo, 0.01) assert.InDelta(t, float64(123), obj.Foo, 0.01)
obj = FooStructUseNumber{} obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -1293,13 +1293,13 @@ func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath
}() }()
obj := FooStructDisallowUnknownFields{} obj := FooStructDisallowUnknownFields{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo) assert.Equal(t, "bar", obj.Foo)
obj = FooStructDisallowUnknownFields{} obj = FooStructDisallowUnknownFields{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), "what") assert.Contains(t, err.Error(), "what")
@ -1309,13 +1309,13 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := FooStruct{} obj := FooStruct{}
req := requestWithBody("POST", 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.Equal(t, "", obj.Foo)
obj = FooStruct{} obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
} }
@ -1324,14 +1324,14 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := protoexample.Test{} obj := protoexample.Test{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
req.Header.Add("Content-Type", MIMEPROTOBUF) req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj) err := b.Bind(req, &obj)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "yes", *obj.Label) assert.Equal(t, "yes", *obj.Label)
obj = protoexample.Test{} obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF) req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj) err = ProtoBuf.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)
@ -1358,28 +1358,28 @@ func TestPlainBinding(t *testing.T) {
assert.Equal(t, "plain", p.Name()) assert.Equal(t, "plain", p.Name())
var s string var s string
req := requestWithBody("POST", "/", "test string") req := requestWithBody(http.MethodPost, "/", "test string")
require.NoError(t, p.Bind(req, &s)) require.NoError(t, p.Bind(req, &s))
assert.Equal(t, "test string", s) assert.Equal(t, "test string", s)
var bs []byte var bs []byte
req = requestWithBody("POST", "/", "test []byte") req = requestWithBody(http.MethodPost, "/", "test []byte")
require.NoError(t, p.Bind(req, &bs)) require.NoError(t, p.Bind(req, &bs))
assert.Equal(t, bs, []byte("test []byte")) assert.Equal(t, bs, []byte("test []byte"))
var i int var i int
req = requestWithBody("POST", "/", "test fail") req = requestWithBody(http.MethodPost, "/", "test fail")
require.Error(t, p.Bind(req, &i)) require.Error(t, p.Bind(req, &i))
req = requestWithBody("POST", "/", "") req = requestWithBody(http.MethodPost, "/", "")
req.Body = &failRead{} req.Body = &failRead{}
require.Error(t, p.Bind(req, &s)) require.Error(t, p.Bind(req, &s))
req = requestWithBody("POST", "/", "") req = requestWithBody(http.MethodPost, "/", "")
require.NoError(t, p.Bind(req, nil)) require.NoError(t, p.Bind(req, nil))
var ptr *string var ptr *string
req = requestWithBody("POST", "/", "") req = requestWithBody(http.MethodPost, "/", "")
require.NoError(t, p.Bind(req, ptr)) require.NoError(t, p.Bind(req, ptr))
} }
@ -1387,7 +1387,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, name, b.Name()) assert.Equal(t, name, b.Name())
obj := protoexample.Test{} obj := protoexample.Test{}
req := requestWithBody("POST", path, body) req := requestWithBody(http.MethodPost, path, body)
req.Body = io.NopCloser(&hook{}) req.Body = io.NopCloser(&hook{})
req.Header.Add("Content-Type", MIMEPROTOBUF) req.Header.Add("Content-Type", MIMEPROTOBUF)
@ -1402,7 +1402,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, "obj is not ProtoMessage", err.Error()) assert.Equal(t, "obj is not ProtoMessage", err.Error())
obj = protoexample.Test{} obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody) req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF) req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj) err = ProtoBuf.Bind(req, &obj)
require.Error(t, err) require.Error(t, err)

View File

@ -6,7 +6,7 @@ package binding
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "errors"
"mime/multipart" "mime/multipart"
"reflect" "reflect"
"strconv" "strconv"
@ -494,7 +494,7 @@ type customUnmarshalParamType struct {
func (f *customUnmarshalParamType) UnmarshalParam(param string) error { func (f *customUnmarshalParamType) UnmarshalParam(param string) error {
parts := strings.Split(param, ":") parts := strings.Split(param, ":")
if len(parts) != 3 { if len(parts) != 3 {
return fmt.Errorf("invalid format") return errors.New("invalid format")
} }
f.Protocol = parts[0] f.Protocol = parts[0]
f.Path = parts[1] f.Path = parts[1]
@ -556,7 +556,7 @@ func (p *customPath) UnmarshalParam(param string) error {
elems := strings.Split(param, "/") elems := strings.Split(param, "/")
n := len(elems) n := len(elems)
if n < 2 { if n < 2 {
return fmt.Errorf("invalid format") return errors.New("invalid format")
} }
*p = elems *p = elems
@ -600,7 +600,7 @@ func (o *objectID) UnmarshalParam(param string) error {
func convertTo(s string) (objectID, error) { func convertTo(s string) (objectID, error) {
var nilObjectID objectID var nilObjectID objectID
if len(s) != 24 { if len(s) != 24 {
return nilObjectID, fmt.Errorf("invalid format") return nilObjectID, errors.New("invalid format")
} }
var oid [12]byte var oid [12]byte

View File

@ -116,7 +116,7 @@ func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request
err := mw.Close() err := mw.Close()
require.NoError(t, err) require.NoError(t, err)
req, err := http.NewRequest("POST", "/", &body) req, err := http.NewRequest(http.MethodPost, "/", &body)
require.NoError(t, err) require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary()) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())

View File

@ -7,6 +7,7 @@ package gin
import ( import (
"errors" "errors"
"io" "io"
"io/fs"
"log" "log"
"math" "math"
"mime/multipart" "mime/multipart"
@ -186,10 +187,9 @@ func (c *Context) FullPath() string {
func (c *Context) Next() { func (c *Context) Next() {
c.index++ c.index++
for c.index < int8(len(c.handlers)) { for c.index < int8(len(c.handlers)) {
if c.handlers[c.index] == nil { if c.handlers[c.index] != nil {
continue c.handlers[c.index](c)
} }
c.handlers[c.index](c)
c.index++ c.index++
} }
} }
@ -291,248 +291,171 @@ func (c *Context) MustGet(key string) any {
panic("Key \"" + key + "\" does not exist") panic("Key \"" + key + "\" does not exist")
} }
// GetString returns the value associated with the key as a string. func getTyped[T any](c *Context, key string) (res T) {
func (c *Context) GetString(key string) (s string) {
if val, ok := c.Get(key); ok && val != nil { if val, ok := c.Get(key); ok && val != nil {
s, _ = val.(string) res, _ = val.(T)
} }
return return
} }
// GetString returns the value associated with the key as a string.
func (c *Context) GetString(key string) (s string) {
return getTyped[string](c, key)
}
// GetBool returns the value associated with the key as a boolean. // GetBool returns the value associated with the key as a boolean.
func (c *Context) GetBool(key string) (b bool) { func (c *Context) GetBool(key string) (b bool) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[bool](c, key)
b, _ = val.(bool)
}
return
} }
// GetInt returns the value associated with the key as an integer. // GetInt returns the value associated with the key as an integer.
func (c *Context) GetInt(key string) (i int) { func (c *Context) GetInt(key string) (i int) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[int](c, key)
i, _ = val.(int)
}
return
} }
// GetInt8 returns the value associated with the key as an integer 8. // GetInt8 returns the value associated with the key as an integer 8.
func (c *Context) GetInt8(key string) (i8 int8) { func (c *Context) GetInt8(key string) (i8 int8) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[int8](c, key)
i8, _ = val.(int8)
}
return
} }
// GetInt16 returns the value associated with the key as an integer 16. // GetInt16 returns the value associated with the key as an integer 16.
func (c *Context) GetInt16(key string) (i16 int16) { func (c *Context) GetInt16(key string) (i16 int16) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[int16](c, key)
i16, _ = val.(int16)
}
return
} }
// GetInt32 returns the value associated with the key as an integer 32. // GetInt32 returns the value associated with the key as an integer 32.
func (c *Context) GetInt32(key string) (i32 int32) { func (c *Context) GetInt32(key string) (i32 int32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[int32](c, key)
i32, _ = val.(int32)
}
return
} }
// GetInt64 returns the value associated with the key as an integer 64. // GetInt64 returns the value associated with the key as an integer 64.
func (c *Context) GetInt64(key string) (i64 int64) { func (c *Context) GetInt64(key string) (i64 int64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[int64](c, key)
i64, _ = val.(int64)
}
return
} }
// GetUint returns the value associated with the key as an unsigned integer. // GetUint returns the value associated with the key as an unsigned integer.
func (c *Context) GetUint(key string) (ui uint) { func (c *Context) GetUint(key string) (ui uint) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[uint](c, key)
ui, _ = val.(uint)
}
return
} }
// GetUint8 returns the value associated with the key as an unsigned integer 8. // GetUint8 returns the value associated with the key as an unsigned integer 8.
func (c *Context) GetUint8(key string) (ui8 uint8) { func (c *Context) GetUint8(key string) (ui8 uint8) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[uint8](c, key)
ui8, _ = val.(uint8)
}
return
} }
// GetUint16 returns the value associated with the key as an unsigned integer 16. // GetUint16 returns the value associated with the key as an unsigned integer 16.
func (c *Context) GetUint16(key string) (ui16 uint16) { func (c *Context) GetUint16(key string) (ui16 uint16) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[uint16](c, key)
ui16, _ = val.(uint16)
}
return
} }
// GetUint32 returns the value associated with the key as an unsigned integer 32. // GetUint32 returns the value associated with the key as an unsigned integer 32.
func (c *Context) GetUint32(key string) (ui32 uint32) { func (c *Context) GetUint32(key string) (ui32 uint32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[uint32](c, key)
ui32, _ = val.(uint32)
}
return
} }
// GetUint64 returns the value associated with the key as an unsigned integer 64. // GetUint64 returns the value associated with the key as an unsigned integer 64.
func (c *Context) GetUint64(key string) (ui64 uint64) { func (c *Context) GetUint64(key string) (ui64 uint64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[uint64](c, key)
ui64, _ = val.(uint64)
}
return
} }
// GetFloat32 returns the value associated with the key as a float32. // GetFloat32 returns the value associated with the key as a float32.
func (c *Context) GetFloat32(key string) (f32 float32) { func (c *Context) GetFloat32(key string) (f32 float32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[float32](c, key)
f32, _ = val.(float32)
}
return
} }
// GetFloat64 returns the value associated with the key as a float64. // GetFloat64 returns the value associated with the key as a float64.
func (c *Context) GetFloat64(key string) (f64 float64) { func (c *Context) GetFloat64(key string) (f64 float64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[float64](c, key)
f64, _ = val.(float64)
}
return
} }
// GetTime returns the value associated with the key as time. // GetTime returns the value associated with the key as time.
func (c *Context) GetTime(key string) (t time.Time) { func (c *Context) GetTime(key string) (t time.Time) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[time.Time](c, key)
t, _ = val.(time.Time)
}
return
} }
// GetDuration returns the value associated with the key as a duration. // GetDuration returns the value associated with the key as a duration.
func (c *Context) GetDuration(key string) (d time.Duration) { func (c *Context) GetDuration(key string) (d time.Duration) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[time.Duration](c, key)
d, _ = val.(time.Duration)
}
return
} }
// GetIntSlice returns the value associated with the key as a slice of integers.
func (c *Context) GetIntSlice(key string) (is []int) { func (c *Context) GetIntSlice(key string) (is []int) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]int](c, key)
is, _ = val.([]int)
}
return
} }
// GetInt8Slice returns the value associated with the key as a slice of int8 integers.
func (c *Context) GetInt8Slice(key string) (i8s []int8) { func (c *Context) GetInt8Slice(key string) (i8s []int8) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]int8](c, key)
i8s, _ = val.([]int8)
}
return
} }
// GetInt16Slice returns the value associated with the key as a slice of int16 integers.
func (c *Context) GetInt16Slice(key string) (i16s []int16) { func (c *Context) GetInt16Slice(key string) (i16s []int16) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]int16](c, key)
i16s, _ = val.([]int16)
}
return
} }
// GetInt32Slice returns the value associated with the key as a slice of int32 integers.
func (c *Context) GetInt32Slice(key string) (i32s []int32) { func (c *Context) GetInt32Slice(key string) (i32s []int32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]int32](c, key)
i32s, _ = val.([]int32)
}
return
} }
// GetInt64Slice returns the value associated with the key as a slice of int64 integers.
func (c *Context) GetInt64Slice(key string) (i64s []int64) { func (c *Context) GetInt64Slice(key string) (i64s []int64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]int64](c, key)
i64s, _ = val.([]int64)
}
return
} }
// GetUintSlice returns the value associated with the key as a slice of unsigned integers.
func (c *Context) GetUintSlice(key string) (uis []uint) { func (c *Context) GetUintSlice(key string) (uis []uint) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]uint](c, key)
uis, _ = val.([]uint)
}
return
} }
// GetUint8Slice returns the value associated with the key as a slice of uint8 integers.
func (c *Context) GetUint8Slice(key string) (ui8s []uint8) { func (c *Context) GetUint8Slice(key string) (ui8s []uint8) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]uint8](c, key)
ui8s, _ = val.([]uint8)
}
return
} }
// GetUint16Slice returns the value associated with the key as a slice of uint16 integers.
func (c *Context) GetUint16Slice(key string) (ui16s []uint16) { func (c *Context) GetUint16Slice(key string) (ui16s []uint16) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]uint16](c, key)
ui16s, _ = val.([]uint16)
}
return
} }
// GetUint32Slice returns the value associated with the key as a slice of uint32 integers.
func (c *Context) GetUint32Slice(key string) (ui32s []uint32) { func (c *Context) GetUint32Slice(key string) (ui32s []uint32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]uint32](c, key)
ui32s, _ = val.([]uint32)
}
return
} }
// GetUint64Slice returns the value associated with the key as a slice of uint64 integers.
func (c *Context) GetUint64Slice(key string) (ui64s []uint64) { func (c *Context) GetUint64Slice(key string) (ui64s []uint64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]uint64](c, key)
ui64s, _ = val.([]uint64)
}
return
} }
// GetFloat32Slice returns the value associated with the key as a slice of float32 numbers.
func (c *Context) GetFloat32Slice(key string) (f32s []float32) { func (c *Context) GetFloat32Slice(key string) (f32s []float32) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]float32](c, key)
f32s, _ = val.([]float32)
}
return
} }
// GetFloat64Slice returns the value associated with the key as a slice of float64 numbers.
func (c *Context) GetFloat64Slice(key string) (f64s []float64) { func (c *Context) GetFloat64Slice(key string) (f64s []float64) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]float64](c, key)
f64s, _ = val.([]float64)
}
return
} }
// GetStringSlice returns the value associated with the key as a slice of strings. // GetStringSlice returns the value associated with the key as a slice of strings.
func (c *Context) GetStringSlice(key string) (ss []string) { func (c *Context) GetStringSlice(key string) (ss []string) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[[]string](c, key)
ss, _ = val.([]string)
}
return
} }
// GetStringMap returns the value associated with the key as a map of interfaces. // GetStringMap returns the value associated with the key as a map of interfaces.
func (c *Context) GetStringMap(key string) (sm map[string]any) { func (c *Context) GetStringMap(key string) (sm map[string]any) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[map[string]any](c, key)
sm, _ = val.(map[string]any)
}
return
} }
// GetStringMapString returns the value associated with the key as a map of strings. // GetStringMapString returns the value associated with the key as a map of strings.
func (c *Context) GetStringMapString(key string) (sms map[string]string) { func (c *Context) GetStringMapString(key string) (sms map[string]string) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[map[string]string](c, key)
sms, _ = val.(map[string]string)
}
return
} }
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
if val, ok := c.Get(key); ok && val != nil { return getTyped[map[string][]string](c, key)
smss, _ = val.(map[string][]string)
}
return
} }
/************************************/ /************************************/
@ -754,14 +677,22 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
} }
// SaveUploadedFile uploads the form file to specific dst. // SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm ...fs.FileMode) error {
src, err := file.Open() src, err := file.Open()
if err != nil { if err != nil {
return err return err
} }
defer src.Close() defer src.Close()
if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil { if len(perm) <= 0 {
perm = append(perm, 0o750)
}
if err = os.MkdirAll(filepath.Dir(dst), perm[0]); err != nil {
return err
}
if err = os.Chmod(filepath.Dir(dst), perm[0]); err != nil {
return err return err
} }
@ -951,7 +882,7 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error {
return c.ShouldBindBodyWith(obj, binding.TOML) return c.ShouldBindBodyWith(obj, binding.TOML)
} }
// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). // ShouldBindBodyWithPlain is a shortcut for c.ShouldBindBodyWith(obj, binding.Plain).
func (c *Context) ShouldBindBodyWithPlain(obj any) error { func (c *Context) ShouldBindBodyWithPlain(obj any) error {
return c.ShouldBindBodyWith(obj, binding.Plain) return c.ShouldBindBodyWith(obj, binding.Plain)
} }
@ -1086,7 +1017,7 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string,
} }
http.SetCookie(c.Writer, &http.Cookie{ http.SetCookie(c.Writer, &http.Cookie{
Name: name, Name: name,
Value: url.QueryEscape(value), Value: value,
MaxAge: maxAge, MaxAge: maxAge,
Path: path, Path: path,
Domain: domain, Domain: domain,

View File

@ -11,13 +11,16 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"io/fs"
"mime/multipart" "mime/multipart"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os" "os"
"path/filepath"
"reflect" "reflect"
"strconv"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -59,7 +62,7 @@ func createMultipartRequest() *http.Request {
must(mw.WriteField("time_location", "31/12/2016 14:55")) must(mw.WriteField("time_location", "31/12/2016 14:55"))
must(mw.WriteField("names[a]", "thinkerou")) must(mw.WriteField("names[a]", "thinkerou"))
must(mw.WriteField("names[b]", "tianou")) must(mw.WriteField("names[b]", "tianou"))
req, err := http.NewRequest("POST", "/", body) req, err := http.NewRequest(http.MethodPost, "/", body)
must(err) must(err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary) req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req return req
@ -80,7 +83,7 @@ func TestContextFormFile(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
mw.Close() mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf) c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file") f, err := c.FormFile("file")
require.NoError(t, err) require.NoError(t, err)
@ -94,7 +97,7 @@ func TestContextFormFileFailed(t *testing.T) {
mw := multipart.NewWriter(buf) mw := multipart.NewWriter(buf)
mw.Close() mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.Request.Header.Set("Content-Type", mw.FormDataContentType())
c.engine.MaxMultipartMemory = 8 << 20 c.engine.MaxMultipartMemory = 8 << 20
f, err := c.FormFile("file") f, err := c.FormFile("file")
@ -112,7 +115,7 @@ func TestContextMultipartForm(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
mw.Close() mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf) c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.MultipartForm() f, err := c.MultipartForm()
require.NoError(t, err) require.NoError(t, err)
@ -127,7 +130,7 @@ func TestSaveUploadedOpenFailed(t *testing.T) {
mw.Close() mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf) c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f := &multipart.FileHeader{ f := &multipart.FileHeader{
@ -145,7 +148,7 @@ func TestSaveUploadedCreateFailed(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
mw.Close() mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf) c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType()) c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file") f, err := c.FormFile("file")
require.NoError(t, err) require.NoError(t, err)
@ -154,6 +157,45 @@ func TestSaveUploadedCreateFailed(t *testing.T) {
require.Error(t, c.SaveUploadedFile(f, "/")) require.Error(t, c.SaveUploadedFile(f, "/"))
} }
func TestSaveUploadedFileWithPermission(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "permission_test")
require.NoError(t, err)
_, err = w.Write([]byte("permission_test"))
require.NoError(t, err)
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file")
require.NoError(t, err)
assert.Equal(t, "permission_test", f.Filename)
var mode fs.FileMode = 0o755
require.NoError(t, c.SaveUploadedFile(f, "permission_test", mode))
info, err := os.Stat(filepath.Dir("permission_test"))
require.NoError(t, err)
assert.Equal(t, info.Mode().Perm(), mode)
}
func TestSaveUploadedFileWithPermissionFailed(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "permission_test")
require.NoError(t, err)
_, err = w.Write([]byte("permission_test"))
require.NoError(t, err)
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file")
require.NoError(t, err)
assert.Equal(t, "permission_test", f.Filename)
var mode fs.FileMode = 0o644
require.Error(t, c.SaveUploadedFile(f, "test/permission_test", mode))
}
func TestContextReset(t *testing.T) { func TestContextReset(t *testing.T) {
router := New() router := New()
c := router.allocateContext(0) c := router.allocateContext(0)
@ -323,7 +365,7 @@ func TestContextGetFloat32(t *testing.T) {
key := "float32" key := "float32"
value := float32(3.14) value := float32(3.14)
c.Set(key, value) c.Set(key, value)
assert.Equal(t, value, c.GetFloat32(key)) assert.InDelta(t, value, c.GetFloat32(key), 0.01)
} }
func TestContextGetFloat64(t *testing.T) { func TestContextGetFloat64(t *testing.T) {
@ -480,7 +522,7 @@ func TestContextGetStringMapStringSlice(t *testing.T) {
func TestContextCopy(t *testing.T) { func TestContextCopy(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.index = 2 c.index = 2
c.Request, _ = http.NewRequest("POST", "/hola", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/hola", nil)
c.handlers = HandlersChain{func(c *Context) {}} c.handlers = HandlersChain{func(c *Context) {}}
c.Params = Params{Param{Key: "foo", Value: "bar"}} c.Params = Params{Param{Key: "foo", Value: "bar"}}
c.Set("foo", "bar") c.Set("foo", "bar")
@ -537,7 +579,7 @@ func TestContextHandler(t *testing.T) {
func TestContextQuery(t *testing.T) { func TestContextQuery(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil) c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?foo=bar&page=10&id=", nil)
value, ok := c.GetQuery("foo") value, ok := c.GetQuery("foo")
assert.True(t, ok) assert.True(t, ok)
@ -610,7 +652,6 @@ func TestContextInitQueryCache(t *testing.T) {
assert.Equal(t, test.expectedQueryCache, test.testContext.queryCache) assert.Equal(t, test.expectedQueryCache, test.testContext.queryCache)
}) })
} }
} }
func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
@ -631,7 +672,7 @@ func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
func TestContextQueryAndPostForm(t *testing.T) { func TestContextQueryAndPostForm(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second") body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
c.Request, _ = http.NewRequest("POST", c.Request, _ = http.NewRequest(http.MethodPost,
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body) "/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
c.Request.Header.Add("Content-Type", MIMEPOSTForm) c.Request.Header.Add("Content-Type", MIMEPOSTForm)
@ -651,7 +692,7 @@ func TestContextQueryAndPostForm(t *testing.T) {
assert.Empty(t, value) assert.Empty(t, value)
assert.Empty(t, c.PostForm("both")) assert.Empty(t, c.PostForm("both"))
assert.Empty(t, c.DefaultPostForm("both", "nothing")) assert.Empty(t, c.DefaultPostForm("both", "nothing"))
assert.Equal(t, "GET", c.Query("both"), "GET") assert.Equal(t, http.MethodGet, c.Query("both"), http.MethodGet)
value, ok = c.GetQuery("id") value, ok = c.GetQuery("id")
assert.True(t, ok) assert.True(t, ok)
@ -699,7 +740,7 @@ func TestContextQueryAndPostForm(t *testing.T) {
values = c.QueryArray("both") values = c.QueryArray("both")
assert.Len(t, values, 1) assert.Len(t, values, 1)
assert.Equal(t, "GET", values[0]) assert.Equal(t, http.MethodGet, values[0])
dicts, ok := c.GetQueryMap("ids") dicts, ok := c.GetQueryMap("ids")
assert.True(t, ok) assert.True(t, ok)
@ -832,9 +873,16 @@ func TestContextSetCookiePathEmpty(t *testing.T) {
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
} }
func TestContextSetCookieWithSpace(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("user", "gin test", 1, "/", "localhost", true, true)
assert.Equal(t, "user=\"gin test\"; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
}
func TestContextGetCookie(t *testing.T) { func TestContextGetCookie(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("GET", "/get", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/get", nil)
c.Request.Header.Set("Cookie", "user=gin") c.Request.Header.Set("Cookie", "user=gin")
cookie, _ := c.Cookie("user") cookie, _ := c.Cookie("user")
assert.Equal(t, "gin", cookie) assert.Equal(t, "gin", cookie)
@ -886,7 +934,7 @@ func TestContextRenderJSON(t *testing.T) {
func TestContextRenderJSONP(t *testing.T) { func TestContextRenderJSONP(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil) c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?callback=x", nil)
c.JSONP(http.StatusCreated, H{"foo": "bar"}) c.JSONP(http.StatusCreated, H{"foo": "bar"})
@ -900,7 +948,7 @@ func TestContextRenderJSONP(t *testing.T) {
func TestContextRenderJSONPWithoutCallback(t *testing.T) { func TestContextRenderJSONPWithoutCallback(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com", nil) c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com", nil)
c.JSONP(http.StatusCreated, H{"foo": "bar"}) c.JSONP(http.StatusCreated, H{"foo": "bar"})
@ -1043,7 +1091,7 @@ func TestContextRenderHTML2(t *testing.T) {
c, router := CreateTestContext(w) c, router := CreateTestContext(w)
// print debug warning log when Engine.trees > 0 // print debug warning log when Engine.trees > 0
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 1) assert.Len(t, router.trees, 1)
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
@ -1153,7 +1201,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
} }
// TestContextData tests that the response can be written from `bytestring` // TestContextRenderData tests that the response can be written from `bytestring`
// with specified MIME type // with specified MIME type
func TestContextRenderData(t *testing.T) { func TestContextRenderData(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -1199,7 +1247,7 @@ func TestContextRenderFile(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "/", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
c.File("./gin.go") c.File("./gin.go")
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
@ -1213,7 +1261,7 @@ func TestContextRenderFileFromFS(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "/some/path", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/some/path", nil)
c.FileFromFS("./gin.go", Dir(".", false)) c.FileFromFS("./gin.go", Dir(".", false))
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
@ -1229,7 +1277,7 @@ func TestContextRenderAttachment(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
newFilename := "new_filename.go" newFilename := "new_filename.go"
c.Request, _ = http.NewRequest("GET", "/", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
c.FileAttachment("./gin.go", newFilename) c.FileAttachment("./gin.go", newFilename)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
@ -1243,7 +1291,7 @@ func TestContextRenderAndEscapeAttachment(t *testing.T) {
maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go" maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go"
actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go" actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go"
c.Request, _ = http.NewRequest("GET", "/", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
c.FileAttachment("./gin.go", maliciousFilename) c.FileAttachment("./gin.go", maliciousFilename)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
@ -1256,7 +1304,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
newFilename := "new🧡_filename.go" newFilename := "new🧡_filename.go"
c.Request, _ = http.NewRequest("GET", "/", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
c.FileAttachment("./gin.go", newFilename) c.FileAttachment("./gin.go", newFilename)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
@ -1335,7 +1383,7 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(299, "/new_path") }) assert.Panics(t, func() { c.Redirect(299, "/new_path") })
assert.Panics(t, func() { c.Redirect(309, "/new_path") }) assert.Panics(t, func() { c.Redirect(309, "/new_path") })
@ -1349,7 +1397,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
c.Redirect(http.StatusFound, "http://google.com") c.Redirect(http.StatusFound, "http://google.com")
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
@ -1361,7 +1409,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
c.Redirect(http.StatusCreated, "/resource") c.Redirect(http.StatusCreated, "/resource")
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
@ -1371,7 +1419,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
func TestContextRenderRedirectAll(t *testing.T) { func TestContextRenderRedirectAll(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") }) assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") }) assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
assert.Panics(t, func() { c.Redirect(299, "/resource") }) assert.Panics(t, func() { c.Redirect(299, "/resource") })
@ -1383,7 +1431,7 @@ func TestContextRenderRedirectAll(t *testing.T) {
func TestContextNegotiationWithJSON(t *testing.T) { func TestContextNegotiationWithJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
c.Negotiate(http.StatusOK, Negotiate{ c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEJSON, MIMEXML, MIMEYAML, MIMEYAML2}, Offered: []string{MIMEJSON, MIMEXML, MIMEYAML, MIMEYAML2},
@ -1398,7 +1446,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
func TestContextNegotiationWithXML(t *testing.T) { func TestContextNegotiationWithXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
c.Negotiate(http.StatusOK, Negotiate{ c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, Offered: []string{MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2},
@ -1413,7 +1461,7 @@ func TestContextNegotiationWithXML(t *testing.T) {
func TestContextNegotiationWithYAML(t *testing.T) { func TestContextNegotiationWithYAML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
c.Negotiate(http.StatusOK, Negotiate{ c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML, MIMEYAML2}, Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML, MIMEYAML2},
@ -1428,7 +1476,7 @@ func TestContextNegotiationWithYAML(t *testing.T) {
func TestContextNegotiationWithTOML(t *testing.T) { func TestContextNegotiationWithTOML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
c.Negotiate(http.StatusOK, Negotiate{ c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2}, Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2},
@ -1443,7 +1491,7 @@ func TestContextNegotiationWithTOML(t *testing.T) {
func TestContextNegotiationWithHTML(t *testing.T) { func TestContextNegotiationWithHTML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, router := CreateTestContext(w) c, router := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ) router.SetHTMLTemplate(templ)
@ -1461,7 +1509,7 @@ func TestContextNegotiationWithHTML(t *testing.T) {
func TestContextNegotiationNotSupport(t *testing.T) { func TestContextNegotiationNotSupport(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
c.Negotiate(http.StatusOK, Negotiate{ c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEPOSTForm}, Offered: []string{MIMEPOSTForm},
@ -1474,7 +1522,7 @@ func TestContextNegotiationNotSupport(t *testing.T) {
func TestContextNegotiationFormat(t *testing.T) { func TestContextNegotiationFormat(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "", 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))
@ -1483,7 +1531,7 @@ func TestContextNegotiationFormat(t *testing.T) {
func TestContextNegotiationFormatWithAccept(t *testing.T) { func TestContextNegotiationFormatWithAccept(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML)) assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
@ -1493,7 +1541,7 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) {
func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) { func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("Accept", "*/*") c.Request.Header.Add("Accept", "*/*")
assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
@ -1504,7 +1552,7 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML)) assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
c, _ = CreateTestContext(httptest.NewRecorder()) c, _ = CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("Accept", "text/*") c.Request.Header.Add("Accept", "text/*")
assert.Equal(t, "*/*", c.NegotiateFormat("*/*")) assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
@ -1517,7 +1565,7 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
func TestContextNegotiationFormatCustom(t *testing.T) { func TestContextNegotiationFormatCustom(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8") c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
c.Accepted = nil c.Accepted = nil
@ -1530,7 +1578,7 @@ func TestContextNegotiationFormatCustom(t *testing.T) {
func TestContextNegotiationFormat2(t *testing.T) { func TestContextNegotiationFormat2(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", 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.Equal(t, "", c.NegotiateFormat("image/tiff"))
@ -1550,7 +1598,7 @@ func TestContextIsAborted(t *testing.T) {
assert.True(t, c.IsAborted()) assert.True(t, c.IsAborted())
} }
// TestContextData tests that the response can be written from `bytestring` // TestContextAbortWithStatus tests that the response can be written from `bytestring`
// with specified MIME type // with specified MIME type
func TestContextAbortWithStatus(t *testing.T) { func TestContextAbortWithStatus(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -1658,7 +1706,7 @@ func TestContextAbortWithError(t *testing.T) {
func TestContextClientIP(t *testing.T) { func TestContextClientIP(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs() c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
resetContextForClientIPTests(c) resetContextForClientIPTests(c)
@ -1801,7 +1849,7 @@ func resetContextForClientIPTests(c *Context) {
func TestContextContentType(t *testing.T) { func TestContextContentType(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Set("Content-Type", "application/json; charset=utf-8") c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
assert.Equal(t, "application/json", c.ContentType()) assert.Equal(t, "application/json", c.ContentType())
@ -1809,7 +1857,7 @@ func TestContextContentType(t *testing.T) {
func TestContextAutoBindJSON(t *testing.T) { func TestContextAutoBindJSON(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEJSON) c.Request.Header.Add("Content-Type", MIMEJSON)
var obj struct { var obj struct {
@ -1826,7 +1874,7 @@ func TestContextBindWithJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
var obj struct { var obj struct {
@ -1843,7 +1891,7 @@ func TestContextBindWithXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?> c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
<root> <root>
<foo>FOO</foo> <foo>FOO</foo>
<bar>BAR</bar> <bar>BAR</bar>
@ -1864,7 +1912,7 @@ func TestContextBindPlain(t *testing.T) {
// string // string
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`))
c.Request.Header.Add("Content-Type", MIMEPlain) c.Request.Header.Add("Content-Type", MIMEPlain)
var s string var s string
@ -1874,7 +1922,7 @@ func TestContextBindPlain(t *testing.T) {
assert.Equal(t, 0, w.Body.Len()) assert.Equal(t, 0, w.Body.Len())
// []byte // []byte
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`))
c.Request.Header.Add("Content-Type", MIMEPlain) c.Request.Header.Add("Content-Type", MIMEPlain)
var bs []byte var bs []byte
@ -1888,7 +1936,7 @@ func TestContextBindHeader(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("rate", "8000") c.Request.Header.Add("rate", "8000")
c.Request.Header.Add("domain", "music") c.Request.Header.Add("domain", "music")
c.Request.Header.Add("limit", "1000") c.Request.Header.Add("limit", "1000")
@ -1910,7 +1958,7 @@ func TestContextBindWithQuery(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
var obj struct { var obj struct {
Foo string `form:"foo"` Foo string `form:"foo"`
@ -1926,7 +1974,7 @@ func TestContextBindWithYAML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo"))
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
var obj struct { var obj struct {
@ -1943,7 +1991,7 @@ func TestContextBindWithTOML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'"))
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
var obj struct { var obj struct {
@ -1960,7 +2008,7 @@ func TestContextBadAutoBind(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEJSON) c.Request.Header.Add("Content-Type", MIMEJSON)
var obj struct { var obj struct {
Foo string `json:"foo"` Foo string `json:"foo"`
@ -1979,7 +2027,7 @@ func TestContextBadAutoBind(t *testing.T) {
func TestContextAutoShouldBindJSON(t *testing.T) { func TestContextAutoShouldBindJSON(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEJSON) c.Request.Header.Add("Content-Type", MIMEJSON)
var obj struct { var obj struct {
@ -1996,7 +2044,7 @@ func TestContextShouldBindWithJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
var obj struct { var obj struct {
@ -2013,7 +2061,7 @@ func TestContextShouldBindWithXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?> c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
<root> <root>
<foo>FOO</foo> <foo>FOO</foo>
<bar>BAR</bar> <bar>BAR</bar>
@ -2034,7 +2082,7 @@ func TestContextShouldBindPlain(t *testing.T) {
// string // string
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`))
c.Request.Header.Add("Content-Type", MIMEPlain) c.Request.Header.Add("Content-Type", MIMEPlain)
var s string var s string
@ -2044,7 +2092,7 @@ func TestContextShouldBindPlain(t *testing.T) {
assert.Equal(t, 0, w.Body.Len()) assert.Equal(t, 0, w.Body.Len())
// []byte // []byte
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`))
c.Request.Header.Add("Content-Type", MIMEPlain) c.Request.Header.Add("Content-Type", MIMEPlain)
var bs []byte var bs []byte
@ -2058,7 +2106,7 @@ func TestContextShouldBindHeader(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.Header.Add("rate", "8000") c.Request.Header.Add("rate", "8000")
c.Request.Header.Add("domain", "music") c.Request.Header.Add("domain", "music")
c.Request.Header.Add("limit", "1000") c.Request.Header.Add("limit", "1000")
@ -2080,7 +2128,7 @@ func TestContextShouldBindWithQuery(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused")) c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
var obj struct { var obj struct {
Foo string `form:"foo"` Foo string `form:"foo"`
@ -2100,7 +2148,7 @@ func TestContextShouldBindWithYAML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo"))
c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
var obj struct { var obj struct {
@ -2117,7 +2165,7 @@ func TestContextShouldBindWithTOML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
var obj struct { var obj struct {
@ -2134,7 +2182,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
c.Request.Header.Add("Content-Type", MIMEJSON) c.Request.Header.Add("Content-Type", MIMEJSON)
var obj struct { var obj struct {
Foo string `json:"foo"` Foo string `json:"foo"`
@ -2198,7 +2246,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest( c.Request, _ = http.NewRequest(
"POST", "http://example.com", bytes.NewBufferString(tt.bodyA), http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyA),
) )
// When it binds to typeA and typeB, it finds the body is // When it binds to typeA and typeB, it finds the body is
// not typeB but typeA. // not typeB but typeA.
@ -2216,7 +2264,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest( c.Request, _ = http.NewRequest(
"POST", "http://example.com", bytes.NewBufferString(tt.bodyB), http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyB),
) )
objA := typeA{} objA := typeA{}
require.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA)) require.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
@ -2263,7 +2311,7 @@ func TestContextShouldBindBodyWithJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
type typeJSON struct { type typeJSON struct {
Foo string `json:"foo" binding:"required"` Foo string `json:"foo" binding:"required"`
@ -2327,7 +2375,7 @@ func TestContextShouldBindBodyWithXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
type typeXML struct { type typeXML struct {
Foo string `xml:"foo" binding:"required"` Foo string `xml:"foo" binding:"required"`
@ -2391,7 +2439,7 @@ func TestContextShouldBindBodyWithYAML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
type typeYAML struct { type typeYAML struct {
Foo string `yaml:"foo" binding:"required"` Foo string `yaml:"foo" binding:"required"`
@ -2456,7 +2504,7 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
type typeTOML struct { type typeTOML struct {
Foo string `toml:"foo" binding:"required"` Foo string `toml:"foo" binding:"required"`
@ -2525,7 +2573,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body)) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
type typeJSON struct { type typeJSON struct {
Foo string `json:"foo" binding:"required"` Foo string `json:"foo" binding:"required"`
@ -2562,7 +2610,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) {
func TestContextGolangContext(t *testing.T) { func TestContextGolangContext(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
require.NoError(t, c.Err()) require.NoError(t, c.Err())
assert.Nil(t, c.Done()) assert.Nil(t, c.Done())
ti, ok := c.Deadline() ti, ok := c.Deadline()
@ -2580,7 +2628,7 @@ func TestContextGolangContext(t *testing.T) {
func TestWebsocketsRequired(t *testing.T) { func TestWebsocketsRequired(t *testing.T) {
// Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2 // Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("GET", "/chat", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
c.Request.Header.Set("Host", "server.example.com") c.Request.Header.Set("Host", "server.example.com")
c.Request.Header.Set("Upgrade", "websocket") c.Request.Header.Set("Upgrade", "websocket")
c.Request.Header.Set("Connection", "Upgrade") c.Request.Header.Set("Connection", "Upgrade")
@ -2593,7 +2641,7 @@ func TestWebsocketsRequired(t *testing.T) {
// Normal request, no websocket required. // Normal request, no websocket required.
c, _ = CreateTestContext(httptest.NewRecorder()) c, _ = CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("GET", "/chat", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
c.Request.Header.Set("Host", "server.example.com") c.Request.Header.Set("Host", "server.example.com")
assert.False(t, c.IsWebsocket()) assert.False(t, c.IsWebsocket())
@ -2601,7 +2649,7 @@ func TestWebsocketsRequired(t *testing.T) {
func TestGetRequestHeaderValue(t *testing.T) { func TestGetRequestHeaderValue(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("GET", "/chat", nil) c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
c.Request.Header.Set("Gin-Version", "1.0.0") c.Request.Header.Set("Gin-Version", "1.0.0")
assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version")) assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version"))
@ -2611,7 +2659,7 @@ func TestGetRequestHeaderValue(t *testing.T) {
func TestContextGetRawData(t *testing.T) { func TestContextGetRawData(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
body := bytes.NewBufferString("Fetch binary post data") body := bytes.NewBufferString("Fetch binary post data")
c.Request, _ = http.NewRequest("POST", "/", body) c.Request, _ = http.NewRequest(http.MethodPost, "/", body)
c.Request.Header.Add("Content-Type", MIMEPOSTForm) c.Request.Header.Add("Content-Type", MIMEPOSTForm)
data, err := c.GetRawData() data, err := c.GetRawData()
@ -2634,7 +2682,7 @@ func TestContextRenderDataFromReader(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String()) assert.Equal(t, body, w.Body.String())
assert.Equal(t, contentType, w.Header().Get("Content-Type")) assert.Equal(t, contentType, w.Header().Get("Content-Type"))
assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length"))
assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition")) assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition"))
} }
@ -2652,7 +2700,7 @@ func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String()) assert.Equal(t, body, w.Body.String())
assert.Equal(t, contentType, w.Header().Get("Content-Type")) assert.Equal(t, contentType, w.Header().Get("Content-Type"))
assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length"))
} }
type TestResponseRecorder struct { type TestResponseRecorder struct {
@ -2740,8 +2788,8 @@ func TestRaceParamsContextCopy(t *testing.T) {
}(c.Copy(), c.Param("name")) }(c.Copy(), c.Param("name"))
}) })
} }
PerformRequest(router, "GET", "/name1/api") PerformRequest(router, http.MethodGet, "/name1/api")
PerformRequest(router, "GET", "/name2/api") PerformRequest(router, http.MethodGet, "/name2/api")
wg.Wait() wg.Wait()
} }
@ -2760,7 +2808,7 @@ func TestContextWithKeysMutex(t *testing.T) {
func TestRemoteIPFail(t *testing.T) { func TestRemoteIPFail(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.RemoteAddr = "[:::]:80" c.Request.RemoteAddr = "[:::]:80"
ip := net.ParseIP(c.RemoteIP()) ip := net.ParseIP(c.RemoteIP())
trust := c.engine.isTrustedProxy(ip) trust := c.engine.isTrustedProxy(ip)
@ -2857,11 +2905,12 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
{ {
name: "c with struct context key", name: "c with struct context key",
getContextAndKey: func() (*Context, any) { getContextAndKey: func() (*Context, any) {
var key struct{} type KeyStruct struct{} // https://staticcheck.dev/docs/checks/#SA1029
var key KeyStruct
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag // enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value")) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value"))
return c, key return c, key
}, },
@ -2873,7 +2922,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag // enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value")) c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
return c, contextKey("key") return c, contextKey("key")
}, },
@ -2896,7 +2945,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
// enable ContextWithFallback feature flag // enable ContextWithFallback feature flag
c.engine.ContextWithFallback = true c.engine.ContextWithFallback = true
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
return c, "key" return c, "key"
}, },
value: nil, value: nil,
@ -3028,7 +3077,7 @@ func TestInterceptedHeader(t *testing.T) {
c.Header("X-Test-2", "present") c.Header("X-Test-2", "present")
c.String(http.StatusOK, "hello world") c.String(http.StatusOK, "hello world")
}) })
c.Request = httptest.NewRequest("GET", "/", nil) c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
r.HandleContext(c) r.HandleContext(c)
// Result() has headers frozen when WriteHeaderNow() has been called // Result() has headers frozen when WriteHeaderNow() has been called
// 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
@ -3037,3 +3086,47 @@ func TestInterceptedHeader(t *testing.T) {
assert.Equal(t, "", w.Result().Header.Get("X-Test")) assert.Equal(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"))
} }
func TestContextNext(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
// Test with no handlers
c.Next()
assert.Equal(t, int8(0), c.index)
// Test with one handler
c.index = -1
c.handlers = HandlersChain{func(c *Context) {
c.Set("key", "value")
}}
c.Next()
assert.Equal(t, int8(1), c.index)
value, exists := c.Get("key")
assert.True(t, exists)
assert.Equal(t, "value", value)
// Test with multiple handlers
c.handlers = HandlersChain{
func(c *Context) {
c.Set("key1", "value1")
c.Next()
c.Set("key2", "value2")
},
nil,
func(c *Context) {
c.Set("key3", "value3")
},
}
c.index = -1
c.Next()
assert.Equal(t, int8(4), c.index)
value, exists = c.Get("key1")
assert.True(t, exists)
assert.Equal(t, "value1", value)
value, exists = c.Get("key2")
assert.True(t, exists)
assert.Equal(t, "value2", value)
value, exists = c.Get("key3")
assert.True(t, exists)
assert.Equal(t, "value3", value)
}

View File

@ -10,6 +10,7 @@ import (
"html/template" "html/template"
"io" "io"
"log" "log"
"net/http"
"os" "os"
"runtime" "runtime"
"strings" "strings"
@ -60,7 +61,7 @@ func TestDebugPrintError(t *testing.T) {
func TestDebugPrintRoutes(t *testing.T) { func TestDebugPrintRoutes(t *testing.T) {
re := captureOutput(t, func() { re := captureOutput(t, func() {
SetMode(DebugMode) SetMode(DebugMode)
debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest}) debugPrintRoute(http.MethodGet, "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
SetMode(TestMode) SetMode(TestMode)
}) })
assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re) assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)
@ -72,7 +73,7 @@ func TestDebugPrintRouteFunc(t *testing.T) {
} }
re := captureOutput(t, func() { re := captureOutput(t, func() {
SetMode(DebugMode) SetMode(DebugMode)
debugPrintRoute("GET", "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest}) debugPrintRoute(http.MethodGet, "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest})
SetMode(TestMode) SetMode(TestMode)
}) })
assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param1/:param2 --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re) assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param1/:param2 --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)

View File

@ -18,7 +18,7 @@ func TestBindWith(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
var obj struct { var obj struct {
Foo string `form:"foo"` Foo string `form:"foo"`

View File

@ -172,7 +172,7 @@ func main() {
router := gin.Default() router := gin.Default()
// Query string parameters are parsed using the existing underlying request object. // Query string parameters are parsed using the existing underlying request object.
// The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) { router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest") firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
@ -516,19 +516,19 @@ Sample Output
```go ```go
func main() { func main() {
router := gin.New() router := gin.New()
// skip logging for desired paths by setting SkipPaths in LoggerConfig // skip logging for desired paths by setting SkipPaths in LoggerConfig
loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}} loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}}
// skip logging based on your logic by setting Skip func in LoggerConfig // skip logging based on your logic by setting Skip func in LoggerConfig
loggerConfig.Skip = func(c *gin.Context) bool { loggerConfig.Skip = func(c *gin.Context) bool {
// as an example skip non server side errors // as an example skip non server side errors
return c.Writer.Status() < http.StatusInternalServerError return c.Writer.Status() < http.StatusInternalServerError
} }
router.Use(gin.LoggerWithConfig(loggerConfig)) router.Use(gin.LoggerWithConfig(loggerConfig))
router.Use(gin.Recovery()) router.Use(gin.Recovery())
// skipped // skipped
router.GET("/metrics", func(c *gin.Context) { router.GET("/metrics", func(c *gin.Context) {
c.Status(http.StatusNotImplemented) c.Status(http.StatusNotImplemented)
@ -543,7 +543,7 @@ func main() {
router.GET("/data", func(c *gin.Context) { router.GET("/data", func(c *gin.Context) {
c.Status(http.StatusNotImplemented) c.Status(http.StatusNotImplemented)
}) })
router.Run(":8080") router.Run(":8080")
} }
@ -615,7 +615,7 @@ You can also specify that specific fields are required. If a field is decorated
```go ```go
// Binding from JSON // Binding from JSON
type Login struct { type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"` User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"` Password string `form:"password" json:"password" xml:"password" binding:"required"`
} }
@ -1252,7 +1252,7 @@ func main() {
#### JSONP #### JSONP
Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists. Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
```go ```go
func main() { func main() {
@ -1301,7 +1301,7 @@ func main() {
#### PureJSON #### PureJSON
Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead.
This feature is unavailable in Go 1.6 and lower. This feature is unavailable in Go 1.6 and lower.
```go ```go
@ -1336,7 +1336,7 @@ func main() {
router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico") router.StaticFile("/favicon.ico", "./resources/favicon.ico")
router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system")) router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system"))
// Listen and serve on 0.0.0.0:8080 // Listen and serve on 0.0.0.0:8080
router.Run(":8080") router.Run(":8080")
} }
@ -2320,7 +2320,7 @@ or network CIDRs from where clients which their request headers related to clien
IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
IPv6 CIDRs. IPv6 CIDRs.
**Attention:** Gin trust all proxies by default if you don't specify a trusted **Attention:** Gin trust all proxies by default if you don't specify a trusted
proxy using the function above, **this is NOT safe**. At the same time, if you don't proxy using the function above, **this is NOT safe**. At the same time, if you don't
use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`, use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`,
then `Context.ClientIP()` will return the remote address directly to avoid some then `Context.ClientIP()` will return the remote address directly to avoid some
@ -2349,7 +2349,7 @@ func main() {
``` ```
**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` **Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform`
to skip TrustedProxies check, it has a higher priority than TrustedProxies. to skip TrustedProxies check, it has a higher priority than TrustedProxies.
Look at the example below: Look at the example below:
```go ```go

2
gin.go
View File

@ -598,7 +598,7 @@ func (engine *Engine) RunQUIC(addr, certFile, keyFile string) (err error) {
if engine.isUnsafeTrustedProxies() { if engine.isUnsafeTrustedProxies() {
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
} }
err = http3.ListenAndServeQUIC(addr, certFile, keyFile, engine.Handler()) err = http3.ListenAndServeQUIC(addr, certFile, keyFile, engine.Handler())

View File

@ -73,7 +73,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -131,7 +131,7 @@ func TestLoadHTMLGlobTestMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -151,7 +151,7 @@ func TestLoadHTMLGlobReleaseMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -178,7 +178,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
}, },
} }
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := client.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -198,7 +198,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) res, err := http.Get(ts.URL + "/raw")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -229,7 +229,7 @@ func TestLoadHTMLFilesTestMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -249,7 +249,7 @@ func TestLoadHTMLFilesDebugMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -269,7 +269,7 @@ func TestLoadHTMLFilesReleaseMode(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := http.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -296,7 +296,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
}, },
} }
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) res, err := client.Get(ts.URL + "/test")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -316,7 +316,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
) )
defer ts.Close() defer ts.Close()
res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) res, err := http.Get(ts.URL + "/raw")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -327,31 +327,31 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
func TestAddRoute(t *testing.T) { func TestAddRoute(t *testing.T) {
router := New() router := New()
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 1) assert.Len(t, router.trees, 1)
assert.NotNil(t, router.trees.get("GET")) assert.NotNil(t, router.trees.get(http.MethodGet))
assert.Nil(t, router.trees.get("POST")) assert.Nil(t, router.trees.get(http.MethodPost))
router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 2) assert.Len(t, router.trees, 2)
assert.NotNil(t, router.trees.get("GET")) assert.NotNil(t, router.trees.get(http.MethodGet))
assert.NotNil(t, router.trees.get("POST")) assert.NotNil(t, router.trees.get(http.MethodPost))
router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 2) assert.Len(t, router.trees, 2)
} }
func TestAddRouteFails(t *testing.T) { func TestAddRouteFails(t *testing.T) {
router := New() router := New()
assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) }) assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) })
assert.Panics(t, func() { router.addRoute("GET", "a", HandlersChain{func(_ *Context) {}}) }) assert.Panics(t, func() { router.addRoute(http.MethodGet, "a", HandlersChain{func(_ *Context) {}}) })
assert.Panics(t, func() { router.addRoute("GET", "/", HandlersChain{}) }) assert.Panics(t, func() { router.addRoute(http.MethodGet, "/", HandlersChain{}) })
router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
assert.Panics(t, func() { assert.Panics(t, func() {
router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
}) })
} }
@ -493,27 +493,27 @@ func TestListOfRoutes(t *testing.T) {
assert.Len(t, list, 7) assert.Len(t, list, 7)
assertRoutePresent(t, list, RouteInfo{ assertRoutePresent(t, list, RouteInfo{
Method: "GET", Method: http.MethodGet,
Path: "/favicon.ico", Path: "/favicon.ico",
Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
}) })
assertRoutePresent(t, list, RouteInfo{ assertRoutePresent(t, list, RouteInfo{
Method: "GET", Method: http.MethodGet,
Path: "/", Path: "/",
Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
}) })
assertRoutePresent(t, list, RouteInfo{ assertRoutePresent(t, list, RouteInfo{
Method: "GET", Method: http.MethodGet,
Path: "/users/", Path: "/users/",
Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$",
}) })
assertRoutePresent(t, list, RouteInfo{ assertRoutePresent(t, list, RouteInfo{
Method: "GET", Method: http.MethodGet,
Path: "/users/:id", Path: "/users/:id",
Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
}) })
assertRoutePresent(t, list, RouteInfo{ assertRoutePresent(t, list, RouteInfo{
Method: "POST", Method: http.MethodPost,
Path: "/users/:id", Path: "/users/:id",
Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$",
}) })
@ -531,7 +531,7 @@ func TestEngineHandleContext(t *testing.T) {
} }
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
w := PerformRequest(r, "GET", "/") w := PerformRequest(r, http.MethodGet, "/")
assert.Equal(t, 301, w.Code) assert.Equal(t, 301, w.Code)
}) })
} }
@ -564,7 +564,7 @@ func TestEngineHandleContextManyReEntries(t *testing.T) {
}) })
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
w := PerformRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value w := PerformRequest(r, http.MethodGet, "/"+strconv.Itoa(expectValue-1)) // include 0 value
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Equal(t, expectValue, w.Body.Len()) assert.Equal(t, expectValue, w.Body.Len())
}) })
@ -712,8 +712,8 @@ func TestNewOptionFunc(t *testing.T) {
r := New(fc) r := New(fc)
routes := r.Routes() routes := r.Routes()
assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"})
assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"})
} }
func TestWithOptionFunc(t *testing.T) { func TestWithOptionFunc(t *testing.T) {
@ -729,8 +729,8 @@ func TestWithOptionFunc(t *testing.T) {
}) })
routes := r.Routes() routes := r.Routes()
assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"}) assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"})
assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"}) assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"})
} }
type Birthday string type Birthday string
@ -749,7 +749,7 @@ func TestCustomUnmarshalStruct(t *testing.T) {
_ = ctx.BindQuery(&request) _ = ctx.BindQuery(&request)
ctx.JSON(200, request.Birthday) ctx.JSON(200, request.Birthday)
}) })
req := httptest.NewRequest("GET", "/test?birthday=2000-01-01", nil) req := httptest.NewRequest(http.MethodGet, "/test?birthday=2000-01-01", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
route.ServeHTTP(w, req) route.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
@ -761,7 +761,7 @@ func TestMethodNotAllowedNoRoute(t *testing.T) {
g := New() g := New()
g.HandleMethodNotAllowed = true g.HandleMethodNotAllowed = true
req := httptest.NewRequest("GET", "/", nil) req := httptest.NewRequest(http.MethodGet, "/", nil)
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
assert.NotPanics(t, func() { g.ServeHTTP(resp, req) }) assert.NotPanics(t, func() { g.ServeHTTP(resp, req) })
assert.Equal(t, http.StatusNotFound, resp.Code) assert.Equal(t, http.StatusNotFound, resp.Code)

View File

@ -10,6 +10,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
@ -411,7 +412,7 @@ func exampleFromPath(path string) (string, Params) {
} }
if start >= 0 { if start >= 0 {
if c == '/' { if c == '/' {
value := fmt.Sprint(rand.Intn(100000)) value := strconv.Itoa(rand.Intn(100000))
params = append(params, Param{ params = append(params, Param{
Key: path[start:i], Key: path[start:i],
Value: value, Value: value,
@ -425,7 +426,7 @@ func exampleFromPath(path string) (string, Params) {
} }
} }
if start >= 0 { if start >= 0 {
value := fmt.Sprint(rand.Intn(100000)) value := strconv.Itoa(rand.Intn(100000))
params = append(params, Param{ params = append(params, Param{
Key: path[start:], Key: path[start:],
Value: value, Value: value,

View File

@ -31,9 +31,9 @@ func TestLogger(t *testing.T) {
router.HEAD("/example", func(c *Context) {}) router.HEAD("/example", func(c *Context) {})
router.OPTIONS("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {})
PerformRequest(router, "GET", "/example?a=100") PerformRequest(router, http.MethodGet, "/example?a=100")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
assert.Contains(t, buffer.String(), "a=100") assert.Contains(t, buffer.String(), "a=100")
@ -41,21 +41,21 @@ func TestLogger(t *testing.T) {
// like integration tests because they test the whole logging process rather // like integration tests because they test the whole logging process rather
// than individual functions. Im not sure where these should go. // than individual functions. Im not sure where these should go.
buffer.Reset() buffer.Reset()
PerformRequest(router, "POST", "/example") PerformRequest(router, http.MethodPost, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "POST") assert.Contains(t, buffer.String(), http.MethodPost)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "PUT", "/example") PerformRequest(router, http.MethodPut, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "PUT") assert.Contains(t, buffer.String(), http.MethodPut)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "DELETE", "/example") PerformRequest(router, http.MethodDelete, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "DELETE") assert.Contains(t, buffer.String(), http.MethodDelete)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
@ -77,9 +77,9 @@ func TestLogger(t *testing.T) {
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "GET", "/notfound") PerformRequest(router, http.MethodGet, "/notfound")
assert.Contains(t, buffer.String(), "404") assert.Contains(t, buffer.String(), "404")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/notfound") assert.Contains(t, buffer.String(), "/notfound")
} }
@ -95,9 +95,9 @@ func TestLoggerWithConfig(t *testing.T) {
router.HEAD("/example", func(c *Context) {}) router.HEAD("/example", func(c *Context) {})
router.OPTIONS("/example", func(c *Context) {}) router.OPTIONS("/example", func(c *Context) {})
PerformRequest(router, "GET", "/example?a=100") PerformRequest(router, http.MethodGet, "/example?a=100")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
assert.Contains(t, buffer.String(), "a=100") assert.Contains(t, buffer.String(), "a=100")
@ -105,21 +105,21 @@ func TestLoggerWithConfig(t *testing.T) {
// like integration tests because they test the whole logging process rather // like integration tests because they test the whole logging process rather
// than individual functions. Im not sure where these should go. // than individual functions. Im not sure where these should go.
buffer.Reset() buffer.Reset()
PerformRequest(router, "POST", "/example") PerformRequest(router, http.MethodPost, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "POST") assert.Contains(t, buffer.String(), http.MethodPost)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "PUT", "/example") PerformRequest(router, http.MethodPut, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "PUT") assert.Contains(t, buffer.String(), http.MethodPut)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "DELETE", "/example") PerformRequest(router, http.MethodDelete, "/example")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "DELETE") assert.Contains(t, buffer.String(), http.MethodDelete)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
@ -141,9 +141,9 @@ func TestLoggerWithConfig(t *testing.T) {
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
buffer.Reset() buffer.Reset()
PerformRequest(router, "GET", "/notfound") PerformRequest(router, http.MethodGet, "/notfound")
assert.Contains(t, buffer.String(), "404") assert.Contains(t, buffer.String(), "404")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/notfound") assert.Contains(t, buffer.String(), "/notfound")
} }
@ -169,12 +169,12 @@ func TestLoggerWithFormatter(t *testing.T) {
) )
})) }))
router.GET("/example", func(c *Context) {}) router.GET("/example", func(c *Context) {})
PerformRequest(router, "GET", "/example?a=100") PerformRequest(router, http.MethodGet, "/example?a=100")
// output test // output test
assert.Contains(t, buffer.String(), "[FORMATTER TEST]") assert.Contains(t, buffer.String(), "[FORMATTER TEST]")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
assert.Contains(t, buffer.String(), "a=100") assert.Contains(t, buffer.String(), "a=100")
} }
@ -210,12 +210,12 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
gotKeys = c.Keys gotKeys = c.Keys
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
}) })
PerformRequest(router, "GET", "/example?a=100") PerformRequest(router, http.MethodGet, "/example?a=100")
// output test // output test
assert.Contains(t, buffer.String(), "[FORMATTER TEST]") assert.Contains(t, buffer.String(), "[FORMATTER TEST]")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
assert.Contains(t, buffer.String(), "GET") assert.Contains(t, buffer.String(), http.MethodGet)
assert.Contains(t, buffer.String(), "/example") assert.Contains(t, buffer.String(), "/example")
assert.Contains(t, buffer.String(), "a=100") assert.Contains(t, buffer.String(), "a=100")
@ -225,7 +225,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
assert.Equal(t, 200, gotParam.StatusCode) assert.Equal(t, 200, gotParam.StatusCode)
assert.NotEmpty(t, gotParam.Latency) assert.NotEmpty(t, gotParam.Latency)
assert.Equal(t, "20.20.20.20", gotParam.ClientIP) assert.Equal(t, "20.20.20.20", gotParam.ClientIP)
assert.Equal(t, "GET", gotParam.Method) assert.Equal(t, http.MethodGet, gotParam.Method)
assert.Equal(t, "/example?a=100", gotParam.Path) assert.Equal(t, "/example?a=100", gotParam.Path)
assert.Empty(t, gotParam.ErrorMessage) assert.Empty(t, gotParam.ErrorMessage)
assert.Equal(t, gotKeys, gotParam.Keys) assert.Equal(t, gotKeys, gotParam.Keys)
@ -239,7 +239,7 @@ func TestDefaultLogFormatter(t *testing.T) {
StatusCode: 200, StatusCode: 200,
Latency: time.Second * 5, Latency: time.Second * 5,
ClientIP: "20.20.20.20", ClientIP: "20.20.20.20",
Method: "GET", Method: http.MethodGet,
Path: "/", Path: "/",
ErrorMessage: "", ErrorMessage: "",
isTerm: false, isTerm: false,
@ -250,7 +250,7 @@ func TestDefaultLogFormatter(t *testing.T) {
StatusCode: 200, StatusCode: 200,
Latency: time.Second * 5, Latency: time.Second * 5,
ClientIP: "20.20.20.20", ClientIP: "20.20.20.20",
Method: "GET", Method: http.MethodGet,
Path: "/", Path: "/",
ErrorMessage: "", ErrorMessage: "",
isTerm: true, isTerm: true,
@ -260,7 +260,7 @@ func TestDefaultLogFormatter(t *testing.T) {
StatusCode: 200, StatusCode: 200,
Latency: time.Millisecond * 9876543210, Latency: time.Millisecond * 9876543210,
ClientIP: "20.20.20.20", ClientIP: "20.20.20.20",
Method: "GET", Method: http.MethodGet,
Path: "/", Path: "/",
ErrorMessage: "", ErrorMessage: "",
isTerm: true, isTerm: true,
@ -271,7 +271,7 @@ func TestDefaultLogFormatter(t *testing.T) {
StatusCode: 200, StatusCode: 200,
Latency: time.Millisecond * 9876543210, Latency: time.Millisecond * 9876543210,
ClientIP: "20.20.20.20", ClientIP: "20.20.20.20",
Method: "GET", Method: http.MethodGet,
Path: "/", Path: "/",
ErrorMessage: "", ErrorMessage: "",
isTerm: false, isTerm: false,
@ -292,10 +292,10 @@ func TestColorForMethod(t *testing.T) {
return p.MethodColor() return p.MethodColor()
} }
assert.Equal(t, blue, colorForMethod("GET"), "get should be blue") assert.Equal(t, blue, colorForMethod(http.MethodGet), "get should be blue")
assert.Equal(t, cyan, colorForMethod("POST"), "post should be cyan") assert.Equal(t, cyan, colorForMethod(http.MethodPost), "post should be cyan")
assert.Equal(t, yellow, colorForMethod("PUT"), "put should be yellow") assert.Equal(t, yellow, colorForMethod(http.MethodPut), "put should be yellow")
assert.Equal(t, red, colorForMethod("DELETE"), "delete should be red") assert.Equal(t, red, colorForMethod(http.MethodDelete), "delete should be red")
assert.Equal(t, green, colorForMethod("PATCH"), "patch should be green") assert.Equal(t, green, colorForMethod("PATCH"), "patch should be green")
assert.Equal(t, magenta, colorForMethod("HEAD"), "head should be magenta") assert.Equal(t, magenta, colorForMethod("HEAD"), "head should be magenta")
assert.Equal(t, white, colorForMethod("OPTIONS"), "options should be white") assert.Equal(t, white, colorForMethod("OPTIONS"), "options should be white")
@ -369,15 +369,15 @@ func TestErrorLogger(t *testing.T) {
c.String(http.StatusInternalServerError, "hola!") c.String(http.StatusInternalServerError, "hola!")
}) })
w := PerformRequest(router, "GET", "/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.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String())
w = PerformRequest(router, "GET", "/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.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String())
w = PerformRequest(router, "GET", "/print") w = PerformRequest(router, http.MethodGet, "/print")
assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String()) assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String())
} }
@ -389,11 +389,11 @@ func TestLoggerWithWriterSkippingPaths(t *testing.T) {
router.GET("/logged", func(c *Context) {}) router.GET("/logged", func(c *Context) {})
router.GET("/skipped", func(c *Context) {}) router.GET("/skipped", func(c *Context) {})
PerformRequest(router, "GET", "/logged") PerformRequest(router, http.MethodGet, "/logged")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
buffer.Reset() buffer.Reset()
PerformRequest(router, "GET", "/skipped") PerformRequest(router, http.MethodGet, "/skipped")
assert.Contains(t, buffer.String(), "") assert.Contains(t, buffer.String(), "")
} }
@ -407,11 +407,11 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) {
router.GET("/logged", func(c *Context) {}) router.GET("/logged", func(c *Context) {})
router.GET("/skipped", func(c *Context) {}) router.GET("/skipped", func(c *Context) {})
PerformRequest(router, "GET", "/logged") PerformRequest(router, http.MethodGet, "/logged")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
buffer.Reset() buffer.Reset()
PerformRequest(router, "GET", "/skipped") PerformRequest(router, http.MethodGet, "/skipped")
assert.Contains(t, buffer.String(), "") assert.Contains(t, buffer.String(), "")
} }
@ -427,11 +427,11 @@ func TestLoggerWithConfigSkipper(t *testing.T) {
router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) }) router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) })
router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) }) router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) })
PerformRequest(router, "GET", "/logged") PerformRequest(router, http.MethodGet, "/logged")
assert.Contains(t, buffer.String(), "200") assert.Contains(t, buffer.String(), "200")
buffer.Reset() buffer.Reset()
PerformRequest(router, "GET", "/skipped") PerformRequest(router, http.MethodGet, "/skipped")
assert.Contains(t, buffer.String(), "") assert.Contains(t, buffer.String(), "")
} }

View File

@ -35,7 +35,7 @@ func TestMiddlewareGeneralCase(t *testing.T) {
signature += " XX " signature += " XX "
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
@ -71,7 +71,7 @@ func TestMiddlewareNoRoute(t *testing.T) {
signature += " X " signature += " X "
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
@ -108,7 +108,7 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) {
signature += " XX " signature += " XX "
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusMethodNotAllowed, w.Code) assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
@ -149,7 +149,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) {
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
@ -175,7 +175,7 @@ func TestMiddlewareAbort(t *testing.T) {
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusUnauthorized, w.Code) assert.Equal(t, http.StatusUnauthorized, w.Code)
@ -196,7 +196,7 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) {
c.Next() c.Next()
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusGone, w.Code) assert.Equal(t, http.StatusGone, w.Code)
@ -219,7 +219,7 @@ func TestMiddlewareFailHandlersChain(t *testing.T) {
signature += "C" signature += "C"
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/") w := PerformRequest(router, http.MethodGet, "/")
// TEST // TEST
assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, http.StatusInternalServerError, w.Code)
@ -246,7 +246,7 @@ func TestMiddlewareWrite(t *testing.T) {
}) })
}) })
w := PerformRequest(router, "GET", "/") 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.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))

View File

@ -5,7 +5,6 @@
package gin package gin
import ( import (
"fmt"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -26,14 +25,14 @@ func TestPanicClean(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery", w := PerformRequest(router, http.MethodGet, "/recovery",
header{ header{
Key: "Host", Key: "Host",
Value: "www.google.com", Value: "www.google.com",
}, },
header{ header{
Key: "Authorization", Key: "Authorization",
Value: fmt.Sprintf("Bearer %s", password), Value: "Bearer " + password,
}, },
header{ header{
Key: "Content-Type", Key: "Content-Type",
@ -56,7 +55,7 @@ func TestPanicInHandler(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Contains(t, buffer.String(), "panic recovered") assert.Contains(t, buffer.String(), "panic recovered")
@ -67,7 +66,7 @@ func TestPanicInHandler(t *testing.T) {
// Debug mode prints the request // Debug mode prints the request
SetMode(DebugMode) SetMode(DebugMode)
// RUN // RUN
w = PerformRequest(router, "GET", "/recovery") w = PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Contains(t, buffer.String(), "GET /recovery") assert.Contains(t, buffer.String(), "GET /recovery")
@ -84,7 +83,7 @@ func TestPanicWithAbort(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
} }
@ -135,7 +134,7 @@ func TestPanicWithBrokenPipe(t *testing.T) {
panic(e) panic(e)
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, expectCode, w.Code) assert.Equal(t, expectCode, w.Code)
assert.Contains(t, strings.ToLower(buf.String()), expectMsg) assert.Contains(t, strings.ToLower(buf.String()), expectMsg)
@ -156,7 +155,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "panic recovered") assert.Contains(t, buffer.String(), "panic recovered")
@ -167,7 +166,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
// Debug mode prints the request // Debug mode prints the request
SetMode(DebugMode) SetMode(DebugMode)
// RUN // RUN
w = PerformRequest(router, "GET", "/recovery") w = PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "GET /recovery") assert.Contains(t, buffer.String(), "GET /recovery")
@ -191,7 +190,7 @@ func TestCustomRecovery(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "panic recovered") assert.Contains(t, buffer.String(), "panic recovered")
@ -202,7 +201,7 @@ func TestCustomRecovery(t *testing.T) {
// Debug mode prints the request // Debug mode prints the request
SetMode(DebugMode) SetMode(DebugMode)
// RUN // RUN
w = PerformRequest(router, "GET", "/recovery") w = PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "GET /recovery") assert.Contains(t, buffer.String(), "GET /recovery")
@ -226,7 +225,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
panic("Oupps, Houston, we have a problem") panic("Oupps, Houston, we have a problem")
}) })
// RUN // RUN
w := PerformRequest(router, "GET", "/recovery") w := PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "panic recovered") assert.Contains(t, buffer.String(), "panic recovered")
@ -237,7 +236,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
// Debug mode prints the request // Debug mode prints the request
SetMode(DebugMode) SetMode(DebugMode)
// RUN // RUN
w = PerformRequest(router, "GET", "/recovery") w = PerformRequest(router, http.MethodGet, "/recovery")
// TEST // TEST
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, buffer.String(), "GET /recovery") assert.Contains(t, buffer.String(), "GET /recovery")

View File

@ -369,7 +369,7 @@ func TestRenderXML(t *testing.T) {
} }
func TestRenderRedirect(t *testing.T) { func TestRenderRedirect(t *testing.T) {
req, err := http.NewRequest("GET", "/test-redirect", nil) req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil)
require.NoError(t, err) require.NoError(t, err)
data1 := Redirect{ data1 := Redirect{

View File

@ -523,8 +523,8 @@ func TestRouteNotAllowedEnabled3(t *testing.T) {
w := PerformRequest(router, http.MethodPut, "/path") w := PerformRequest(router, http.MethodPut, "/path")
assert.Equal(t, http.StatusMethodNotAllowed, w.Code) assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
allowed := w.Header().Get("Allow") allowed := w.Header().Get("Allow")
assert.Contains(t, allowed, "GET") assert.Contains(t, allowed, http.MethodGet)
assert.Contains(t, allowed, "POST") assert.Contains(t, allowed, http.MethodPost)
} }
func TestRouteNotAllowedDisabled(t *testing.T) { func TestRouteNotAllowedDisabled(t *testing.T) {
@ -557,10 +557,10 @@ func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) {
{"/nope", http.StatusNotFound, ""}, // NotFound {"/nope", http.StatusNotFound, ""}, // NotFound
} }
for _, tr := range testRoutes { for _, tr := range testRoutes {
w := PerformRequest(router, "GET", tr.route) w := PerformRequest(router, http.MethodGet, tr.route)
assert.Equal(t, tr.code, w.Code) assert.Equal(t, tr.code, w.Code)
if w.Code != http.StatusNotFound { if w.Code != http.StatusNotFound {
assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) assert.Equal(t, tr.location, w.Header().Get("Location"))
} }
} }
} }
@ -590,7 +590,7 @@ func TestRouterNotFound(t *testing.T) {
w := PerformRequest(router, http.MethodGet, tr.route) w := PerformRequest(router, http.MethodGet, tr.route)
assert.Equal(t, tr.code, w.Code) assert.Equal(t, tr.code, w.Code)
if w.Code != http.StatusNotFound { if w.Code != http.StatusNotFound {
assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) assert.Equal(t, tr.location, w.Header().Get("Location"))
} }
} }
@ -786,6 +786,6 @@ func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) {
v1.GET("/orgs/:id", handlerTest1) v1.GET("/orgs/:id", handlerTest1)
v1.DELETE("/orgs/:id", handlerTest1) v1.DELETE("/orgs/:id", handlerTest1)
w := PerformRequest(r, "GET", "/base/v1/user/groups") w := PerformRequest(r, http.MethodGet, "/base/v1/user/groups")
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
} }

View File

@ -369,7 +369,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
// currently fixed width 1 for '/' // currently fixed width 1 for '/'
i-- i--
if path[i] != '/' { if i < 0 || path[i] != '/' {
panic("no / before catch-all in path '" + fullPath + "'") panic("no / before catch-all in path '" + fullPath + "'")
} }

View File

@ -993,3 +993,28 @@ func TestTreeInvalidEscape(t *testing.T) {
} }
} }
} }
func TestWildcardInvalidSlash(t *testing.T) {
const panicMsgPrefix = "no / before catch-all in path"
routes := map[string]bool{
"/foo/bar": true,
"/foo/x*zy": false,
"/foo/b*r": false,
}
for route, valid := range routes {
tree := &node{}
recv := catchPanic(func() {
tree.addRoute(route, nil)
})
if recv == nil != valid {
t.Fatalf("%s should be %t but got %v", route, valid, recv)
}
if rs, ok := recv.(string); recv != nil && (!ok || !strings.HasPrefix(rs, panicMsgPrefix)) {
t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsgPrefix, route, recv)
}
}
}

View File

@ -29,7 +29,7 @@ type testStruct struct {
} }
func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) {
assert.Equal(t.T, "POST", req.Method) assert.Equal(t.T, http.MethodPost, req.Method)
assert.Equal(t.T, "/path", req.URL.Path) assert.Equal(t.T, "/path", req.URL.Path)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "hello") fmt.Fprint(w, "hello")
@ -39,17 +39,17 @@ func TestWrap(t *testing.T) {
router := New() router := New()
router.POST("/path", WrapH(&testStruct{t})) router.POST("/path", WrapH(&testStruct{t}))
router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) { router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) {
assert.Equal(t, "GET", req.Method) assert.Equal(t, http.MethodGet, req.Method)
assert.Equal(t, "/path2", req.URL.Path) assert.Equal(t, "/path2", req.URL.Path)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "hola!") fmt.Fprint(w, "hola!")
})) }))
w := PerformRequest(router, "POST", "/path") w := PerformRequest(router, http.MethodPost, "/path")
assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Equal(t, "hello", w.Body.String()) assert.Equal(t, "hello", w.Body.String())
w = PerformRequest(router, "GET", "/path2") w = PerformRequest(router, http.MethodGet, "/path2")
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Equal(t, "hola!", w.Body.String()) assert.Equal(t, "hola!", w.Body.String())
} }
@ -119,13 +119,13 @@ func TestBindMiddleware(t *testing.T) {
called = true called = true
value = c.MustGet(BindKey).(*bindTestStruct) value = c.MustGet(BindKey).(*bindTestStruct)
}) })
PerformRequest(router, "GET", "/?foo=hola&bar=10") PerformRequest(router, http.MethodGet, "/?foo=hola&bar=10")
assert.True(t, called) assert.True(t, called)
assert.Equal(t, "hola", value.Foo) assert.Equal(t, "hola", value.Foo)
assert.Equal(t, 10, value.Bar) assert.Equal(t, 10, value.Bar)
called = false called = false
PerformRequest(router, "GET", "/?foo=hola&bar=1") PerformRequest(router, http.MethodGet, "/?foo=hola&bar=1")
assert.False(t, called) assert.False(t, called)
assert.Panics(t, func() { assert.Panics(t, func() {