Skip to content

Commit

Permalink
Merge branch 'gin-gonic:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Bisstocuz committed Sep 29, 2021
2 parents 25af02a + 97b3c0d commit 7f40bb7
Show file tree
Hide file tree
Showing 19 changed files with 196 additions and 102 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/gin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ jobs:
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.41.1
version: v1.42.1
args: --verbose
test:
needs: lint
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: [1.13, 1.14, 1.15, 1.16]
go: [1.13, 1.14, 1.15, 1.16, 1.17]
test-tags: ['', nomsgpack]
include:
- os: ubuntu-latest
Expand Down
52 changes: 33 additions & 19 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,38 @@ run:
timeout: 5m
linters:
enable:
- gofmt
- misspell
- revive
- asciicheck
- depguard
- dogsled
- durationcheck
- errcheck
- errorlint
- exportloopref
- gci
- gofmt
- goimports
- gosec
- misspell
- nakedret
- nilerr
- nolintlint
- revive
- wastedassign
issues:
exclude-rules:
- linters:
- deadcode
text: "`static` is unused"
- linters:
- structcheck
- unused
text: "`data` is unused"
- linters:
- staticcheck
text: "SA1019:"
- linters:
- revive
text: "var-naming:"
- linters:
- revive
text: "exported:"
- linters:
- structcheck
- unused
text: "`data` is unused"
- linters:
- staticcheck
text: "SA1019:"
- linters:
- revive
text: "var-naming:"
- linters:
- revive
text: "exported:"
- path: _test\.go
linters:
- gosec # security is not make sense in tests
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1827,7 +1827,7 @@ func main() {
quit := make(chan os.Signal)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
// kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
Expand Down
7 changes: 7 additions & 0 deletions binding/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,13 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
err := b.Bind(req, &obj)
assert.Error(t, err)

invalid_obj := FooStruct{}
req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`))
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = b.Bind(req, &invalid_obj)
assert.Error(t, err)
assert.Equal(t, err.Error(), "obj is not ProtoMessage")

obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
Expand Down
3 changes: 2 additions & 1 deletion binding/form.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package binding

import (
"errors"
"net/http"
)

Expand All @@ -22,7 +23,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
if err := req.ParseMultipartForm(defaultMemory); err != nil && err != http.ErrNotMultipart {
if err := req.ParseMultipartForm(defaultMemory); err != nil && !errors.Is(err, http.ErrNotMultipart) {
return err
}
if err := mapForm(obj, req.Form); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions binding/form_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
case reflect.Int64:
switch value.Interface().(type) {
case time.Duration:
return setTimeDuration(val, value, field)
return setTimeDuration(val, value)
}
return setIntField(val, 64, value)
case reflect.Uint:
Expand Down Expand Up @@ -359,7 +359,7 @@ func setSlice(vals []string, value reflect.Value, field reflect.StructField) err
return nil
}

func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
func setTimeDuration(val string, value reflect.Value) error {
d, err := time.ParseDuration(val)
if err != nil {
return err
Expand Down
7 changes: 6 additions & 1 deletion binding/protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package binding

import (
"errors"
"io/ioutil"
"net/http"

Expand All @@ -26,7 +27,11 @@ func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
}

func (protobufBinding) BindBody(body []byte, obj interface{}) error {
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
msg, ok := obj.(proto.Message)
if !ok {
return errors.New("obj is not ProtoMessage")
}
if err := proto.Unmarshal(body, msg); err != nil {
return err
}
// Here it's same to return validate(obj), but util now we can't add
Expand Down
70 changes: 38 additions & 32 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ func (c *Context) Error(err error) *Error {
panic("err is nil")
}

parsedError, ok := err.(*Error)
var parsedError *Error
ok := errors.As(err, &parsedError)
if !ok {
parsedError = &Error{
Err: err,
Expand Down Expand Up @@ -383,6 +384,15 @@ func (c *Context) Param(key string) string {
return c.Params.ByName(key)
}

// AddParam adds param to context and
// replaces path param key with given value for e2e testing purposes
// Example Route: "/user/:id"
// AddParam("id", 1)
// Result: "/user/1"
func (c *Context) AddParam(key, value string) {
c.Params = append(c.Params, Param{Key: key, Value: value})
}

// Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
Expand All @@ -391,9 +401,9 @@ func (c *Context) Param(key string) string {
// c.Query("name") == "Manu"
// c.Query("value") == ""
// c.Query("wtf") == ""
func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key)
return value
func (c *Context) Query(key string) (value string) {
value, _ = c.GetQuery(key)
return
}

// DefaultQuery returns the keyed url query value if it exists,
Expand Down Expand Up @@ -427,9 +437,9 @@ func (c *Context) GetQuery(key string) (string, bool) {

// QueryArray returns a slice of strings for a given query key.
// The length of the slice depends on the number of params with the given key.
func (c *Context) QueryArray(key string) []string {
values, _ := c.GetQueryArray(key)
return values
func (c *Context) QueryArray(key string) (values []string) {
values, _ = c.GetQueryArray(key)
return
}

func (c *Context) initQueryCache() {
Expand All @@ -444,18 +454,16 @@ func (c *Context) initQueryCache() {

// GetQueryArray returns a slice of strings for a given query key, plus
// a boolean value whether at least one value exists for the given key.
func (c *Context) GetQueryArray(key string) ([]string, bool) {
func (c *Context) GetQueryArray(key string) (values []string, ok bool) {
c.initQueryCache()
if values, ok := c.queryCache[key]; ok && len(values) > 0 {
return values, true
}
return []string{}, false
values, ok = c.queryCache[key]
return
}

// QueryMap returns a map for a given query key.
func (c *Context) QueryMap(key string) map[string]string {
dicts, _ := c.GetQueryMap(key)
return dicts
func (c *Context) QueryMap(key string) (dicts map[string]string) {
dicts, _ = c.GetQueryMap(key)
return
}

// GetQueryMap returns a map for a given query key, plus a boolean value
Expand All @@ -467,9 +475,9 @@ func (c *Context) GetQueryMap(key string) (map[string]string, bool) {

// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
func (c *Context) PostForm(key string) string {
value, _ := c.GetPostForm(key)
return value
func (c *Context) PostForm(key string) (value string) {
value, _ = c.GetPostForm(key)
return
}

// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
Expand Down Expand Up @@ -498,17 +506,17 @@ func (c *Context) GetPostForm(key string) (string, bool) {

// PostFormArray returns a slice of strings for a given form key.
// The length of the slice depends on the number of params with the given key.
func (c *Context) PostFormArray(key string) []string {
values, _ := c.GetPostFormArray(key)
return values
func (c *Context) PostFormArray(key string) (values []string) {
values, _ = c.GetPostFormArray(key)
return
}

func (c *Context) initFormCache() {
if c.formCache == nil {
c.formCache = make(url.Values)
req := c.Request
if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
if err != http.ErrNotMultipart {
if !errors.Is(err, http.ErrNotMultipart) {
debugPrint("error on parse multipart form array: %v", err)
}
}
Expand All @@ -518,18 +526,16 @@ func (c *Context) initFormCache() {

// GetPostFormArray returns a slice of strings for a given form key, plus
// a boolean value whether at least one value exists for the given key.
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
func (c *Context) GetPostFormArray(key string) (values []string, ok bool) {
c.initFormCache()
if values := c.formCache[key]; len(values) > 0 {
return values, true
}
return []string{}, false
values, ok = c.formCache[key]
return
}

// PostFormMap returns a map for a given form key.
func (c *Context) PostFormMap(key string) map[string]string {
dicts, _ := c.GetPostFormMap(key)
return dicts
func (c *Context) PostFormMap(key string) (dicts map[string]string) {
dicts, _ = c.GetPostFormMap(key)
return
}

// GetPostFormMap returns a map for a given form key, plus a boolean value
Expand Down Expand Up @@ -729,8 +735,8 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e

// ClientIP implements a best effort algorithm to return the real client IP.
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
// If it's it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
// If the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy,
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
// the remote IP (coming form Request.RemoteAddr) is returned.
func (c *Context) ClientIP() string {
// Check if we're running on a trusted platform
Expand Down
31 changes: 31 additions & 0 deletions context_1.16_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2021 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

//go:build !go1.17
// +build !go1.17

package gin

import (
"bytes"
"mime/multipart"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestContextFormFileFailed16(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
c.engine.MaxMultipartMemory = 8 << 20
f, err := c.FormFile("file")
assert.Error(t, err)
assert.Nil(t, f)
}
33 changes: 33 additions & 0 deletions context_1.17_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2021 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

//go:build go1.17
// +build go1.17

package gin

import (
"bytes"
"mime/multipart"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestContextFormFileFailed17(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
c.engine.MaxMultipartMemory = 8 << 20
assert.Panics(t, func() {
f, err := c.FormFile("file")
assert.Error(t, err)
assert.Nil(t, f)
})
}

0 comments on commit 7f40bb7

Please sign in to comment.