From 094f9a9105f8e7b971a01a64677eef5a1f7bcd9b Mon Sep 17 00:00:00 2001 From: Dan Markham Date: Tue, 7 May 2019 03:32:32 -0700 Subject: [PATCH 1/8] v1.4.0 + #1631 (remove go1.6/go1,7 support) (#1851) * remove go1.6 support * remove build tag * remove todo * remove go1.6 support: https://github.com/gin-gonic/gin/pull/1383/commits * update readme * remove go1.7 support * fix embedmd error * test * revert it * revert it * remove context_17 * add pusher test * v1.4.0 rc1 --- .travis.yml | 2 - CHANGELOG.md | 58 ++++++++++++++++++++++++++- README.md | 2 +- context.go | 19 ++++----- context_17.go | 17 -------- context_17_test.go | 27 ------------- context_test.go | 19 ++++++--- debug.go | 4 +- debug_test.go | 2 +- gin_integration_test.go | 37 ++++++++++++++++++ recovery_test.go | 2 - render/json.go | 18 +++++++++ render/json_17.go | 31 --------------- render/redirect.go | 4 +- render/render_17_test.go | 26 ------------- render/render_test.go | 12 ++++++ response_writer.go | 13 ++++++- response_writer_1.7.go | 12 ------ response_writer_1.8.go | 25 ------------ vendor/vendor.json | 84 +++++++++++++++++++++++++++++----------- version.go | 2 +- 21 files changed, 225 insertions(+), 191 deletions(-) delete mode 100644 context_17.go delete mode 100644 context_17_test.go delete mode 100644 render/json_17.go delete mode 100644 render/render_17_test.go delete mode 100644 response_writer_1.7.go delete mode 100644 response_writer_1.8.go diff --git a/.travis.yml b/.travis.yml index 2fd9c8a2..f6ec8a82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ language: go matrix: fast_finish: true include: - - go: 1.6.x - - go: 1.7.x - go: 1.8.x - go: 1.9.x - go: 1.10.x diff --git a/CHANGELOG.md b/CHANGELOG.md index e6a108ca..8ea2495d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,60 @@ -# CHANGELOG + +### Gin 1.4.0 + +- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569) +- [NEW] Refactor of form mapping multipart requesta [#1829](https://github.com/gin-gonic/gin/pull/1829) +- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830) +- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802) +- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264) +- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797) +- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789) +- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804) +- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779) +- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791) +- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794) +- [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749) +- [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252) +- [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775) +- [NEW] Extend context.File to allow for the content-dispositon attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260) +- [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112) +- [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238) +- [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020) +- [NEW] Add context.HandlerNames() [#1729](https://github.com/gin-gonic/gin/pull/1729) +- [FIX] Change color methods to public in the defaultLogger. [#1771](https://github.com/gin-gonic/gin/pull/1771) +- [FIX] Update writeHeaders method to use http.Header.Set [#1722](https://github.com/gin-gonic/gin/pull/1722) +- [NEW] Add response size to LogFormatterParams [#1752](https://github.com/gin-gonic/gin/pull/1752) +- [NEW] Allow ignoring field on form mapping [#1733](https://github.com/gin-gonic/gin/pull/1733) +- [NEW] Add a function to force color in console output. [#1724](https://github.com/gin-gonic/gin/pull/1724) +- [FIX] Context.Next() - recheck len of handlers on every iteration. [#1745](https://github.com/gin-gonic/gin/pull/1745) +- [FIX] Fix all errcheck warnings [#1739](https://github.com/gin-gonic/gin/pull/1739) [#1653](https://github.com/gin-gonic/gin/pull/1653) +- [NEW] context: inherits context cancellation and deadline from http.Request context for Go>=1.7 [#1690](https://github.com/gin-gonic/gin/pull/1690) +- [NEW] Binding for URL Params [#1694](https://github.com/gin-gonic/gin/pull/1694) +- [NEW] Add LoggerWithFormatter method [#1677](https://github.com/gin-gonic/gin/pull/1677) +- [FIX] CI testing updates [#1671](https://github.com/gin-gonic/gin/pull/1671) [#1670](https://github.com/gin-gonic/gin/pull/1670) [#1682](https://github.com/gin-gonic/gin/pull/1682) [#1669](https://github.com/gin-gonic/gin/pull/1669) +- [FIX] StaticFS(): Send 404 when path does not exist [#1663](https://github.com/gin-gonic/gin/pull/1663) +- [FIX] Handle nil body for JSON binding [#1638](https://github.com/gin-gonic/gin/pull/1638) +- [FIX] Support bind uri param [#1612](https://github.com/gin-gonic/gin/pull/1612) +- [FIX] recovery: fix issue with syscall import on google app engine [#1640](https://github.com/gin-gonic/gin/pull/1640) +- [FIX] Make sure the debug log contains line breaks [#1650](https://github.com/gin-gonic/gin/pull/1650) +- [FIX] Panic stack trace being printed during recovery of broken pipe [#1089](https://github.com/gin-gonic/gin/pull/1089) [#1259](https://github.com/gin-gonic/gin/pull/1259) +- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609) +- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618) +- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600) +- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559) +- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565) +- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571) +- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570) +- [FIX] Remove sensitive request information from panic log. [#1370](https://github.com/gin-gonic/gin/pull/1370) +- [FIX] log.Println() does not print timestamp [#829](https://github.com/gin-gonic/gin/pull/829) [#1560](https://github.com/gin-gonic/gin/pull/1560) +- [NEW] Add PureJSON renderer [#694](https://github.com/gin-gonic/gin/pull/694) +- [FIX] Add missing copyright and update if/else [#1497](https://github.com/gin-gonic/gin/pull/1497) +- [FIX] Update msgpack usage [#1498](https://github.com/gin-gonic/gin/pull/1498) +- [FIX] Use protobuf on render [#1496](https://github.com/gin-gonic/gin/pull/1496) +- [FIX] Add support for Protobuf format response [#1479](https://github.com/gin-gonic/gin/pull/1479) +- [NEW] Set default time format in form binding [#1487](https://github.com/gin-gonic/gin/pull/1487) +- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485) +- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491) + ### Gin 1.3.0 diff --git a/README.md b/README.md index 594c8bfa..3e817a78 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,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. Download and install it: +1. The first need [Go](https://golang.org/) installed (**version 1.8+ is required**), then you can use the below Go command to install Gin. ```sh $ go get -u github.com/gin-gonic/gin diff --git a/context.go b/context.go index 5dc7f8a0..af747a1e 100644 --- a/context.go +++ b/context.go @@ -439,11 +439,6 @@ func (c *Context) GetPostFormArray(key string) ([]string, bool) { if values := req.PostForm[key]; len(values) > 0 { return values, true } - if req.MultipartForm != nil && req.MultipartForm.File != nil { - if values := req.MultipartForm.Value[key]; len(values) > 0 { - return values, true - } - } return []string{}, false } @@ -462,13 +457,7 @@ func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { debugPrint("error on parse multipart form map: %v", err) } } - dicts, exist := c.get(req.PostForm, key) - - if !exist && req.MultipartForm != nil && req.MultipartForm.File != nil { - dicts, exist = c.get(req.MultipartForm.Value, key) - } - - return dicts, exist + return c.get(req.PostForm, key) } // get is an internal method and returns a map which satisfy conditions. @@ -828,6 +817,12 @@ func (c *Context) AsciiJSON(code int, obj interface{}) { 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{}) { + 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{}) { diff --git a/context_17.go b/context_17.go deleted file mode 100644 index 8e9f75ad..00000000 --- a/context_17.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018 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. - -// +build go1.7 - -package gin - -import ( - "github.com/gin-gonic/gin/render" -) - -// 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{}) { - c.Render(code, render.PureJSON{Data: obj}) -} diff --git a/context_17_test.go b/context_17_test.go deleted file mode 100644 index 5b9ebcdc..00000000 --- a/context_17_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 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. - -// +build go1.7 - -package gin - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -// Tests that the response is serialized as JSON -// and Content-Type is set to application/json -// and special HTML characters are preserved -func TestContextRenderPureJSON(t *testing.T) { - w := httptest.NewRecorder() - c, _ := CreateTestContext(w) - c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": ""}) - assert.Equal(t, http.StatusCreated, w.Code) - assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) -} diff --git a/context_test.go b/context_test.go index 0da5fbe6..490e4490 100644 --- a/context_test.go +++ b/context_test.go @@ -622,8 +622,7 @@ func TestContextGetCookie(t *testing.T) { } func TestContextBodyAllowedForStatus(t *testing.T) { - // todo(thinkerou): go1.6 not support StatusProcessing - assert.False(t, false, bodyAllowedForStatus(102)) + assert.False(t, false, bodyAllowedForStatus(http.StatusProcessing)) assert.False(t, false, bodyAllowedForStatus(http.StatusNoContent)) assert.False(t, false, bodyAllowedForStatus(http.StatusNotModified)) assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError)) @@ -794,6 +793,18 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) { assert.Equal(t, "application/json", w.Header().Get("Content-Type")) } +// Tests that the response is serialized as JSON +// and Content-Type is set to application/json +// and special HTML characters are preserved +func TestContextRenderPureJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": ""}) + assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) +} + // Tests that the response executes the templates // and responds with Content-Type set to text/html func TestContextRenderHTML(t *testing.T) { @@ -1092,9 +1103,7 @@ func TestContextRenderRedirectAll(t *testing.T) { assert.Panics(t, func() { c.Redirect(299, "/resource") }) assert.Panics(t, func() { c.Redirect(309, "/resource") }) assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") }) - // todo(thinkerou): go1.6 not support StatusPermanentRedirect(308) - // when we upgrade go version we can use http.StatusPermanentRedirect - assert.NotPanics(t, func() { c.Redirect(308, "/resource") }) + assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") }) } func TestContextNegotiationWithJSON(t *testing.T) { diff --git a/debug.go b/debug.go index 98c67cf7..6d40a5da 100644 --- a/debug.go +++ b/debug.go @@ -14,7 +14,7 @@ import ( "strings" ) -const ginSupportMinGoVer = 6 +const ginSupportMinGoVer = 8 // IsDebugging returns true if the framework is running in debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode. @@ -69,7 +69,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.6 or later and Go 1.7 will be required soon. + debugPrint(`[WARNING] Now Gin requires Go 1.8 or later and Go 1.9 will be required soon. `) } diff --git a/debug_test.go b/debug_test.go index d338f0a0..86a67773 100644 --- a/debug_test.go +++ b/debug_test.go @@ -91,7 +91,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.6 or later and Go 1.7 will be required soon.\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.8 or later and Go 1.9 will be required soon.\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/gin_integration_test.go b/gin_integration_test.go index b80cbb24..9beec14d 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -8,6 +8,7 @@ import ( "bufio" "crypto/tls" "fmt" + "html/template" "io/ioutil" "net" "net/http" @@ -69,6 +70,42 @@ func TestRunTLS(t *testing.T) { testRequest(t, "https://localhost:8443/example") } +func TestPusher(t *testing.T) { + var html = template.Must(template.New("https").Parse(` + + + Https Test + + + +

