Skip to content

Commit 5f94d6f

Browse files
authored
Merge branch 'master' into fix-cors
2 parents 51ef7a6 + d062a6a commit 5f94d6f

21 files changed

+165
-106
lines changed

.github/workflows/gin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- name: Setup golangci-lint
2222
uses: golangci/golangci-lint-action@v2
2323
with:
24-
version: v1.42.1
24+
version: v1.43.0
2525
args: --verbose
2626
test:
2727
needs: lint

AUTHORS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ People and companies, who have contributed, in alphabetical order.
109109
- Fix typo in comment
110110

111111

112+
**@jincheng9 (Jincheng Zhang)**
113+
- ★ support TSR when wildcard follows named param
114+
- Fix errors and typos in comments
115+
116+
112117
**@joiggama (Ignacio Galindo)**
113118
- Add utf-8 charset header on renders
114119

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Gin ChangeLog
22

3+
## Gin v1.7.7
4+
5+
### BUGFIXES
6+
7+
* Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862).
8+
* Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878).
9+
* Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843).
10+
* Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918).
11+
12+
### ENHANCEMENTS
13+
14+
* TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819).
15+
* TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906).
16+
17+
### DOCS
18+
19+
* NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)).
20+
21+
## Gin v1.7.6
22+
23+
### BUGFIXES
24+
25+
* bump new release to fix v1.7.5 release error by using v1.7.4 codes.
26+
27+
## Gin v1.7.4
28+
29+
### BUGFIXES
30+
31+
* bump new release to fix checksum mismatch
32+
333
## Gin v1.7.3
434

