Repository: gin-gonic/gin
Pull request #3033 (2022-01-21 17:17:05)

fix: default trusted proxies should add ipv6


Patch:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..e27022d
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,49 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [ master ]
+  schedule:
+    - cron: '0 17 * * 5'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+
+    permissions:
+      # required for all workflows
+      security-events: write
+
+    strategy:
+      fail-fast: false
+      matrix:
+        # Override automatic language detection by changing the below list
+        # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
+        # TODO: Enable for javascript later
+        language: [ 'go']
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      # Initializes the CodeQL tools for scanning.
+      - name: Initialize CodeQL
+        uses: github/codeql-action/init@v2
+        with:
+          languages: ${{ matrix.language }}
+          # If you wish to specify custom queries, you can do so here or in a config file.
+          # By default, queries listed here will override any specified in a config file.
+          # Prefix the list here with "+" to use these queries and those in the config file.
+          # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+      - name: Perform CodeQL Analysis
+        uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml
index c25a909..e8ef30c 100644
--- a/.github/workflows/gin.yml
+++ b/.github/workflows/gin.yml
@@ -13,22 +13,22 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Setup go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: '^1.16'
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup golangci-lint
-        uses: golangci/golangci-lint-action@v2
+        uses: golangci/golangci-lint-action@v3.2.0
         with:
-          version: v1.43.0
+          version: v1.45.0
           args: --verbose
   test:
     needs: lint
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest]
-        go: [1.13, 1.14, 1.15, 1.16, 1.17]
+        go: [1.14, 1.15, 1.16, 1.17, 1.18]
         test-tags: ['', nomsgpack]
         include:
           - os: ubuntu-latest
@@ -43,16 +43,16 @@ jobs:
       GOPROXY: https://proxy.golang.org
     steps:
       - name: Set up Go ${{ matrix.go }}
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: ${{ matrix.go }}
 
       - name: Checkout Code
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
           ref: ${{ github.ref }}
-          
-      - uses: actions/cache@v2
+
+      - uses: actions/cache@v3
         with:
           path: |
             ${{ matrix.go-build }}
@@ -65,7 +65,7 @@ jobs:
         run: make test
 
       - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v2
+        uses: codecov/codecov-action@v3
         with:
           flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
   notification-gitter:
diff --git a/README.md b/README.md
index b75df63..af9a1e5 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
 
 To install Gin package, you need to install Go and set your Go workspace first.
 