Welcome, Ginner!

+ + +`)) + + router := New() + router.Static("./assets", "./assets") + router.SetHTMLTemplate(html) + + go func() { + router.GET("/pusher", func(c *Context) { + if pusher := c.Writer.Pusher(); pusher != nil { + pusher.Push("/assets/app.js", nil) + } + c.String(http.StatusOK, "it worked") + }) + + assert.NoError(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + }() + + // have to wait for the goroutine to start and run the server + // otherwise the main thread will complete + time.Sleep(5 * time.Millisecond) + + assert.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")) + testRequest(t, "https://localhost:8449/pusher") +} + func TestRunEmptyWithEnv(t *testing.T) { os.Setenv("PORT", "3123") router := New() diff --git a/recovery_test.go b/recovery_test.go index e1a0713f..21a0a480 100644 --- a/recovery_test.go +++ b/recovery_test.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. -// +build go1.7 - package gin import ( diff --git a/render/json.go b/render/json.go index c7cf330e..18f27fa9 100644 --- a/render/json.go +++ b/render/json.go @@ -43,6 +43,11 @@ type AsciiJSON struct { // SecureJSONPrefix is a string which represents SecureJSON prefix. type SecureJSONPrefix string +// PureJSON contains the given interface object. +type PureJSON struct { + Data interface{} +} + var jsonContentType = []string{"application/json; charset=utf-8"} var jsonpContentType = []string{"application/javascript; charset=utf-8"} var jsonAsciiContentType = []string{"application/json"} @@ -174,3 +179,16 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { func (r AsciiJSON) WriteContentType(w http.ResponseWriter) { writeContentType(w, jsonAsciiContentType) } + +// Render (PureJSON) writes custom ContentType and encodes the given interface object. +func (r PureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + encoder := json.NewEncoder(w) + encoder.SetEscapeHTML(false) + return encoder.Encode(r.Data) +} + +// WriteContentType (PureJSON) writes custom ContentType. +func (r PureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} diff --git a/render/json_17.go b/render/json_17.go deleted file mode 100644 index 208193c7..00000000 --- a/render/json_17.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 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. - -// +build go1.7 - -package render - -import ( - "net/http" - - "github.com/gin-gonic/gin/internal/json" -) - -// PureJSON contains the given interface object. -type PureJSON struct { - Data interface{} -} - -// Render (PureJSON) writes custom ContentType and encodes the given interface object. -func (r PureJSON) Render(w http.ResponseWriter) error { - r.WriteContentType(w) - encoder := json.NewEncoder(w) - encoder.SetEscapeHTML(false) - return encoder.Encode(r.Data) -} - -// WriteContentType (PureJSON) writes custom ContentType. -func (r PureJSON) WriteContentType(w http.ResponseWriter) { - writeContentType(w, jsonContentType) -} diff --git a/render/redirect.go b/render/redirect.go index 9c145fe2..c006691c 100644 --- a/render/redirect.go +++ b/render/redirect.go @@ -18,9 +18,7 @@ type Redirect struct { // Render (Redirect) redirects the http request to new location and writes redirect response. func (r Redirect) Render(w http.ResponseWriter) error { - // todo(thinkerou): go1.6 not support StatusPermanentRedirect(308) - // when we upgrade go version we can use http.StatusPermanentRedirect - if (r.Code < 300 || r.Code > 308) && r.Code != 201 { + if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated { panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) } http.Redirect(w, r.Request, r.Location, r.Code) diff --git a/render/render_17_test.go b/render/render_17_test.go deleted file mode 100644 index 68330090..00000000 --- a/render/render_17_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 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. - -// +build go1.7 - -package render - -import ( - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestRenderPureJSON(t *testing.T) { - w := httptest.NewRecorder() - data := map[string]interface{}{ - "foo": "bar", - "html": "", - } - err := (PureJSON{data}).Render(w) - assert.NoError(t, err) - assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) - assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) -} diff --git a/render/render_test.go b/render/render_test.go index 76e29eeb..3aa5dbcc 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -215,6 +215,18 @@ func TestRenderAsciiJSONFail(t *testing.T) { assert.Error(t, (AsciiJSON{data}).Render(w)) } +func TestRenderPureJSON(t *testing.T) { + w := httptest.NewRecorder() + data := map[string]interface{}{ + "foo": "bar", + "html": "", + } + err := (PureJSON{data}).Render(w) + assert.NoError(t, err) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) +} + type xmlmap map[string]interface{} // Allows type H to be used with xml.Marshal diff --git a/response_writer.go b/response_writer.go index 923b53f8..26826689 100644 --- a/response_writer.go +++ b/response_writer.go @@ -16,7 +16,8 @@ const ( defaultStatus = http.StatusOK ) -type responseWriterBase interface { +// ResponseWriter ... +type ResponseWriter interface { http.ResponseWriter http.Hijacker http.Flusher @@ -37,6 +38,9 @@ type responseWriterBase interface { // Forces to write the http header (status code + headers). WriteHeaderNow() + + // get the http.Pusher for server push + Pusher() http.Pusher } type responseWriter struct { @@ -113,3 +117,10 @@ func (w *responseWriter) Flush() { w.WriteHeaderNow() w.ResponseWriter.(http.Flusher).Flush() } + +func (w *responseWriter) Pusher() (pusher http.Pusher) { + if pusher, ok := w.ResponseWriter.(http.Pusher); ok { + return pusher + } + return nil +} diff --git a/response_writer_1.7.go b/response_writer_1.7.go deleted file mode 100644 index 801d196b..00000000 --- a/response_writer_1.7.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !go1.8 - -// Copyright 2018 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. - -package gin - -// ResponseWriter ... -type ResponseWriter interface { - responseWriterBase -} diff --git a/response_writer_1.8.go b/response_writer_1.8.go deleted file mode 100644 index 527c0038..00000000 --- a/response_writer_1.8.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build go1.8 - -// Copyright 2018 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. - -package gin - -import ( - "net/http" -) - -// ResponseWriter ... -type ResponseWriter interface { - responseWriterBase - // get the http.Pusher for server push - Pusher() http.Pusher -} - -func (w *responseWriter) Pusher() (pusher http.Pusher) { - if pusher, ok := w.ResponseWriter.(http.Pusher); ok { - return pusher - } - return nil -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 6050e8f6..fc7bb11d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1,5 +1,5 @@ { - "comment": "v1.3.0", + "comment": "v1.4.0", "ignore": "test", "package": [ { @@ -13,16 +13,16 @@ { "checksumSHA1": "QeKwBtN2df+j+4stw3bQJ6yO4EY=", "path": "github.com/gin-contrib/sse", - "revision": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae", - "revisionTime": "2017-01-09T09:34:21Z" + "revision": "5545eab6dad3bbbd6c5ae9186383c2a9d23c0dae", + "revisionTime": "2019-03-01T06:25:29Z" }, { - "checksumSHA1": "mE9XW26JSpe4meBObM6J/Oeq0eg=", + "checksumSHA1": "Y2MOwzNZfl4NRNDbLCZa6sgx7O0=", "path": "github.com/golang/protobuf/proto", - "revision": "aa810b61a9c79d51363740d207bb46cf8e620ed5", - "revisionTime": "2018-08-14T21:14:27Z", - "version": "v1.2", - "versionExact": "v1.2.0" + "revision": "c823c79ea1570fb5ff454033735a8e68575d1d0f", + "revisionTime": "2019-02-05T22:20:52Z", + "version": "v1.3", + "versionExact": "v1.3.0" }, { "checksumSHA1": "WqeEgS7pqqkwK8mlrAZmDgtWJMY=", @@ -33,12 +33,24 @@ "versionExact": "v1.1.5" }, { - "checksumSHA1": "w5RcOnfv5YDr3j2bd1YydkPiZx4=", + "checksumSHA1": "rrXDDvz+nQ2KRLQk6nxWaE5Zj1U=", "path": "github.com/mattn/go-isatty", - "revision": "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c", - "revisionTime": "2017-11-07T05:05:31Z", + "revision": "369ecd8cea9851e459abb67eb171853e3986591e", + "revisionTime": "2019-02-25T17:38:24Z", "version": "v0.0", - "versionExact": "v0.0.4" + "versionExact": "v0.0.6" + }, + { + "checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=", + "path": "github.com/modern-go/concurrent", + "revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94", + "revisionTime": "2018-03-06T01:26:44Z" + }, + { + "checksumSHA1": "qvH48wzTIV3QKSDqI0dLFtVjaDI=", + "path": "github.com/modern-go/reflect2", + "revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8", + "revisionTime": "2018-07-18T01:23:57Z" }, { "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", @@ -46,6 +58,20 @@ "revision": "5d4384ee4fb2527b0a1256a821ebfc92f91efefc", "revisionTime": "2018-12-26T10:54:42Z" }, + { + "checksumSHA1": "cpNsoLqBprpKh+VZTBOZNVXzBEk=", + "path": "github.com/stretchr/objx", + "revision": "c61a9dfcced1815e7d40e214d00d1a8669a9f58c", + "revisionTime": "2019-02-11T16:23:28Z" + }, + { + "checksumSHA1": "DBdcVxnvaINHhWyyGgih/Mel6gE=", + "path": "github.com/stretchr/testify", + "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", + "revisionTime": "2018-12-05T02:12:43Z", + "version": "v1.3", + "versionExact": "v1.3.0" + }, { "checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=", "path": "github.com/stretchr/testify/assert", @@ -55,12 +81,26 @@ "versionExact": "v1.2.2" }, { - "checksumSHA1": "5Bd8RPhhaKcEXkagzPqymP4Gx5E=", + "checksumSHA1": "fg3TzS9/QK3wZbzei3Z6O8XPLHg=", + "path": "github.com/stretchr/testify/http", + "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", + "revisionTime": "2018-12-05T02:12:43Z", + "version": "v1.3", + "versionExact": "v1.3.0" + }, + { + "checksumSHA1": "lsdl3fgOiM4Iuy7xjTQxiBtAwB0=", + "path": "github.com/stretchr/testify/mock", + "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", + "revisionTime": "2018-12-05T02:12:43Z", + "version": "v1.3", + "versionExact": "v1.3.0" + }, + { + "checksumSHA1": "WIhpR3EKGueRSJsYOZ6PIsfL4SI=", "path": "github.com/ugorji/go/codec", - "revision": "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab", - "revisionTime": "2018-04-07T10:07:33Z", - "version": "v1.1", - "versionExact": "v1.1.1" + "revision": "e444a5086c436778cf9281a7059a3d58b9e17935", + "revisionTime": "2019-02-04T20:13:41Z" }, { "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=", @@ -83,13 +123,13 @@ "versionExact": "v8.18.2" }, { - "checksumSHA1": "ZSWoOPUNRr5+3dhkLK3C4cZAQPk=", + "checksumSHA1": "QqDq2x8XOU7IoOR98Cx1eiV5QY8=", "path": "gopkg.in/yaml.v2", - "revision": "5420a8b6744d3b0345ab293f6fcba19c978f1183", - "revisionTime": "2018-03-28T19:50:20Z", + "revision": "51d6538a90f86fe93ac480b35f37b2be17fef232", + "revisionTime": "2018-11-15T11:05:04Z", "version": "v2.2", - "versionExact": "v2.2.1" + "versionExact": "v2.2.2" } ], "rootPath": "github.com/gin-gonic/gin" -} +} \ No newline at end of file diff --git a/version.go b/version.go index 028caebe..07e7859f 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.4.0-dev" +const Version = "v1.4.0" From 66d2c30c54ff8042f5ae13d9ebb26dfe556561fe Mon Sep 17 00:00:00 2001 From: Dmitry Kutakov Date: Tue, 7 May 2019 14:06:55 +0300 Subject: [PATCH 2/8] binding: move tests of mapping to separate test file (#1842) * move tests of mapping to separate test file make 100% coverage of form_mapping.go from form_mapping_test.go file * fix tests for go 1.6 go 1.6 doesn't support `t.Run(...)` subtests --- binding/binding_test.go | 406 ----------------------------------- binding/form_mapping_test.go | 271 +++++++++++++++++++++++ 2 files changed, 271 insertions(+), 406 deletions(-) create mode 100644 binding/form_mapping_test.go diff --git a/binding/binding_test.go b/binding/binding_test.go index ee788225..73bb7700 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -114,71 +114,6 @@ type FooStructForBoolType struct { BoolFoo bool `form:"bool_foo"` } -type FooBarStructForIntType struct { - IntFoo int `form:"int_foo"` - IntBar int `form:"int_bar" binding:"required"` -} - -type FooBarStructForInt8Type struct { - Int8Foo int8 `form:"int8_foo"` - Int8Bar int8 `form:"int8_bar" binding:"required"` -} - -type FooBarStructForInt16Type struct { - Int16Foo int16 `form:"int16_foo"` - Int16Bar int16 `form:"int16_bar" binding:"required"` -} - -type FooBarStructForInt32Type struct { - Int32Foo int32 `form:"int32_foo"` - Int32Bar int32 `form:"int32_bar" binding:"required"` -} - -type FooBarStructForInt64Type struct { - Int64Foo int64 `form:"int64_foo"` - Int64Bar int64 `form:"int64_bar" binding:"required"` -} - -type FooBarStructForUintType struct { - UintFoo uint `form:"uint_foo"` - UintBar uint `form:"uint_bar" binding:"required"` -} - -type FooBarStructForUint8Type struct { - Uint8Foo uint8 `form:"uint8_foo"` - Uint8Bar uint8 `form:"uint8_bar" binding:"required"` -} - -type FooBarStructForUint16Type struct { - Uint16Foo uint16 `form:"uint16_foo"` - Uint16Bar uint16 `form:"uint16_bar" binding:"required"` -} - -type FooBarStructForUint32Type struct { - Uint32Foo uint32 `form:"uint32_foo"` - Uint32Bar uint32 `form:"uint32_bar" binding:"required"` -} - -type FooBarStructForUint64Type struct { - Uint64Foo uint64 `form:"uint64_foo"` - Uint64Bar uint64 `form:"uint64_bar" binding:"required"` -} - -type FooBarStructForBoolType struct { - BoolFoo bool `form:"bool_foo"` - BoolBar bool `form:"bool_bar" binding:"required"` -} - -type FooBarStructForFloat32Type struct { - Float32Foo float32 `form:"float32_foo"` - Float32Bar float32 `form:"float32_bar" binding:"required"` -} - -type FooBarStructForFloat64Type struct { - Float64Foo float64 `form:"float64_foo"` - Float64Bar float64 `form:"float64_bar" binding:"required"` -} - type FooStructForStringPtrType struct { PtrFoo *string `form:"ptr_foo"` PtrBar *string `form:"ptr_bar" binding:"required"` @@ -335,110 +270,6 @@ func TestBindingFormForType(t *testing.T) { "/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2", "", "", "SliceMap") - testFormBindingForType(t, "POST", - "/", "/", - "int_foo=&int_bar=-12", "bar2=-123", "Int") - - testFormBindingForType(t, "GET", - "/?int_foo=&int_bar=-12", "/?bar2=-123", - "", "", "Int") - - testFormBindingForType(t, "POST", - "/", "/", - "int8_foo=&int8_bar=-12", "bar2=-123", "Int8") - - testFormBindingForType(t, "GET", - "/?int8_foo=&int8_bar=-12", "/?bar2=-123", - "", "", "Int8") - - testFormBindingForType(t, "POST", - "/", "/", - "int16_foo=&int16_bar=-12", "bar2=-123", "Int16") - - testFormBindingForType(t, "GET", - "/?int16_foo=&int16_bar=-12", "/?bar2=-123", - "", "", "Int16") - - testFormBindingForType(t, "POST", - "/", "/", - "int32_foo=&int32_bar=-12", "bar2=-123", "Int32") - - testFormBindingForType(t, "GET", - "/?int32_foo=&int32_bar=-12", "/?bar2=-123", - "", "", "Int32") - - testFormBindingForType(t, "POST", - "/", "/", - "int64_foo=&int64_bar=-12", "bar2=-123", "Int64") - - testFormBindingForType(t, "GET", - "/?int64_foo=&int64_bar=-12", "/?bar2=-123", - "", "", "Int64") - - testFormBindingForType(t, "POST", - "/", "/", - "uint_foo=&uint_bar=12", "bar2=123", "Uint") - - testFormBindingForType(t, "GET", - "/?uint_foo=&uint_bar=12", "/?bar2=123", - "", "", "Uint") - - testFormBindingForType(t, "POST", - "/", "/", - "uint8_foo=&uint8_bar=12", "bar2=123", "Uint8") - - testFormBindingForType(t, "GET", - "/?uint8_foo=&uint8_bar=12", "/?bar2=123", - "", "", "Uint8") - - testFormBindingForType(t, "POST", - "/", "/", - "uint16_foo=&uint16_bar=12", "bar2=123", "Uint16") - - testFormBindingForType(t, "GET", - "/?uint16_foo=&uint16_bar=12", "/?bar2=123", - "", "", "Uint16") - - testFormBindingForType(t, "POST", - "/", "/", - "uint32_foo=&uint32_bar=12", "bar2=123", "Uint32") - - testFormBindingForType(t, "GET", - "/?uint32_foo=&uint32_bar=12", "/?bar2=123", - "", "", "Uint32") - - testFormBindingForType(t, "POST", - "/", "/", - "uint64_foo=&uint64_bar=12", "bar2=123", "Uint64") - - testFormBindingForType(t, "GET", - "/?uint64_foo=&uint64_bar=12", "/?bar2=123", - "", "", "Uint64") - - testFormBindingForType(t, "POST", - "/", "/", - "bool_foo=&bool_bar=true", "bar2=true", "Bool") - - testFormBindingForType(t, "GET", - "/?bool_foo=&bool_bar=true", "/?bar2=true", - "", "", "Bool") - - testFormBindingForType(t, "POST", - "/", "/", - "float32_foo=&float32_bar=-12.34", "bar2=12.3", "Float32") - - testFormBindingForType(t, "GET", - "/?float32_foo=&float32_bar=-12.34", "/?bar2=12.3", - "", "", "Float32") - - testFormBindingForType(t, "POST", - "/", "/", - "float64_foo=&float64_bar=-12.34", "bar2=12.3", "Float64") - - testFormBindingForType(t, "GET", - "/?float64_foo=&float64_bar=-12.34", "/?bar2=12.3", - "", "", "Float64") - testFormBindingForType(t, "POST", "/", "/", "ptr_bar=test", "bar2=test", "Ptr") @@ -1076,149 +907,6 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s req.Header.Add("Content-Type", MIMEPOSTForm) } switch typ { - case "Int": - obj := FooBarStructForIntType{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, int(0), obj.IntFoo) - assert.Equal(t, int(-12), obj.IntBar) - - obj = FooBarStructForIntType{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Int8": - obj := FooBarStructForInt8Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, int8(0), obj.Int8Foo) - assert.Equal(t, int8(-12), obj.Int8Bar) - - obj = FooBarStructForInt8Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Int16": - obj := FooBarStructForInt16Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, int16(0), obj.Int16Foo) - assert.Equal(t, int16(-12), obj.Int16Bar) - - obj = FooBarStructForInt16Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Int32": - obj := FooBarStructForInt32Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, int32(0), obj.Int32Foo) - assert.Equal(t, int32(-12), obj.Int32Bar) - - obj = FooBarStructForInt32Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Int64": - obj := FooBarStructForInt64Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, int64(0), obj.Int64Foo) - assert.Equal(t, int64(-12), obj.Int64Bar) - - obj = FooBarStructForInt64Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Uint": - obj := FooBarStructForUintType{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, uint(0x0), obj.UintFoo) - assert.Equal(t, uint(0xc), obj.UintBar) - - obj = FooBarStructForUintType{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Uint8": - obj := FooBarStructForUint8Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, uint8(0x0), obj.Uint8Foo) - assert.Equal(t, uint8(0xc), obj.Uint8Bar) - - obj = FooBarStructForUint8Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Uint16": - obj := FooBarStructForUint16Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, uint16(0x0), obj.Uint16Foo) - assert.Equal(t, uint16(0xc), obj.Uint16Bar) - - obj = FooBarStructForUint16Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Uint32": - obj := FooBarStructForUint32Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, uint32(0x0), obj.Uint32Foo) - assert.Equal(t, uint32(0xc), obj.Uint32Bar) - - obj = FooBarStructForUint32Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Uint64": - obj := FooBarStructForUint64Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, uint64(0x0), obj.Uint64Foo) - assert.Equal(t, uint64(0xc), obj.Uint64Bar) - - obj = FooBarStructForUint64Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Float32": - obj := FooBarStructForFloat32Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, float32(0.0), obj.Float32Foo) - assert.Equal(t, float32(-12.34), obj.Float32Bar) - - obj = FooBarStructForFloat32Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Float64": - obj := FooBarStructForFloat64Type{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.Equal(t, float64(0.0), obj.Float64Foo) - assert.Equal(t, float64(-12.34), obj.Float64Bar) - - obj = FooBarStructForFloat64Type{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) - case "Bool": - obj := FooBarStructForBoolType{} - err := b.Bind(req, &obj) - assert.NoError(t, err) - assert.False(t, obj.BoolFoo) - assert.True(t, obj.BoolBar) - - obj = FooBarStructForBoolType{} - req = requestWithBody(method, badPath, badBody) - err = JSON.Bind(req, &obj) - assert.Error(t, err) case "Slice": obj := FooStructForSliceType{} err := b.Bind(req, &obj) @@ -1454,97 +1142,3 @@ func requestWithBody(method, path, body string) (req *http.Request) { req, _ = http.NewRequest(method, path, bytes.NewBufferString(body)) return } - -func TestCanSet(t *testing.T) { - type CanSetStruct struct { - lowerStart string `form:"lower"` - } - - var c CanSetStruct - assert.Nil(t, mapForm(&c, nil)) -} - -func formPostRequest(path, body string) *http.Request { - req := requestWithBody("POST", path, body) - req.Header.Add("Content-Type", MIMEPOSTForm) - return req -} - -func TestBindingSliceDefault(t *testing.T) { - var s struct { - Friends []string `form:"friends,default=mike"` - } - req := formPostRequest("", "") - err := Form.Bind(req, &s) - assert.NoError(t, err) - - assert.Len(t, s.Friends, 1) - assert.Equal(t, "mike", s.Friends[0]) -} - -func TestBindingStructField(t *testing.T) { - var s struct { - Opts struct { - Port int - } `form:"opts"` - } - req := formPostRequest("", `opts={"Port": 8000}`) - err := Form.Bind(req, &s) - assert.NoError(t, err) - assert.Equal(t, 8000, s.Opts.Port) -} - -func TestBindingUnknownTypeChan(t *testing.T) { - var s struct { - Stop chan bool `form:"stop"` - } - req := formPostRequest("", "stop=true") - err := Form.Bind(req, &s) - assert.Error(t, err) - assert.Equal(t, errUnknownType, err) -} - -func TestBindingTimeDuration(t *testing.T) { - var s struct { - Timeout time.Duration `form:"timeout"` - } - - // ok - req := formPostRequest("", "timeout=5s") - err := Form.Bind(req, &s) - assert.NoError(t, err) - assert.Equal(t, 5*time.Second, s.Timeout) - - // error - req = formPostRequest("", "timeout=wrong") - err = Form.Bind(req, &s) - assert.Error(t, err) -} - -func TestBindingArray(t *testing.T) { - var s struct { - Nums [2]int `form:"nums,default=4"` - } - - // default - req := formPostRequest("", "") - err := Form.Bind(req, &s) - assert.Error(t, err) - assert.Equal(t, [2]int{0, 0}, s.Nums) - - // ok - req = formPostRequest("", "nums=3&nums=8") - err = Form.Bind(req, &s) - assert.NoError(t, err) - assert.Equal(t, [2]int{3, 8}, s.Nums) - - // not enough vals - req = formPostRequest("", "nums=3") - err = Form.Bind(req, &s) - assert.Error(t, err) - - // error - req = formPostRequest("", "nums=3&nums=wrong") - err = Form.Bind(req, &s) - assert.Error(t, err) -} diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go new file mode 100644 index 00000000..c9d6111b --- /dev/null +++ b/binding/form_mapping_test.go @@ -0,0 +1,271 @@ +// Copyright 2019 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. + +package binding + +import ( + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMappingBaseTypes(t *testing.T) { + intPtr := func(i int) *int { + return &i + } + for _, tt := range []struct { + name string + value interface{} + form string + expect interface{} + }{ + {"base type", struct{ F int }{}, "9", int(9)}, + {"base type", struct{ F int8 }{}, "9", int8(9)}, + {"base type", struct{ F int16 }{}, "9", int16(9)}, + {"base type", struct{ F int32 }{}, "9", int32(9)}, + {"base type", struct{ F int64 }{}, "9", int64(9)}, + {"base type", struct{ F uint }{}, "9", uint(9)}, + {"base type", struct{ F uint8 }{}, "9", uint8(9)}, + {"base type", struct{ F uint16 }{}, "9", uint16(9)}, + {"base type", struct{ F uint32 }{}, "9", uint32(9)}, + {"base type", struct{ F uint64 }{}, "9", uint64(9)}, + {"base type", struct{ F bool }{}, "True", true}, + {"base type", struct{ F float32 }{}, "9.1", float32(9.1)}, + {"base type", struct{ F float64 }{}, "9.1", float64(9.1)}, + {"base type", struct{ F string }{}, "test", string("test")}, + {"base type", struct{ F *int }{}, "9", intPtr(9)}, + + // zero values + {"zero value", struct{ F int }{}, "", int(0)}, + {"zero value", struct{ F uint }{}, "", uint(0)}, + {"zero value", struct{ F bool }{}, "", false}, + {"zero value", struct{ F float32 }{}, "", float32(0)}, + } { + tp := reflect.TypeOf(tt.value) + testName := tt.name + ":" + tp.Field(0).Type.String() + + val := reflect.New(reflect.TypeOf(tt.value)) + val.Elem().Set(reflect.ValueOf(tt.value)) + + field := val.Elem().Type().Field(0) + + _, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form") + assert.NoError(t, err, testName) + + actual := val.Elem().Field(0).Interface() + assert.Equal(t, tt.expect, actual, testName) + } +} + +func TestMappingDefault(t *testing.T) { + var s struct { + Int int `form:",default=9"` + Slice []int `form:",default=9"` + Array [1]int `form:",default=9"` + } + err := mappingByPtr(&s, formSource{}, "form") + assert.NoError(t, err) + + assert.Equal(t, 9, s.Int) + assert.Equal(t, []int{9}, s.Slice) + assert.Equal(t, [1]int{9}, s.Array) +} + +func TestMappingSkipField(t *testing.T) { + var s struct { + A int + } + err := mappingByPtr(&s, formSource{}, "form") + assert.NoError(t, err) + + assert.Equal(t, 0, s.A) +} + +func TestMappingIgnoreField(t *testing.T) { + var s struct { + A int `form:"A"` + B int `form:"-"` + } + err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form") + assert.NoError(t, err) + + assert.Equal(t, 9, s.A) + assert.Equal(t, 0, s.B) +} + +func TestMappingUnexportedField(t *testing.T) { + var s struct { + A int `form:"a"` + b int `form:"b"` + } + err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form") + assert.NoError(t, err) + + assert.Equal(t, 9, s.A) + assert.Equal(t, 0, s.b) +} + +func TestMappingPrivateField(t *testing.T) { + var s struct { + f int `form:"field"` + } + err := mappingByPtr(&s, formSource{"field": {"6"}}, "form") + assert.NoError(t, err) + assert.Equal(t, int(0), s.f) +} + +func TestMappingUnknownFieldType(t *testing.T) { + var s struct { + U uintptr + } + + err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form") + assert.Error(t, err) + assert.Equal(t, errUnknownType, err) +} + +func TestMappingURI(t *testing.T) { + var s struct { + F int `uri:"field"` + } + err := mapUri(&s, map[string][]string{"field": {"6"}}) + assert.NoError(t, err) + assert.Equal(t, int(6), s.F) +} + +func TestMappingForm(t *testing.T) { + var s struct { + F int `form:"field"` + } + err := mapForm(&s, map[string][]string{"field": {"6"}}) + assert.NoError(t, err) + assert.Equal(t, int(6), s.F) +} + +func TestMappingTime(t *testing.T) { + var s struct { + Time time.Time + LocalTime time.Time `time_format:"2006-01-02"` + ZeroValue time.Time + CSTTime time.Time `time_format:"2006-01-02" time_location:"Asia/Shanghai"` + UTCTime time.Time `time_format:"2006-01-02" time_utc:"1"` + } + + var err error + time.Local, err = time.LoadLocation("Europe/Berlin") + assert.NoError(t, err) + + err = mapForm(&s, map[string][]string{ + "Time": {"2019-01-20T16:02:58Z"}, + "LocalTime": {"2019-01-20"}, + "ZeroValue": {}, + "CSTTime": {"2019-01-20"}, + "UTCTime": {"2019-01-20"}, + }) + assert.NoError(t, err) + + assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String()) + assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String()) + assert.Equal(t, "2019-01-19 23:00:00 +0000 UTC", s.LocalTime.UTC().String()) + assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", s.ZeroValue.String()) + assert.Equal(t, "2019-01-20 00:00:00 +0800 CST", s.CSTTime.String()) + assert.Equal(t, "2019-01-19 16:00:00 +0000 UTC", s.CSTTime.UTC().String()) + assert.Equal(t, "2019-01-20 00:00:00 +0000 UTC", s.UTCTime.String()) + + // wrong location + var wrongLoc struct { + Time time.Time `time_location:"wrong"` + } + err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}}) + assert.Error(t, err) + + // wrong time value + var wrongTime struct { + Time time.Time + } + err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}}) + assert.Error(t, err) +} + +func TestMapiingTimeDuration(t *testing.T) { + var s struct { + D time.Duration + } + + // ok + err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form") + assert.NoError(t, err) + assert.Equal(t, 5*time.Second, s.D) + + // error + err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form") + assert.Error(t, err) +} + +func TestMappingSlice(t *testing.T) { + var s struct { + Slice []int `form:"slice,default=9"` + } + + // default value + err := mappingByPtr(&s, formSource{}, "form") + assert.NoError(t, err) + assert.Equal(t, []int{9}, s.Slice) + + // ok + err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form") + assert.NoError(t, err) + assert.Equal(t, []int{3, 4}, s.Slice) + + // error + err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form") + assert.Error(t, err) +} + +func TestMappingArray(t *testing.T) { + var s struct { + Array [2]int `form:"array,default=9"` + } + + // wrong default + err := mappingByPtr(&s, formSource{}, "form") + assert.Error(t, err) + + // ok + err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form") + assert.NoError(t, err) + assert.Equal(t, [2]int{3, 4}, s.Array) + + // error - not enough vals + err = mappingByPtr(&s, formSource{"array": {"3"}}, "form") + assert.Error(t, err) + + // error - wrong value + err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form") + assert.Error(t, err) +} + +func TestMappingStructField(t *testing.T) { + var s struct { + J struct { + I int + } + } + + err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form") + assert.NoError(t, err) + assert.Equal(t, 9, s.J.I) +} + +func TestMappingMapField(t *testing.T) { + var s struct { + M map[string]int + } + + err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form") + assert.NoError(t, err) + assert.Equal(t, map[string]int{"one": 1}, s.M) +} From b6425689dc657ad20762ada1591ebcc50f668c09 Mon Sep 17 00:00:00 2001 From: Dan Markham Date: Tue, 7 May 2019 04:32:35 -0700 Subject: [PATCH 3/8] Clean the Request Path early (#1817) This will reduce the number of times we have todo a redirect. and allow multiple slashes in path to be routed! fixes #1644 --- gin.go | 1 + routes_test.go | 52 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/gin.go b/gin.go index 2d24092f..4dbe9836 100644 --- a/gin.go +++ b/gin.go @@ -372,6 +372,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) { rPath = c.Request.URL.RawPath unescape = engine.UnescapePathValues } + rPath = cleanPath(rPath) // Find root of the tree for the given HTTP method t := engine.trees diff --git a/routes_test.go b/routes_test.go index de363a8c..e16c1376 100644 --- a/routes_test.go +++ b/routes_test.go @@ -22,7 +22,7 @@ type header struct { } func performRequest(r http.Handler, method, path string, headers ...header) *httptest.ResponseRecorder { - req, _ := http.NewRequest(method, path, nil) + req := httptest.NewRequest(method, path, nil) for _, h := range headers { req.Header.Add(h.Key, h.Value) } @@ -257,6 +257,39 @@ func TestRouteParamsByName(t *testing.T) { assert.Equal(t, "/is/super/great", wild) } +// TestContextParamsGet tests that a parameter can be parsed from the URL even with extra slashes. +func TestRouteParamsByNameWithExtraSlash(t *testing.T) { + name := "" + lastName := "" + wild := "" + router := New() + router.GET("/test/:name/:last_name/*wild", func(c *Context) { + name = c.Params.ByName("name") + lastName = c.Params.ByName("last_name") + var ok bool + wild, ok = c.Params.Get("wild") + + assert.True(t, ok) + assert.Equal(t, name, c.Param("name")) + assert.Equal(t, name, c.Param("name")) + assert.Equal(t, lastName, c.Param("last_name")) + + assert.Empty(t, c.Param("wtf")) + assert.Empty(t, c.Params.ByName("wtf")) + + wtf, ok := c.Params.Get("wtf") + assert.Empty(t, wtf) + assert.False(t, ok) + }) + + w := performRequest(router, "GET", "//test//john//smith//is//super//great") + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "john", name) + assert.Equal(t, "smith", lastName) + assert.Equal(t, "/is/super/great", wild) +} + // TestHandleStaticFile - ensure the static file handles properly func TestRouteStaticFile(t *testing.T) { // SETUP file @@ -386,15 +419,14 @@ func TestRouterNotFound(t *testing.T) { code int location string }{ - {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/ - {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/ - {"", http.StatusMovedPermanently, "/"}, // TSR +/ - {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case - {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case - {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/ - {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ - {"/../path", http.StatusMovedPermanently, "/path"}, // CleanPath - {"/nope", http.StatusNotFound, ""}, // NotFound + {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/ + {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/ + {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case + {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case + {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/ + {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ + {"/../path", http.StatusOK, ""}, // CleanPath + {"/nope", http.StatusNotFound, ""}, // NotFound } for _, tr := range testRoutes { w := performRequest(router, "GET", tr.route) From b75d67cd51eb53c3c3a2fc406524c940021ffbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Tue, 7 May 2019 19:43:05 +0800 Subject: [PATCH 4/8] update vendor: ugorji/go (#1879) * update vendor: ugorji/go * fix --- go.mod | 10 +++++----- go.sum | 29 ++++++++++++--------------- vendor/vendor.json | 50 +++++++++++++++++----------------------------- 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/go.mod b/go.mod index 01227574..1c5e995c 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,14 @@ go 1.12 require ( github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 - github.com/golang/protobuf v1.3.0 - github.com/json-iterator/go v1.1.5 - github.com/mattn/go-isatty v0.0.6 + github.com/golang/protobuf v1.3.1 + github.com/json-iterator/go v1.1.6 + github.com/mattn/go-isatty v0.0.7 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/stretchr/testify v1.3.0 - github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 - golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 + github.com/ugorji/go v1.1.4 + golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index 84cf8378..58104682 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,12 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= -github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= -github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= -github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -17,18 +17,15 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= -github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA= -github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU= -golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= diff --git a/vendor/vendor.json b/vendor/vendor.json index fc7bb11d..4de0bfd1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -25,20 +25,20 @@ "versionExact": "v1.3.0" }, { - "checksumSHA1": "WqeEgS7pqqkwK8mlrAZmDgtWJMY=", + "checksumSHA1": "TB2vxux9xQbvsTHOVt4aRTuvSn4=", "path": "github.com/json-iterator/go", - "revision": "1624edc4454b8682399def8740d46db5e4362ba4", - "revisionTime": "2018-08-06T06:07:27Z", + "revision": "0ff49de124c6f76f8494e194af75bde0f1a49a29", + "revisionTime": "2019-03-06T14:29:09Z", "version": "v1.1", - "versionExact": "v1.1.5" + "versionExact": "v1.1.6" }, { - "checksumSHA1": "rrXDDvz+nQ2KRLQk6nxWaE5Zj1U=", + "checksumSHA1": "Ya+baVBU/RkXXUWD3LGFmGJiiIg=", "path": "github.com/mattn/go-isatty", - "revision": "369ecd8cea9851e459abb67eb171853e3986591e", - "revisionTime": "2019-02-25T17:38:24Z", + "revision": "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7", + "revisionTime": "2019-03-12T13:58:54Z", "version": "v0.0", - "versionExact": "v0.0.6" + "versionExact": "v0.0.7" }, { "checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=", @@ -81,38 +81,24 @@ "versionExact": "v1.2.2" }, { - "checksumSHA1": "fg3TzS9/QK3wZbzei3Z6O8XPLHg=", - "path": "github.com/stretchr/testify/http", - "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", - "revisionTime": "2018-12-05T02:12:43Z", - "version": "v1.3", - "versionExact": "v1.3.0" - }, - { - "checksumSHA1": "lsdl3fgOiM4Iuy7xjTQxiBtAwB0=", - "path": "github.com/stretchr/testify/mock", - "revision": "ffdc059bfe9ce6a4e144ba849dbedead332c6053", - "revisionTime": "2018-12-05T02:12:43Z", - "version": "v1.3", - "versionExact": "v1.3.0" - }, - { - "checksumSHA1": "WIhpR3EKGueRSJsYOZ6PIsfL4SI=", + "checksumSHA1": "csplo594qomjp2IZj82y7mTueOw=", "path": "github.com/ugorji/go/codec", - "revision": "e444a5086c436778cf9281a7059a3d58b9e17935", - "revisionTime": "2019-02-04T20:13:41Z" + "revision": "2adff0894ba3bc2eeb9f9aea45fefd49802e1a13", + "revisionTime": "2019-04-08T19:08:48Z", + "version": "v1.1", + "versionExact": "v1.1.4" }, { "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=", "path": "golang.org/x/net/context", - "revision": "49bb7cea24b1df9410e1712aa6433dae904ff66a", - "revisionTime": "2018-10-11T05:27:23Z" + "revision": "f4e77d36d62c17c2336347bb2670ddbd02d092b7", + "revisionTime": "2019-05-02T22:26:14Z" }, { - "checksumSHA1": "SiJNkx+YGtq3Gtr6Ldu6OW83O+U=", + "checksumSHA1": "2gaep1KNRDNyDA3O+KgPTQsGWvs=", "path": "golang.org/x/sys/unix", - "revision": "fa43e7bc11baaae89f3f902b2b4d832b68234844", - "revisionTime": "2018-10-11T14:35:51Z" + "revision": "a43fa875dd822b81eb6d2ad538bc1f4caba169bd", + "revisionTime": "2019-05-02T15:41:39Z" }, { "checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=", From 5a7e3095b29adc7a9caf89fe570badff28997be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Wed, 8 May 2019 11:10:34 +0800 Subject: [PATCH 5/8] Update README.md about go version (#1885) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e817a78..e980edbc 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ $ go run main.go ## Prerequisite -Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. +Now Gin requires Go 1.8 or later and Go 1.10 will be required next major version. ## Quick start From 04eecb1283dbd84c3babae7b3923ac71b18a56f9 Mon Sep 17 00:00:00 2001 From: Uwe Dauernheim Date: Fri, 10 May 2019 08:03:25 +0200 Subject: [PATCH 6/8] Use DefaultWriter and DefaultErrorWriter for debug messages (#1891) Aligns behaviour according to documentation. --- debug.go | 7 ++++--- debug_test.go | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/debug.go b/debug.go index 6d40a5da..19e380fb 100644 --- a/debug.go +++ b/debug.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" "html/template" - "os" "runtime" "strconv" "strings" @@ -54,7 +53,7 @@ func debugPrint(format string, values ...interface{}) { if !strings.HasSuffix(format, "\n") { format += "\n" } - fmt.Fprintf(os.Stderr, "[GIN-debug] "+format, values...) + fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...) } } @@ -98,6 +97,8 @@ at initialization. ie. before any route is registered or the router is listening func debugPrintError(err error) { if err != nil { - debugPrint("[ERROR] %v\n", err) + if IsDebugging() { + fmt.Fprintf(DefaultErrorWriter, "[GIN-debug] [ERROR] %v\n", err) + } } } diff --git a/debug_test.go b/debug_test.go index 86a67773..9ace2989 100644 --- a/debug_test.go +++ b/debug_test.go @@ -111,15 +111,15 @@ func captureOutput(t *testing.T, f func()) string { if err != nil { panic(err) } - stdout := os.Stdout - stderr := os.Stderr + defaultWriter := DefaultWriter + defaultErrorWriter := DefaultErrorWriter defer func() { - os.Stdout = stdout - os.Stderr = stderr + DefaultWriter = defaultWriter + DefaultErrorWriter = defaultErrorWriter log.SetOutput(os.Stderr) }() - os.Stdout = writer - os.Stderr = writer + DefaultWriter = writer + DefaultErrorWriter = writer log.SetOutput(writer) out := make(chan string) wg := new(sync.WaitGroup) From 965d74cebb31c1e5e0c09ec256c32d0c5db9072c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Sun, 12 May 2019 18:47:27 +0800 Subject: [PATCH 7/8] add dev version (#1886) * add dev version * Update version.go * Update version.go --- README.md | 4 ---- version.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index e980edbc..10fb1d45 100644 --- a/README.md +++ b/README.md @@ -119,10 +119,6 @@ $ curl https://raw.githubusercontent.com/gin-gonic/examples/master/basic/main.go $ go run main.go ``` -## Prerequisite - -Now Gin requires Go 1.8 or later and Go 1.10 will be required next major version. - ## Quick start ```sh diff --git a/version.go b/version.go index 07e7859f..028caebe 100644 --- a/version.go +++ b/version.go @@ -5,4 +5,4 @@ package gin // Version is the current gin framework's version. -const Version = "v1.4.0" +const Version = "v1.4.0-dev" From 8ee9d959a0bcc132fae25ce61881c7effbe5c2f5 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Mon, 13 May 2019 10:17:31 +0800 Subject: [PATCH 8/8] Now you can parse the inline lowercase start structure (#1893) * Now you can parse the inline lowercase start structure package main import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" ) type appkey struct { Appkey string `json:"appkey" form:"appkey"` } type Query struct { Page int `json:"page" form:"page"` Size int `json:"size" form:"size"` appkey } func main() { router := gin.Default() router.POST("/login", func(c *gin.Context) { var q2 Query if c.ShouldBindQuery(&q2) == nil { c.JSON(200, &q2) } }) router.Run(":8088") } http client: old: curl -X POST "127.0.0.1:8088/login?appkey=china&page=1&size=10" {"page":1,"size":10,"appkey":""} now: curl -X POST "127.0.0.1:8088/login?appkey=china&page=1&size=10" {"page":1,"size":10,"appkey":"china"} * Modify judgment conditions --- binding/binding_test.go | 39 +++++++++++++++++++++++++++++++++++++++ binding/form_mapping.go | 17 ++++++++++------- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/binding/binding_test.go b/binding/binding_test.go index 73bb7700..6710e42b 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -24,6 +24,16 @@ import ( "github.com/ugorji/go/codec" ) +type appkey struct { + Appkey string `json:"appkey" form:"appkey"` +} + +type QueryTest struct { + Page int `json:"page" form:"page"` + Size int `json:"size" form:"size"` + appkey +} + type FooStruct struct { Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required"` } @@ -189,6 +199,18 @@ func TestBindingForm2(t *testing.T) { "", "") } +func TestBindingFormEmbeddedStruct(t *testing.T) { + testFormBindingEmbeddedStruct(t, "POST", + "/", "/", + "page=1&size=2&appkey=test-appkey", "bar2=foo") +} + +func TestBindingFormEmbeddedStruct2(t *testing.T) { + testFormBindingEmbeddedStruct(t, "GET", + "/?page=1&size=2&appkey=test-appkey", "/?bar2=foo", + "", "") +} + func TestBindingFormDefaultValue(t *testing.T) { testFormBindingDefaultValue(t, "POST", "/", "/", @@ -688,6 +710,23 @@ func TestUriInnerBinding(t *testing.T) { assert.Equal(t, tag.S.Age, expectedAge) } +func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, badBody string) { + b := Form + assert.Equal(t, "form", b.Name()) + + obj := QueryTest{} + req := requestWithBody(method, path, body) + if method == "POST" { + req.Header.Add("Content-Type", MIMEPOSTForm) + } + err := b.Bind(req, &obj) + assert.NoError(t, err) + assert.Equal(t, 1, obj.Page) + assert.Equal(t, 2, obj.Size) + assert.Equal(t, "test-appkey", obj.Appkey) + +} + func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { b := Form assert.Equal(t, "form", b.Name()) diff --git a/binding/form_mapping.go b/binding/form_mapping.go index aaacf6c5..32c5b668 100644 --- a/binding/form_mapping.go +++ b/binding/form_mapping.go @@ -70,12 +70,14 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag return isSetted, nil } - ok, err := tryToSetValue(value, field, setter, tag) - if err != nil { - return false, err - } - if ok { - return true, nil + if vKind != reflect.Struct || !field.Anonymous { + ok, err := tryToSetValue(value, field, setter, tag) + if err != nil { + return false, err + } + if ok { + return true, nil + } } if vKind == reflect.Struct { @@ -83,7 +85,8 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag var isSetted bool for i := 0; i < value.NumField(); i++ { - if !value.Field(i).CanSet() { + sf := tValue.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported continue } ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)