Merge branch 'master' into master

This commit is contained in:
Jithin James 2018-01-26 14:24:01 +05:30 committed by GitHub
commit a076bf34f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1613 additions and 411 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ vendor/*
!vendor/vendor.json !vendor/vendor.json
coverage.out coverage.out
count.out count.out
test

View File

@ -2,14 +2,14 @@ GOFMT ?= gofmt "-s"
PACKAGES ?= $(shell go list ./... | grep -v /vendor/) PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*") GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
all: build all: install
install: deps install: deps
govendor sync govendor sync
.PHONY: test .PHONY: test
test: test:
go test -v -covermode=count -coverprofile=coverage.out sh coverage.sh
.PHONY: fmt .PHONY: fmt
fmt: fmt:

View File

@ -74,10 +74,10 @@ BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104
BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848 BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848
BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609 BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609
(1): Total Repetitions achieved in constant time, higher means more confident result - (1): Total Repetitions achieved in constant time, higher means more confident result
(2): Single Repetition Duration (ns/op), lower is better - (2): Single Repetition Duration (ns/op), lower is better
(3): Heap Memory (B/op), lower is better - (3): Heap Memory (B/op), lower is better
(4): Average Allocations per Repetition (allocs/op), lower is better - (4): Average Allocations per Repetition (allocs/op), lower is better
## Gin v1. stable ## Gin v1. stable
@ -117,7 +117,7 @@ $ go get github.com/kardianos/govendor
2. Create your project folder and `cd` inside 2. Create your project folder and `cd` inside
```sh ```sh
$ mkdir -p ~/go/src/github.com/myusername/project && cd "$_" $ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_"
``` ```
3. Vendor init your project and add gin 3. Vendor init your project and add gin
@ -385,7 +385,7 @@ func main() {
r := gin.New() r := gin.New()
// Global middleware // Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even you set with GIN_MODE=release. // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout // By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger()) r.Use(gin.Logger())
@ -435,7 +435,7 @@ func main() {
c.String(200, "pong") c.String(200, "pong")
}) })
r.Run(":8080")    router.Run(":8080")
} }
``` ```
@ -472,7 +472,7 @@ func main() {
// Example for binding JSON ({"user": "manu", "password": "123"}) // Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) { router.POST("/loginJSON", func(c *gin.Context) {
var json Login var json Login
if err = c.ShouldBindJSON(&json); err == nil { if err := c.ShouldBindJSON(&json); err == nil {
if json.User == "manu" && json.Password == "123" { if json.User == "manu" && json.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else { } else {
@ -540,7 +540,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
validator "gopkg.in/go-playground/validator.v8" "gopkg.in/go-playground/validator.v8"
) )
type Booking struct { type Booking struct {
@ -1071,7 +1071,7 @@ func main() {
### Goroutines inside a middleware ### Goroutines inside a middleware
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy. When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
```go ```go
func main() { func main() {
@ -1133,7 +1133,7 @@ func main() {
example for 1-line LetsEncrypt HTTPS servers. example for 1-line LetsEncrypt HTTPS servers.
[embedmd]:# (examples/auto-tls/example1.go go) [embedmd]:# (examples/auto-tls/example1/main.go go)
```go ```go
package main package main
@ -1158,7 +1158,7 @@ func main() {
example for custom autocert manager. example for custom autocert manager.
[embedmd]:# (examples/auto-tls/example2.go go) [embedmd]:# (examples/auto-tls/example2/main.go go)
```go ```go
package main package main
@ -1190,7 +1190,7 @@ func main() {
### Run multiple service using Gin ### Run multiple service using Gin
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the folling example: See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
[embedmd]:# (examples/multiple-service/main.go go) [embedmd]:# (examples/multiple-service/main.go go)
```go ```go
@ -1344,6 +1344,52 @@ func main() {
} }
``` ```
## Testing
The `net/http/httptest` package is preferable way for HTTP testing.
```go
package main
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
return r
}
func main() {
r := setupRouter()
r.Run(":8080")
}
```
Test for code example above:
```go
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
```
## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge) ## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.

12
auth.go
View File

@ -17,8 +17,8 @@ const AuthUserKey = "user"
type Accounts map[string]string type Accounts map[string]string
type authPair struct { type authPair struct {
Value string value string
User string user string
} }
type authPairs []authPair type authPairs []authPair
@ -28,8 +28,8 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
return "", false return "", false
} }
for _, pair := range a { for _, pair := range a {
if pair.Value == authValue { if pair.value == authValue {
return pair.User, true return pair.user, true
} }
} }
return "", false return "", false
@ -74,8 +74,8 @@ func processAccounts(accounts Accounts) authPairs {
assert1(user != "", "User can not be empty") assert1(user != "", "User can not be empty")
value := authorizationHeader(user, password) value := authorizationHeader(user, password)
pairs = append(pairs, authPair{ pairs = append(pairs, authPair{
Value: value, value: value,
User: user, user: user,
}) })
} }
return pairs return pairs

View File

@ -22,16 +22,16 @@ func TestBasicAuth(t *testing.T) {
assert.Len(t, pairs, 3) assert.Len(t, pairs, 3)
assert.Contains(t, pairs, authPair{ assert.Contains(t, pairs, authPair{
User: "bar", user: "bar",
Value: "Basic YmFyOmZvbw==", value: "Basic YmFyOmZvbw==",
}) })
assert.Contains(t, pairs, authPair{ assert.Contains(t, pairs, authPair{
User: "foo", user: "foo",
Value: "Basic Zm9vOmJhcg==", value: "Basic Zm9vOmJhcg==",
}) })
assert.Contains(t, pairs, authPair{ assert.Contains(t, pairs, authPair{
User: "admin", user: "admin",
Value: "Basic YWRtaW46cGFzc3dvcmQ=", value: "Basic YWRtaW46cGFzc3dvcmQ=",
}) })
} }

View File

@ -59,8 +59,6 @@ func BenchmarkOneRouteJSON(B *testing.B) {
runRequest(B, router, "GET", "/json") runRequest(B, router, "GET", "/json")
} }
var htmlContentType = []string{"text/html; charset=utf-8"}
func BenchmarkOneRouteHTML(B *testing.B) { func BenchmarkOneRouteHTML(B *testing.B) {
router := New() router := New()
t := template.Must(template.New("index").Parse(` t := template.Must(template.New("index").Parse(`

View File

@ -7,7 +7,7 @@ package binding
import ( import (
"net/http" "net/http"
validator "gopkg.in/go-playground/validator.v8" "gopkg.in/go-playground/validator.v8"
) )
const ( const (

View File

@ -6,9 +6,13 @@ package binding
import ( import (
"bytes" "bytes"
"encoding/json"
"errors"
"io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"testing" "testing"
"time"
"github.com/gin-gonic/gin/binding/example" "github.com/gin-gonic/gin/binding/example"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -25,6 +29,116 @@ type FooBarStruct struct {
Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"` Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"`
} }
type FooStructUseNumber struct {
Foo interface{} `json:"foo" binding:"required"`
}
type FooBarStructForTimeType struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`
TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`
}
type FooStructForTimeTypeNotFormat struct {
TimeFoo time.Time `form:"time_foo"`
}
type FooStructForTimeTypeFailFormat struct {
TimeFoo time.Time `form:"time_foo" time_format:"2017-11-15"`
}
type FooStructForTimeTypeFailLocation struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_location:"/asia/chongqing"`
}
type FooStructForMapType struct {
// Unknown type: not support map
MapFoo map[string]interface{} `form:"map_foo"`
}
type InvalidNameType struct {
TestName string `invalid_name:"test_name"`
}
type InvalidNameMapType struct {
TestName struct {
MapFoo map[string]interface{} `form:"map_foo"`
}
}
type FooStructForSliceType struct {
SliceFoo []int `form:"slice_foo"`
}
type FooStructForSliceMapType struct {
// Unknown type: not support map
SliceMapFoo []map[string]interface{} `form:"slice_map_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"`
}
func TestBindingDefault(t *testing.T) { func TestBindingDefault(t *testing.T) {
assert.Equal(t, Default("GET", ""), Form) assert.Equal(t, Default("GET", ""), Form)
assert.Equal(t, Default("GET", MIMEJSON), Form) assert.Equal(t, Default("GET", MIMEJSON), Form)
@ -55,6 +169,20 @@ func TestBindingJSON(t *testing.T) {
`{"foo": "bar"}`, `{"bar": "foo"}`) `{"foo": "bar"}`, `{"bar": "foo"}`)
} }
func TestBindingJSONUseNumber(t *testing.T) {
testBodyBindingUseNumber(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber2(t *testing.T) {
testBodyBindingUseNumber2(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingForm(t *testing.T) { func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST", testFormBinding(t, "POST",
"/", "/", "/", "/",
@ -67,6 +195,174 @@ func TestBindingForm2(t *testing.T) {
"", "") "", "")
} }
func TestBindingFormForTime(t *testing.T) {
testFormBindingForTime(t, "POST",
"/", "/",
"time_foo=2017-11-15&time_bar=", "bar2=foo")
testFormBindingForTimeNotFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailLocation(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
}
func TestBindingFormForTime2(t *testing.T) {
testFormBindingForTime(t, "GET",
"/?time_foo=2017-11-15&time_bar=", "/?bar2=foo",
"", "")
testFormBindingForTimeNotFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailLocation(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
}
func TestBindingFormInvalidName(t *testing.T) {
testFormBindingInvalidName(t, "POST",
"/", "/",
"test_name=bar", "bar2=foo")
}
func TestBindingFormInvalidName2(t *testing.T) {
testFormBindingInvalidName2(t, "POST",
"/", "/",
"map_foo=bar", "bar2=foo")
}
func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST",
"/", "/",
"map_foo=", "bar2=1", "Map")
testFormBindingForType(t, "POST",
"/", "/",
"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
testFormBindingForType(t, "GET",
"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
"", "", "Slice")
testFormBindingForType(t, "POST",
"/", "/",
"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
testFormBindingForType(t, "GET",
"/?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")
}
func TestBindingQuery(t *testing.T) { func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST", testQueryBinding(t, "POST",
"/?foo=bar&bar=foo", "/", "/?foo=bar&bar=foo", "/",
@ -79,6 +375,18 @@ func TestBindingQuery2(t *testing.T) {
"foo=unused", "") "foo=unused", "")
} }
func TestBindingQueryFail(t *testing.T) {
testQueryBindingFail(t, "POST",
"/?map_foo=", "/",
"map_foo=unused", "bar2=foo")
}
func TestBindingQueryFail2(t *testing.T) {
testQueryBindingFail(t, "GET",
"/?map_foo=", "/?bar2=foo",
"map_foo=unused", "")
}
func TestBindingXML(t *testing.T) { func TestBindingXML(t *testing.T) {
testBodyBinding(t, testBodyBinding(t,
XML, "xml", XML, "xml",
@ -86,12 +394,25 @@ func TestBindingXML(t *testing.T) {
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>") "<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
} }
func TestBindingXMLFail(t *testing.T) {
testBodyBindingFail(t,
XML, "xml",
"/", "/",
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
}
func createFormPostRequest() *http.Request { func createFormPostRequest() *http.Request {
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo")) req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
req.Header.Set("Content-Type", MIMEPOSTForm) req.Header.Set("Content-Type", MIMEPOSTForm)
return req return req
} }
func createFormPostRequestFail() *http.Request {
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=bar"))
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormMultipartRequest() *http.Request { func createFormMultipartRequest() *http.Request {
boundary := "--testboundary" boundary := "--testboundary"
body := new(bytes.Buffer) body := new(bytes.Buffer)
@ -106,24 +427,53 @@ func createFormMultipartRequest() *http.Request {
return req return req
} }
func createFormMultipartRequestFail() *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
mw.SetBoundary(boundary)
mw.WriteField("map_foo", "bar")
req, _ := http.NewRequest("POST", "/?map_foo=getfoo", body)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func TestBindingFormPost(t *testing.T) { func TestBindingFormPost(t *testing.T) {
req := createFormPostRequest() req := createFormPostRequest()
var obj FooBarStruct var obj FooBarStruct
FormPost.Bind(req, &obj) FormPost.Bind(req, &obj)
assert.Equal(t, FormPost.Name(), "form-urlencoded")
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, obj.Foo, "bar")
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, obj.Bar, "foo")
} }
func TestBindingFormPostFail(t *testing.T) {
req := createFormPostRequestFail()
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingFormMultipart(t *testing.T) { func TestBindingFormMultipart(t *testing.T) {
req := createFormMultipartRequest() req := createFormMultipartRequest()
var obj FooBarStruct var obj FooBarStruct
FormMultipart.Bind(req, &obj) FormMultipart.Bind(req, &obj)
assert.Equal(t, FormMultipart.Name(), "multipart/form-data")
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, obj.Foo, "bar")
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, obj.Bar, "foo")
} }
func TestBindingFormMultipartFail(t *testing.T) {
req := createFormMultipartRequestFail()
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingProtoBuf(t *testing.T) { func TestBindingProtoBuf(t *testing.T) {
test := &example.Test{ test := &example.Test{
Label: proto.String("yes"), Label: proto.String("yes"),
@ -136,6 +486,18 @@ func TestBindingProtoBuf(t *testing.T) {
string(data), string(data[1:])) string(data), string(data[1:]))
} }
func TestBindingProtoBufFail(t *testing.T) {
test := &example.Test{
Label: proto.String("yes"),
}
data, _ := proto.Marshal(test)
testProtoBodyBindingFail(t,
ProtoBuf, "protobuf",
"/", "/",
string(data), string(data[1:]))
}
func TestBindingMsgPack(t *testing.T) { func TestBindingMsgPack(t *testing.T) {
test := FooStruct{ test := FooStruct{
Foo: "bar", Foo: "bar",
@ -216,6 +578,323 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
assert.Error(t, err) assert.Error(t, err)
} }
func TestFormBindingFail(t *testing.T) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormPostBindingFail(t *testing.T) {
b := FormPost
assert.Equal(t, b.Name(), "form-urlencoded")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormMultipartBindingFail(t *testing.T) {
b := FormMultipart
assert.Equal(t, b.Name(), "multipart/form-data")
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooBarStructForTimeType{}
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, obj.TimeFoo.Unix(), int64(1510675200))
assert.Equal(t, obj.TimeFoo.Location().String(), "Asia/Chongqing")
assert.Equal(t, obj.TimeBar.Unix(), int64(-62135596800))
assert.Equal(t, obj.TimeBar.Location().String(), "UTC")
obj = FooBarStructForTimeType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeNotFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailLocation{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := InvalidNameType{}
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, obj.TestName, "")
obj = InvalidNameType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, b.Name(), "form")
obj := InvalidNameMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = InvalidNameMapType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) {
b := Form
assert.Equal(t, b.Name(), "form")
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
switch typ {
case "Int":
obj := FooBarStructForIntType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, obj.IntFoo, int(0))
assert.Equal(t, obj.IntBar, int(-12))
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, obj.Int8Foo, int8(0))
assert.Equal(t, obj.Int8Bar, int8(-12))
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, obj.Int16Foo, int16(0))
assert.Equal(t, obj.Int16Bar, int16(-12))
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, obj.Int32Foo, int32(0))
assert.Equal(t, obj.Int32Bar, int32(-12))
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, obj.Int64Foo, int64(0))
assert.Equal(t, obj.Int64Bar, int64(-12))
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, obj.UintFoo, uint(0x0))
assert.Equal(t, obj.UintBar, uint(0xc))
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, obj.Uint8Foo, uint8(0x0))
assert.Equal(t, obj.Uint8Bar, uint8(0xc))
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, obj.Uint16Foo, uint16(0x0))
assert.Equal(t, obj.Uint16Bar, uint16(0xc))
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, obj.Uint32Foo, uint32(0x0))
assert.Equal(t, obj.Uint32Bar, uint32(0xc))
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, obj.Uint64Foo, uint64(0x0))
assert.Equal(t, obj.Uint64Bar, uint64(0xc))
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, obj.Float32Foo, float32(0.0))
assert.Equal(t, obj.Float32Bar, float32(-12.34))
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, obj.Float64Foo, float64(0.0))
assert.Equal(t, obj.Float64Bar, float64(-12.34))
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.Equal(t, obj.BoolFoo, false)
assert.Equal(t, obj.BoolBar, true)
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)
assert.NoError(t, err)
assert.Equal(t, obj.SliceFoo, []int{1, 2})
obj = FooStructForSliceType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Map":
obj := FooStructForMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
case "SliceMap":
obj := FooStructForSliceMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
}
func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) { func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Query b := Query
assert.Equal(t, b.Name(), "query") assert.Equal(t, b.Name(), "query")
@ -231,6 +910,19 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, obj.Bar, "foo")
} }
func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, b.Name(), "query")
obj := FooStructForMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name) assert.Equal(t, b.Name(), name)
@ -246,6 +938,58 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
assert.Error(t, err) assert.Error(t, err)
} }
func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = true
err := b.Bind(req, &obj)
assert.NoError(t, err)
// we hope it is int64(123)
v, e := obj.Foo.(json.Number).Int64()
assert.NoError(t, e)
assert.Equal(t, v, int64(123))
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = false
err := b.Bind(req, &obj)
assert.NoError(t, err)
// it will return float64(123) if not use EnableDecoderUseNumber
// maybe it is not hoped
assert.Equal(t, obj.Foo, float64(123))
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := FooStruct{}
req := requestWithBody("POST", path, body)
err := b.Bind(req, &obj)
assert.Error(t, err)
assert.Equal(t, obj.Foo, "")
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name) assert.Equal(t, b.Name(), name)
@ -263,6 +1007,30 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Error(t, err) assert.Error(t, err)
} }
type hook struct{}
func (h hook) Read([]byte) (int, error) {
return 0, errors.New("error")
}
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name)
obj := example.Test{}
req := requestWithBody("POST", path, body)
req.Body = ioutil.NopCloser(&hook{})
req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = example.Test{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
assert.Error(t, err)
}
func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name) assert.Equal(t, b.Name(), name)

View File

@ -179,12 +179,3 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
value.Set(reflect.ValueOf(t)) value.Set(reflect.ValueOf(t))
return nil return nil
} }
// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
panic("Pointers are not accepted as binding models")
}
}

View File

@ -10,9 +10,7 @@ import (
"github.com/gin-gonic/gin/json" "github.com/gin-gonic/gin/json"
) )
var ( var EnableDecoderUseNumber = false
EnableDecoderUseNumber = false
)
type jsonBinding struct{} type jsonBinding struct{}

View File

@ -4,9 +4,7 @@
package binding package binding
import ( import "net/http"
"net/http"
)
type queryBinding struct{} type queryBinding struct{}

View File

@ -10,9 +10,8 @@ import (
"testing" "testing"
"time" "time"
validator "gopkg.in/go-playground/validator.v8"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/go-playground/validator.v8"
) )
type testInterface interface { type testInterface interface {

View File

@ -33,9 +33,7 @@ const (
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
) )
const ( const abortIndex int8 = math.MaxInt8 / 2
abortIndex int8 = math.MaxInt8 / 2
)
// Context is the most important part of gin. It allows us to pass variables between middleware, // Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example. // manage the flow, validate the JSON of a request and render a JSON response for example.
@ -105,8 +103,7 @@ func (c *Context) Handler() HandlerFunc {
// See example in GitHub. // See example in GitHub.
func (c *Context) Next() { func (c *Context) Next() {
c.index++ c.index++
s := int8(len(c.handlers)) for s := int8(len(c.handlers)); c.index < s; c.index++ {
for ; c.index < s; c.index++ {
c.handlers[c.index](c) c.handlers[c.index](c)
} }
} }
@ -452,10 +449,10 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
// Depending the "Content-Type" header different bindings are used: // Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding // "application/json" --> JSON binding
// "application/xml" --> XML binding // "application/xml" --> XML binding
// otherwise --> returns an error // otherwise --> returns an error.
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // 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 decodes the json payload into the struct specified as a pointer.
// It will writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. // 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 interface{}) error {
b := binding.Default(c.Request.Method, c.ContentType()) b := binding.Default(c.Request.Method, c.ContentType())
return c.MustBindWith(obj, b) return c.MustBindWith(obj, b)
@ -521,11 +518,11 @@ func (c *Context) ClientIP() string {
clientIP = clientIP[0:index] clientIP = clientIP[0:index]
} }
clientIP = strings.TrimSpace(clientIP) clientIP = strings.TrimSpace(clientIP)
if len(clientIP) > 0 { if clientIP != "" {
return clientIP return clientIP
} }
clientIP = strings.TrimSpace(c.requestHeader("X-Real-Ip")) clientIP = strings.TrimSpace(c.requestHeader("X-Real-Ip"))
if len(clientIP) > 0 { if clientIP != "" {
return clientIP return clientIP
} }
} }
@ -588,7 +585,7 @@ func (c *Context) Status(code int) {
// It writes a header in the response. // It writes a header in the response.
// If value == "", this method removes the header `c.Writer.Header().Del(key)` // If value == "", this method removes the header `c.Writer.Header().Del(key)`
func (c *Context) Header(key, value string) { func (c *Context) Header(key, value string) {
if len(value) == 0 { if value == "" {
c.Writer.Header().Del(key) c.Writer.Header().Del(key)
} else { } else {
c.Writer.Header().Set(key, value) c.Writer.Header().Set(key, value)

View File

@ -180,14 +180,14 @@ func TestContextSetGet(t *testing.T) {
c.Set("foo", "bar") c.Set("foo", "bar")
value, err := c.Get("foo") value, err := c.Get("foo")
assert.Equal(t, value, "bar") assert.Equal(t, "bar", value)
assert.True(t, err) assert.True(t, err)
value, err = c.Get("foo2") value, err = c.Get("foo2")
assert.Nil(t, value) assert.Nil(t, value)
assert.False(t, err) assert.False(t, err)
assert.Equal(t, c.MustGet("foo"), "bar") assert.Equal(t, "bar", c.MustGet("foo"))
assert.Panics(t, func() { c.MustGet("no_exist") }) assert.Panics(t, func() { c.MustGet("no_exist") })
} }
@ -221,7 +221,7 @@ func TestContextGetString(t *testing.T) {
func TestContextSetGetBool(t *testing.T) { func TestContextSetGetBool(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Set("bool", true) c.Set("bool", true)
assert.Equal(t, true, c.GetBool("bool")) assert.True(t, c.GetBool("bool"))
} }
func TestContextGetInt(t *testing.T) { func TestContextGetInt(t *testing.T) {
@ -342,26 +342,26 @@ func TestContextQuery(t *testing.T) {
value, ok := c.GetQuery("foo") value, ok := c.GetQuery("foo")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "bar") assert.Equal(t, "bar", value)
assert.Equal(t, c.DefaultQuery("foo", "none"), "bar") assert.Equal(t, "bar", c.DefaultQuery("foo", "none"))
assert.Equal(t, c.Query("foo"), "bar") assert.Equal(t, "bar", c.Query("foo"))
value, ok = c.GetQuery("page") value, ok = c.GetQuery("page")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "10") assert.Equal(t, "10", value)
assert.Equal(t, c.DefaultQuery("page", "0"), "10") assert.Equal(t, "10", c.DefaultQuery("page", "0"))
assert.Equal(t, c.Query("page"), "10") assert.Equal(t, "10", c.Query("page"))
value, ok = c.GetQuery("id") value, ok = c.GetQuery("id")
assert.True(t, ok) assert.True(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Equal(t, c.DefaultQuery("id", "nada"), "") assert.Empty(t, c.DefaultQuery("id", "nada"))
assert.Empty(t, c.Query("id")) assert.Empty(t, c.Query("id"))
value, ok = c.GetQuery("NoKey") value, ok = c.GetQuery("NoKey")
assert.False(t, ok) assert.False(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Equal(t, c.DefaultQuery("NoKey", "nada"), "nada") assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada"))
assert.Empty(t, c.Query("NoKey")) assert.Empty(t, c.Query("NoKey"))
// postform should not mess // postform should not mess
@ -377,29 +377,29 @@ func TestContextQueryAndPostForm(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "/?both=GET&id=main&id=omit&array[]=first&array[]=second", body) c.Request, _ = http.NewRequest("POST", "/?both=GET&id=main&id=omit&array[]=first&array[]=second", body)
c.Request.Header.Add("Content-Type", MIMEPOSTForm) c.Request.Header.Add("Content-Type", MIMEPOSTForm)
assert.Equal(t, c.DefaultPostForm("foo", "none"), "bar") assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
assert.Equal(t, c.PostForm("foo"), "bar") assert.Equal(t, "bar", c.PostForm("foo"))
assert.Empty(t, c.Query("foo")) assert.Empty(t, c.Query("foo"))
value, ok := c.GetPostForm("page") value, ok := c.GetPostForm("page")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "11") assert.Equal(t, "11", value)
assert.Equal(t, c.DefaultPostForm("page", "0"), "11") assert.Equal(t, "11", c.DefaultPostForm("page", "0"))
assert.Equal(t, c.PostForm("page"), "11") assert.Equal(t, "11", c.PostForm("page"))
assert.Equal(t, c.Query("page"), "") assert.Empty(t, c.Query("page"))
value, ok = c.GetPostForm("both") value, ok = c.GetPostForm("both")
assert.True(t, ok) assert.True(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Empty(t, c.PostForm("both")) assert.Empty(t, c.PostForm("both"))
assert.Equal(t, c.DefaultPostForm("both", "nothing"), "") assert.Empty(t, c.DefaultPostForm("both", "nothing"))
assert.Equal(t, c.Query("both"), "GET") assert.Equal(t, "GET", c.Query("both"), "GET")
value, ok = c.GetQuery("id") value, ok = c.GetQuery("id")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "main") assert.Equal(t, "main", value)
assert.Equal(t, c.DefaultPostForm("id", "000"), "000") assert.Equal(t, "000", c.DefaultPostForm("id", "000"))
assert.Equal(t, c.Query("id"), "main") assert.Equal(t, "main", c.Query("id"))
assert.Empty(t, c.PostForm("id")) assert.Empty(t, c.PostForm("id"))
value, ok = c.GetQuery("NoKey") value, ok = c.GetQuery("NoKey")
@ -408,8 +408,8 @@ func TestContextQueryAndPostForm(t *testing.T) {
value, ok = c.GetPostForm("NoKey") value, ok = c.GetPostForm("NoKey")
assert.False(t, ok) assert.False(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Equal(t, c.DefaultPostForm("NoKey", "nada"), "nada") assert.Equal(t, "nada", c.DefaultPostForm("NoKey", "nada"))
assert.Equal(t, c.DefaultQuery("NoKey", "nothing"), "nothing") assert.Equal(t, "nothing", c.DefaultQuery("NoKey", "nothing"))
assert.Empty(t, c.PostForm("NoKey")) assert.Empty(t, c.PostForm("NoKey"))
assert.Empty(t, c.Query("NoKey")) assert.Empty(t, c.Query("NoKey"))
@ -421,11 +421,11 @@ func TestContextQueryAndPostForm(t *testing.T) {
Array []string `form:"array[]"` Array []string `form:"array[]"`
} }
assert.NoError(t, c.Bind(&obj)) assert.NoError(t, c.Bind(&obj))
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo, "bar")
assert.Equal(t, obj.ID, "main") assert.Equal(t, "main", obj.ID, "main")
assert.Equal(t, obj.Page, 11) assert.Equal(t, 11, obj.Page, 11)
assert.Equal(t, obj.Both, "") assert.Empty(t, obj.Both)
assert.Equal(t, obj.Array, []string{"first", "second"}) assert.Equal(t, []string{"first", "second"}, obj.Array)
values, ok := c.GetQueryArray("array[]") values, ok := c.GetQueryArray("array[]")
assert.True(t, ok) assert.True(t, ok)
@ -460,37 +460,37 @@ func TestContextPostFormMultipart(t *testing.T) {
BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"` BlankTime time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
} }
assert.NoError(t, c.Bind(&obj)) assert.NoError(t, c.Bind(&obj))
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, obj.Bar, "10") assert.Equal(t, "10", obj.Bar)
assert.Equal(t, obj.BarAsInt, 10) assert.Equal(t, 10, obj.BarAsInt)
assert.Equal(t, obj.Array, []string{"first", "second"}) assert.Equal(t, []string{"first", "second"}, obj.Array)
assert.Equal(t, obj.ID, "") assert.Empty(t, obj.ID)
assert.Equal(t, obj.TimeLocal.Format("02/01/2006 15:04"), "31/12/2016 14:55") assert.Equal(t, "31/12/2016 14:55", obj.TimeLocal.Format("02/01/2006 15:04"))
assert.Equal(t, obj.TimeLocal.Location(), time.Local) assert.Equal(t, time.Local, obj.TimeLocal.Location())
assert.Equal(t, obj.TimeUTC.Format("02/01/2006 15:04"), "31/12/2016 14:55") assert.Equal(t, "31/12/2016 14:55", obj.TimeUTC.Format("02/01/2006 15:04"))
assert.Equal(t, obj.TimeUTC.Location(), time.UTC) assert.Equal(t, time.UTC, obj.TimeUTC.Location())
loc, _ := time.LoadLocation("Asia/Tokyo") loc, _ := time.LoadLocation("Asia/Tokyo")
assert.Equal(t, obj.TimeLocation.Format("02/01/2006 15:04"), "31/12/2016 14:55") assert.Equal(t, "31/12/2016 14:55", obj.TimeLocation.Format("02/01/2006 15:04"))
assert.Equal(t, obj.TimeLocation.Location(), loc) assert.Equal(t, loc, obj.TimeLocation.Location())
assert.True(t, obj.BlankTime.IsZero()) assert.True(t, obj.BlankTime.IsZero())
value, ok := c.GetQuery("foo") value, ok := c.GetQuery("foo")
assert.False(t, ok) assert.False(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Empty(t, c.Query("bar")) assert.Empty(t, c.Query("bar"))
assert.Equal(t, c.DefaultQuery("id", "nothing"), "nothing") assert.Equal(t, "nothing", c.DefaultQuery("id", "nothing"))
value, ok = c.GetPostForm("foo") value, ok = c.GetPostForm("foo")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "bar") assert.Equal(t, "bar", value)
assert.Equal(t, c.PostForm("foo"), "bar") assert.Equal(t, "bar", c.PostForm("foo"))
value, ok = c.GetPostForm("array") value, ok = c.GetPostForm("array")
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, value, "first") assert.Equal(t, "first", value)
assert.Equal(t, c.PostForm("array"), "first") assert.Equal(t, "first", c.PostForm("array"))
assert.Equal(t, c.DefaultPostForm("bar", "nothing"), "10") assert.Equal(t, "10", c.DefaultPostForm("bar", "nothing"))
value, ok = c.GetPostForm("id") value, ok = c.GetPostForm("id")
assert.True(t, ok) assert.True(t, ok)
@ -501,7 +501,7 @@ func TestContextPostFormMultipart(t *testing.T) {
value, ok = c.GetPostForm("nokey") value, ok = c.GetPostForm("nokey")
assert.False(t, ok) assert.False(t, ok)
assert.Empty(t, value) assert.Empty(t, value)
assert.Equal(t, c.DefaultPostForm("nokey", "nothing"), "nothing") assert.Equal(t, "nothing", c.DefaultPostForm("nokey", "nothing"))
values, ok := c.GetPostFormArray("array") values, ok := c.GetPostFormArray("array")
assert.True(t, ok) assert.True(t, ok)
@ -523,13 +523,13 @@ func TestContextPostFormMultipart(t *testing.T) {
func TestContextSetCookie(t *testing.T) { func TestContextSetCookie(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.SetCookie("user", "gin", 1, "/", "localhost", true, true) c.SetCookie("user", "gin", 1, "/", "localhost", true, true)
assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie"))
} }
func TestContextSetCookiePathEmpty(t *testing.T) { func TestContextSetCookiePathEmpty(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.SetCookie("user", "gin", 1, "", "localhost", true, true) c.SetCookie("user", "gin", 1, "", "localhost", true, true)
assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure", c.Writer.Header().Get("Set-Cookie"))
} }
func TestContextGetCookie(t *testing.T) { func TestContextGetCookie(t *testing.T) {
@ -537,17 +537,17 @@ func TestContextGetCookie(t *testing.T) {
c.Request, _ = http.NewRequest("GET", "/get", nil) c.Request, _ = http.NewRequest("GET", "/get", nil)
c.Request.Header.Set("Cookie", "user=gin") c.Request.Header.Set("Cookie", "user=gin")
cookie, _ := c.Cookie("user") cookie, _ := c.Cookie("user")
assert.Equal(t, cookie, "gin") assert.Equal(t, "gin", cookie)
_, err := c.Cookie("nokey") _, err := c.Cookie("nokey")
assert.Error(t, err) assert.Error(t, err)
} }
func TestContextBodyAllowedForStatus(t *testing.T) { func TestContextBodyAllowedForStatus(t *testing.T) {
assert.Equal(t, false, bodyAllowedForStatus(102)) assert.False(t, false, bodyAllowedForStatus(102))
assert.Equal(t, false, bodyAllowedForStatus(204)) assert.False(t, false, bodyAllowedForStatus(204))
assert.Equal(t, false, bodyAllowedForStatus(304)) assert.False(t, false, bodyAllowedForStatus(304))
assert.Equal(t, true, bodyAllowedForStatus(500)) assert.True(t, true, bodyAllowedForStatus(500))
} }
type TestPanicRender struct { type TestPanicRender struct {
@ -593,7 +593,7 @@ func TestContextRenderNoContentJSON(t *testing.T) {
c.JSON(204, H{"foo": "bar"}) c.JSON(204, H{"foo": "bar"})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
@ -620,7 +620,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) {
c.JSON(204, H{"foo": "bar"}) c.JSON(204, H{"foo": "bar"})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/vnd.api+json") assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/vnd.api+json")
} }
@ -632,7 +632,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) c.IndentedJSON(201, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String()) assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
@ -645,7 +645,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) {
c.IndentedJSON(204, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) c.IndentedJSON(204, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
@ -658,9 +658,9 @@ func TestContextRenderSecureJSON(t *testing.T) {
router.SecureJsonPrefix("&&&START&&&") router.SecureJsonPrefix("&&&START&&&")
c.SecureJSON(201, []string{"foo", "bar"}) c.SecureJSON(201, []string{"foo", "bar"})
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "&&&START&&&[\"foo\",\"bar\"]") assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no Custom JSON is rendered if code is 204 // Tests that no Custom JSON is rendered if code is 204
@ -671,8 +671,8 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
c.SecureJSON(204, []string{"foo", "bar"}) c.SecureJSON(204, []string{"foo", "bar"})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that the response executes the templates // Tests that the response executes the templates
@ -680,14 +680,39 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
func TestContextRenderHTML(t *testing.T) { func TestContextRenderHTML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, router := CreateTestContext(w) c, router := CreateTestContext(w)
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ) router.SetHTMLTemplate(templ)
c.HTML(201, "t", H{"name": "alexandernyquist"}) c.HTML(201, "t", H{"name": "alexandernyquist"})
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "Hello alexandernyquist") assert.Equal(t, "Hello alexandernyquist", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
}
func TestContextRenderHTML2(t *testing.T) {
w := httptest.NewRecorder()
c, router := CreateTestContext(w)
// print debug warning log when Engine.trees > 0
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 1)
var b bytes.Buffer
setup(&b)
defer teardown()
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ)
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", b.String())
c.HTML(201, "t", H{"name": "alexandernyquist"})
assert.Equal(t, 201, w.Code)
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no HTML is rendered if code is 204 // Tests that no HTML is rendered if code is 204
@ -700,8 +725,8 @@ func TestContextRenderNoContentHTML(t *testing.T) {
c.HTML(204, "t", H{"name": "alexandernyquist"}) c.HTML(204, "t", H{"name": "alexandernyquist"})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestContextXML tests that the response is serialized as XML // TestContextXML tests that the response is serialized as XML
@ -712,9 +737,9 @@ func TestContextRenderXML(t *testing.T) {
c.XML(201, H{"foo": "bar"}) c.XML(201, H{"foo": "bar"})
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "<map><foo>bar</foo></map>") assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8") assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no XML is rendered if code is 204 // Tests that no XML is rendered if code is 204
@ -725,8 +750,8 @@ func TestContextRenderNoContentXML(t *testing.T) {
c.XML(204, H{"foo": "bar"}) c.XML(204, H{"foo": "bar"})
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8") assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestContextString tests that the response is returned // TestContextString tests that the response is returned
@ -737,9 +762,9 @@ func TestContextRenderString(t *testing.T) {
c.String(201, "test %s %d", "string", 2) c.String(201, "test %s %d", "string", 2)
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "test string 2") assert.Equal(t, "test string 2", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no String is rendered if code is 204 // Tests that no String is rendered if code is 204
@ -750,8 +775,8 @@ func TestContextRenderNoContentString(t *testing.T) {
c.String(204, "test %s %d", "string", 2) c.String(204, "test %s %d", "string", 2)
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestContextString tests that the response is returned // TestContextString tests that the response is returned
@ -763,9 +788,9 @@ func TestContextRenderHTMLString(t *testing.T) {
c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Content-Type", "text/html; charset=utf-8")
c.String(201, "<html>%s %d</html>", "string", 3) c.String(201, "<html>%s %d</html>", "string", 3)
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "<html>string 3</html>") assert.Equal(t, "<html>string 3</html>", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no HTML String is rendered if code is 204 // Tests that no HTML String is rendered if code is 204
@ -777,8 +802,8 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
c.String(204, "<html>%s %d</html>", "string", 3) c.String(204, "<html>%s %d</html>", "string", 3)
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestContextData tests that the response can be written from `bytesting` // TestContextData tests that the response can be written from `bytesting`
@ -789,9 +814,9 @@ func TestContextRenderData(t *testing.T) {
c.Data(201, "text/csv", []byte(`foo,bar`)) c.Data(201, "text/csv", []byte(`foo,bar`))
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "foo,bar") assert.Equal(t, "foo,bar", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/csv") assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type"))
} }
// Tests that no Custom Data is rendered if code is 204 // Tests that no Custom Data is rendered if code is 204
@ -802,8 +827,8 @@ func TestContextRenderNoContentData(t *testing.T) {
c.Data(204, "text/csv", []byte(`foo,bar`)) c.Data(204, "text/csv", []byte(`foo,bar`))
assert.Equal(t, 204, w.Code) assert.Equal(t, 204, w.Code)
assert.Equal(t, "", w.Body.String()) assert.Empty(t, w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/csv") assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type"))
} }
func TestContextRenderSSE(t *testing.T) { func TestContextRenderSSE(t *testing.T) {
@ -830,9 +855,9 @@ func TestContextRenderFile(t *testing.T) {
c.Request, _ = http.NewRequest("GET", "/", nil) c.Request, _ = http.NewRequest("GET", "/", nil)
c.File("./gin.go") c.File("./gin.go")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {") assert.Contains(t, w.Body.String(), "func New() *Engine {")
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestContextRenderYAML tests that the response is serialized as YAML // TestContextRenderYAML tests that the response is serialized as YAML
@ -843,9 +868,9 @@ func TestContextRenderYAML(t *testing.T) {
c.YAML(201, H{"foo": "bar"}) c.YAML(201, H{"foo": "bar"})
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Body.String(), "foo: bar\n") assert.Equal(t, "foo: bar\n", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/x-yaml; charset=utf-8") assert.Equal(t, "application/x-yaml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
func TestContextHeaders(t *testing.T) { func TestContextHeaders(t *testing.T) {
@ -853,13 +878,13 @@ func TestContextHeaders(t *testing.T) {
c.Header("Content-Type", "text/plain") c.Header("Content-Type", "text/plain")
c.Header("X-Custom", "value") c.Header("X-Custom", "value")
assert.Equal(t, c.Writer.Header().Get("Content-Type"), "text/plain") assert.Equal(t, "text/plain", c.Writer.Header().Get("Content-Type"))
assert.Equal(t, c.Writer.Header().Get("X-Custom"), "value") assert.Equal(t, "value", c.Writer.Header().Get("X-Custom"))
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")
c.Header("X-Custom", "") c.Header("X-Custom", "")
assert.Equal(t, c.Writer.Header().Get("Content-Type"), "text/html") assert.Equal(t, "text/html", c.Writer.Header().Get("Content-Type"))
_, exist := c.Writer.Header()["X-Custom"] _, exist := c.Writer.Header()["X-Custom"]
assert.False(t, exist) assert.False(t, exist)
} }
@ -875,8 +900,8 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
c.Redirect(301, "/path") c.Redirect(301, "/path")
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
assert.Equal(t, w.Code, 301) assert.Equal(t, 301, w.Code)
assert.Equal(t, w.Header().Get("Location"), "/path") assert.Equal(t, "/path", w.Header().Get("Location"))
} }
func TestContextRenderRedirectWithAbsolutePath(t *testing.T) { func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
@ -887,8 +912,8 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
c.Redirect(302, "http://google.com") c.Redirect(302, "http://google.com")
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
assert.Equal(t, w.Code, 302) assert.Equal(t, 302, w.Code)
assert.Equal(t, w.Header().Get("Location"), "http://google.com") assert.Equal(t, "http://google.com", w.Header().Get("Location"))
} }
func TestContextRenderRedirectWith201(t *testing.T) { func TestContextRenderRedirectWith201(t *testing.T) {
@ -899,8 +924,8 @@ func TestContextRenderRedirectWith201(t *testing.T) {
c.Redirect(201, "/resource") c.Redirect(201, "/resource")
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
assert.Equal(t, w.Code, 201) assert.Equal(t, 201, w.Code)
assert.Equal(t, w.Header().Get("Location"), "/resource") assert.Equal(t, "/resource", w.Header().Get("Location"))
} }
func TestContextRenderRedirectAll(t *testing.T) { func TestContextRenderRedirectAll(t *testing.T) {
@ -981,8 +1006,8 @@ func TestContextNegotiationFormat(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest("POST", "", nil)
assert.Panics(t, func() { c.NegotiateFormat() }) assert.Panics(t, func() { c.NegotiateFormat() })
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON) assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML) assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
} }
func TestContextNegotiationFormatWithAccept(t *testing.T) { func TestContextNegotiationFormatWithAccept(t *testing.T) {
@ -990,9 +1015,9 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEXML) assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEHTML) assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEXML, MIMEHTML))
assert.Equal(t, c.NegotiateFormat(MIMEJSON), "") assert.Empty(t, c.NegotiateFormat(MIMEJSON))
} }
func TestContextNegotiationFormatCustum(t *testing.T) { func TestContextNegotiationFormatCustum(t *testing.T) {
@ -1003,9 +1028,9 @@ func TestContextNegotiationFormatCustum(t *testing.T) {
c.Accepted = nil c.Accepted = nil
c.SetAccepted(MIMEJSON, MIMEXML) c.SetAccepted(MIMEJSON, MIMEXML)
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON) assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEXML) assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON) assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
} }
func TestContextIsAborted(t *testing.T) { func TestContextIsAborted(t *testing.T) {
@ -1031,9 +1056,9 @@ func TestContextAbortWithStatus(t *testing.T) {
c.index = 4 c.index = 4
c.AbortWithStatus(401) c.AbortWithStatus(401)
assert.Equal(t, c.index, abortIndex) assert.Equal(t, abortIndex, c.index)
assert.Equal(t, c.Writer.Status(), 401) assert.Equal(t, 401, c.Writer.Status())
assert.Equal(t, w.Code, 401) assert.Equal(t, 401, w.Code)
assert.True(t, c.IsAborted()) assert.True(t, c.IsAborted())
} }
@ -1053,13 +1078,13 @@ func TestContextAbortWithStatusJSON(t *testing.T) {
c.AbortWithStatusJSON(415, in) c.AbortWithStatusJSON(415, in)
assert.Equal(t, c.index, abortIndex) assert.Equal(t, abortIndex, c.index)
assert.Equal(t, c.Writer.Status(), 415) assert.Equal(t, 415, c.Writer.Status())
assert.Equal(t, w.Code, 415) assert.Equal(t, 415, w.Code)
assert.True(t, c.IsAborted()) assert.True(t, c.IsAborted())
contentType := w.Header().Get("Content-Type") contentType := w.Header().Get("Content-Type")
assert.Equal(t, contentType, "application/json; charset=utf-8") assert.Equal(t, "application/json; charset=utf-8", contentType)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(w.Body) buf.ReadFrom(w.Body)
@ -1073,7 +1098,7 @@ func TestContextError(t *testing.T) {
c.Error(errors.New("first error")) c.Error(errors.New("first error"))
assert.Len(t, c.Errors, 1) assert.Len(t, c.Errors, 1)
assert.Equal(t, c.Errors.String(), "Error #01: first error\n") assert.Equal(t, "Error #01: first error\n", c.Errors.String())
c.Error(&Error{ c.Error(&Error{
Err: errors.New("second error"), Err: errors.New("second error"),
@ -1082,13 +1107,13 @@ func TestContextError(t *testing.T) {
}) })
assert.Len(t, c.Errors, 2) assert.Len(t, c.Errors, 2)
assert.Equal(t, c.Errors[0].Err, errors.New("first error")) assert.Equal(t, errors.New("first error"), c.Errors[0].Err)
assert.Nil(t, c.Errors[0].Meta) assert.Nil(t, c.Errors[0].Meta)
assert.Equal(t, c.Errors[0].Type, ErrorTypePrivate) assert.Equal(t, ErrorTypePrivate, c.Errors[0].Type)
assert.Equal(t, c.Errors[1].Err, errors.New("second error")) assert.Equal(t, errors.New("second error"), c.Errors[1].Err)
assert.Equal(t, c.Errors[1].Meta, "some data 2") assert.Equal(t, "some data 2", c.Errors[1].Meta)
assert.Equal(t, c.Errors[1].Type, ErrorTypePublic) assert.Equal(t, ErrorTypePublic, c.Errors[1].Type)
assert.Equal(t, c.Errors.Last(), c.Errors[1]) assert.Equal(t, c.Errors.Last(), c.Errors[1])
@ -1106,12 +1131,12 @@ func TestContextTypedError(t *testing.T) {
c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate)
for _, err := range c.Errors.ByType(ErrorTypePublic) { for _, err := range c.Errors.ByType(ErrorTypePublic) {
assert.Equal(t, err.Type, ErrorTypePublic) assert.Equal(t, ErrorTypePublic, err.Type)
} }
for _, err := range c.Errors.ByType(ErrorTypePrivate) { for _, err := range c.Errors.ByType(ErrorTypePrivate) {
assert.Equal(t, err.Type, ErrorTypePrivate) assert.Equal(t, ErrorTypePrivate, err.Type)
} }
assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "interno 0"}) assert.Equal(t, []string{"externo 0", "interno 0"}, c.Errors.Errors())
} }
func TestContextAbortWithError(t *testing.T) { func TestContextAbortWithError(t *testing.T) {
@ -1120,8 +1145,8 @@ func TestContextAbortWithError(t *testing.T) {
c.AbortWithError(401, errors.New("bad input")).SetMeta("some input") c.AbortWithError(401, errors.New("bad input")).SetMeta("some input")
assert.Equal(t, w.Code, 401) assert.Equal(t, 401, w.Code)
assert.Equal(t, c.index, abortIndex) assert.Equal(t, abortIndex, c.index)
assert.True(t, c.IsAborted()) assert.True(t, c.IsAborted())
} }
@ -1152,7 +1177,7 @@ func TestContextClientIP(t *testing.T) {
// no port // no port
c.Request.RemoteAddr = "50.50.50.50" c.Request.RemoteAddr = "50.50.50.50"
assert.Equal(t, "", c.ClientIP()) assert.Empty(t, c.ClientIP())
} }
func TestContextContentType(t *testing.T) { func TestContextContentType(t *testing.T) {
@ -1160,7 +1185,7 @@ func TestContextContentType(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Set("Content-Type", "application/json; charset=utf-8") c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
assert.Equal(t, c.ContentType(), "application/json") assert.Equal(t, "application/json", c.ContentType())
} }
func TestContextAutoBindJSON(t *testing.T) { func TestContextAutoBindJSON(t *testing.T) {
@ -1173,8 +1198,8 @@ func TestContextAutoBindJSON(t *testing.T) {
Bar string `json:"bar"` Bar string `json:"bar"`
} }
assert.NoError(t, c.Bind(&obj)) assert.NoError(t, c.Bind(&obj))
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo)
assert.Empty(t, c.Errors) assert.Empty(t, c.Errors)
} }
@ -1190,9 +1215,9 @@ func TestContextBindWithJSON(t *testing.T) {
Bar string `json:"bar"` Bar string `json:"bar"`
} }
assert.NoError(t, c.BindJSON(&obj)) assert.NoError(t, c.BindJSON(&obj))
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, w.Body.Len(), 0) assert.Equal(t, 0, w.Body.Len())
} }
func TestContextBindWithQuery(t *testing.T) { func TestContextBindWithQuery(t *testing.T) {
@ -1228,7 +1253,7 @@ func TestContextBadAutoBind(t *testing.T) {
assert.Empty(t, obj.Bar) assert.Empty(t, obj.Bar)
assert.Empty(t, obj.Foo) assert.Empty(t, obj.Foo)
assert.Equal(t, w.Code, 400) assert.Equal(t, 400, w.Code)
assert.True(t, c.IsAborted()) assert.True(t, c.IsAborted())
} }
@ -1242,8 +1267,8 @@ func TestContextAutoShouldBindJSON(t *testing.T) {
Bar string `json:"bar"` Bar string `json:"bar"`
} }
assert.NoError(t, c.ShouldBind(&obj)) assert.NoError(t, c.ShouldBind(&obj))
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo)
assert.Empty(t, c.Errors) assert.Empty(t, c.Errors)
} }
@ -1259,9 +1284,9 @@ func TestContextShouldBindWithJSON(t *testing.T) {
Bar string `json:"bar"` Bar string `json:"bar"`
} }
assert.NoError(t, c.ShouldBindJSON(&obj)) assert.NoError(t, c.ShouldBindJSON(&obj))
assert.Equal(t, obj.Bar, "foo") assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, w.Body.Len(), 0) assert.Equal(t, 0, w.Body.Len())
} }
func TestContextShouldBindWithQuery(t *testing.T) { func TestContextShouldBindWithQuery(t *testing.T) {
@ -1311,7 +1336,7 @@ func TestContextGolangContext(t *testing.T) {
assert.Nil(t, c.Value("foo")) assert.Nil(t, c.Value("foo"))
c.Set("foo", "bar") c.Set("foo", "bar")
assert.Equal(t, c.Value("foo"), "bar") assert.Equal(t, "bar", c.Value("foo"))
assert.Nil(t, c.Value(1)) assert.Nil(t, c.Value(1))
} }
@ -1343,7 +1368,7 @@ func TestGetRequestHeaderValue(t *testing.T) {
c.Request.Header.Set("Gin-Version", "1.0.0") c.Request.Header.Set("Gin-Version", "1.0.0")
assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version")) assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version"))
assert.Equal(t, "", c.GetHeader("Connection")) assert.Empty(t, c.GetHeader("Connection"))
} }
func TestContextGetRawData(t *testing.T) { func TestContextGetRawData(t *testing.T) {

13
coverage.sh Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
echo "mode: count" > coverage.out
for d in $(go list ./... | grep -E 'gin$|binding$|render$'); do
go test -v -covermode=count -coverprofile=profile.out $d
if [ -f profile.out ]; then
cat profile.out | grep -v "mode:" >> coverage.out
rm profile.out
fi
done

View File

@ -19,7 +19,7 @@ func init() {
} }
// IsDebugging returns true if the framework is running in debug mode. // IsDebugging returns true if the framework is running in debug mode.
// Use SetMode(gin.Release) to disable debug mode. // Use SetMode(gin.ReleaseMode) to disable debug mode.
func IsDebugging() bool { func IsDebugging() bool {
return ginMode == debugCode return ginMode == debugCode
} }

31
deprecated_test.go Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2014 Manu Martinez-Almeida. 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 (
"bytes"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin/binding"
"github.com/stretchr/testify/assert"
)
func TestBindWith(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
var obj struct {
Foo string `form:"foo"`
Bar string `form:"bar"`
}
assert.NoError(t, c.BindWith(&obj, binding.Form))
assert.Equal(t, "foo", obj.Bar)
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, 0, w.Body.Len())
}

View File

@ -148,7 +148,7 @@ func (a errorMsgs) String() string {
} }
var buffer bytes.Buffer var buffer bytes.Buffer
for i, msg := range a { for i, msg := range a {
fmt.Fprintf(&buffer, "Error #%02d: %s\n", (i + 1), msg.Err) fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err)
if msg.Meta != nil { if msg.Meta != nil {
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
} }

View File

@ -6,7 +6,7 @@ import (
var DB = make(map[string]string) var DB = make(map[string]string)
func main() { func setupRouter() *gin.Engine {
// Disable Console Color // Disable Console Color
// gin.DisableConsoleColor() // gin.DisableConsoleColor()
r := gin.Default() r := gin.Default()
@ -53,6 +53,11 @@ func main() {
} }
}) })
return r
}
func main() {
r := setupRouter()
// Listen and Server in 0.0.0.0:8080 // Listen and Server in 0.0.0.0:8080
r.Run(":8080") r.Run(":8080")
} }

View File

@ -0,0 +1,20 @@
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}

View File

@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
validator "gopkg.in/go-playground/validator.v8" "gopkg.in/go-playground/validator.v8"
) )
type Booking struct { type Booking struct {

View File

@ -29,8 +29,8 @@ func statsWorker() {
"timestamp": uint64(time.Now().Unix()), "timestamp": uint64(time.Now().Unix()),
"HeapInuse": stats.HeapInuse, "HeapInuse": stats.HeapInuse,
"StackInuse": stats.StackInuse, "StackInuse": stats.StackInuse,
"Mallocs": (stats.Mallocs - lastMallocs), "Mallocs": stats.Mallocs - lastMallocs,
"Frees": (stats.Frees - lastFrees), "Frees": stats.Frees - lastFrees,
"Inbound": uint64(messages.Get("inbound")), "Inbound": uint64(messages.Get("inbound")),
"Outbound": uint64(messages.Get("outbound")), "Outbound": uint64(messages.Get("outbound")),
"Connected": connectedUsers(), "Connected": connectedUsers(),

18
fixtures/testdata/cert.pem vendored Normal file
View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC9DCCAdygAwIBAgIQUNSK+OxWHYYFxHVJV0IlpDANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MTExNjEyMDA0N1oXDTE4MTExNjEyMDA0
N1owEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKmyj/YZpD59Bpy4w3qf6VzMw9uUBsWp+IP4kl7z5cmGHYUHn/YopTLH
vR23GAB12p6Km5QWzCBuJF4j61PJXHfg3/rjShZ77JcQ3kzxuy1iKDI+DNKN7Klz
rdjJ49QD0lczZHeBvvCL7JsJFKFjGy62rnStuW8LaIEdtjXT+GUZTxJh6G7yPYfD
MS1IsdMQGOdbGwNa+qogMuQPh0TzHw+C73myKrjY6pREijknMC/rnIMz9dLPt6Kl
xXy4br443dpY6dYGIhDuKhROT+vZ05HKasuuQUFhY7v/KoUpEZMB9rfUSzjQ5fak
eDUAMniXRcd+DmwvboG2TI6ixmuPK+ECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgWg
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDwYDVR0RBAgwBocE
fwAAATANBgkqhkiG9w0BAQsFAAOCAQEAMXOLvj7BFsxdbcfRPBd0OFrH/8lI7vPV
LRcJ6r5iv0cnNvZXXbIOQLbg4clJAWjoE08nRm1KvNXhKdns0ELEV86YN2S6jThq
rIGrBqKtaJLB3M9BtDSiQ6SGPLYrWvmhj3Avi8PbSGy51bpGbqopd16j6LYU7Cp2
TefMRlOAFtHojpCVon1CMpqcNxS0WNlQ3lUBSrw3HB0o12x++roja2ibF54tSHXB
KUuadoEzN+mMBwenEBychmAGzdiG4GQHRmhigh85+mtW6UMGiqyCZHs0EgE9FCLL
sRrsTI/VOzLz6lluygXkOsXrP+PP0SvmE3eylWjj9e2nj/u/Cy2YKg==
-----END CERTIFICATE-----

27
fixtures/testdata/key.pem vendored Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAqbKP9hmkPn0GnLjDep/pXMzD25QGxan4g/iSXvPlyYYdhQef
9iilMse9HbcYAHXanoqblBbMIG4kXiPrU8lcd+Df+uNKFnvslxDeTPG7LWIoMj4M
0o3sqXOt2Mnj1APSVzNkd4G+8IvsmwkUoWMbLraudK25bwtogR22NdP4ZRlPEmHo
bvI9h8MxLUix0xAY51sbA1r6qiAy5A+HRPMfD4LvebIquNjqlESKOScwL+ucgzP1
0s+3oqXFfLhuvjjd2ljp1gYiEO4qFE5P69nTkcpqy65BQWFju/8qhSkRkwH2t9RL
ONDl9qR4NQAyeJdFx34ObC9ugbZMjqLGa48r4QIDAQABAoIBAD5mhd+GMEo2KU9J
9b/Ku8I/HapJtW/L/7Fvn0tBPncrVQGM+zpGWfDhV95sbGwG6lwwNeNvuqIWPlNL
vAY0XkdKrrIQEDdSXH50WnpKzXxzwrou7QIj5Cmvevbjzl4xBZDBOilj0XWczmV4
IljyG5XC4UXQeAaoWEZaSZ1jk8yAt2Zq1Hgg7HqhHsK/arWXBgax+4K5nV/s9gZx
yjKU9mXTIs7k/aNnZqwQKqcZF+l3mvbZttOaFwsP14H0I8OFWhnM9hie54Dejqxi
f4/llNxDqUs6lqJfP3qNxtORLcFe75M+Yl8v7g2hkjtLdZBakPzSTEx3TAK/UHgi
aM8DdxECgYEA3fmg/PI4EgUEj0C3SCmQXR/CnQLMUQgb54s0asp4akvp+M7YCcr1
pQd3HFUpBwhBcJg5LeSe87vLupY7pHCKk56cl9WY6hse0b9sP/7DWJuGiO62m0E0
vNjQ2jpG99oR2ROIHHeWsGCpGLmrRT/kY+vR3M+AOLZniXlOCw8k0aUCgYEAw7WL
XFWLxgZYQYilywqrQmfv1MBfaUCvykO6oWB+f6mmnihSFjecI+nDw/b3yXVYGEgy
0ebkuw0jP8suC8wBqX9WuXj+9nZNomJRssJyOMiEhDEqUiTztFPSp9pdruoakLTh
Wk1p9NralOqGPUmxpXlFKVmYRTUbluikVxDypI0CgYBn6sqEQH0hann0+o4TWWn9
PrYkPUAbm1k8771tVTZERR/W3Dbldr/DL5iCihe39BR2urziEEqdvkglJNntJMar
TzDuIBADYQjvltb9qq4XGFBGYMLaMg+XbUVxNKEuvUdnwa4R7aZ9EfN34MwekkfA
w5Cu9/GGG1ajVEfGA6PwBQKBgA3o71jGs8KFXOx7e90sivOTU5Z5fc6LTHNB0Rf7
NcJ5GmCPWRY/KZfb25AoE4B8GKDRMNt+X69zxZeZJ1KrU0rqxA02rlhyHB54gnoE
G/4xMkn6/JkOC0w70PMhMBtohC7YzFOQwQEoNPT0nkno3Pl33xSLS6lPlwBo1JVj
nPtZAoGACXNLXYkR5vexE+w6FGl59r4RQhu1XU8Mr5DIHeB7kXPN3RKbS201M+Tb
SB5jbu0iDV477XkzSNmhaksFf2wM9MT6CaE+8n3UU5tMa+MmBGgwYTp/i9HkqVh5
jjpJifn1VWBINd4cpNzwCg9LXoo0tbtUPWwGzqVeyo/YE5GIHGo=
-----END RSA PRIVATE KEY-----

34
gin.go
View File

@ -14,8 +14,8 @@ import (
"github.com/gin-gonic/gin/render" "github.com/gin-gonic/gin/render"
) )
// Version is Framework's version.
const ( const (
// Version is Framework's version.
Version = "v1.2" Version = "v1.2"
defaultMultipartMemory = 32 << 20 // 32 MB defaultMultipartMemory = 32 << 20 // 32 MB
) )
@ -49,16 +49,6 @@ type RoutesInfo []RouteInfo
// Create an instance of Engine, by using New() or Default() // Create an instance of Engine, by using New() or Default()
type Engine struct { type Engine struct {
RouterGroup RouterGroup
delims render.Delims
secureJsonPrefix string
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
// Enables automatic redirection if the current route can't be matched but a // Enables automatic redirection if the current route can't be matched but a
// handler for the path with (without) the trailing slash exists. // handler for the path with (without) the trailing slash exists.
@ -102,6 +92,17 @@ type Engine struct {
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
// method call. // method call.
MaxMultipartMemory int64 MaxMultipartMemory int64
delims render.Delims
secureJsonPrefix string
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
} }
var _ IRouter = &Engine{} var _ IRouter = &Engine{}
@ -159,11 +160,14 @@ func (engine *Engine) Delims(left, right string) *Engine {
return engine return engine
} }
// SecureJsonPrefix sets the secureJsonPrefix used in Context.SecureJSON.
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine { func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
engine.secureJsonPrefix = prefix engine.secureJsonPrefix = prefix
return engine return engine
} }
// LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
left := engine.delims.Left left := engine.delims.Left
right := engine.delims.Right right := engine.delims.Right
@ -178,6 +182,8 @@ func (engine *Engine) LoadHTMLGlob(pattern string) {
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
// LoadHTMLFiles loads a slice of HTML files
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLFiles(files ...string) { func (engine *Engine) LoadHTMLFiles(files ...string) {
if IsDebugging() { if IsDebugging() {
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims} engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
@ -188,6 +194,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) {
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
// SetHTMLTemplate associate a template with HTML renderer.
func (engine *Engine) SetHTMLTemplate(templ *template.Template) { func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
if len(engine.trees) > 0 { if len(engine.trees) > 0 {
debugPrintWARNINGSetHTMLTemplate() debugPrintWARNINGSetHTMLTemplate()
@ -196,6 +203,7 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)} engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
} }
// SetFuncMap sets the FuncMap used for template.FuncMap.
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
engine.FuncMap = funcMap engine.FuncMap = funcMap
} }
@ -403,8 +411,8 @@ func redirectTrailingSlash(c *Context) {
code = 307 code = 307
} }
if len(path) > 1 && path[len(path)-1] == '/' { if length := len(path); length > 1 && path[length-1] == '/' {
req.URL.Path = path[:len(path)-1] req.URL.Path = path[:length-1]
} else { } else {
req.URL.Path = path + "/" req.URL.Path = path + "/"
} }

View File

@ -94,7 +94,7 @@ func TestUnixSocket(t *testing.T) {
c, err := net.Dial("unix", "/tmp/unix_unit_test") c, err := net.Dial("unix", "/tmp/unix_unit_test")
assert.NoError(t, err) assert.NoError(t, err)
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n") fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
scanner := bufio.NewScanner(c) scanner := bufio.NewScanner(c)
var response string var response string
for scanner.Scan() { for scanner.Scan() {

View File

@ -5,6 +5,7 @@
package gin package gin
import ( import (
"crypto/tls"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
@ -21,9 +22,9 @@ func formatAsDate(t time.Time) string {
return fmt.Sprintf("%d/%02d/%02d", year, month, day) return fmt.Sprintf("%d/%02d/%02d", year, month, day)
} }
func setupHTMLFiles(t *testing.T) func() { func setupHTMLFiles(t *testing.T, mode string, tls bool) func() {
go func() { go func() {
SetMode(TestMode) SetMode(mode)
router := New() router := New()
router.Delims("{[{", "}]}") router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{ router.SetFuncMap(template.FuncMap{
@ -38,16 +39,21 @@ func setupHTMLFiles(t *testing.T) func() {
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
}) })
}) })
if tls {
// these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1`
router.RunTLS(":9999", "./fixtures/testdata/cert.pem", "./fixtures/testdata/key.pem")
} else {
router.Run(":8888") router.Run(":8888")
}
}() }()
t.Log("waiting 1 second for server startup") t.Log("waiting 1 second for server startup")
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
return func() {} return func() {}
} }
func setupHTMLGlob(t *testing.T) func() { func setupHTMLGlob(t *testing.T, mode string, tls bool) func() {
go func() { go func() {
SetMode(DebugMode) SetMode(mode)
router := New() router := New()
router.Delims("{[{", "}]}") router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{ router.SetFuncMap(template.FuncMap{
@ -62,16 +68,20 @@ func setupHTMLGlob(t *testing.T) func() {
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
}) })
}) })
if tls {
// these files generated by `go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1`
router.RunTLS(":9999", "./fixtures/testdata/cert.pem", "./fixtures/testdata/key.pem")
} else {
router.Run(":8888") router.Run(":8888")
}
}() }()
t.Log("waiting 1 second for server startup") t.Log("waiting 1 second for server startup")
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
return func() {} return func() {}
} }
//TODO
func TestLoadHTMLGlob(t *testing.T) { func TestLoadHTMLGlob(t *testing.T) {
td := setupHTMLGlob(t) td := setupHTMLGlob(t, DebugMode, false)
res, err := http.Get("http://127.0.0.1:8888/test") res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -83,9 +93,55 @@ func TestLoadHTMLGlob(t *testing.T) {
td() td()
} }
func TestLoadHTMLGlob2(t *testing.T) {
td := setupHTMLGlob(t, TestMode, false)
res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLGlob3(t *testing.T) {
td := setupHTMLGlob(t, ReleaseMode, false)
res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLGlobUsingTLS(t *testing.T) {
td := setupHTMLGlob(t, DebugMode, true)
// Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: tr}
res, err := client.Get("https://127.0.0.1:9999/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLGlobFromFuncMap(t *testing.T) { func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
time.Now() time.Now()
td := setupHTMLGlob(t) td := setupHTMLGlob(t, DebugMode, false)
res, err := http.Get("http://127.0.0.1:8888/raw") res, err := http.Get("http://127.0.0.1:8888/raw")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -97,9 +153,6 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
td() td()
} }
// func (engine *Engine) LoadHTMLFiles(files ...string) {
// func (engine *Engine) RunTLS(addr string, cert string, key string) error {
func init() { func init() {
SetMode(TestMode) SetMode(TestMode)
} }
@ -117,17 +170,17 @@ func TestCreateEngine(t *testing.T) {
// router.LoadHTMLGlob("*.testtmpl") // router.LoadHTMLGlob("*.testtmpl")
// r := router.HTMLRender.(render.HTMLDebug) // r := router.HTMLRender.(render.HTMLDebug)
// assert.Empty(t, r.Files) // assert.Empty(t, r.Files)
// assert.Equal(t, r.Glob, "*.testtmpl") // assert.Equal(t, "*.testtmpl", r.Glob)
// //
// router.LoadHTMLFiles("index.html.testtmpl", "login.html.testtmpl") // router.LoadHTMLFiles("index.html.testtmpl", "login.html.testtmpl")
// r = router.HTMLRender.(render.HTMLDebug) // r = router.HTMLRender.(render.HTMLDebug)
// assert.Empty(t, r.Glob) // assert.Empty(t, r.Glob)
// assert.Equal(t, r.Files, []string{"index.html", "login.html"}) // assert.Equal(t, []string{"index.html", "login.html"}, r.Files)
// SetMode(TestMode) // SetMode(TestMode)
// } // }
func TestLoadHTMLFiles(t *testing.T) { func TestLoadHTMLFiles(t *testing.T) {
td := setupHTMLFiles(t) td := setupHTMLFiles(t, TestMode, false)
res, err := http.Get("http://127.0.0.1:8888/test") res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -138,9 +191,52 @@ func TestLoadHTMLFiles(t *testing.T) {
td() td()
} }
func TestLoadHTMLFiles2(t *testing.T) {
td := setupHTMLFiles(t, DebugMode, false)
res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLFiles3(t *testing.T) {
td := setupHTMLFiles(t, ReleaseMode, false)
res, err := http.Get("http://127.0.0.1:8888/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLFilesUsingTLS(t *testing.T) {
td := setupHTMLFiles(t, TestMode, true)
// Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: tr}
res, err := client.Get("https://127.0.0.1:9999/test")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
td()
}
func TestLoadHTMLFilesFuncMap(t *testing.T) { func TestLoadHTMLFilesFuncMap(t *testing.T) {
time.Now() time.Now()
td := setupHTMLFiles(t) td := setupHTMLFiles(t, TestMode, false)
res, err := http.Get("http://127.0.0.1:8888/raw") res, err := http.Get("http://127.0.0.1:8888/raw")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -152,10 +248,6 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
td() td()
} }
func TestLoadHTMLReleaseMode(t *testing.T) {
}
func TestAddRoute(t *testing.T) { func TestAddRoute(t *testing.T) {
router := New() router := New()
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})

View File

@ -6,9 +6,7 @@
package json package json
import ( import "encoding/json"
"encoding/json"
)
var ( var (
Marshal = json.Marshal Marshal = json.Marshal

View File

@ -6,9 +6,7 @@
package json package json
import ( import "github.com/json-iterator/go"
"github.com/json-iterator/go"
)
var ( var (
json = jsoniter.ConfigCompatibleWithStandardLibrary json = jsoniter.ConfigCompatibleWithStandardLibrary

View File

@ -82,21 +82,21 @@ func TestLogger(t *testing.T) {
} }
func TestColorForMethod(t *testing.T) { func TestColorForMethod(t *testing.T) {
assert.Equal(t, colorForMethod("GET"), string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), "get should be blue") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), colorForMethod("GET"), "get should be blue")
assert.Equal(t, colorForMethod("POST"), string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), "post should be cyan") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), colorForMethod("POST"), "post should be cyan")
assert.Equal(t, colorForMethod("PUT"), string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), "put should be yellow") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow")
assert.Equal(t, colorForMethod("DELETE"), string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), "delete should be red") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForMethod("DELETE"), "delete should be red")
assert.Equal(t, colorForMethod("PATCH"), string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), "patch should be green") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForMethod("PATCH"), "patch should be green")
assert.Equal(t, colorForMethod("HEAD"), string([]byte{27, 91, 57, 55, 59, 52, 53, 109}), "head should be magenta") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 53, 109}), colorForMethod("HEAD"), "head should be magenta")
assert.Equal(t, colorForMethod("OPTIONS"), string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), "options should be white") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForMethod("OPTIONS"), "options should be white")
assert.Equal(t, colorForMethod("TRACE"), string([]byte{27, 91, 48, 109}), "trace is not defined and should be the reset color") assert.Equal(t, string([]byte{27, 91, 48, 109}), colorForMethod("TRACE"), "trace is not defined and should be the reset color")
} }
func TestColorForStatus(t *testing.T) { func TestColorForStatus(t *testing.T) {
assert.Equal(t, colorForStatus(200), string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), "2xx should be green") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForStatus(200), "2xx should be green")
assert.Equal(t, colorForStatus(301), string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), "3xx should be white") assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForStatus(301), "3xx should be white")
assert.Equal(t, colorForStatus(404), string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), "4xx should be yellow") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForStatus(404), "4xx should be yellow")
assert.Equal(t, colorForStatus(2), string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), "other things should be red") assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForStatus(2), "other things should be red")
} }
func TestErrorLogger(t *testing.T) { func TestErrorLogger(t *testing.T) {

12
mode.go
View File

@ -14,9 +14,9 @@ import (
const ENV_GIN_MODE = "GIN_MODE" const ENV_GIN_MODE = "GIN_MODE"
const ( const (
DebugMode string = "debug" DebugMode = "debug"
ReleaseMode string = "release" ReleaseMode = "release"
TestMode string = "test" TestMode = "test"
) )
const ( const (
debugCode = iota debugCode = iota
@ -39,16 +39,12 @@ var modeName = DebugMode
func init() { func init() {
mode := os.Getenv(ENV_GIN_MODE) mode := os.Getenv(ENV_GIN_MODE)
if mode == "" {
SetMode(DebugMode)
} else {
SetMode(mode) SetMode(mode)
}
} }
func SetMode(value string) { func SetMode(value string) {
switch value { switch value {
case DebugMode: case DebugMode, "":
ginMode = debugCode ginMode = debugCode
case ReleaseMode: case ReleaseMode:
ginMode = releaseCode ginMode = releaseCode

View File

@ -17,21 +17,21 @@ func init() {
} }
func TestSetMode(t *testing.T) { func TestSetMode(t *testing.T) {
assert.Equal(t, ginMode, testCode) assert.Equal(t, testCode, ginMode)
assert.Equal(t, Mode(), TestMode) assert.Equal(t, TestMode, Mode())
os.Unsetenv(ENV_GIN_MODE) os.Unsetenv(ENV_GIN_MODE)
SetMode(DebugMode) SetMode(DebugMode)
assert.Equal(t, ginMode, debugCode) assert.Equal(t, debugCode, ginMode)
assert.Equal(t, Mode(), DebugMode) assert.Equal(t, DebugMode, Mode())
SetMode(ReleaseMode) SetMode(ReleaseMode)
assert.Equal(t, ginMode, releaseCode) assert.Equal(t, releaseCode, ginMode)
assert.Equal(t, Mode(), ReleaseMode) assert.Equal(t, ReleaseMode, Mode())
SetMode(TestMode) SetMode(TestMode)
assert.Equal(t, ginMode, testCode) assert.Equal(t, testCode, ginMode)
assert.Equal(t, Mode(), TestMode) assert.Equal(t, TestMode, Mode())
assert.Panics(t, func() { SetMode("unknown") }) assert.Panics(t, func() { SetMode("unknown") })
} }

View File

@ -67,8 +67,8 @@ var cleanTests = []struct {
func TestPathClean(t *testing.T) { func TestPathClean(t *testing.T) {
for _, test := range cleanTests { for _, test := range cleanTests {
assert.Equal(t, cleanPath(test.path), test.result) assert.Equal(t, test.result, cleanPath(test.path))
assert.Equal(t, cleanPath(test.result), test.result) assert.Equal(t, test.result, cleanPath(test.result))
} }
} }

View File

@ -22,7 +22,7 @@ func TestPanicInHandler(t *testing.T) {
// RUN // RUN
w := performRequest(router, "GET", "/recovery") w := performRequest(router, "GET", "/recovery")
// TEST // TEST
assert.Equal(t, w.Code, 500) assert.Equal(t, 500, w.Code)
assert.Contains(t, buffer.String(), "GET /recovery") assert.Contains(t, buffer.String(), "GET /recovery")
assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem") assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
assert.Contains(t, buffer.String(), "TestPanicInHandler") assert.Contains(t, buffer.String(), "TestPanicInHandler")
@ -39,5 +39,5 @@ func TestPanicWithAbort(t *testing.T) {
// RUN // RUN
w := performRequest(router, "GET", "/recovery") w := performRequest(router, "GET", "/recovery")
// TEST // TEST
assert.Equal(t, w.Code, 400) assert.Equal(t, 400, w.Code)
} }

View File

@ -7,7 +7,9 @@ package render
import ( import (
"bytes" "bytes"
"encoding/xml" "encoding/xml"
"errors"
"html/template" "html/template"
"net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -24,6 +26,9 @@ func TestRenderMsgPack(t *testing.T) {
"foo": "bar", "foo": "bar",
} }
(MsgPack{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/msgpack; charset=utf-8")
err := (MsgPack{data}).Render(w) err := (MsgPack{data}).Render(w)
assert.NoError(t, err) assert.NoError(t, err)
@ -45,6 +50,9 @@ func TestRenderJSON(t *testing.T) {
"foo": "bar", "foo": "bar",
} }
(JSON{data}).WriteContentType(w)
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
err := (JSON{data}).Render(w) err := (JSON{data}).Render(w)
assert.NoError(t, err) assert.NoError(t, err)
@ -52,6 +60,14 @@ func TestRenderJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
} }
func TestRenderJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
assert.Panics(t, func() { (JSON{data}).Render(w) })
}
func TestRenderIndentedJSON(t *testing.T) { func TestRenderIndentedJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
data := map[string]interface{}{ data := map[string]interface{}{
@ -66,12 +82,24 @@ func TestRenderIndentedJSON(t *testing.T) {
assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8") assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8")
} }
func TestRenderIndentedJSONPanics(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (IndentedJSON{data}).Render(w)
assert.Error(t, err)
}
func TestRenderSecureJSON(t *testing.T) { func TestRenderSecureJSON(t *testing.T) {
w1 := httptest.NewRecorder() w1 := httptest.NewRecorder()
data := map[string]interface{}{ data := map[string]interface{}{
"foo": "bar", "foo": "bar",
} }
(SecureJSON{"while(1);", data}).WriteContentType(w1)
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
err1 := (SecureJSON{"while(1);", data}).Render(w1) err1 := (SecureJSON{"while(1);", data}).Render(w1)
assert.NoError(t, err1) assert.NoError(t, err1)
@ -91,6 +119,15 @@ func TestRenderSecureJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type")) assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
} }
func TestRenderSecureJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
err := (SecureJSON{"while(1);", data}).Render(w)
assert.Error(t, err)
}
type xmlmap map[string]interface{} type xmlmap map[string]interface{}
// Allows type H to be used with xml.Marshal // Allows type H to be used with xml.Marshal
@ -111,10 +148,38 @@ func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return err return err
} }
} }
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
return err return e.EncodeToken(xml.EndElement{Name: start.Name})
} }
return nil
func TestRenderYAML(t *testing.T) {
w := httptest.NewRecorder()
data := `
a : Easy!
b:
c: 2
d: [3, 4]
`
(YAML{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/x-yaml; charset=utf-8")
err := (YAML{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n")
assert.Equal(t, w.Header().Get("Content-Type"), "application/x-yaml; charset=utf-8")
}
type fail struct{}
// Hook MarshalYAML
func (ft *fail) MarshalYAML() (interface{}, error) {
return nil, errors.New("fail")
}
func TestRenderYAMLFail(t *testing.T) {
w := httptest.NewRecorder()
err := (YAML{&fail{}}).Render(w)
assert.Error(t, err)
} }
func TestRenderXML(t *testing.T) { func TestRenderXML(t *testing.T) {
@ -123,6 +188,9 @@ func TestRenderXML(t *testing.T) {
"foo": "bar", "foo": "bar",
} }
(XML{data}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "application/xml; charset=utf-8")
err := (XML{data}).Render(w) err := (XML{data}).Render(w)
assert.NoError(t, err) assert.NoError(t, err)
@ -131,7 +199,30 @@ func TestRenderXML(t *testing.T) {
} }
func TestRenderRedirect(t *testing.T) { func TestRenderRedirect(t *testing.T) {
// TODO req, err := http.NewRequest("GET", "/test-redirect", nil)
assert.NoError(t, err)
data1 := Redirect{
Code: 301,
Request: req,
Location: "/new/location",
}
w := httptest.NewRecorder()
err = data1.Render(w)
assert.NoError(t, err)
data2 := Redirect{
Code: 200,
Request: req,
Location: "/new/location",
}
w = httptest.NewRecorder()
assert.Panics(t, func() { data2.Render(w) })
// only improve coverage
data2.WriteContentType(w)
} }
func TestRenderData(t *testing.T) { func TestRenderData(t *testing.T) {
@ -151,6 +242,12 @@ func TestRenderData(t *testing.T) {
func TestRenderString(t *testing.T) { func TestRenderString(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
(String{
Format: "hello %s %d",
Data: []interface{}{},
}).WriteContentType(w)
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
err := (String{ err := (String{
Format: "hola %s %d", Format: "hola %s %d",
Data: []interface{}{"manu", 2}, Data: []interface{}{"manu", 2},
@ -161,6 +258,19 @@ func TestRenderString(t *testing.T) {
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
} }
func TestRenderStringLenZero(t *testing.T) {
w := httptest.NewRecorder()
err := (String{
Format: "hola %s %d",
Data: []interface{}{},
}).Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "hola %s %d")
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
}
func TestRenderHTMLTemplate(t *testing.T) { func TestRenderHTMLTemplate(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
@ -176,3 +286,64 @@ func TestRenderHTMLTemplate(t *testing.T) {
assert.Equal(t, w.Body.String(), "Hello alexandernyquist") assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
} }
func TestRenderHTMLTemplateEmptyName(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
htmlRender := HTMLProduction{Template: templ}
instance := htmlRender.Instance("", map[string]interface{}{
"name": "alexandernyquist",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugFiles(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{Files: []string{"../fixtures/basic/hello.tmpl"},
Glob: "",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "<h1>Hello thinkerou</h1>")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugGlob(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{Files: nil,
Glob: "../fixtures/basic/hello*",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
"name": "thinkerou",
})
err := instance.Render(w)
assert.NoError(t, err)
assert.Equal(t, w.Body.String(), "<h1>Hello thinkerou</h1>")
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
}
func TestRenderHTMLDebugPanics(t *testing.T) {
htmlRender := HTMLDebug{Files: nil,
Glob: "",
Delims: Delims{"{{", "}}"},
FuncMap: nil,
}
assert.Panics(t, func() { htmlRender.Instance("", nil) })
}

View File

@ -20,15 +20,15 @@ func TestRouterGroupBasic(t *testing.T) {
group.Use(func(c *Context) {}) group.Use(func(c *Context) {})
assert.Len(t, group.Handlers, 2) assert.Len(t, group.Handlers, 2)
assert.Equal(t, group.BasePath(), "/hola") assert.Equal(t, "/hola", group.BasePath())
assert.Equal(t, group.engine, router) assert.Equal(t, router, group.engine)
group2 := group.Group("manu") group2 := group.Group("manu")
group2.Use(func(c *Context) {}, func(c *Context) {}) group2.Use(func(c *Context) {}, func(c *Context) {})
assert.Len(t, group2.Handlers, 4) assert.Len(t, group2.Handlers, 4)
assert.Equal(t, group2.BasePath(), "/hola/manu") assert.Equal(t, "/hola/manu", group2.BasePath())
assert.Equal(t, group2.engine, router) assert.Equal(t, router, group2.engine)
} }
func TestRouterGroupBasicHandle(t *testing.T) { func TestRouterGroupBasicHandle(t *testing.T) {
@ -44,10 +44,10 @@ func TestRouterGroupBasicHandle(t *testing.T) {
func performRequestInGroup(t *testing.T, method string) { func performRequestInGroup(t *testing.T, method string) {
router := New() router := New()
v1 := router.Group("v1", func(c *Context) {}) v1 := router.Group("v1", func(c *Context) {})
assert.Equal(t, v1.BasePath(), "/v1") assert.Equal(t, "/v1", v1.BasePath())
login := v1.Group("/login/", func(c *Context) {}, func(c *Context) {}) login := v1.Group("/login/", func(c *Context) {}, func(c *Context) {})
assert.Equal(t, login.BasePath(), "/v1/login/") assert.Equal(t, "/v1/login/", login.BasePath())
handler := func(c *Context) { handler := func(c *Context) {
c.String(400, "the method was %s and index %d", c.Request.Method, c.index) c.String(400, "the method was %s and index %d", c.Request.Method, c.index)
@ -80,12 +80,12 @@ func performRequestInGroup(t *testing.T, method string) {
} }
w := performRequest(router, method, "/v1/login/test") w := performRequest(router, method, "/v1/login/test")
assert.Equal(t, w.Code, 400) assert.Equal(t, 400, w.Code)
assert.Equal(t, w.Body.String(), "the method was "+method+" and index 3") assert.Equal(t, "the method was "+method+" and index 3", w.Body.String())
w = performRequest(router, method, "/v1/test") w = performRequest(router, method, "/v1/test")
assert.Equal(t, w.Code, 400) assert.Equal(t, 400, w.Code)
assert.Equal(t, w.Body.String(), "the method was "+method+" and index 1") assert.Equal(t, "the method was "+method+" and index 1", w.Body.String())
} }
func TestRouterGroupInvalidStatic(t *testing.T) { func TestRouterGroupInvalidStatic(t *testing.T) {

View File

@ -36,7 +36,7 @@ func testRouteOK(method string, t *testing.T) {
w := performRequest(r, method, "/test") w := performRequest(r, method, "/test")
assert.True(t, passed) assert.True(t, passed)
assert.Equal(t, w.Code, http.StatusOK) assert.Equal(t, http.StatusOK, w.Code)
performRequest(r, method, "/test2") performRequest(r, method, "/test2")
assert.True(t, passedAny) assert.True(t, passedAny)
@ -53,7 +53,7 @@ func testRouteNotOK(method string, t *testing.T) {
w := performRequest(router, method, "/test") w := performRequest(router, method, "/test")
assert.False(t, passed) assert.False(t, passed)
assert.Equal(t, w.Code, http.StatusNotFound) assert.Equal(t, http.StatusNotFound, w.Code)
} }
// TestSingleRouteOK tests that POST route is correctly invoked. // TestSingleRouteOK tests that POST route is correctly invoked.
@ -74,7 +74,7 @@ func testRouteNotOK2(method string, t *testing.T) {
w := performRequest(router, method, "/test") w := performRequest(router, method, "/test")
assert.False(t, passed) assert.False(t, passed)
assert.Equal(t, w.Code, http.StatusMethodNotAllowed) assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
} }
func TestRouterMethod(t *testing.T) { func TestRouterMethod(t *testing.T) {
@ -93,8 +93,8 @@ func TestRouterMethod(t *testing.T) {
w := performRequest(router, "PUT", "/hey") w := performRequest(router, "PUT", "/hey")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Equal(t, w.Body.String(), "called") assert.Equal(t, "called", w.Body.String())
} }
func TestRouterGroupRouteOK(t *testing.T) { func TestRouterGroupRouteOK(t *testing.T) {
@ -143,43 +143,43 @@ func TestRouteRedirectTrailingSlash(t *testing.T) {
router.PUT("/path4/", func(c *Context) {}) router.PUT("/path4/", func(c *Context) {})
w := performRequest(router, "GET", "/path/") w := performRequest(router, "GET", "/path/")
assert.Equal(t, w.Header().Get("Location"), "/path") assert.Equal(t, "/path", w.Header().Get("Location"))
assert.Equal(t, w.Code, 301) assert.Equal(t, 301, w.Code)
w = performRequest(router, "GET", "/path2") w = performRequest(router, "GET", "/path2")
assert.Equal(t, w.Header().Get("Location"), "/path2/") assert.Equal(t, "/path2/", w.Header().Get("Location"))
assert.Equal(t, w.Code, 301) assert.Equal(t, 301, w.Code)
w = performRequest(router, "POST", "/path3/") w = performRequest(router, "POST", "/path3/")
assert.Equal(t, w.Header().Get("Location"), "/path3") assert.Equal(t, "/path3", w.Header().Get("Location"))
assert.Equal(t, w.Code, 307) assert.Equal(t, 307, w.Code)
w = performRequest(router, "PUT", "/path4") w = performRequest(router, "PUT", "/path4")
assert.Equal(t, w.Header().Get("Location"), "/path4/") assert.Equal(t, "/path4/", w.Header().Get("Location"))
assert.Equal(t, w.Code, 307) assert.Equal(t, 307, w.Code)
w = performRequest(router, "GET", "/path") w = performRequest(router, "GET", "/path")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
w = performRequest(router, "GET", "/path2/") w = performRequest(router, "GET", "/path2/")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
w = performRequest(router, "POST", "/path3") w = performRequest(router, "POST", "/path3")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
w = performRequest(router, "PUT", "/path4/") w = performRequest(router, "PUT", "/path4/")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
router.RedirectTrailingSlash = false router.RedirectTrailingSlash = false
w = performRequest(router, "GET", "/path/") w = performRequest(router, "GET", "/path/")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
w = performRequest(router, "GET", "/path2") w = performRequest(router, "GET", "/path2")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
w = performRequest(router, "POST", "/path3/") w = performRequest(router, "POST", "/path3/")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
w = performRequest(router, "PUT", "/path4") w = performRequest(router, "PUT", "/path4")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
} }
func TestRouteRedirectFixedPath(t *testing.T) { func TestRouteRedirectFixedPath(t *testing.T) {
@ -193,20 +193,20 @@ func TestRouteRedirectFixedPath(t *testing.T) {
router.POST("/Path4/", func(c *Context) {}) router.POST("/Path4/", func(c *Context) {})
w := performRequest(router, "GET", "/PATH") w := performRequest(router, "GET", "/PATH")
assert.Equal(t, w.Header().Get("Location"), "/path") assert.Equal(t, "/path", w.Header().Get("Location"))
assert.Equal(t, w.Code, 301) assert.Equal(t, 301, w.Code)
w = performRequest(router, "GET", "/path2") w = performRequest(router, "GET", "/path2")
assert.Equal(t, w.Header().Get("Location"), "/Path2") assert.Equal(t, "/Path2", w.Header().Get("Location"))
assert.Equal(t, w.Code, 301) assert.Equal(t, 301, w.Code)
w = performRequest(router, "POST", "/path3") w = performRequest(router, "POST", "/path3")
assert.Equal(t, w.Header().Get("Location"), "/PATH3") assert.Equal(t, "/PATH3", w.Header().Get("Location"))
assert.Equal(t, w.Code, 307) assert.Equal(t, 307, w.Code)
w = performRequest(router, "POST", "/path4") w = performRequest(router, "POST", "/path4")
assert.Equal(t, w.Header().Get("Location"), "/Path4/") assert.Equal(t, "/Path4/", w.Header().Get("Location"))
assert.Equal(t, w.Code, 307) assert.Equal(t, 307, w.Code)
} }
// TestContextParamsGet tests that a parameter can be parsed from the URL. // TestContextParamsGet tests that a parameter can be parsed from the URL.
@ -236,10 +236,10 @@ func TestRouteParamsByName(t *testing.T) {
w := performRequest(router, "GET", "/test/john/smith/is/super/great") w := performRequest(router, "GET", "/test/john/smith/is/super/great")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Equal(t, name, "john") assert.Equal(t, "john", name)
assert.Equal(t, lastName, "smith") assert.Equal(t, "smith", lastName)
assert.Equal(t, wild, "/is/super/great") assert.Equal(t, "/is/super/great", wild)
} }
// TestHandleStaticFile - ensure the static file handles properly // TestHandleStaticFile - ensure the static file handles properly
@ -265,15 +265,15 @@ func TestRouteStaticFile(t *testing.T) {
w2 := performRequest(router, "GET", "/result") w2 := performRequest(router, "GET", "/result")
assert.Equal(t, w, w2) assert.Equal(t, w, w2)
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Equal(t, w.Body.String(), "Gin Web Framework") assert.Equal(t, "Gin Web Framework", w.Body.String())
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
w3 := performRequest(router, "HEAD", "/using_static/"+filename) w3 := performRequest(router, "HEAD", "/using_static/"+filename)
w4 := performRequest(router, "HEAD", "/result") w4 := performRequest(router, "HEAD", "/result")
assert.Equal(t, w3, w4) assert.Equal(t, w3, w4)
assert.Equal(t, w3.Code, 200) assert.Equal(t, 200, w3.Code)
} }
// TestHandleStaticDir - ensure the root/sub dir handles properly // TestHandleStaticDir - ensure the root/sub dir handles properly
@ -283,9 +283,9 @@ func TestRouteStaticListingDir(t *testing.T) {
w := performRequest(router, "GET", "/") w := performRequest(router, "GET", "/")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "gin.go") assert.Contains(t, w.Body.String(), "gin.go")
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
} }
// TestHandleHeadToDir - ensure the root/sub dir handles properly // TestHandleHeadToDir - ensure the root/sub dir handles properly
@ -295,7 +295,7 @@ func TestRouteStaticNoListing(t *testing.T) {
w := performRequest(router, "GET", "/") w := performRequest(router, "GET", "/")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
assert.NotContains(t, w.Body.String(), "gin.go") assert.NotContains(t, w.Body.String(), "gin.go")
} }
@ -310,12 +310,12 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
w := performRequest(router, "GET", "/gin.go") w := performRequest(router, "GET", "/gin.go")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "package gin") assert.Contains(t, w.Body.String(), "package gin")
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
assert.NotEqual(t, w.HeaderMap.Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") assert.NotEqual(t, w.HeaderMap.Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
assert.Equal(t, w.HeaderMap.Get("Expires"), "Mon, 02 Jan 2006 15:04:05 MST") assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.HeaderMap.Get("Expires"))
assert.Equal(t, w.HeaderMap.Get("x-GIN"), "Gin Framework") assert.Equal(t, "Gin Framework", w.HeaderMap.Get("x-GIN"))
} }
func TestRouteNotAllowedEnabled(t *testing.T) { func TestRouteNotAllowedEnabled(t *testing.T) {
@ -323,14 +323,14 @@ func TestRouteNotAllowedEnabled(t *testing.T) {
router.HandleMethodNotAllowed = true router.HandleMethodNotAllowed = true
router.POST("/path", func(c *Context) {}) router.POST("/path", func(c *Context) {})
w := performRequest(router, "GET", "/path") w := performRequest(router, "GET", "/path")
assert.Equal(t, w.Code, http.StatusMethodNotAllowed) assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
router.NoMethod(func(c *Context) { router.NoMethod(func(c *Context) {
c.String(http.StatusTeapot, "responseText") c.String(http.StatusTeapot, "responseText")
}) })
w = performRequest(router, "GET", "/path") w = performRequest(router, "GET", "/path")
assert.Equal(t, w.Body.String(), "responseText") assert.Equal(t, "responseText", w.Body.String())
assert.Equal(t, w.Code, http.StatusTeapot) assert.Equal(t, http.StatusTeapot, w.Code)
} }
func TestRouteNotAllowedDisabled(t *testing.T) { func TestRouteNotAllowedDisabled(t *testing.T) {
@ -338,14 +338,14 @@ func TestRouteNotAllowedDisabled(t *testing.T) {
router.HandleMethodNotAllowed = false router.HandleMethodNotAllowed = false
router.POST("/path", func(c *Context) {}) router.POST("/path", func(c *Context) {})
w := performRequest(router, "GET", "/path") w := performRequest(router, "GET", "/path")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
router.NoMethod(func(c *Context) { router.NoMethod(func(c *Context) {
c.String(http.StatusTeapot, "responseText") c.String(http.StatusTeapot, "responseText")
}) })
w = performRequest(router, "GET", "/path") w = performRequest(router, "GET", "/path")
assert.Equal(t, w.Body.String(), "404 page not found") assert.Equal(t, "404 page not found", w.Body.String())
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
} }
func TestRouterNotFound(t *testing.T) { func TestRouterNotFound(t *testing.T) {
@ -372,9 +372,9 @@ func TestRouterNotFound(t *testing.T) {
} }
for _, tr := range testRoutes { for _, tr := range testRoutes {
w := performRequest(router, "GET", tr.route) w := performRequest(router, "GET", tr.route)
assert.Equal(t, w.Code, tr.code) assert.Equal(t, tr.code, w.Code)
if w.Code != 404 { if w.Code != 404 {
assert.Equal(t, fmt.Sprint(w.Header().Get("Location")), tr.location) assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
} }
} }
@ -385,20 +385,20 @@ func TestRouterNotFound(t *testing.T) {
notFound = true notFound = true
}) })
w := performRequest(router, "GET", "/nope") w := performRequest(router, "GET", "/nope")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
assert.True(t, notFound) assert.True(t, notFound)
// Test other method than GET (want 307 instead of 301) // Test other method than GET (want 307 instead of 301)
router.PATCH("/path", func(c *Context) {}) router.PATCH("/path", func(c *Context) {})
w = performRequest(router, "PATCH", "/path/") w = performRequest(router, "PATCH", "/path/")
assert.Equal(t, w.Code, 307) assert.Equal(t, 307, w.Code)
assert.Equal(t, fmt.Sprint(w.Header()), "map[Location:[/path]]") assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header()))
// Test special case where no node for the prefix "/" exists // Test special case where no node for the prefix "/" exists
router = New() router = New()
router.GET("/a", func(c *Context) {}) router.GET("/a", func(c *Context) {})
w = performRequest(router, "GET", "/") w = performRequest(router, "GET", "/")
assert.Equal(t, w.Code, 404) assert.Equal(t, 404, w.Code)
} }
func TestRouteRawPath(t *testing.T) { func TestRouteRawPath(t *testing.T) {
@ -409,15 +409,15 @@ func TestRouteRawPath(t *testing.T) {
name := c.Params.ByName("name") name := c.Params.ByName("name")
num := c.Params.ByName("num") num := c.Params.ByName("num")
assert.Equal(t, c.Param("name"), name) assert.Equal(t, name, c.Param("name"))
assert.Equal(t, c.Param("num"), num) assert.Equal(t, num, c.Param("num"))
assert.Equal(t, "Some/Other/Project", name) assert.Equal(t, "Some/Other/Project", name)
assert.Equal(t, "222", num) assert.Equal(t, "222", num)
}) })
w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222") w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
} }
func TestRouteRawPathNoUnescape(t *testing.T) { func TestRouteRawPathNoUnescape(t *testing.T) {
@ -429,15 +429,15 @@ func TestRouteRawPathNoUnescape(t *testing.T) {
name := c.Params.ByName("name") name := c.Params.ByName("name")
num := c.Params.ByName("num") num := c.Params.ByName("num")
assert.Equal(t, c.Param("name"), name) assert.Equal(t, name, c.Param("name"))
assert.Equal(t, c.Param("num"), num) assert.Equal(t, num, c.Param("num"))
assert.Equal(t, "Some%2FOther%2FProject", name) assert.Equal(t, "Some%2FOther%2FProject", name)
assert.Equal(t, "333", num) assert.Equal(t, "333", num)
}) })
w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333") w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333")
assert.Equal(t, w.Code, 200) assert.Equal(t, 200, w.Code)
} }
func TestRouteServeErrorWithWriteHeader(t *testing.T) { func TestRouteServeErrorWithWriteHeader(t *testing.T) {

14
tree.go
View File

@ -87,13 +87,13 @@ const (
type node struct { type node struct {
path string path string
wildChild bool
nType nodeType
maxParams uint8
indices string indices string
children []*node children []*node
handlers HandlersChain handlers HandlersChain
priority uint32 priority uint32
nType nodeType
maxParams uint8
wildChild bool
} }
// increments priority of the given child and reorders if necessary. // increments priority of the given child and reorders if necessary.
@ -384,7 +384,7 @@ walk: // Outer loop for walking the tree
// Nothing found. // Nothing found.
// We can recommend to redirect to the same URL without a // We can recommend to redirect to the same URL without a
// trailing slash if a leaf exists for that path. // trailing slash if a leaf exists for that path.
tsr = (path == "/" && n.handlers != nil) tsr = path == "/" && n.handlers != nil
return return
} }
@ -424,7 +424,7 @@ walk: // Outer loop for walking the tree
} }
// ... but we can't // ... but we can't
tsr = (len(path) == end+1) tsr = len(path) == end+1
return return
} }
@ -435,7 +435,7 @@ walk: // Outer loop for walking the tree
// No handle found. Check if a handle for this path + a // No handle found. Check if a handle for this path + a
// trailing slash exists for TSR recommendation // trailing slash exists for TSR recommendation
n = n.children[0] n = n.children[0]
tsr = (n.path == "/" && n.handlers != nil) tsr = n.path == "/" && n.handlers != nil
} }
return return
@ -530,7 +530,7 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa
// Nothing found. We can recommend to redirect to the same URL // Nothing found. We can recommend to redirect to the same URL
// without a trailing slash if a leaf exists for that path // without a trailing slash if a leaf exists for that path
found = (fixTrailingSlash && path == "/" && n.handlers != nil) found = fixTrailingSlash && path == "/" && n.handlers != nil
return return
} }

View File

@ -5,22 +5,11 @@
package gin package gin
import ( import (
"fmt"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
) )
func printChildren(n *node, prefix string) {
fmt.Printf(" %02d:%02d %s%s[%d] %v %t %d \r\n", n.priority, n.maxParams, prefix, n.path, len(n.children), n.handlers, n.wildChild, n.nType)
for l := len(n.path); l > 0; l-- {
prefix += " "
}
for _, child := range n.children {
printChildren(child, prefix)
}
}
// Used as a workaround since we can't compare functions or their addressses // Used as a workaround since we can't compare functions or their addressses
var fakeHandlerValue string var fakeHandlerValue string

View File

@ -33,18 +33,23 @@ func Bind(val interface{}) HandlerFunc {
} }
} }
// WrapF is a helper function for wrapping http.HandlerFunc
// Returns a Gin middleware
func WrapF(f http.HandlerFunc) HandlerFunc { func WrapF(f http.HandlerFunc) HandlerFunc {
return func(c *Context) { return func(c *Context) {
f(c.Writer, c.Request) f(c.Writer, c.Request)
} }
} }
// WrapH is a helper function for wrapping http.Handler
// Returns a Gin middleware
func WrapH(h http.Handler) HandlerFunc { func WrapH(h http.Handler) HandlerFunc {
return func(c *Context) { return func(c *Context) {
h.ServeHTTP(c.Writer, c.Request) h.ServeHTTP(c.Writer, c.Request)
} }
} }
// H is a shortcup for map[string]interface{}
type H map[string]interface{} type H map[string]interface{}
// MarshalXML allows type H to be used with xml.Marshal. // MarshalXML allows type H to be used with xml.Marshal.
@ -65,10 +70,8 @@ func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return err return err
} }
} }
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
return err return e.EncodeToken(xml.EndElement{Name: start.Name})
}
return nil
} }
func assert1(guard bool, text string) { func assert1(guard bool, text string) {

View File

@ -21,8 +21,8 @@ type testStruct struct {
} }
func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) {
assert.Equal(t.T, req.Method, "POST") assert.Equal(t.T, "POST", req.Method)
assert.Equal(t.T, req.URL.Path, "/path") assert.Equal(t.T, "/path", req.URL.Path)
w.WriteHeader(500) w.WriteHeader(500)
fmt.Fprint(w, "hello") fmt.Fprint(w, "hello")
} }
@ -31,50 +31,50 @@ func TestWrap(t *testing.T) {
router := New() router := New()
router.POST("/path", WrapH(&testStruct{t})) router.POST("/path", WrapH(&testStruct{t}))
router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) { router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) {
assert.Equal(t, req.Method, "GET") assert.Equal(t, "GET", req.Method)
assert.Equal(t, req.URL.Path, "/path2") assert.Equal(t, "/path2", req.URL.Path)
w.WriteHeader(400) w.WriteHeader(400)
fmt.Fprint(w, "hola!") fmt.Fprint(w, "hola!")
})) }))
w := performRequest(router, "POST", "/path") w := performRequest(router, "POST", "/path")
assert.Equal(t, w.Code, 500) assert.Equal(t, 500, w.Code)
assert.Equal(t, w.Body.String(), "hello") assert.Equal(t, "hello", w.Body.String())
w = performRequest(router, "GET", "/path2") w = performRequest(router, "GET", "/path2")
assert.Equal(t, w.Code, 400) assert.Equal(t, 400, w.Code)
assert.Equal(t, w.Body.String(), "hola!") assert.Equal(t, "hola!", w.Body.String())
} }
func TestLastChar(t *testing.T) { func TestLastChar(t *testing.T) {
assert.Equal(t, lastChar("hola"), uint8('a')) assert.Equal(t, uint8('a'), lastChar("hola"))
assert.Equal(t, lastChar("adios"), uint8('s')) assert.Equal(t, uint8('s'), lastChar("adios"))
assert.Panics(t, func() { lastChar("") }) assert.Panics(t, func() { lastChar("") })
} }
func TestParseAccept(t *testing.T) { func TestParseAccept(t *testing.T) {
parts := parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8") parts := parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8")
assert.Len(t, parts, 4) assert.Len(t, parts, 4)
assert.Equal(t, parts[0], "text/html") assert.Equal(t, "text/html", parts[0])
assert.Equal(t, parts[1], "application/xhtml+xml") assert.Equal(t, "application/xhtml+xml", parts[1])
assert.Equal(t, parts[2], "application/xml") assert.Equal(t, "application/xml", parts[2])
assert.Equal(t, parts[3], "*/*") assert.Equal(t, "*/*", parts[3])
} }
func TestChooseData(t *testing.T) { func TestChooseData(t *testing.T) {
A := "a" A := "a"
B := "b" B := "b"
assert.Equal(t, chooseData(A, B), A) assert.Equal(t, A, chooseData(A, B))
assert.Equal(t, chooseData(nil, B), B) assert.Equal(t, B, chooseData(nil, B))
assert.Panics(t, func() { chooseData(nil, nil) }) assert.Panics(t, func() { chooseData(nil, nil) })
} }
func TestFilterFlags(t *testing.T) { func TestFilterFlags(t *testing.T) {
result := filterFlags("text/html ") result := filterFlags("text/html ")
assert.Equal(t, result, "text/html") assert.Equal(t, "text/html", result)
result = filterFlags("text/html;") result = filterFlags("text/html;")
assert.Equal(t, result, "text/html") assert.Equal(t, "text/html", result)
} }
func TestFunctionName(t *testing.T) { func TestFunctionName(t *testing.T) {
@ -86,16 +86,16 @@ func somefunction() {
} }
func TestJoinPaths(t *testing.T) { func TestJoinPaths(t *testing.T) {
assert.Equal(t, joinPaths("", ""), "") assert.Equal(t, "", joinPaths("", ""))
assert.Equal(t, joinPaths("", "/"), "/") assert.Equal(t, "/", joinPaths("", "/"))
assert.Equal(t, joinPaths("/a", ""), "/a") assert.Equal(t, "/a", joinPaths("/a", ""))
assert.Equal(t, joinPaths("/a/", ""), "/a/") assert.Equal(t, "/a/", joinPaths("/a/", ""))
assert.Equal(t, joinPaths("/a/", "/"), "/a/") assert.Equal(t, "/a/", joinPaths("/a/", "/"))
assert.Equal(t, joinPaths("/a", "/"), "/a/") assert.Equal(t, "/a/", joinPaths("/a", "/"))
assert.Equal(t, joinPaths("/a", "/hola"), "/a/hola") assert.Equal(t, "/a/hola", joinPaths("/a", "/hola"))
assert.Equal(t, joinPaths("/a/", "/hola"), "/a/hola") assert.Equal(t, "/a/hola", joinPaths("/a/", "/hola"))
assert.Equal(t, joinPaths("/a/", "/hola/"), "/a/hola/") assert.Equal(t, "/a/hola/", joinPaths("/a/", "/hola/"))
assert.Equal(t, joinPaths("/a/", "/hola//"), "/a/hola/") assert.Equal(t, "/a/hola/", joinPaths("/a/", "/hola//"))
} }
type bindTestStruct struct { type bindTestStruct struct {
@ -113,8 +113,8 @@ func TestBindMiddleware(t *testing.T) {
}) })
performRequest(router, "GET", "/?foo=hola&bar=10") performRequest(router, "GET", "/?foo=hola&bar=10")
assert.True(t, called) assert.True(t, called)
assert.Equal(t, value.Foo, "hola") assert.Equal(t, "hola", value.Foo)
assert.Equal(t, value.Bar, 10) assert.Equal(t, 10, value.Bar)
called = false called = false
performRequest(router, "GET", "/?foo=hola&bar=1") performRequest(router, "GET", "/?foo=hola&bar=1")

18
vendor/vendor.json vendored
View File

@ -22,10 +22,10 @@
"revisionTime": "2017-01-09T09:34:21Z" "revisionTime": "2017-01-09T09:34:21Z"
}, },
{ {
"checksumSHA1": "FJKrZuFmeLJp8HDeJc6UkIDBPUw=", "checksumSHA1": "+vZNyF2MykVjenLg1TpjjgjthV0=",
"path": "github.com/gin-gonic/autotls", "path": "github.com/gin-gonic/autotls",
"revision": "5b3297bdcee778ff3bbdc99ab7c41e1c2677d22d", "revision": "8ca25fbde72bb72a00466215b94b489c71fcb815",
"revisionTime": "2017-04-16T09:39:34Z" "revisionTime": "2017-09-16T16:54:15Z"
}, },
{ {
"checksumSHA1": "qlPUeFabwF4RKAOF1H+yBFU1Veg=", "checksumSHA1": "qlPUeFabwF4RKAOF1H+yBFU1Veg=",
@ -65,6 +65,12 @@
"revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506", "revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506",
"revisionTime": "2016-09-25T22:06:09Z" "revisionTime": "2016-09-25T22:06:09Z"
}, },
{
"checksumSHA1": "IopMW+arBezL5bqOfrVU6UEfn28=",
"path": "github.com/thinkerou/favicon",
"revision": "94a442a49da6e2d44bdd5e0d2e2e185c43a19d93",
"revisionTime": "2017-07-10T14:05:20Z"
},
{ {
"checksumSHA1": "CoxdaTYdPZNJXr8mJfLxye428N0=", "checksumSHA1": "CoxdaTYdPZNJXr8mJfLxye428N0=",
"path": "github.com/ugorji/go/codec", "path": "github.com/ugorji/go/codec",
@ -90,6 +96,12 @@
"revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a", "revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a",
"revisionTime": "2016-10-18T08:54:36Z" "revisionTime": "2016-10-18T08:54:36Z"
}, },
{
"checksumSHA1": "S0DP7Pn7sZUmXc55IzZnNvERu6s=",
"path": "golang.org/x/sync/errgroup",
"revision": "8e0aa688b654ef28caa72506fa5ec8dba9fc7690",
"revisionTime": "2017-07-19T03:38:01Z"
},
{ {
"checksumSHA1": "TVEkpH3gq84iQ39I4R+mlDwjuVI=", "checksumSHA1": "TVEkpH3gq84iQ39I4R+mlDwjuVI=",
"path": "golang.org/x/sys/unix", "path": "golang.org/x/sys/unix",