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/README.md b/README.md index 804041f9..8a0c2760 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 0a6d6271..82210c50 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 -}