-1. The first need [Go](https://golang.org/) installed (**version 1.13+ is required**), then you can use the below Go command to install Gin.
+1. You first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin.
 
 ```sh
 $ go get -u github.com/gin-gonic/gin
@@ -385,7 +385,7 @@ func main() {
 	router.MaxMultipartMemory = 8 << 20  // 8 MiB
 	router.POST("/upload", func(c *gin.Context) {
 		// Single file
-		file, _ := c.FormFile("Filename")
+		file, _ := c.FormFile("file")
 		log.Println(file.Filename)
 
 		// Upload the file to specific dst.
@@ -417,7 +417,7 @@ func main() {
 	router.POST("/upload", func(c *gin.Context) {
 		// Multipart form
 		form, _ := c.MultipartForm()
-		files := form.File["Filename[]"]
+		files := form.File["upload[]"]
 
 		for _, file := range files {
 			log.Println(file.Filename)
@@ -513,6 +513,7 @@ func main() {
 
 		// nested group
 		testing := authorized.Group("testing")
+		// visit 0.0.0.0:8080/testing/analytics
 		testing.GET("/analytics", analyticsEndpoint)
 	}
 
@@ -1243,7 +1244,8 @@ func main() {
 	router.Static("/assets", "./assets")
 	router.StaticFS("/more_static", http.Dir("my_file_system"))
 	router.StaticFile("/favicon.ico", "./resources/favicon.ico")
-
+	router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system"))
+	
 	// Listen and serve on 0.0.0.0:8080
 	router.Run(":8080")
 }
@@ -1409,7 +1411,7 @@ import (
 
 func formatAsDate(t time.Time) string {
     year, month, day := t.Date()
-    return fmt.Sprintf("%d%02d/%02d", year, month, day)
+    return fmt.Sprintf("%d/%02d/%02d", year, month, day)
 }
 
 func main() {
diff --git a/any.go b/any.go
new file mode 100644
index 0000000..a0104dc
--- /dev/null
+++ b/any.go
@@ -0,0 +1,10 @@
+// Copyright 2022 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.18
+// +build !go1.18
+
+package gin
+
+type any = interface{}
diff --git a/auth.go b/auth.go
index 4d8a6ce..482c499 100644
--- a/auth.go
+++ b/auth.go
@@ -31,7 +31,7 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
 		return "", false
 	}
 	for _, pair := range a {
-		if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
+		if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 {
 			return pair.user, true
 		}
 	}
diff --git a/binding/any.go b/binding/any.go
new file mode 100644
index 0000000..1331d39
--- /dev/null
+++ b/binding/any.go
@@ -0,0 +1,10 @@
+// Copyright 2022 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.18
+// +build !go1.18
+
+package binding
+
+type any = interface{}
diff --git a/binding/binding.go b/binding/binding.go
index 0414a34..703a1cf 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -29,21 +29,21 @@ const (
 // the form POST.
 type Binding interface {
 	Name() string
-	Bind(*http.Request, interface{}) error
+	Bind(*http.Request, any) error
 }
 
 // BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
 // but it reads the body from supplied bytes instead of req.Body.
 type BindingBody interface {
 	Binding
-	BindBody([]byte, interface{}) error
+	BindBody([]byte, any) error
 }
 
 // BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
 // but it reads the Params.
 type BindingUri interface {
 	Name() string
-	BindUri(map[string][]string, interface{}) error
+	BindUri(map[string][]string, any) error
 }
 
 // StructValidator is the minimal interface which needs to be implemented in
@@ -57,11 +57,11 @@ type StructValidator interface {
 	// If the received type is a struct or pointer to a struct, the validation should be performed.
 	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
 	// Otherwise nil must be returned.
-	ValidateStruct(interface{}) error
+	ValidateStruct(any) error
 
 	// Engine returns the underlying validator engine which powers the
 	// StructValidator implementation.
-	Engine() interface{}
+	Engine() any
 }
 
 // Validator is the default validator which implements the StructValidator
@@ -110,7 +110,7 @@ func Default(method, contentType string) Binding {
 	}
 }
 
-func validate(obj interface{}) error {
+func validate(obj any) error {
 	if Validator == nil {
 		return nil
 	}
diff --git a/binding/binding_nomsgpack.go b/binding/binding_nomsgpack.go
index f0b667b..b381854 100644
--- a/binding/binding_nomsgpack.go
+++ b/binding/binding_nomsgpack.go
@@ -27,21 +27,21 @@ const (
 // the form POST.
 type Binding interface {
 	Name() string
-	Bind(*http.Request, interface{}) error
+	Bind(*http.Request, any) error
 }
 
 // BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
 // but it reads the body from supplied bytes instead of req.Body.
 type BindingBody interface {
 	Binding
-	BindBody([]byte, interface{}) error
+	BindBody([]byte, any) error
 }
 
 // BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
 // but it reads the Params.
 type BindingUri interface {
 	Name() string
-	BindUri(map[string][]string, interface{}) error
+	BindUri(map[string][]string, any) error
 }
 
 // StructValidator is the minimal interface which needs to be implemented in
@@ -54,11 +54,11 @@ type StructValidator interface {
 	// If the received type is a struct or pointer to a struct, the validation should be performed.
 	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
 	// Otherwise nil must be returned.
-	ValidateStruct(interface{}) error
+	ValidateStruct(any) error
 
 	// Engine returns the underlying validator engine which powers the
 	// StructValidator implementation.
-	Engine() interface{}
+	Engine() any
 }
 
 // Validator is the default validator which implements the StructValidator
@@ -104,7 +104,7 @@ func Default(method, contentType string) Binding {
 	}
 }
 
-func validate(obj interface{}) error {
+func validate(obj any) error {
 	if Validator == nil {
 		return nil
 	}
diff --git a/binding/default_validator.go b/binding/default_validator.go
index bd8764b..3515a8c 100644
--- a/binding/default_validator.go
+++ b/binding/default_validator.go
@@ -46,7 +46,7 @@ func (err SliceValidationError) Error() string {
 var _ StructValidator = &defaultValidator{}
 
 // ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
-func (v *defaultValidator) ValidateStruct(obj interface{}) error {
+func (v *defaultValidator) ValidateStruct(obj any) error {
 	if obj == nil {
 		return nil
 	}
@@ -75,7 +75,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
 }
 
 // validateStruct receives struct type
-func (v *defaultValidator) validateStruct(obj interface{}) error {
+func (v *defaultValidator) validateStruct(obj any) error {
 	v.lazyinit()
 	return v.validate.Struct(obj)
 }
@@ -84,7 +84,7 @@ func (v *defaultValidator) validateStruct(obj interface{}) error {
 // Validator instance. This is useful if you want to register custom validations
 // or struct level validations. See validator GoDoc for more info -
 // https://pkg.go.dev/github.com/go-playground/validator/v10
-func (v *defaultValidator) Engine() interface{} {
+func (v *defaultValidator) Engine() any {
 	v.lazyinit()
 	return v.validate
 }
diff --git a/binding/form.go b/binding/form.go
index fa2a654..f5cbf57 100644
--- a/binding/form.go
+++ b/binding/form.go
@@ -19,7 +19,7 @@ func (formBinding) Name() string {
 	return "form"
 }
 
-func (formBinding) Bind(req *http.Request, obj interface{}) error {
+func (formBinding) Bind(req *http.Request, obj any) error {
 	if err := req.ParseForm(); err != nil {
 		return err
 	}
@@ -36,7 +36,7 @@ func (formPostBinding) Name() string {
 	return "form-urlencoded"
 }
 
-func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
+func (formPostBinding) Bind(req *http.Request, obj any) error {
 	if err := req.ParseForm(); err != nil {
 		return err
 	}
@@ -50,7 +50,7 @@ func (formMultipartBinding) Name() string {
 	return "multipart/form-data"
 }
 
-func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
+func (formMultipartBinding) Bind(req *http.Request, obj any) error {
 	if err := req.ParseMultipartForm(defaultMemory); err != nil {
 		return err
 	}
diff --git a/binding/form_mapping.go b/binding/form_mapping.go
index fa7ad1b..c24dd55 100644
--- a/binding/form_mapping.go
+++ b/binding/form_mapping.go
@@ -26,24 +26,24 @@ var (
 	ErrConvertToMapString = errors.New("can not convert to map of strings")
 )
 
-func mapURI(ptr interface{}, m map[string][]string) error {
+func mapURI(ptr any, m map[string][]string) error {
 	return mapFormByTag(ptr, m, "uri")
 }
 
-func mapForm(ptr interface{}, form map[string][]string) error {
+func mapForm(ptr any, form map[string][]string) error {
 	return mapFormByTag(ptr, form, "form")
 }
 
-func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error {
+func MapFormWithTag(ptr any, form map[string][]string, tag string) error {
 	return mapFormByTag(ptr, form, tag)
 }
 
 var emptyField = reflect.StructField{}
 
-func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
+func mapFormByTag(ptr any, form map[string][]string, tag string) error {
 	// Check if ptr is a map
 	ptrVal := reflect.ValueOf(ptr)
-	var pointed interface{}
+	var pointed any
 	if ptrVal.Kind() == reflect.Ptr {
 		ptrVal = ptrVal.Elem()
 		pointed = ptrVal.Interface()
@@ -73,7 +73,7 @@ func (form formSource) TrySet(value reflect.Value, field reflect.StructField, ta
 	return setByForm(value, field, form, tagValue, opt)
 }
 
-func mappingByPtr(ptr interface{}, setter setter, tag string) error {
+func mappingByPtr(ptr any, setter setter, tag string) error {
 	_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
 	return err
 }
@@ -376,7 +376,7 @@ func head(str, sep string) (head string, tail string) {
 	return str[:idx], str[idx+len(sep):]
 }
 
-func setFormMap(ptr interface{}, form map[string][]string) error {
+func setFormMap(ptr any, form map[string][]string) error {
 	el := reflect.TypeOf(ptr).Elem()
 
 	if el.Kind() == reflect.Slice {
diff --git a/binding/header.go b/binding/header.go
index b99302a..14f525e 100644
--- a/binding/header.go
+++ b/binding/header.go
@@ -12,7 +12,7 @@ func (headerBinding) Name() string {
 	return "header"
 }
 
-func (headerBinding) Bind(req *http.Request, obj interface{}) error {
+func (headerBinding) Bind(req *http.Request, obj any) error {
 
 	if err := mapHeader(obj, req.Header); err != nil {
 		return err
@@ -21,7 +21,7 @@ func (headerBinding) Bind(req *http.Request, obj interface{}) error {
 	return validate(obj)
 }
 
-func mapHeader(ptr interface{}, h map[string][]string) error {
+func mapHeader(ptr any, h map[string][]string) error {
 	return mappingByPtr(ptr, headerSource(h), "header")
 }
 
diff --git a/binding/json.go b/binding/json.go
index 45aaa49..2e3e1dd 100644
--- a/binding/json.go
+++ b/binding/json.go
@@ -30,18 +30,18 @@ func (jsonBinding) Name() string {
 	return "json"
 }
 
-func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
+func (jsonBinding) Bind(req *http.Request, obj any) error {
 	if req == nil || req.Body == nil {
 		return errors.New("invalid request")
 	}
 	return decodeJSON(req.Body, obj)
 }
 
-func (jsonBinding) BindBody(body []byte, obj interface{}) error {
+func (jsonBinding) BindBody(body []byte, obj any) error {
 	return decodeJSON(bytes.NewReader(body), obj)
 }
 
-func decodeJSON(r io.Reader, obj interface{}) error {
+func decodeJSON(r io.Reader, obj any) error {
 	decoder := json.NewDecoder(r)
 	if EnableDecoderUseNumber {
 		decoder.UseNumber()
diff --git a/binding/msgpack.go b/binding/msgpack.go
index 2a44299..6519771 100644
--- a/binding/msgpack.go
+++ b/binding/msgpack.go
@@ -21,15 +21,15 @@ func (msgpackBinding) Name() string {
 	return "msgpack"
 }
 
-func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
+func (msgpackBinding) Bind(req *http.Request, obj any) error {
 	return decodeMsgPack(req.Body, obj)
 }
 
-func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
+func (msgpackBinding) BindBody(body []byte, obj any) error {
 	return decodeMsgPack(bytes.NewReader(body), obj)
 }
 
-func decodeMsgPack(r io.Reader, obj interface{}) error {
+func decodeMsgPack(r io.Reader, obj any) error {
 	cdc := new(codec.MsgpackHandle)
 	if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
 		return err
diff --git a/binding/protobuf.go b/binding/protobuf.go
index a4e4715..ace8f65 100644
--- a/binding/protobuf.go
+++ b/binding/protobuf.go
@@ -18,7 +18,7 @@ func (protobufBinding) Name() string {
 	return "protobuf"
 }
 
-func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
+func (b protobufBinding) Bind(req *http.Request, obj any) error {
 	buf, err := ioutil.ReadAll(req.Body)
 	if err != nil {
 		return err
@@ -26,7 +26,7 @@ func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
 	return b.BindBody(buf, obj)
 }
 
-func (protobufBinding) BindBody(body []byte, obj interface{}) error {
+func (protobufBinding) BindBody(body []byte, obj any) error {
 	msg, ok := obj.(proto.Message)
 	if !ok {
 		return errors.New("obj is not ProtoMessage")
diff --git a/binding/query.go b/binding/query.go
index 219743f..9790ce6 100644
--- a/binding/query.go
+++ b/binding/query.go
@@ -12,7 +12,7 @@ func (queryBinding) Name() string {
 	return "query"
 }
 
-func (queryBinding) Bind(req *http.Request, obj interface{}) error {
+func (queryBinding) Bind(req *http.Request, obj any) error {
 	values := req.URL.Query()
 	if err := mapForm(obj, values); err != nil {
 		return err
diff --git a/binding/uri.go b/binding/uri.go
index a3c0df5..4d44ab9 100644
--- a/binding/uri.go
+++ b/binding/uri.go
@@ -10,7 +10,7 @@ func (uriBinding) Name() string {
 	return "uri"
 }
 
-func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
+func (uriBinding) BindUri(m map[string][]string, obj any) error {
 	if err := mapURI(obj, m); err != nil {
 		return err
 	}
diff --git a/binding/xml.go b/binding/xml.go
index 4e90114..e62af4a 100644
--- a/binding/xml.go
+++ b/binding/xml.go
@@ -17,14 +17,14 @@ func (xmlBinding) Name() string {
 	return "xml"
 }
 
-func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
+func (xmlBinding) Bind(req *http.Request, obj any) error {
 	return decodeXML(req.Body, obj)
 }
 
-func (xmlBinding) BindBody(body []byte, obj interface{}) error {
+func (xmlBinding) BindBody(body []byte, obj any) error {
 	return decodeXML(bytes.NewReader(body), obj)
 }
-func decodeXML(r io.Reader, obj interface{}) error {
+func decodeXML(r io.Reader, obj any) error {
 	decoder := xml.NewDecoder(r)
 	if err := decoder.Decode(obj); err != nil {
 		return err
diff --git a/binding/yaml.go b/binding/yaml.go
index a2d36d6..183f141 100644
--- a/binding/yaml.go
+++ b/binding/yaml.go
@@ -18,15 +18,15 @@ func (yamlBinding) Name() string {
 	return "yaml"
 }
 
-func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
+func (yamlBinding) Bind(req *http.Request, obj any) error {
 	return decodeYAML(req.Body, obj)
 }
 
-func (yamlBinding) BindBody(body []byte, obj interface{}) error {
+func (yamlBinding) BindBody(body []byte, obj any) error {
 	return decodeYAML(bytes.NewReader(body), obj)
 }
 
-func decodeYAML(r io.Reader, obj interface{}) error {
+func decodeYAML(r io.Reader, obj any) error {
 	decoder := yaml.NewDecoder(r)
 	if err := decoder.Decode(obj); err != nil {
 		return err
diff --git a/context.go b/context.go
index d69df70..5e53aaf 100644
--- a/context.go
+++ b/context.go
@@ -6,7 +6,6 @@ package gin
 
 import (
 	"errors"
-	"fmt"
 	"io"
 	"io/ioutil"
 	"log"
@@ -40,6 +39,9 @@ const (
 // BodyBytesKey indicates a default body bytes key.
 const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
 
+// ContextKey is the key that a Context returns itself for.
+const ContextKey = "_gin-gonic/gin/contextkey"
+
 // abortIndex represents a typical value used in abort functions.
 const abortIndex int8 = math.MaxInt8 >> 1
 
@@ -63,7 +65,7 @@ type Context struct {
 	mu sync.RWMutex
 
 	// Keys is a key/value pair exclusively for the context of each request.
-	Keys map[string]interface{}
+	Keys map[string]any
 
 	// Errors is a list of errors attached to all the handlers/middlewares who used this context.
 	Errors errorMsgs
@@ -99,6 +101,7 @@ func (c *Context) reset() {
 	c.Accepted = nil
 	c.queryCache = nil
 	c.formCache = nil
+	c.sameSite = 0
 	*c.params = (*c.params)[:0]
 	*c.skippedNodes = (*c.skippedNodes)[:0]
 }
@@ -116,7 +119,7 @@ func (c *Context) Copy() *Context {
 	cp.Writer = &cp.writermem
 	cp.index = abortIndex
 	cp.handlers = nil
-	cp.Keys = map[string]interface{}{}
+	cp.Keys = map[string]any{}
 	for k, v := range c.Keys {
 		cp.Keys[k] = v
 	}
@@ -195,7 +198,7 @@ func (c *Context) AbortWithStatus(code int) {
 // AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
 // This method stops the chain, writes the status code and return a JSON body.
 // It also sets the Content-Type as "application/json".
-func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
+func (c *Context) AbortWithStatusJSON(code int, jsonObj any) {
 	c.Abort()
 	c.JSON(code, jsonObj)
 }
@@ -241,10 +244,10 @@ func (c *Context) Error(err error) *Error {
 
 // Set is used to store a new key/value pair exclusively for this context.
 // It also lazy initializes  c.Keys if it was not used previously.
-func (c *Context) Set(key string, value interface{}) {
+func (c *Context) Set(key string, value any) {
 	c.mu.Lock()
 	if c.Keys == nil {
-		c.Keys = make(map[string]interface{})
+		c.Keys = make(map[string]any)
 	}
 
 	c.Keys[key] = value
@@ -253,7 +256,7 @@ func (c *Context) Set(key string, value interface{}) {
 
 // Get returns the value for the given key, ie: (value, true).
 // If the value does not exist it returns (nil, false)
-func (c *Context) Get(key string) (value interface{}, exists bool) {
+func (c *Context) Get(key string) (value any, exists bool) {
 	c.mu.RLock()
 	value, exists = c.Keys[key]
 	c.mu.RUnlock()
@@ -261,7 +264,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) {
 }
 
 // MustGet returns the value for the given key if it exists, otherwise it panics.
-func (c *Context) MustGet(key string) interface{} {
+func (c *Context) MustGet(key string) any {
 	if value, exists := c.Get(key); exists {
 		return value
 	}
@@ -349,9 +352,9 @@ func (c *Context) GetStringSlice(key string) (ss []string) {
 }
 
 // GetStringMap returns the value associated with the key as a map of interfaces.
-func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
+func (c *Context) GetStringMap(key string) (sm map[string]any) {
 	if val, ok := c.Get(key); ok && val != nil {
-		sm, _ = val.(map[string]interface{})
+		sm, _ = val.(map[string]any)
 	}
 	return
 }
@@ -608,39 +611,39 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
 // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
 // It decodes the json payload into the struct specified as a pointer.
 // It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
-func (c *Context) Bind(obj interface{}) error {
+func (c *Context) Bind(obj any) error {
 	b := binding.Default(c.Request.Method, c.ContentType())
 	return c.MustBindWith(obj, b)
 }
 
 // BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
-func (c *Context) BindJSON(obj interface{}) error {
+func (c *Context) BindJSON(obj any) error {
 	return c.MustBindWith(obj, binding.JSON)
 }
 
 // BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
-func (c *Context) BindXML(obj interface{}) error {
+func (c *Context) BindXML(obj any) error {
 	return c.MustBindWith(obj, binding.XML)
 }
 
 // BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
-func (c *Context) BindQuery(obj interface{}) error {
+func (c *Context) BindQuery(obj any) error {
 	return c.MustBindWith(obj, binding.Query)
 }
 
 // BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
-func (c *Context) BindYAML(obj interface{}) error {
+func (c *Context) BindYAML(obj any) error {
 	return c.MustBindWith(obj, binding.YAML)
 }
 
 // BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
-func (c *Context) BindHeader(obj interface{}) error {
+func (c *Context) BindHeader(obj any) error {
 	return c.MustBindWith(obj, binding.Header)
 }
 
 // BindUri binds the passed struct pointer using binding.Uri.
 // It will abort the request with HTTP 400 if any error occurs.
-func (c *Context) BindUri(obj interface{}) error {
+func (c *Context) BindUri(obj any) error {
 	if err := c.ShouldBindUri(obj); err != nil {
 		c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
 		return err
@@ -651,7 +654,7 @@ func (c *Context) BindUri(obj interface{}) error {
 // MustBindWith binds the passed struct pointer using the specified binding engine.
 // It will abort the request with HTTP 400 if any error occurs.
 // See the binding package.
-func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) MustBindWith(obj any, b binding.Binding) error {
 	if err := c.ShouldBindWith(obj, b); err != nil {
 		c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
 		return err
@@ -666,38 +669,38 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
 // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
 // It decodes the json payload into the struct specified as a pointer.
 // Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid.
-func (c *Context) ShouldBind(obj interface{}) error {
+func (c *Context) ShouldBind(obj any) error {
 	b := binding.Default(c.Request.Method, c.ContentType())
 	return c.ShouldBindWith(obj, b)
 }
 
 // ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
-func (c *Context) ShouldBindJSON(obj interface{}) error {
+func (c *Context) ShouldBindJSON(obj any) error {
 	return c.ShouldBindWith(obj, binding.JSON)
 }
 
 // ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
-func (c *Context) ShouldBindXML(obj interface{}) error {
+func (c *Context) ShouldBindXML(obj any) error {
 	return c.ShouldBindWith(obj, binding.XML)
 }
 
 // ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
-func (c *Context) ShouldBindQuery(obj interface{}) error {
+func (c *Context) ShouldBindQuery(obj any) error {
 	return c.ShouldBindWith(obj, binding.Query)
 }
 
 // ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
-func (c *Context) ShouldBindYAML(obj interface{}) error {
+func (c *Context) ShouldBindYAML(obj any) error {
 	return c.ShouldBindWith(obj, binding.YAML)
 }
 
 // ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
-func (c *Context) ShouldBindHeader(obj interface{}) error {
+func (c *Context) ShouldBindHeader(obj any) error {
 	return c.ShouldBindWith(obj, binding.Header)
 }
 
 // ShouldBindUri binds the passed struct pointer using the specified binding engine.
-func (c *Context) ShouldBindUri(obj interface{}) error {
+func (c *Context) ShouldBindUri(obj any) error {
 	m := make(map[string][]string)
 	for _, v := range c.Params {
 		m[v.Key] = []string{v.Value}
@@ -707,7 +710,7 @@ func (c *Context) ShouldBindUri(obj interface{}) error {
 
 // ShouldBindWith binds the passed struct pointer using the specified binding engine.
 // See the binding package.
-func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) ShouldBindWith(obj any, b binding.Binding) error {
 	return b.Bind(c.Request, obj)
 }
 
@@ -716,7 +719,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
 //
 // NOTE: This method reads the body before binding. So you should use
 // ShouldBindWith for better performance if you need to call only once.
-func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
+func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) {
 	var body []byte
 	if cb, ok := c.Get(BodyBytesKey); ok {
 		if cbb, ok := cb.([]byte); ok {
@@ -901,7 +904,7 @@ func (c *Context) Render(code int, r render.Render) {
 // HTML renders the HTTP template specified by its file name.
 // It also updates the HTTP code and sets the Content-Type as "text/html".
 // See http://golang.org/doc/articles/wiki/
-func (c *Context) HTML(code int, name string, obj interface{}) {
+func (c *Context) HTML(code int, name string, obj any) {
 	instance := c.engine.HTMLRender.Instance(name, obj)
 	c.Render(code, instance)
 }
@@ -910,21 +913,21 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
 // It also sets the Content-Type as "application/json".
 // WARNING: we recommend using this only for development purposes since printing pretty JSON is
 // more CPU and bandwidth consuming. Use Context.JSON() instead.
-func (c *Context) IndentedJSON(code int, obj interface{}) {
+func (c *Context) IndentedJSON(code int, obj any) {
 	c.Render(code, render.IndentedJSON{Data: obj})
 }
 
 // SecureJSON serializes the given struct as Secure JSON into the response body.
 // Default prepends "while(1)," to response body if the given struct is array values.
 // It also sets the Content-Type as "application/json".
-func (c *Context) SecureJSON(code int, obj interface{}) {
+func (c *Context) SecureJSON(code int, obj any) {
 	c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
 }
 
 // JSONP serializes the given struct as JSON into the response body.
 // It adds padding to response body to request data from a server residing in a different domain than the client.
 // It also sets the Content-Type as "application/javascript".
-func (c *Context) JSONP(code int, obj interface{}) {
+func (c *Context) JSONP(code int, obj any) {
 	callback := c.DefaultQuery("callback", "")
 	if callback == "" {
 		c.Render(code, render.JSON{Data: obj})
@@ -935,40 +938,40 @@ func (c *Context) JSONP(code int, obj interface{}) {
 
 // JSON serializes the given struct as JSON into the response body.
 // It also sets the Content-Type as "application/json".
-func (c *Context) JSON(code int, obj interface{}) {
+func (c *Context) JSON(code int, obj any) {
 	c.Render(code, render.JSON{Data: obj})
 }
 
 // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
 // It also sets the Content-Type as "application/json".
-func (c *Context) AsciiJSON(code int, obj interface{}) {
+func (c *Context) AsciiJSON(code int, obj any) {
 	c.Render(code, render.AsciiJSON{Data: obj})
 }
 
 // PureJSON serializes the given struct as JSON into the response body.
 // PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
-func (c *Context) PureJSON(code int, obj interface{}) {
+func (c *Context) PureJSON(code int, obj any) {
 	c.Render(code, render.PureJSON{Data: obj})
 }
 
 // XML serializes the given struct as XML into the response body.
 // It also sets the Content-Type as "application/xml".
-func (c *Context) XML(code int, obj interface{}) {
+func (c *Context) XML(code int, obj any) {
 	c.Render(code, render.XML{Data: obj})
 }
 
 // YAML serializes the given struct as YAML into the response body.
-func (c *Context) YAML(code int, obj interface{}) {
+func (c *Context) YAML(code int, obj any) {
 	c.Render(code, render.YAML{Data: obj})
 }
 
 // ProtoBuf serializes the given struct as ProtoBuf into the response body.
-func (c *Context) ProtoBuf(code int, obj interface{}) {
+func (c *Context) ProtoBuf(code int, obj any) {
 	c.Render(code, render.ProtoBuf{Data: obj})
 }
 
 // String writes the given string into the response body.
-func (c *Context) String(code int, format string, values ...interface{}) {
+func (c *Context) String(code int, format string, values ...any) {
 	c.Render(code, render.String{Format: format, Data: values})
 }
 
@@ -1018,12 +1021,16 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
 // FileAttachment writes the specified file into the body stream in an efficient way
 // On the client side, the file will typically be downloaded with the given filename
 func (c *Context) FileAttachment(filepath, filename string) {
-	c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
+	if isASCII(filename) {
+		c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
+	} else {
+		c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
+	}
 	http.ServeFile(c.Writer, c.Request, filepath)
 }
 
 // SSEvent writes a Server-Sent Event into the body stream.
-func (c *Context) SSEvent(name string, message interface{}) {
+func (c *Context) SSEvent(name string, message any) {
 	c.Render(-1, sse.Event{
 		Event: name,
 		Data:  message,
@@ -1057,11 +1064,11 @@ func (c *Context) Stream(step func(w io.Writer) bool) bool {
 type Negotiate struct {
 	Offered  []string
 	HTMLName string
-	HTMLData interface{}
-	JSONData interface{}
-	XMLData  interface{}
-	YAMLData interface{}
-	Data     interface{}
+	HTMLData any
+	JSONData any
+	XMLData  any
+	YAMLData any
+	Data     any
 }
 
 // Negotiate calls different Render according to acceptable Accept format.
@@ -1155,10 +1162,13 @@ func (c *Context) Err() error {
 // Value returns the value associated with this context for key, or nil
 // if no value is associated with key. Successive calls to Value with
 // the same key returns the same result.
-func (c *Context) Value(key interface{}) interface{} {
+func (c *Context) Value(key any) any {
 	if key == 0 {
 		return c.Request
 	}
+	if key == ContextKey {
+		return c
+	}
 	if keyAsString, ok := key.(string); ok {
 		if val, exists := c.Get(keyAsString); exists {
 			return val
diff --git a/debug.go b/debug.go
index ed31386..d8d6c8e 100644
--- a/debug.go
+++ b/debug.go
@@ -12,7 +12,7 @@ import (
 	"strings"
 )
 
-const ginSupportMinGoVer = 13
+const ginSupportMinGoVer = 14
 
 // IsDebugging returns true if the framework is running in debug mode.
 // Use SetMode(gin.ReleaseMode) to disable debug mode.
@@ -47,7 +47,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
 	}
 }
 
-func debugPrint(format string, values ...interface{}) {
+func debugPrint(format string, values ...any) {
 	if IsDebugging() {
 		if !strings.HasSuffix(format, "\n") {
 			format += "\n"
@@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
 
 func debugPrintWARNINGDefault() {
 	if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
-		debugPrint(`[WARNING] Now Gin requires Go 1.13+.
+		debugPrint(`[WARNING] Now Gin requires Go 1.14+.
 
 `)
 	}