535
### BUGFIXES

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
7878
- [http2 server push](#http2-server-push)
7979
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
8080
- [Set and get a cookie](#set-and-get-a-cookie)
81-
- [Don't trust all proxies](#don't-trust-all-proxies)
81+
- [Don't trust all proxies](#dont-trust-all-proxies)
8282
- [Testing](#testing)
8383
- [Users](#users)
8484

@@ -384,8 +384,8 @@ func main() {
384384
// Set a lower memory limit for multipart forms (default is 32 MiB)
385385
router.MaxMultipartMemory = 8 << 20 // 8 MiB
386386
router.POST("/upload", func(c *gin.Context) {
387-
// single file
388-
file, _ := c.FormFile("file")
387+
// Single file
388+
file, _ := c.FormFile("Filename")
389389
log.Println(file.Filename)
390390

391391
// Upload the file to specific dst.
@@ -417,7 +417,7 @@ func main() {
417417
router.POST("/upload", func(c *gin.Context) {
418418
// Multipart form
419419
form, _ := c.MultipartForm()
420-
files := form.File["upload[]"]
420+
files := form.File["Filename[]"]
421421

422422
for _, file := range files {
423423
log.Println(file.Filename)
@@ -906,7 +906,7 @@ func startPage(c *gin.Context) {
906906
var person Person
907907
// If `GET`, only `Form` binding engine (`query`) used.
908908
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
909-
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
909+
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88
910910
if c.ShouldBind(&person) == nil {
911911
log.Println(person.Name)
912912
log.Println(person.Address)

binding/binding.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type BindingBody interface {
4040
}
4141

4242
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
43-
// but it read the Params.
43+
// but it reads the Params.
4444
type BindingUri interface {
4545
Name() string
4646
BindUri(map[string][]string, interface{}) error

binding/binding_nomsgpack.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type BindingBody interface {
3838
}
3939

4040
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
41-
// but it read the Params.
41+
// but it reads the Params.
4242
type BindingUri interface {
4343
Name() string
4444
BindUri(map[string][]string, interface{}) error

binding/default_validator.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ type defaultValidator struct {
1818
validate *validator.Validate
1919
}
2020

21-
type sliceValidateError []error
21+
type SliceValidationError []error
2222

23-
// Error concatenates all error elements in sliceValidateError into a single string separated by \n.
24-
func (err sliceValidateError) Error() string {
23+
// Error concatenates all error elements in SliceValidationError into a single string separated by \n.
24+
func (err SliceValidationError) Error() string {
2525
n := len(err)
2626
switch n {
2727
case 0:
@@ -59,7 +59,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
5959
return v.validateStruct(obj)
6060
case reflect.Slice, reflect.Array:
6161
count := value.Len()
62-
validateRet := make(sliceValidateError, 0)
62+
validateRet := make(SliceValidationError, 0)
6363
for i := 0; i < count; i++ {
6464
if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
6565
validateRet = append(validateRet, err)

binding/default_validator_benchmark_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import (
66
"testing"
77
)
88

9-
func BenchmarkSliceValidateError(b *testing.B) {
9+
func BenchmarkSliceValidationError(b *testing.B) {
1010
const size int = 100
1111
for i := 0; i < b.N; i++ {
12-
e := make(sliceValidateError, size)
12+
e := make(SliceValidationError, size)
1313
for j := 0; j < size; j++ {
1414
e[j] = errors.New(strconv.Itoa(j))
1515
}

binding/default_validator_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@ import (
99
"testing"
1010
)
1111

12-
func TestSliceValidateError(t *testing.T) {
12+
func TestSliceValidationError(t *testing.T) {
1313
tests := []struct {
1414
name string
15-
err sliceValidateError
15+
err SliceValidationError
1616
want string
1717
}{
18-
{"has nil elements", sliceValidateError{errors.New("test error"), nil}, "[0]: test error"},
19-
{"has zero elements", sliceValidateError{}, ""},
20-
{"has one element", sliceValidateError{errors.New("test one error")}, "[0]: test one error"},
18+
{"has nil elements", SliceValidationError{errors.New("test error"), nil}, "[0]: test error"},
19+
{"has zero elements", SliceValidationError{}, ""},
20+
{"has one element", SliceValidationError{errors.New("test one error")}, "[0]: test one error"},
2121
{"has two elements",
22-
sliceValidateError{
22+
SliceValidationError{
2323
errors.New("first error"),
2424
errors.New("second error"),
2525
},
2626
"[0]: first error\n[1]: second error",
2727
},
2828
{"has many elements",
29-
sliceValidateError{
29+
SliceValidationError{
3030
errors.New("first error"),
3131
errors.New("second error"),
3232
nil,
@@ -40,7 +40,7 @@ func TestSliceValidateError(t *testing.T) {
4040
for _, tt := range tests {
4141
t.Run(tt.name, func(t *testing.T) {
4242
if got := tt.err.Error(); got != tt.want {
43-
t.Errorf("sliceValidateError.Error() = %v, want %v", got, tt.want)
43+
t.Errorf("SliceValidationError.Error() = %v, want %v", got, tt.want)
4444
}
4545
})
4646
}

context.go

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type Context struct {
5959
params *Params
6060
skippedNodes *[]skippedNode
6161

62-
// This mutex protect Keys map
62+
// This mutex protects Keys map.
6363
mu sync.RWMutex
6464

6565
// Keys is a key/value pair exclusively for the context of each request.
@@ -71,10 +71,10 @@ type Context struct {
7171
// Accepted defines a list of manually accepted formats for content negotiation.
7272
Accepted []string
7373

74-
// queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
74+
// queryCache caches the query result from c.Request.URL.Query().
7575
queryCache url.Values
7676

77-
// formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
77+
// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
7878
// or PUT body parameters.
7979
formCache url.Values
8080

@@ -252,7 +252,7 @@ func (c *Context) Set(key string, value interface{}) {
252252
}
253253

254254
// Get returns the value for the given key, ie: (value, true).
255-
// If the value does not exists it returns (nil, false)
255+
// If the value does not exist it returns (nil, false)
256256
func (c *Context) Get(key string) (value interface{}, exists bool) {
257257
c.mu.RLock()
258258
value, exists = c.Keys[key]
@@ -602,7 +602,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
602602
}
603603

604604
// Bind checks the Content-Type to select a binding engine automatically,
605-
// Depending the "Content-Type" header different bindings are used:
605+
// Depending on the "Content-Type" header different bindings are used:
606606
// "application/json" --> JSON binding
607607
// "application/xml" --> XML binding
608608
// otherwise --> returns an error.
@@ -661,7 +661,7 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
661661
}
662662

663663
// ShouldBind checks the Content-Type to select a binding engine automatically,
664-
// Depending the "Content-Type" header different bindings are used:
664+
// Depending on the "Content-Type" header different bindings are used:
665665
// "application/json" --> JSON binding
666666
// "application/xml" --> XML binding
667667
// otherwise --> returns an error
@@ -739,7 +739,7 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e
739739
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
740740
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
741741
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
742-
// the remote IP (coming form Request.RemoteAddr) is returned.
742+
// the remote IP (coming from Request.RemoteAddr) is returned.
743743
func (c *Context) ClientIP() string {
744744
// Check if we're running on a trusted platform, continue running backwards if error
745745
if c.engine.TrustedPlatform != "" {
@@ -757,10 +757,14 @@ func (c *Context) ClientIP() string {
757757
}
758758
}
759759

760-
remoteIP, trusted := c.RemoteIP()
760+
// It also checks if the remoteIP is a trusted proxy or not.
761+
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
762+
// defined by Engine.SetTrustedProxies()
763+
remoteIP := net.ParseIP(c.RemoteIP())
761764
if remoteIP == nil {
762765
return ""
763766
}
767+
trusted := c.engine.isTrustedProxy(remoteIP)
764768

765769
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
766770
for _, headerName := range c.engine.RemoteIPHeaders {
@@ -773,53 +777,13 @@ func (c *Context) ClientIP() string {
773777
return remoteIP.String()
774778
}
775779

776-
func (e *Engine) isTrustedProxy(ip net.IP) bool {
777-
if e.trustedCIDRs != nil {
778-
for _, cidr := range e.trustedCIDRs {
779-
if cidr.Contains(ip) {
780-
return true
781-
}
782-
}
783-
}
784-
return false
785-
}
786-
787780
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
788-
// It also checks if the remoteIP is a trusted proxy or not.
789-
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
790-
// defined by Engine.SetTrustedProxies()
791-
func (c *Context) RemoteIP() (net.IP, bool) {
781+
func (c *Context) RemoteIP() string {
792782
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
793783
if err != nil {
794-
return nil, false
795-
}
796-
remoteIP := net.ParseIP(ip)
797-
if remoteIP == nil {
798-
return nil, false
799-
}
800-
801-
return remoteIP, c.engine.isTrustedProxy(remoteIP)
802-
}
803-
804-
func (e *Engine) validateHeader(header string) (clientIP string, valid bool) {
805-
if header == "" {
806-
return "", false
807-
}
808-
items := strings.Split(header, ",")
809-
for i := len(items) - 1; i >= 0; i-- {
810-
ipStr := strings.TrimSpace(items[i])
811-
ip := net.ParseIP(ipStr)
812-
if ip == nil {
813-
return "", false
814-
}
815-
816-
// X-Forwarded-For is appended by proxy
817-
// Check IPs in reverse order and stop when find untrusted proxy
818-
if (i == 0) || (!e.isTrustedProxy(ip)) {
819-
return ipStr, true
820-
}
784+
return ""
821785
}
822-
return
786+
return ip
823787
}
824788

825789
// ContentType returns the Content-Type header of the request.
@@ -863,7 +827,7 @@ func (c *Context) Status(code int) {
863827
c.Writer.WriteHeader(code)
864828
}
865829

866-
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
830+
// Header is an intelligent shortcut for c.Writer.Header().Set(key, value).
867831
// It writes a header in the response.
868832
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
869833
func (c *Context) Header(key, value string) {
@@ -946,7 +910,7 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
946910

947911
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
948912
// It also sets the Content-Type as "application/json".
949-
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
913+
// WARNING: we recommend using this only for development purposes since printing pretty JSON is
950914
// more CPU and bandwidth consuming. Use Context.JSON() instead.
951915
func (c *Context) IndentedJSON(code int, obj interface{}) {
952916
c.Render(code, render.IndentedJSON{Data: obj})
@@ -1010,7 +974,7 @@ func (c *Context) String(code int, format string, values ...interface{}) {
1010974
c.Render(code, render.String{Format: format, Data: values})
1011975
}
1012976

1013-
// Redirect returns a HTTP redirect to the specific location.
977+
// Redirect returns an HTTP redirect to the specific location.
1014978
func (c *Context) Redirect(code int, location string) {
1015979
c.Render(-1, render.Redirect{
1016980
Code: code,
@@ -1102,7 +1066,7 @@ type Negotiate struct {
11021066
Data interface{}
11031067
}
11041068

1105-
// Negotiate calls different Render according acceptable Accept format.
1069+
// Negotiate calls different Render according to acceptable Accept format.
11061070
func (c *Context) Negotiate(code int, config Negotiate) {
11071071
switch c.NegotiateFormat(config.Offered...) {
11081072
case binding.MIMEJSON:

0 commit comments

Comments
 (0)