diff --git a/deprecated.go b/deprecated.go
index ab44742..004e13d 100644
--- a/deprecated.go
+++ b/deprecated.go
@@ -12,7 +12,7 @@ import (
 
 // BindWith binds the passed struct pointer using the specified binding engine.
 // See the binding package.
-func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) BindWith(obj any, b binding.Binding) error {
 	log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
 	be deprecated, please check issue #662 and either use MustBindWith() if you
 	want HTTP 400 to be automatically returned if any error occur, or use
diff --git a/errors.go b/errors.go
index 3418cbc..578e857 100644
--- a/errors.go
+++ b/errors.go
@@ -34,7 +34,7 @@ const (
 type Error struct {
 	Err  error
 	Type ErrorType
-	Meta interface{}
+	Meta any
 }
 
 type errorMsgs []*Error
@@ -48,13 +48,13 @@ func (msg *Error) SetType(flags ErrorType) *Error {
 }
 
 // SetMeta sets the error's meta data.
-func (msg *Error) SetMeta(data interface{}) *Error {
+func (msg *Error) SetMeta(data any) *Error {
 	msg.Meta = data
 	return msg
 }
 
 // JSON creates a properly formatted JSON
-func (msg *Error) JSON() interface{} {
+func (msg *Error) JSON() any {
 	jsonData := H{}
 	if msg.Meta != nil {
 		value := reflect.ValueOf(msg.Meta)
@@ -139,14 +139,14 @@ func (a errorMsgs) Errors() []string {
 	return errorStrings
 }
 
-func (a errorMsgs) JSON() interface{} {
+func (a errorMsgs) JSON() any {
 	switch length := len(a); length {
 	case 0:
 		return nil
 	case 1:
 		return a.Last().JSON()
 	default:
-		jsonData := make([]interface{}, length)
+		jsonData := make([]any, length)
 		for i, err := range a {
 			jsonData[i] = err.JSON()
 		}
diff --git a/gin.go b/gin.go
index 9bd4c46..3a831e5 100644
--- a/gin.go
+++ b/gin.go
@@ -16,6 +16,8 @@ import (
 
 	"github.com/gin-gonic/gin/internal/bytesconv"
 	"github.com/gin-gonic/gin/render"
+	"golang.org/x/net/http2"
+	"golang.org/x/net/http2/h2c"
 )
 
 const defaultMultipartMemory = 32 << 20 // 32 MB
@@ -65,10 +67,10 @@ type RoutesInfo []RouteInfo
 
 // Trusted platforms
 const (
-	// When running on Google App Engine. Trust X-Appengine-Remote-Addr
+	// PlatformGoogleAppEngine when running on Google App Engine. Trust X-Appengine-Remote-Addr
 	// for determining the client's IP
 	PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
-	// When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
+	// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
 	// the client's IP
 	PlatformCloudflare = "CF-Connecting-IP"
 )
@@ -78,14 +80,14 @@ const (
 type Engine struct {
 	RouterGroup
 
-	// Enables automatic redirection if the current route can't be matched but a
+	// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
 	// handler for the path with (without) the trailing slash exists.
 	// For example if /foo/ is requested but a route only exists for /foo, the
 	// client is redirected to /foo with http status code 301 for GET requests
 	// and 307 for all other request methods.
 	RedirectTrailingSlash bool
 
-	// If enabled, the router tries to fix the current request path, if no
+	// RedirectFixedPath if enabled, the router tries to fix the current request path, if no
 	// handle is registered for it.
 	// First superfluous path elements like ../ or // are removed.
 	// Afterwards the router does a case-insensitive lookup of the cleaned path.
@@ -96,7 +98,7 @@ type Engine struct {
 	// RedirectTrailingSlash is independent of this option.
 	RedirectFixedPath bool
 
-	// If enabled, the router checks if another method is allowed for the
+	// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
 	// current route, if the current request can not be routed.
 	// If this is the case, the request is answered with 'Method Not Allowed'
 	// and HTTP status code 405.
@@ -104,21 +106,22 @@ type Engine struct {
 	// handler.
 	HandleMethodNotAllowed bool
 
-	// If enabled, client IP will be parsed from the request's headers that
+	// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
 	// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
 	// fetched, it falls back to the IP obtained from
 	// `(*gin.Context).Request.RemoteAddr`.
 	ForwardedByClientIP bool
 
-	// DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
+	// AppEngine was deprecated.
+	// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
 	// #726 #755 If enabled, it will trust some headers starting with
 	// 'X-AppEngine...' for better integration with that PaaS.
 	AppEngine bool
 
-	// If enabled, the url.RawPath will be used to find parameters.
+	// UseRawPath if enabled, the url.RawPath will be used to find parameters.
 	UseRawPath bool
 
-	// If true, the path value will be unescaped.
+	// UnescapePathValues if true, the path value will be unescaped.
 	// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
 	// as url.Path gonna be used, which is already unescaped.
 	UnescapePathValues bool
@@ -127,20 +130,23 @@ type Engine struct {
 	// See the PR #1817 and issue #1644
 	RemoveExtraSlash bool
 
-	// List of headers used to obtain the client IP when
+	// RemoteIPHeaders list of headers used to obtain the client IP when
 	// `(*gin.Engine).ForwardedByClientIP` is `true` and
 	// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
 	// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
 	RemoteIPHeaders []string
 
-	// If set to a constant of value gin.Platform*, trusts the headers set by
+	// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
 	// that platform, for example to determine the client IP
 	TrustedPlatform string
 
-	// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
+	// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
 	// method call.
 	MaxMultipartMemory int64
 
+	// UseH2C enable h2c support.
+	UseH2C bool
+
 	delims           render.Delims
 	secureJSONPrefix string
 	HTMLRender       render.HTMLRender
@@ -189,11 +195,11 @@ func New() *Engine {
 		trees:                  make(methodTrees, 0, 9),
 		delims:                 render.Delims{Left: "{{", Right: "}}"},
 		secureJSONPrefix:       "while(1);",
-		trustedProxies:         []string{"0.0.0.0/0"},
+		trustedProxies:         []string{"0.0.0.0/0", "::/0"},
 		trustedCIDRs:           defaultTrustedCIDRs,
 	}
 	engine.RouterGroup.engine = engine
-	engine.pool.New = func() interface{} {
+	engine.pool.New = func() any {
 		return engine.allocateContext()
 	}
 	return engine
@@ -207,6 +213,15 @@ func Default() *Engine {
 	return engine
 }
 
+func (engine *Engine) Handler() http.Handler {
+	if !engine.UseH2C {
+		return engine
+	}
+
+	h2s := &http2.Server{}
+	return h2c.NewHandler(engine, h2s)
+}
+
 func (engine *Engine) allocateContext() *Context {
 	v := make(Params, 0, engine.maxParams)
 	skippedNodes := make([]skippedNode, 0, engine.maxSections)
@@ -361,7 +376,7 @@ func (engine *Engine) Run(addr ...string) (err error) {
 
 	address := resolveAddress(addr)
 	debugPrint("Listening and serving HTTP on %s\n", address)
-	err = http.ListenAndServe(address, engine)
+	err = http.ListenAndServe(address, engine.Handler())
 	return
 }
 
@@ -480,7 +495,7 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
 			"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
 	}
 
-	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
+	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler())
 	return
 }
 
@@ -503,7 +518,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
 	defer listener.Close()
 	defer os.Remove(file)
 
-	err = http.Serve(listener, engine)
+	err = http.Serve(listener, engine.Handler())
 	return
 }
 
@@ -540,7 +555,7 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
 			"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
 	}
 
-	err = http.Serve(listener, engine)
+	err = http.Serve(listener, engine.Handler())
 	return
 }
 
diff --git a/go.mod b/go.mod
index 3548835..2da1fe3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,17 +1,30 @@
 module github.com/gin-gonic/gin
 
-go 1.13
+go 1.18
 
 require (
 	github.com/gin-contrib/sse v0.1.0
 	github.com/go-playground/validator/v10 v10.10.0
-	github.com/goccy/go-json v0.9.0
+	github.com/goccy/go-json v0.9.7
 	github.com/json-iterator/go v1.1.12
 	github.com/mattn/go-isatty v0.0.14
-	github.com/stretchr/testify v1.7.0
-	github.com/ugorji/go/codec v1.2.6
-	google.golang.org/protobuf v1.27.1
+	github.com/stretchr/testify v1.7.1
+	github.com/ugorji/go/codec v1.2.7
+	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+	google.golang.org/protobuf v1.28.0
 	gopkg.in/yaml.v2 v2.4.0
 )
 
-retract v1.7.5
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/go-playground/locales v0.14.0 // indirect
+	github.com/go-playground/universal-translator v0.18.0 // indirect
+	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
+	golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
+	golang.org/x/text v0.3.6 // indirect
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
+)
diff --git a/go.sum b/go.sum
index 59efbf7..d7a221f 100644
--- a/go.sum
+++ b/go.sum
@@ -12,8 +12,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
 github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
 github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
 github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
-github.com/goccy/go-json v0.9.0 h1:2flW7bkbrRgU8VuDi0WXDqTmPimjv1thfxkPe8sug+8=
-github.com/goccy/go-json v0.9.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
+github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -45,14 +45,15 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
-github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
-github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
-github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -67,8 +68,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/logger.go b/logger.go
index 61b8454..1f9d63a 100644
--- a/logger.go
+++ b/logger.go
@@ -44,7 +44,7 @@ type LoggerConfig struct {
 	// Optional. Default value is gin.DefaultWriter.
 	Output io.Writer
 
-	// SkipPaths is a url path array which logs are not written.
+	// SkipPaths is an url path array which logs are not written.
 	// Optional.
 	SkipPaths []string
 }
@@ -75,7 +75,7 @@ type LogFormatterParams struct {
 	// BodySize is the size of the Response Body
 	BodySize int
 	// Keys are the keys set on the request's context.
-	Keys map[string]interface{}
+	Keys map[string]any
 }
 
 // StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
@@ -161,12 +161,12 @@ func ForceConsoleColor() {
 	consoleColorMode = forceColor
 }
 
-// ErrorLogger returns a handlerfunc for any error type.
+// ErrorLogger returns a HandlerFunc for any error type.
 func ErrorLogger() HandlerFunc {
 	return ErrorLoggerT(ErrorTypeAny)
 }
 
-// ErrorLoggerT returns a handlerfunc for a given error type.
+// ErrorLoggerT returns a HandlerFunc for a given error type.
 func ErrorLoggerT(typ ErrorType) HandlerFunc {
 	return func(c *Context) {
 		c.Next()
diff --git a/mode.go b/mode.go
index 1fb994b..4e7d734 100644
--- a/mode.go
+++ b/mode.go
@@ -5,6 +5,7 @@
 package gin
 
 import (
+	"flag"
 	"io"
 	"os"
 
@@ -54,7 +55,11 @@ func init() {
 // SetMode sets gin mode according to input string.
 func SetMode(value string) {
 	if value == "" {
-		value = DebugMode
+		if flag.Lookup("test.v") != nil {
+			value = TestMode
+		} else {
+			value = DebugMode
+		}
 	}
 
 	switch value {
diff --git a/recovery.go b/recovery.go
index 40eba3b..3efe146 100644
--- a/recovery.go
+++ b/recovery.go
@@ -28,7 +28,7 @@ var (
 )
 
 // RecoveryFunc defines the function passable to CustomRecovery.
-type RecoveryFunc func(c *Context, err interface{})
+type RecoveryFunc func(c *Context, err any)
 
 // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
 func Recovery() HandlerFunc {
@@ -102,7 +102,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
 	}
 }
 
-func defaultHandleRecovery(c *Context, err interface{}) {
+func defaultHandleRecovery(c *Context, err any) {
 	c.AbortWithStatus(http.StatusInternalServerError)
 }
 
diff --git a/render/any.go b/render/any.go
new file mode 100644
index 0000000..57349be
--- /dev/null
+++ b/render/any.go
@@ -0,0 +1,10 @@
+// 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.18
+// +build !go1.18
+
+package render
+
+type any = interface{}
diff --git a/render/html.go b/render/html.go
index 6696ece..bdfaf11 100644
--- a/render/html.go
+++ b/render/html.go
@@ -20,7 +20,7 @@ type Delims struct {
 // HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
 type HTMLRender interface {
 	// Instance returns an HTML instance.
-	Instance(string, interface{}) Render
+	Instance(string, any) Render
 }
 
 // HTMLProduction contains template reference and its delims.
@@ -41,13 +41,13 @@ type HTMLDebug struct {
 type HTML struct {
 	Template *template.Template
 	Name     string
-	Data     interface{}
+	Data     any
 }
 
 var htmlContentType = []string{"text/html; charset=utf-8"}
 
 // Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
-func (r HTMLProduction) Instance(name string, data interface{}) Render {
+func (r HTMLProduction) Instance(name string, data any) Render {
 	return HTML{
 		Template: r.Template,
 		Name:     name,
@@ -56,7 +56,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
 }
 
 // Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
-func (r HTMLDebug) Instance(name string, data interface{}) Render {
+func (r HTMLDebug) Instance(name string, data any) Render {
 	return HTML{
 		Template: r.loadTemplate(),
 		Name:     name,
diff --git a/render/json.go b/render/json.go
index 3ebcee9..0a7dcef 100644
--- a/render/json.go
+++ b/render/json.go
@@ -16,34 +16,34 @@ import (
 
 // JSON contains the given interface object.
 type JSON struct {
-	Data interface{}
+	Data any
 }
 
 // IndentedJSON contains the given interface object.
 type IndentedJSON struct {
-	Data interface{}
+	Data any
 }
 
 // SecureJSON contains the given interface object and its prefix.
 type SecureJSON struct {
 	Prefix string
-	Data   interface{}
+	Data   any
 }
 
 // JsonpJSON contains the given interface object its callback.
 type JsonpJSON struct {
 	Callback string
-	Data     interface{}
+	Data     any
 }
 
 // AsciiJSON contains the given interface object.
 type AsciiJSON struct {
-	Data interface{}
+	Data any
 }
 
 // PureJSON contains the given interface object.
 type PureJSON struct {
-	Data interface{}
+	Data any
 }
 
 var (
@@ -66,7 +66,7 @@ func (r JSON) WriteContentType(w http.ResponseWriter) {
 }
 
 // WriteJSON marshals the given interface object and writes it with custom ContentType.
-func WriteJSON(w http.ResponseWriter, obj interface{}) error {
+func WriteJSON(w http.ResponseWriter, obj any) error {
 	writeContentType(w, jsonContentType)
 	jsonBytes, err := json.Marshal(obj)
 	if err != nil {
diff --git a/render/msgpack.go b/render/msgpack.go
index 7f17ca4..2c0e9aa 100644
--- a/render/msgpack.go
+++ b/render/msgpack.go
@@ -21,7 +21,7 @@ var (
 
 // MsgPack contains the given interface object.
 type MsgPack struct {
-	Data interface{}
+	Data any
 }
 
 var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
@@ -37,7 +37,7 @@ func (r MsgPack) Render(w http.ResponseWriter) error {
 }
 
 // WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
-func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
+func WriteMsgPack(w http.ResponseWriter, obj any) error {
 	writeContentType(w, msgpackContentType)
 	var mh codec.MsgpackHandle
 	return codec.NewEncoder(w, &mh).Encode(obj)
diff --git a/render/protobuf.go b/render/protobuf.go
index 1d2aa87..2e57f5c 100644
--- a/render/protobuf.go
+++ b/render/protobuf.go
@@ -12,7 +12,7 @@ import (
 
 // ProtoBuf contains the given interface object.
 type ProtoBuf struct {
-	Data interface{}
+	Data any
 }
 
 var protobufContentType = []string{"application/x-protobuf"}
diff --git a/render/text.go b/render/text.go
index 461b720..b77a776 100644
--- a/render/text.go
+++ b/render/text.go
@@ -14,7 +14,7 @@ import (
 // String contains the given interface object slice and its format.
 type String struct {
 	Format string
-	Data   []interface{}
+	Data   []any
 }
 
 var plainContentType = []string{"text/plain; charset=utf-8"}
@@ -30,7 +30,7 @@ func (r String) WriteContentType(w http.ResponseWriter) {
 }
 
 // WriteString writes data according to its format and write custom ContentType.
-func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) {
+func WriteString(w http.ResponseWriter, format string, data []any) (err error) {
 	writeContentType(w, plainContentType)
 	if len(data) > 0 {
 		_, err = fmt.Fprintf(w, format, data...)
diff --git a/render/xml.go b/render/xml.go
index cc5390a..c396a5a 100644
--- a/render/xml.go
+++ b/render/xml.go
@@ -11,7 +11,7 @@ import (
 
 // XML contains the given interface object.
 type XML struct {
-	Data interface{}
+	Data any
 }
 
 var xmlContentType = []string{"application/xml; charset=utf-8"}
diff --git a/render/yaml.go b/render/yaml.go
index 0df7836..0fc7a66 100644
--- a/render/yaml.go
+++ b/render/yaml.go
@@ -12,7 +12,7 @@ import (
 
 // YAML contains the given interface object.
 type YAML struct {
-	Data interface{}
+	Data any
 }
 
 var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
diff --git a/response_writer.go b/response_writer.go
index 2682668..7f9095f 100644
--- a/response_writer.go
+++ b/response_writer.go
@@ -23,23 +23,23 @@ type ResponseWriter interface {
 	http.Flusher
 	http.CloseNotifier
 
-	// Returns the HTTP response status code of the current request.
+	// Status returns the HTTP response status code of the current request.
 	Status() int
 
-	// Returns the number of bytes already written into the response http body.
+	// Size returns the number of bytes already written into the response http body.
 	// See Written()
 	Size() int
 
-	// Writes the string into the response body.
+	// WriteString writes the string into the response body.
 	WriteString(string) (int, error)
 
-	// Returns true if the response body was already written.
+	// Written returns true if the response body was already written.
 	Written() bool
 
-	// Forces to write the http header (status code + headers).
+	// WriteHeaderNow forces to write the http header (status code + headers).
 	WriteHeaderNow()
 
-	// get the http.Pusher for server push
+	// Pusher get the http.Pusher for server push
 	Pusher() http.Pusher
 }
 
@@ -107,12 +107,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
 	return w.ResponseWriter.(http.Hijacker).Hijack()
 }
 
-// CloseNotify implements the http.CloseNotify interface.
+// CloseNotify implements the http.CloseNotifier interface.
 func (w *responseWriter) CloseNotify() <-chan bool {
 	return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
 }
 
-// Flush implements the http.Flush interface.
+// Flush implements the http.Flusher interface.
 func (w *responseWriter) Flush() {
 	w.WriteHeaderNow()
 	w.ResponseWriter.(http.Flusher).Flush()
diff --git a/routergroup.go b/routergroup.go
index 27d7aad..3fba3a9 100644
--- a/routergroup.go
+++ b/routergroup.go
@@ -12,7 +12,7 @@ import (
 )
 
 var (
-	// reg match english letters for http method name
+	// regEnLetter matches english letters for http method name
 	regEnLetter = regexp.MustCompile("^[A-Z]+$")
 
 	// anyMethods for RouterGroup Any method
@@ -44,6 +44,7 @@ type IRoutes interface {
 	HEAD(string, ...HandlerFunc) IRoutes
 
 	StaticFile(string, string) IRoutes
+	StaticFileFS(string, string, http.FileSystem) IRoutes
 	Static(string, string) IRoutes
 	StaticFS(string, http.FileSystem) IRoutes
 }
@@ -153,12 +154,24 @@ func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRou
 // StaticFile registers a single route in order to serve a single file of the local filesystem.
 // router.StaticFile("favicon.ico", "./resources/favicon.ico")
 func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
+	return group.staticFileHandler(relativePath, func(c *Context) {
+		c.File(filepath)
+	})
+}
+
+// StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead..
+// router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false})
+// Gin by default user: gin.Dir()
+func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes {
+	return group.staticFileHandler(relativePath, func(c *Context) {
+		c.FileFromFS(filepath, fs)
+	})
+}
+
+func (group *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes {
 	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
 		panic("URL parameters can not be used when serving a static file")
 	}
-	handler := func(c *Context) {
-		c.File(filepath)
-	}
 	group.GET(relativePath, handler)
 	group.HEAD(relativePath, handler)
 	return group.returnObj()
diff --git a/tree.go b/tree.go
index a30d349..88100ee 100644
--- a/tree.go
+++ b/tree.go
@@ -81,7 +81,7 @@ func longestCommonPrefix(a, b string) int {
 	return i
 }
 
-// addChild will add a child node, keeping wildcards at the end
+// addChild will add a child node, keeping wildcardChild at the end
 func (n *node) addChild(child *node) {
 	if n.wildChild && len(n.children) > 0 {
 		wildcardChild := n.children[len(n.children)-1]
@@ -296,7 +296,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
 			break
 		}
 
-		// The wildcard name must not contain ':' and '*'
+		// The wildcard name must only contain one ':' or '*' character
 		if !valid {
 			panic("only one wildcard per path segment is allowed, has: '" +
 				wildcard + "' in path '" + fullPath + "'")
@@ -325,7 +325,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
 			n.priority++
 
 			// if the path doesn't end with the wildcard, then there
-			// will be another non-wildcard subpath starting with '/'
+			// will be another subpath starting with '/'
 			if len(wildcard) < len(path) {
 				path = path[len(wildcard):]
 
diff --git a/utils.go b/utils.go
index c32f0ee..b41a3b0 100644
--- a/utils.go
+++ b/utils.go
@@ -12,13 +12,14 @@ import (
 	"reflect"
 	"runtime"
 	"strings"
+	"unicode"
 )
 
 // BindKey indicates a default bind key.
 const BindKey = "_gin-gonic/gin/bindkey"
 
 // Bind is a helper function for given interface object and returns a Gin middleware.
-func Bind(val interface{}) HandlerFunc {
+func Bind(val any) HandlerFunc {
 	value := reflect.ValueOf(val)
 	if value.Kind() == reflect.Ptr {
 		panic(`Bind struct can not be a pointer. Example:
@@ -50,7 +51,7 @@ func WrapH(h http.Handler) HandlerFunc {
 }
 
 // H is a shortcut for map[string]interface{}
-type H map[string]interface{}
+type H map[string]any
 
 // MarshalXML allows type H to be used with xml.Marshal.
 func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
@@ -89,7 +90,7 @@ func filterFlags(content string) string {
 	return content
 }
 
-func chooseData(custom, wildcard interface{}) interface{} {
+func chooseData(custom, wildcard any) any {
 	if custom != nil {
 		return custom
 	}
@@ -120,7 +121,7 @@ func lastChar(str string) uint8 {
 	return str[len(str)-1]
 }
 
-func nameOfFunction(f interface{}) string {
+func nameOfFunction(f any) string {
 	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
 }
 
@@ -151,3 +152,13 @@ func resolveAddress(addr []string) string {
 		panic("too many parameters")
 	}
 }
+
+// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
+func isASCII(s string) bool {
+	for i := 0; i < len(s); i++ {
+		if s[i] > unicode.MaxASCII {
+			return false
+		}
+	}
+	return true
+}


Test patch:
diff --git a/binding/binding_test.go b/binding/binding_test.go
index 5b0ce39..d9746e2 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -61,11 +61,11 @@ type FooDefaultBarStruct struct {
 }
 
 type FooStructUseNumber struct {
-	Foo interface{} `json:"foo" binding:"required"`
+	Foo any `json:"foo" binding:"required"`
 }
 
 type FooStructDisallowUnknownFields struct {
-	Foo interface{} `json:"foo" binding:"required"`
+	Foo any `json:"foo" binding:"required"`
 }
 
 type FooBarStructForTimeType struct {
@@ -93,7 +93,7 @@ type FooStructForTimeTypeFailLocation struct {
 }
 
 type FooStructForMapType struct {
-	MapFoo map[string]interface{} `form:"map_foo"`
+	MapFoo map[string]any `form:"map_foo"`
 }
 
 type FooStructForIgnoreFormTag struct {
@@ -106,7 +106,7 @@ type InvalidNameType struct {
 
 type InvalidNameMapType struct {
 	TestName struct {
-		MapFoo map[string]interface{} `form:"map_foo"`
+		MapFoo map[string]any `form:"map_foo"`
 	}
 }
 
@@ -128,7 +128,7 @@ type FooStructForStructPointerType struct {
 
 type FooStructForSliceMapType struct {
 	// Unknown type: not support map
-	SliceMapFoo []map[string]interface{} `form:"slice_map_foo"`
+	SliceMapFoo []map[string]any `form:"slice_map_foo"`
 }
 
 type FooStructForBoolType struct {
@@ -141,7 +141,7 @@ type FooStructForStringPtrType struct {
 }
 
 type FooStructForMapPtrType struct {
-	PtrBar *map[string]interface{} `form:"ptr_bar"`
+	PtrBar *map[string]any `form:"ptr_bar"`
 }
 
 func TestBindingDefault(t *testing.T) {
@@ -768,7 +768,7 @@ func TestHeaderBinding(t *testing.T) {
 	req.Header.Add("fail", `{fail:fail}`)
 
 	type failStruct struct {
-		Fail map[string]interface{} `header:"fail"`
+		Fail map[string]any `header:"fail"`
 	}
 
 	err := h.Bind(req, &failStruct{})
@@ -789,11 +789,11 @@ func TestUriBinding(t *testing.T) {
 	assert.Equal(t, "thinkerou", tag.Name)
 
 	type NotSupportStruct struct {
-		Name map[string]interface{} `uri:"name"`
+		Name map[string]any `uri:"name"`
 	}
 	var not NotSupportStruct
 	assert.Error(t, b.BindUri(m, &not))
-	assert.Equal(t, map[string]interface{}(nil), not.Name)
+	assert.Equal(t, map[string]any(nil), not.Name)
 }
 
 func TestUriInnerBinding(t *testing.T) {
@@ -1339,10 +1339,10 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
 	err := b.Bind(req, &obj)
 	assert.Error(t, err)
 
-	invalid_obj := FooStruct{}
+	invalidobj := FooStruct{}
 	req.Body = ioutil.NopCloser(strings.NewReader(`{"msg":"hello"}`))
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
-	err = b.Bind(req, &invalid_obj)
+	err = b.Bind(req, &invalidobj)
 	assert.Error(t, err)
 	assert.Equal(t, err.Error(), "obj is not ProtoMessage")
 
diff --git a/binding/default_validator_test.go b/binding/default_validator_test.go
index ff13010..df7742b 100644
--- a/binding/default_validator_test.go
+++ b/binding/default_validator_test.go
@@ -54,7 +54,7 @@ func TestDefaultValidator(t *testing.T) {
 	tests := []struct {
 		name    string
 		v       *defaultValidator
-		obj     interface{}
+		obj     any
 		wantErr bool
 	}{
 		{"validate nil obj", &defaultValidator{}, nil, false},
diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go
index 516554e..78f4df0 100644
--- a/binding/form_mapping_test.go
+++ b/binding/form_mapping_test.go
@@ -18,9 +18,9 @@ func TestMappingBaseTypes(t *testing.T) {
 	}
 	for _, tt := range []struct {
 		name   string
-		value  interface{}
+		value  any
 		form   string
-		expect interface{}
+		expect any
 	}{
 		{"base type", struct{ F int }{}, "9", int(9)},
 		{"base type", struct{ F int8 }{}, "9", int8(9)},
diff --git a/binding/msgpack_test.go b/binding/msgpack_test.go
index 75600ba..11561c8 100644
--- a/binding/msgpack_test.go
+++ b/binding/msgpack_test.go
@@ -26,7 +26,7 @@ func TestMsgpackBindingBindBody(t *testing.T) {
 	assert.Equal(t, "FOO", s.Foo)
 }
 
-func msgpackBody(t *testing.T, obj interface{}) []byte {
+func msgpackBody(t *testing.T, obj any) []byte {
 	var bs bytes.Buffer
 	h := &codec.MsgpackHandle{}
 	err := codec.NewEncoder(&bs, h).Encode(obj)
diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go
index 4aaa60b..545914d 100644
--- a/binding/multipart_form_mapping_test.go
+++ b/binding/multipart_form_mapping_test.go
@@ -76,7 +76,7 @@ func TestFormMultipartBindingBindError(t *testing.T) {
 
 	for _, tt := range []struct {
 		name string
-		s    interface{}
+		s    any
 	}{
 		{"wrong type", &struct {
 			Files int `form:"file"`
diff --git a/binding/validate_test.go b/binding/validate_test.go
index 5299fbf..d05c891 100644
--- a/binding/validate_test.go
+++ b/binding/validate_test.go
@@ -59,7 +59,7 @@ type structNoValidationValues struct {
 	StructSlice        []substructNoValidation
 	InterfaceSlice     []testInterface
 
-	UniversalInterface interface{}
+	UniversalInterface any
 	CustomInterface    testInterface
 
 	FloatMap  map[string]float32
@@ -169,7 +169,7 @@ func TestValidateNoValidationPointers(t *testing.T) {
 	//assert.Equal(t, origin, test)
 }
 
-type Object map[string]interface{}
+type Object map[string]any
 
 func TestValidatePrimitives(t *testing.T) {
 	obj := Object{"foo": "bar", "bar": 1}
diff --git a/context_test.go b/context_test.go
index 9e02aed..20038e9 100644
--- a/context_test.go
+++ b/context_test.go
@@ -15,6 +15,7 @@ import (
 	"net"
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"os"
 	"reflect"
 	"strings"
@@ -211,7 +212,7 @@ func TestContextSetGetValues(t *testing.T) {
 	c.Set("uint64", uint64(42))
 	c.Set("float32", float32(4.2))
 	c.Set("float64", 4.2)
-	var a interface{} = 1
+	var a any = 1
 	c.Set("intInterface", a)
 
 	assert.Exactly(t, c.MustGet("string").(string), "this is a string")
@@ -286,7 +287,7 @@ func TestContextGetStringSlice(t *testing.T) {
 
 func TestContextGetStringMap(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	m := make(map[string]interface{})
+	m := make(map[string]any)
 	m["foo"] = 1
 	c.Set("map", m)
 
@@ -1033,6 +1034,19 @@ func TestContextRenderAttachment(t *testing.T) {
 	assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
 }
 
+func TestContextRenderUTF8Attachment(t *testing.T) {
+	w := httptest.NewRecorder()
+	c, _ := CreateTestContext(w)
+	newFilename := "new🧡_filename.go"
+
+	c.Request, _ = http.NewRequest("GET", "/", nil)
+	c.FileAttachment("./gin.go", newFilename)
+
+	assert.Equal(t, 200, w.Code)
+	assert.Contains(t, w.Body.String(), "func New() *Engine {")
+	assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
+}
+
 // TestContextRenderYAML tests that the response is serialized as YAML
 // and Content-Type is set to application/x-yaml
 func TestContextRenderYAML(t *testing.T) {
@@ -1866,6 +1880,7 @@ func TestContextGolangContext(t *testing.T) {
 	assert.Equal(t, ti, time.Time{})
 	assert.False(t, ok)
 	assert.Equal(t, c.Value(0), c.Request)
+	assert.Equal(t, c.Value(ContextKey), c)
 	assert.Nil(t, c.Value("foo"))
 
 	c.Set("foo", "bar")
@@ -2111,12 +2126,12 @@ type contextKey string
 func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 	tests := []struct {
 		name             string
-		getContextAndKey func() (*Context, interface{})
-		value            interface{}
+		getContextAndKey func() (*Context, any)
+		value            any
 	}{
 		{
 			name: "c with struct context key",
-			getContextAndKey: func() (*Context, interface{}) {
+			getContextAndKey: func() (*Context, any) {
 				var key struct{}
 				c := &Context{}
 				c.Request, _ = http.NewRequest("POST", "/", nil)
@@ -2127,7 +2142,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 		},
 		{
 			name: "c with string context key",
-			getContextAndKey: func() (*Context, interface{}) {
+			getContextAndKey: func() (*Context, any) {
 				c := &Context{}
 				c.Request, _ = http.NewRequest("POST", "/", nil)
 				c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
@@ -2137,7 +2152,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 		},
 		{
 			name: "c with nil http.Request",
-			getContextAndKey: func() (*Context, interface{}) {
+			getContextAndKey: func() (*Context, any) {
 				c := &Context{}
 				return c, "key"
 			},
@@ -2145,7 +2160,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 		},
 		{
 			name: "c with nil http.Request.Context()",
-			getContextAndKey: func() (*Context, interface{}) {
+			getContextAndKey: func() (*Context, any) {
 				c := &Context{}
 				c.Request, _ = http.NewRequest("POST", "/", nil)
 				return c, "key"
diff --git a/debug_test.go b/debug_test.go
index 0550999..7c54444 100644
--- a/debug_test.go
+++ b/debug_test.go
@@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
 	})
 	m, e := getMinVer(runtime.Version())
 	if e == nil && m <= ginSupportMinGoVer {
-		assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.13+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
+		assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.14+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
 	} else {
 		assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
 	}
diff --git a/errors_test.go b/errors_test.go
index 9a17d85..ac72dc4 100644
--- a/errors_test.go
+++ b/errors_test.go
@@ -86,7 +86,7 @@ Error #02: second
 Error #03: third
      Meta: map[status:400]
 `, errs.String())
-	assert.Equal(t, []interface{}{
+	assert.Equal(t, []any{
 		H{"error": "first"},
 		H{"error": "second", "meta": "some data"},
 		H{"error": "third", "status": "400"},
diff --git a/gin_integration_test.go b/gin_integration_test.go
index 8c22e7b..0dfa903 100644
--- a/gin_integration_test.go
+++ b/gin_integration_test.go
@@ -15,6 +15,7 @@ import (
 	"net/http/httptest"
 	"os"
 	"path/filepath"
+	"runtime"
 	"sync"
 	"testing"
 	"time"
@@ -281,7 +282,16 @@ func TestFileDescriptor(t *testing.T) {
 	listener, err := net.ListenTCP("tcp", addr)
 	assert.NoError(t, err)
 	socketFile, err := listener.File()
-	assert.NoError(t, err)
+	if isWindows() {
+		// not supported by windows, it is unimplemented now
+		assert.Error(t, err)
+	} else {
+		assert.NoError(t, err)
+	}
+
+	if socketFile == nil {
+		return
+	}
 
 	go func() {
 		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
@@ -547,3 +557,7 @@ func TestTreeRunDynamicRouting(t *testing.T) {
 	testRequest(t, ts.URL+"/addr/dd/aa", "404 Not Found")
 	testRequest(t, ts.URL+"/something/secondthing/121", "404 Not Found")
 }
+
+func isWindows() bool {
+	return runtime.GOOS == "windows"
+}
diff --git a/gin_test.go b/gin_test.go
index 0aece61..a438062 100644
--- a/gin_test.go
+++ b/gin_test.go
@@ -19,6 +19,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"golang.org/x/net/http2"
 )
 
 func formatAsDate(t time.Time) string {
@@ -42,7 +43,7 @@ func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine
 			c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
 		})
 		router.GET("/raw", func(c *Context) {
-			c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
+			c.HTML(http.StatusOK, "raw.tmpl", map[string]any{
 				"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
 			})
 		})
@@ -79,6 +80,44 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) {
 	assert.Equal(t, "<h1>Hello world</h1>", string(resp))
 }
 
+func TestH2c(t *testing.T) {
+	ln, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		fmt.Println(err)
+	}
+	r := Default()
+	r.UseH2C = true
+	r.GET("/", func(c *Context) {
+		c.String(200, "<h1>Hello world</h1>")
+	})
+	go func() {
+		err := http.Serve(ln, r.Handler())
+		if err != nil {
+			fmt.Println(err)
+		}
+	}()
+	defer ln.Close()
+
+	url := "http://" + ln.Addr().String() + "/"
+
+	http := http.Client{
+		Transport: &http2.Transport{
+			AllowHTTP: true,
+			DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
+				return net.Dial(netw, addr)
+			},
+		},
+	}
+
+	res, err := http.Get(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	resp, _ := ioutil.ReadAll(res.Body)
+	assert.Equal(t, "<h1>Hello world</h1>", string(resp))
+}
+
 func TestLoadHTMLGlobTestMode(t *testing.T) {
 	ts := setupHTMLFiles(
 		t,
@@ -163,7 +202,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
 	}
 
 	resp, _ := ioutil.ReadAll(res.Body)
-	assert.Equal(t, "Date: 2017/07/01\n", string(resp))
+	assert.Equal(t, "Date: 2017/07/01", string(resp))
 }
 
 func init() {
@@ -281,7 +320,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
 	}
 
 	resp, _ := ioutil.ReadAll(res.Body)
-	assert.Equal(t, "Date: 2017/07/01\n", string(resp))
+	assert.Equal(t, "Date: 2017/07/01", string(resp))
 }
 
 func TestAddRoute(t *testing.T) {
@@ -428,7 +467,7 @@ func TestNoMethodWithGlobalHandlers(t *testing.T) {
 	compareFunc(t, router.allNoMethod[2], middleware0)
 }
 
-func compareFunc(t *testing.T, a, b interface{}) {
+func compareFunc(t *testing.T, a, b any) {
 	sf1 := reflect.ValueOf(a)
 	sf2 := reflect.ValueOf(b)
 	if sf1.Pointer() != sf2.Pointer() {
diff --git a/githubapi_test.go b/githubapi_test.go
index e74bddd..5fe65a4 100644
--- a/githubapi_test.go
+++ b/githubapi_test.go
@@ -296,8 +296,8 @@ func TestShouldBindUri(t *testing.T) {
 	router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
 		var person Person
 		assert.NoError(t, c.ShouldBindUri(&person))
-		assert.True(t, "" != person.Name)
-		assert.True(t, "" != person.ID)
+		assert.True(t, person.Name != "")
+		assert.True(t, person.ID != "")
 		c.String(http.StatusOK, "ShouldBindUri test OK")
 	})
 
@@ -318,8 +318,8 @@ func TestBindUri(t *testing.T) {
 	router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
 		var person Person
 		assert.NoError(t, c.BindUri(&person))
-		assert.True(t, "" != person.Name)
-		assert.True(t, "" != person.ID)
+		assert.True(t, person.Name != "")
+		assert.True(t, person.ID != "")
 		c.String(http.StatusOK, "BindUri test OK")
 	})
 
diff --git a/logger_test.go b/logger_test.go
index ec5e6cd..b704998 100644
--- a/logger_test.go
+++ b/logger_test.go
@@ -181,7 +181,7 @@ func TestLoggerWithFormatter(t *testing.T) {
 
 func TestLoggerWithConfigFormatting(t *testing.T) {
 	var gotParam LogFormatterParams
-	var gotKeys map[string]interface{}
+	var gotKeys map[string]any
 	buffer := new(bytes.Buffer)
 
 	router := New()
@@ -208,6 +208,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
 		// set dummy ClientIP
 		c.Request.Header.Set("X-Forwarded-For", "20.20.20.20")
 		gotKeys = c.Keys
+		time.Sleep(time.Millisecond)
 	})
 	PerformRequest(router, "GET", "/example?a=100")
 
diff --git a/mode_test.go b/mode_test.go
index 1b5fb2f..6fd9a13 100644
--- a/mode_test.go
+++ b/mode_test.go
@@ -5,6 +5,7 @@
 package gin
 
 import (
+	"flag"
 	"os"
 	"testing"
 
@@ -21,9 +22,16 @@ func TestSetMode(t *testing.T) {
 	assert.Equal(t, TestMode, Mode())
 	os.Unsetenv(EnvGinMode)
 
+	SetMode("")
+	assert.Equal(t, testCode, ginMode)
+	assert.Equal(t, TestMode, Mode())
+
+	tmp := flag.CommandLine
+	flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError)
 	SetMode("")
 	assert.Equal(t, debugCode, ginMode)
 	assert.Equal(t, DebugMode, Mode())
+	flag.CommandLine = tmp
 
 	SetMode(DebugMode)
 	assert.Equal(t, debugCode, ginMode)
diff --git a/recovery_test.go b/recovery_test.go
index ac3669a..2c327a6 100644
--- a/recovery_test.go
+++ b/recovery_test.go
@@ -148,7 +148,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
 	errBuffer := new(bytes.Buffer)
 	buffer := new(bytes.Buffer)
 	router := New()
-	handleRecovery := func(c *Context, err interface{}) {
+	handleRecovery := func(c *Context, err any) {
 		errBuffer.WriteString(err.(string))
 		c.AbortWithStatus(http.StatusBadRequest)
 	}
@@ -183,7 +183,7 @@ func TestCustomRecovery(t *testing.T) {
 	buffer := new(bytes.Buffer)
 	router := New()
 	DefaultErrorWriter = buffer
-	handleRecovery := func(c *Context, err interface{}) {
+	handleRecovery := func(c *Context, err any) {
 		errBuffer.WriteString(err.(string))
 		c.AbortWithStatus(http.StatusBadRequest)
 	}
@@ -218,7 +218,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
 	buffer := new(bytes.Buffer)
 	router := New()
 	DefaultErrorWriter = buffer
-	handleRecovery := func(c *Context, err interface{}) {
+	handleRecovery := func(c *Context, err any) {
 		errBuffer.WriteString(err.(string))
 		c.AbortWithStatus(http.StatusBadRequest)
 	}
diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go
index d16cf6e..7b3601a 100644
--- a/render/render_msgpack_test.go
+++ b/render/render_msgpack_test.go
@@ -21,7 +21,7 @@ import (
 
 func TestRenderMsgPack(t *testing.T) {
 	w := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo": "bar",
 	}
 
diff --git a/render/render_test.go b/render/render_test.go
index e417731..8b28dc3 100644
--- a/render/render_test.go
+++ b/render/render_test.go
@@ -24,7 +24,7 @@ import (
 
 func TestRenderJSON(t *testing.T) {
 	w := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo":  "bar",
 		"html": "<b>",
 	}
@@ -49,7 +49,7 @@ func TestRenderJSONPanics(t *testing.T) {
 
 func TestRenderIndentedJSON(t *testing.T) {
 	w := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo": "bar",
 		"bar": "foo",
 	}
@@ -72,7 +72,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
 
 func TestRenderSecureJSON(t *testing.T) {
 	w1 := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo": "bar",
 	}
 
@@ -86,7 +86,7 @@ func TestRenderSecureJSON(t *testing.T) {
 	assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
 
 	w2 := httptest.NewRecorder()
-	datas := []map[string]interface{}{{
+	datas := []map[string]any{{
 		"foo": "bar",
 	}, {
 		"bar": "foo",
@@ -109,7 +109,7 @@ func TestRenderSecureJSONFail(t *testing.T) {
 
 func TestRenderJsonpJSON(t *testing.T) {
 	w1 := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo": "bar",
 	}
 
@@ -123,7 +123,7 @@ func TestRenderJsonpJSON(t *testing.T) {
 	assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
 
 	w2 := httptest.NewRecorder()
-	datas := []map[string]interface{}{{
+	datas := []map[string]any{{
 		"foo": "bar",
 	}, {
 		"bar": "foo",
@@ -137,7 +137,7 @@ func TestRenderJsonpJSON(t *testing.T) {
 
 func TestRenderJsonpJSONError2(t *testing.T) {
 	w := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo": "bar",
 	}
 	(JsonpJSON{"", data}).WriteContentType(w)
@@ -161,7 +161,7 @@ func TestRenderJsonpJSONFail(t *testing.T) {
 
 func TestRenderAsciiJSON(t *testing.T) {
 	w1 := httptest.NewRecorder()
-	data1 := map[string]interface{}{
+	data1 := map[string]any{
 		"lang": "GO语言",
 		"tag":  "<br>",
 	}
@@ -190,7 +190,7 @@ func TestRenderAsciiJSONFail(t *testing.T) {
 
 func TestRenderPureJSON(t *testing.T) {
 	w := httptest.NewRecorder()
-	data := map[string]interface{}{
+	data := map[string]any{
 		"foo":  "bar",
 		"html": "<b>",
 	}
@@ -200,7 +200,7 @@ func TestRenderPureJSON(t *testing.T) {
 	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
 }
 
-type xmlmap map[string]interface{}
+type xmlmap map[string]any
 
 // Allows type H to be used with xml.Marshal
 func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
@@ -244,7 +244,7 @@ b:
 type fail struct{}
 
 // Hook MarshalYAML
-func (ft *fail) MarshalYAML() (interface{}, error) {
+func (ft *fail) MarshalYAML() (any, error) {
 	return nil, errors.New("fail")
 }
 
@@ -358,13 +358,13 @@ func TestRenderString(t *testing.T) {
 
 	(String{
 		Format: "hello %s %d",
-		Data:   []interface{}{},
+		Data:   []any{},
 	}).WriteContentType(w)
 	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
 
 	err := (String{
 		Format: "hola %s %d",
-		Data:   []interface{}{"manu", 2},
+		Data:   []any{"manu", 2},
 	}).Render(w)
 
 	assert.NoError(t, err)
@@ -377,7 +377,7 @@ func TestRenderStringLenZero(t *testing.T) {
 
 	err := (String{
 		Format: "hola %s %d",
-		Data:   []interface{}{},
+		Data:   []any{},
 	}).Render(w)
 
 	assert.NoError(t, err)
@@ -390,7 +390,7 @@ func TestRenderHTMLTemplate(t *testing.T) {
 	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
 
 	htmlRender := HTMLProduction{Template: templ}
-	instance := htmlRender.Instance("t", map[string]interface{}{
+	instance := htmlRender.Instance("t", map[string]any{
 		"name": "alexandernyquist",
 	})
 
@@ -406,7 +406,7 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) {
 	templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
 
 	htmlRender := HTMLProduction{Template: templ}
-	instance := htmlRender.Instance("", map[string]interface{}{
+	instance := htmlRender.Instance("", map[string]any{
 		"name": "alexandernyquist",
 	})
 
@@ -425,7 +425,7 @@ func TestRenderHTMLDebugFiles(t *testing.T) {
 		Delims:  Delims{Left: "{[{", Right: "}]}"},
 		FuncMap: nil,
 	}
-	instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
+	instance := htmlRender.Instance("hello.tmpl", map[string]any{
 		"name": "thinkerou",
 	})
 
@@ -444,7 +444,7 @@ func TestRenderHTMLDebugGlob(t *testing.T) {
 		Delims:  Delims{Left: "{[{", Right: "}]}"},
 		FuncMap: nil,
 	}
-	instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
+	instance := htmlRender.Instance("hello.tmpl", map[string]any{
 		"name": "thinkerou",
 	})
 
diff --git a/routergroup_test.go b/routergroup_test.go
index 232f595..c1fad3a 100644
--- a/routergroup_test.go
+++ b/routergroup_test.go
@@ -111,6 +111,17 @@ func TestRouterGroupInvalidStaticFile(t *testing.T) {
 	})
 }
 
+func TestRouterGroupInvalidStaticFileFS(t *testing.T) {
+	router := New()
+	assert.Panics(t, func() {
+		router.StaticFileFS("/path/:param", "favicon.ico", Dir(".", false))
+	})
+
+	assert.Panics(t, func() {
+		router.StaticFileFS("/path/*param", "favicon.ico", Dir(".", false))
+	})
+}
+
 func TestRouterGroupTooManyHandlers(t *testing.T) {
 	const (
 		panicValue = "too many handlers"
@@ -177,6 +188,7 @@ func testRoutesInterface(t *testing.T, r IRoutes) {
 	assert.Equal(t, r, r.HEAD("/", handler))
 
 	assert.Equal(t, r, r.StaticFile("/file", "."))
+	assert.Equal(t, r, r.StaticFileFS("/static2", ".", Dir(".", false)))
 	assert.Equal(t, r, r.Static("/static", "."))
 	assert.Equal(t, r, r.StaticFS("/static2", Dir(".", false)))
 }
diff --git a/routes_test.go b/routes_test.go
index b3f0c47..5643097 100644
--- a/routes_test.go
+++ b/routes_test.go
@@ -325,6 +325,40 @@ func TestRouteStaticFile(t *testing.T) {
 	assert.Equal(t, http.StatusOK, w3.Code)
 }
 
+// TestHandleStaticFile - ensure the static file handles properly
+func TestRouteStaticFileFS(t *testing.T) {
+	// SETUP file
+	testRoot, _ := os.Getwd()
+	f, err := ioutil.TempFile(testRoot, "")
+	if err != nil {
+		t.Error(err)
+	}
+	defer os.Remove(f.Name())
+	_, err = f.WriteString("Gin Web Framework")
+	assert.NoError(t, err)
+	f.Close()
+
+	dir, filename := filepath.Split(f.Name())
+	// SETUP gin
+	router := New()
+	router.Static("/using_static", dir)
+	router.StaticFileFS("/result_fs", filename, Dir(dir, false))
+
+	w := PerformRequest(router, http.MethodGet, "/using_static/"+filename)
+	w2 := PerformRequest(router, http.MethodGet, "/result_fs")
+
+	assert.Equal(t, w, w2)
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Equal(t, "Gin Web Framework", w.Body.String())
+	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
+
+	w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename)
+	w4 := PerformRequest(router, http.MethodHead, "/result_fs")
+
+	assert.Equal(t, w3, w4)
+	assert.Equal(t, http.StatusOK, w3.Code)
+}
+
 // TestHandleStaticDir - ensure the root/sub dir handles properly
 func TestRouteStaticListingDir(t *testing.T) {
 	router := New()
diff --git a/testdata/protoexample/any.go b/testdata/protoexample/any.go
new file mode 100644
index 0000000..f16864c
--- /dev/null
+++ b/testdata/protoexample/any.go
@@ -0,0 +1,10 @@
+// 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.18
+// +build !go1.18
+
+package protoexample
+
+type any = interface{}
diff --git a/testdata/protoexample/test.pb.go b/testdata/protoexample/test.pb.go
index bf45e02..6687aae 100644
--- a/testdata/protoexample/test.pb.go
+++ b/testdata/protoexample/test.pb.go
@@ -231,7 +231,7 @@ func file_test_proto_rawDescGZIP() []byte {
 
 var file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_test_proto_goTypes = []interface{}{
+var file_test_proto_goTypes = []any{
 	(FOO)(0),                   // 0: protoexample.FOO
 	(*Test)(nil),               // 1: protoexample.Test
 	(*Test_OptionalGroup)(nil), // 2: protoexample.Test.OptionalGroup
@@ -251,7 +251,7 @@ func file_test_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_test_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Test); i {
 			case 0:
 				return &v.state
@@ -263,7 +263,7 @@ func file_test_proto_init() {
 				return nil
 			}
 		}
-		file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_test_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Test_OptionalGroup); i {
 			case 0:
 				return &v.state
diff --git a/testdata/template/raw.tmpl b/testdata/template/raw.tmpl
index 8bc7570..f3f530a 100644
--- a/testdata/template/raw.tmpl
+++ b/testdata/template/raw.tmpl
@@ -1,1 +1,1 @@
-Date: {[{.now | formatAsDate}]}
+Date: {[{.now | formatAsDate}]}
\ No newline at end of file
diff --git a/tree_test.go b/tree_test.go
index 94c5338..085b580 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -357,7 +357,7 @@ func TestUnescapeParameters(t *testing.T) {
 	checkPriorities(t, tree)
 }
 
-func catchPanic(testFunc func()) (recv interface{}) {
+func catchPanic(testFunc func()) (recv any) {
 	defer func() {
 		recv = recover()
 	}()
diff --git a/utils_test.go b/utils_test.go
index b50914f..d2a740b 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -143,3 +143,8 @@ func TestMarshalXMLforH(t *testing.T) {
 	e := h.MarshalXML(enc, x)
 	assert.Error(t, e)
 }
+
+func TestIsASCII(t *testing.T) {
+	assert.Equal(t, isASCII("test"), true)
+	assert.Equal(t, isASCII("🧡💛💚💙💜"), false)
+}