mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-22 01:12:16 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
9a495ae025
14
.travis.yml
14
.travis.yml
@ -7,12 +7,24 @@ go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- master
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
||||
before_install:
|
||||
- if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
|
||||
|
||||
install:
|
||||
- make install
|
||||
- if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make install; fi
|
||||
- if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi
|
||||
- if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi
|
||||
|
||||
go_import_path: github.com/gin-gonic/gin
|
||||
|
||||
|
34
Makefile
34
Makefile
@ -1,7 +1,9 @@
|
||||
GO ?= go
|
||||
GOFMT ?= gofmt "-s"
|
||||
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||
VETPACKAGES ?= $(shell go list ./... | grep -v /vendor/ | grep -v /examples/)
|
||||
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
|
||||
VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/ | grep -v /examples/)
|
||||
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
|
||||
TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples)
|
||||
|
||||
all: install
|
||||
|
||||
@ -10,7 +12,14 @@ install: deps
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
sh coverage.sh
|
||||
echo "mode: count" > coverage.out
|
||||
for d in $(TESTFOLDER); 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
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@ -18,7 +27,6 @@ fmt:
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
@ -27,14 +35,14 @@ fmt-check:
|
||||
fi;
|
||||
|
||||
vet:
|
||||
go vet $(VETPACKAGES)
|
||||
$(GO) vet $(VETPACKAGES)
|
||||
|
||||
deps:
|
||||
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/kardianos/govendor; \
|
||||
$(GO) get -u github.com/kardianos/govendor; \
|
||||
fi
|
||||
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/campoy/embedmd; \
|
||||
$(GO) get -u github.com/campoy/embedmd; \
|
||||
fi
|
||||
|
||||
embedmd:
|
||||
@ -43,20 +51,26 @@ embedmd:
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/golang/lint/golint; \
|
||||
$(GO) get -u golang.org/x/lint/golint; \
|
||||
fi
|
||||
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -error $(GOFILES)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -w $(GOFILES)
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
go install golang.org/x/lint/golint; \
|
||||
go install github.com/client9/misspell/cmd/misspell; \
|
||||
go install github.com/campoy/embedmd;
|
||||
|
36
README.md
36
README.md
@ -9,6 +9,7 @@
|
||||
[](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
||||
[](https://www.codetriage.com/gin-gonic/gin)
|
||||
[](https://github.com/gin-gonic/gin/releases)
|
||||
|
||||
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
|
||||
|
||||
@ -59,6 +60,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
|
||||
- [http2 server push](#http2-server-push)
|
||||
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
|
||||
- [Set and get a cookie](#set-and-get-a-cookie)
|
||||
- [Testing](#testing)
|
||||
- [Users](#users)
|
||||
|
||||
@ -1720,11 +1722,11 @@ type StructX struct {
|
||||
}
|
||||
|
||||
type StructY struct {
|
||||
Y StructX `form:"name_y"` // HERE hava form
|
||||
Y StructX `form:"name_y"` // HERE have form
|
||||
}
|
||||
|
||||
type StructZ struct {
|
||||
Z *StructZ `form:"name_z"` // HERE hava form
|
||||
Z *StructZ `form:"name_z"` // HERE have form
|
||||
}
|
||||
```
|
||||
|
||||
@ -1879,6 +1881,35 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### Set and get a cookie
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
router.GET("/cookie", func(c *gin.Context) {
|
||||
|
||||
cookie, err := c.Cookie("gin_cookie")
|
||||
|
||||
if err != nil {
|
||||
cookie = "NotSet"
|
||||
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
|
||||
}
|
||||
|
||||
fmt.Printf("Cookie value: %s \n", cookie)
|
||||
})
|
||||
|
||||
router.Run()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
@ -1933,3 +1964,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor
|
||||
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go.
|
||||
* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
|
||||
* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform.
|
||||
* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow.
|
||||
|
@ -122,7 +122,7 @@ func TestBasicAuth401(t *testing.T) {
|
||||
|
||||
assert.False(t, called)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
assert.Equal(t, "Basic realm=\"Authorization Required\"", w.HeaderMap.Get("WWW-Authenticate"))
|
||||
assert.Equal(t, "Basic realm=\"Authorization Required\"", w.Header().Get("WWW-Authenticate"))
|
||||
}
|
||||
|
||||
func TestBasicAuth401WithCustomRealm(t *testing.T) {
|
||||
@ -142,5 +142,5 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) {
|
||||
|
||||
assert.False(t, called)
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.HeaderMap.Get("WWW-Authenticate"))
|
||||
assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.Header().Get("WWW-Authenticate"))
|
||||
}
|
||||
|
@ -416,7 +416,6 @@ func (c *Context) PostFormArray(key string) []string {
|
||||
// a boolean value whether at least one value exists for the given key.
|
||||
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
||||
req := c.Request
|
||||
req.ParseForm()
|
||||
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
||||
if values := req.PostForm[key]; len(values) > 0 {
|
||||
return values, true
|
||||
@ -439,7 +438,6 @@ func (c *Context) PostFormMap(key string) map[string]string {
|
||||
// whether at least one value exists for the given key.
|
||||
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
|
||||
req := c.Request
|
||||
req.ParseForm()
|
||||
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
||||
dicts, exist := c.get(req.PostForm, key)
|
||||
|
||||
@ -467,6 +465,11 @@ func (c *Context) get(m map[string][]string, key string) (map[string]string, boo
|
||||
|
||||
// FormFile returns the first file for the provided form key.
|
||||
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
|
||||
if c.Request.MultipartForm == nil {
|
||||
if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, fh, err := c.Request.FormFile(name)
|
||||
return fh, err
|
||||
}
|
||||
|
@ -23,5 +23,5 @@ func TestContextRenderPureJSON(t *testing.T) {
|
||||
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
@ -84,6 +84,19 @@ func TestContextFormFile(t *testing.T) {
|
||||
assert.NoError(t, c.SaveUploadedFile(f, "test"))
|
||||
}
|
||||
|
||||
func TestContextFormFileFailed(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
mw := multipart.NewWriter(buf)
|
||||
mw.Close()
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
c.engine.MaxMultipartMemory = 8 << 20
|
||||
f, err := c.FormFile("file")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, f)
|
||||
}
|
||||
|
||||
func TestContextMultipartForm(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
mw := multipart.NewWriter(buf)
|
||||
@ -628,7 +641,7 @@ func TestContextRenderJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", 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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that the response is serialized as JSONP
|
||||
@ -642,7 +655,7 @@ func TestContextRenderJSONP(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "x({\"foo\":\"bar\"})", w.Body.String())
|
||||
assert.Equal(t, "application/javascript; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that the response is serialized as JSONP
|
||||
@ -656,7 +669,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", 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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no JSON is rendered if code is 204
|
||||
@ -668,7 +681,7 @@ func TestContextRenderNoContentJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that the response is serialized as JSON
|
||||
@ -682,7 +695,7 @@ func TestContextRenderAPIJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
||||
assert.Equal(t, "application/vnd.api+json", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no Custom JSON is rendered if code is 204
|
||||
@ -695,7 +708,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/vnd.api+json")
|
||||
assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json")
|
||||
}
|
||||
|
||||
// Tests that the response is serialized as JSON
|
||||
@ -708,7 +721,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no Custom JSON is rendered if code is 204
|
||||
@ -720,7 +733,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that the response is serialized as Secure JSON
|
||||
@ -734,7 +747,7 @@ func TestContextRenderSecureJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", 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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no Custom JSON is rendered if code is 204
|
||||
@ -746,7 +759,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
||||
@ -757,7 +770,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "application/json", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that the response executes the templates
|
||||
@ -773,7 +786,7 @@ func TestContextRenderHTML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextRenderHTML2(t *testing.T) {
|
||||
@ -797,7 +810,7 @@ func TestContextRenderHTML2(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no HTML is rendered if code is 204
|
||||
@ -811,7 +824,7 @@ func TestContextRenderNoContentHTML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextXML tests that the response is serialized as XML
|
||||
@ -824,7 +837,7 @@ func TestContextRenderXML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no XML is rendered if code is 204
|
||||
@ -836,7 +849,7 @@ func TestContextRenderNoContentXML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextString tests that the response is returned
|
||||
@ -849,7 +862,7 @@ func TestContextRenderString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "test string 2", w.Body.String())
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no String is rendered if code is 204
|
||||
@ -861,7 +874,7 @@ func TestContextRenderNoContentString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextString tests that the response is returned
|
||||
@ -875,7 +888,7 @@ func TestContextRenderHTMLString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "<html>string 3</html>", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no HTML String is rendered if code is 204
|
||||
@ -888,7 +901,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextData tests that the response can be written from `bytesting`
|
||||
@ -901,7 +914,7 @@ func TestContextRenderData(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "foo,bar", w.Body.String())
|
||||
assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// Tests that no Custom Data is rendered if code is 204
|
||||
@ -913,7 +926,7 @@ func TestContextRenderNoContentData(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
assert.Empty(t, w.Body.String())
|
||||
assert.Equal(t, "text/csv", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextRenderSSE(t *testing.T) {
|
||||
@ -942,7 +955,7 @@ func TestContextRenderFile(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextRenderYAML tests that the response is serialized as YAML
|
||||
@ -955,7 +968,7 @@ func TestContextRenderYAML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, "foo: bar\n", w.Body.String())
|
||||
assert.Equal(t, "application/x-yaml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf
|
||||
@ -979,7 +992,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
assert.Equal(t, string(protoData), w.Body.String())
|
||||
assert.Equal(t, "application/x-protobuf", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextHeaders(t *testing.T) {
|
||||
@ -1062,7 +1075,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", 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.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationWithXML(t *testing.T) {
|
||||
@ -1077,7 +1090,7 @@ func TestContextNegotiationWithXML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationWithHTML(t *testing.T) {
|
||||
@ -1095,7 +1108,7 @@ func TestContextNegotiationWithHTML(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "Hello gin", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationNotSupport(t *testing.T) {
|
||||
@ -1131,7 +1144,7 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) {
|
||||
assert.Empty(t, c.NegotiateFormat(MIMEJSON))
|
||||
}
|
||||
|
||||
func TestContextNegotiationFormatCustum(t *testing.T) {
|
||||
func TestContextNegotiationFormatCustom(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
@ -1627,9 +1640,9 @@ func TestContextRenderDataFromReader(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, body, w.Body.String())
|
||||
assert.Equal(t, contentType, w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, fmt.Sprintf("%d", contentLength), w.HeaderMap.Get("Content-Length"))
|
||||
assert.Equal(t, extraHeaders["Content-Disposition"], w.HeaderMap.Get("Content-Disposition"))
|
||||
assert.Equal(t, contentType, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
|
||||
assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
type TestResponseRecorder struct {
|
||||
|
13
coverage.sh
13
coverage.sh
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "mode: count" > coverage.out
|
||||
|
||||
for d in $(go list ./... | grep -E 'gin$|binding$|render$' | grep -v 'examples'); 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
|
22
debug.go
22
debug.go
@ -8,14 +8,21 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ginSupportMinGoVer = 6
|
||||
|
||||
// IsDebugging returns true if the framework is running in debug mode.
|
||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||
func IsDebugging() bool {
|
||||
return ginMode == debugCode
|
||||
}
|
||||
|
||||
// DebugPrintRouteFunc indicates debug log output format.
|
||||
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
|
||||
|
||||
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||
@ -44,14 +51,25 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
|
||||
|
||||
func debugPrint(format string, values ...interface{}) {
|
||||
if IsDebugging() {
|
||||
fmt.Printf("[GIN-debug] "+format, values...)
|
||||
fmt.Fprintf(os.Stderr, "[GIN-debug] "+format, values...)
|
||||
}
|
||||
}
|
||||
|
||||
func getMinVer(v string) (uint64, error) {
|
||||
first := strings.IndexByte(v, '.')
|
||||
last := strings.LastIndexByte(v, '.')
|
||||
if first == last {
|
||||
return strconv.ParseUint(v[first+1:], 10, 64)
|
||||
}
|
||||
return strconv.ParseUint(v[first+1:last], 10, 64)
|
||||
}
|
||||
|
||||
func debugPrintWARNINGDefault() {
|
||||
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
|
||||
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
||||
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
|
||||
|
||||
`)
|
||||
}
|
||||
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
|
||||
|
||||
`)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@ -88,7 +89,12 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
|
||||
debugPrintWARNINGDefault()
|
||||
SetMode(TestMode)
|
||||
})
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
m, e := getMinVer(runtime.Version())
|
||||
if e == nil && m <= ginSupportMinGoVer {
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
} else {
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebugPrintWARNINGNew(t *testing.T) {
|
||||
@ -129,3 +135,18 @@ func captureOutput(f func()) string {
|
||||
writer.Close()
|
||||
return <-out
|
||||
}
|
||||
|
||||
func TestGetMinVer(t *testing.T) {
|
||||
var m uint64
|
||||
var e error
|
||||
_, e = getMinVer("go1")
|
||||
assert.NotNil(t, e)
|
||||
m, e = getMinVer("go1.1")
|
||||
assert.Equal(t, uint64(1), m)
|
||||
assert.Nil(t, e)
|
||||
m, e = getMinVer("go1.1.1")
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, uint64(1), m)
|
||||
_, e = getMinVer("go1.1.1.1")
|
||||
assert.NotNil(t, e)
|
||||
}
|
||||
|
137
docs/how-to-build-an-effective-middleware.md
Normal file
137
docs/how-to-build-an-effective-middleware.md
Normal file
@ -0,0 +1,137 @@
|
||||
# How to build one effective middleware?
|
||||
|
||||
## Consitituent part
|
||||
|
||||
The middleware has two parts:
|
||||
|
||||
- part one is what is executed once, when you initalize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime.
|
||||
|
||||
- part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler furnction.
|
||||
|
||||
```go
|
||||
func funcName(params string) gin.HandlerFunc {
|
||||
// <---
|
||||
// This is part one
|
||||
// --->
|
||||
// The follow code is an example
|
||||
if err := check(params); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
// <---
|
||||
// This is part two
|
||||
// --->
|
||||
// The follow code is an example
|
||||
c.Set("TestVar", params)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Execution process
|
||||
|
||||
Firstly, we have the follow example code:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
|
||||
router.Use(globalMiddleware())
|
||||
|
||||
router.GET("/rest/n/api/*some", mid1(), mid2(), handler)
|
||||
|
||||
router.Run()
|
||||
}
|
||||
|
||||
func globalMiddleware() gin.HandlerFunc {
|
||||
fmt.Println("globalMiddleware...1")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
fmt.Println("globalMiddleware...2")
|
||||
c.Next()
|
||||
fmt.Println("globalMiddleware...3")
|
||||
}
|
||||
}
|
||||
|
||||
func handler(c *gin.Context) {
|
||||
fmt.Println("exec handler.")
|
||||
}
|
||||
|
||||
func mid1() gin.HandlerFunc {
|
||||
fmt.Println("mid1...1")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
|
||||
fmt.Println("mid1...2")
|
||||
c.Next()
|
||||
fmt.Println("mid1...3")
|
||||
}
|
||||
}
|
||||
|
||||
func mid2() gin.HandlerFunc {
|
||||
fmt.Println("mid2...1")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
fmt.Println("mid2...2")
|
||||
c.Next()
|
||||
fmt.Println("mid2...3")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
According to [Consitituent part](#consitituent-part) said, when we run the gin process, **part one** will execute firstly and will print the follow information:
|
||||
|
||||
```go
|
||||
globalMiddleware...1
|
||||
mid1...1
|
||||
mid2...1
|
||||
```
|
||||
|
||||
And init order are:
|
||||
|
||||
```go
|
||||
globalMiddleware...1
|
||||
|
|
||||
v
|
||||
mid1...1
|
||||
|
|
||||
v
|
||||
mid2...1
|
||||
```
|
||||
|
||||
When we curl one request `curl -v localhost:8080/rest/n/api/some`, **part two** will execute their middleware and output the following information:
|
||||
|
||||
```go
|
||||
globalMiddleware...2
|
||||
mid1...2
|
||||
mid2...2
|
||||
exec handler.
|
||||
mid2...3
|
||||
mid1...3
|
||||
globalMiddleware...3
|
||||
```
|
||||
|
||||
In other words, run order are:
|
||||
|
||||
```go
|
||||
globalMiddleware...2
|
||||
|
|
||||
v
|
||||
mid1...2
|
||||
|
|
||||
v
|
||||
mid2...2
|
||||
|
|
||||
v
|
||||
exec handler.
|
||||
|
|
||||
v
|
||||
mid2...3
|
||||
|
|
||||
v
|
||||
mid1...3
|
||||
|
|
||||
v
|
||||
globalMiddleware...3
|
||||
```
|
16
gin.go
16
gin.go
@ -38,9 +38,10 @@ func (c HandlersChain) Last() HandlerFunc {
|
||||
|
||||
// RouteInfo represents a request route's specification which contains method and path and its handler.
|
||||
type RouteInfo struct {
|
||||
Method string
|
||||
Path string
|
||||
Handler string
|
||||
Method string
|
||||
Path string
|
||||
Handler string
|
||||
HandlerFunc HandlerFunc
|
||||
}
|
||||
|
||||
// RoutesInfo defines a RouteInfo array.
|
||||
@ -266,10 +267,12 @@ func (engine *Engine) Routes() (routes RoutesInfo) {
|
||||
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
||||
path += root.path
|
||||
if len(root.handlers) > 0 {
|
||||
handlerFunc := root.handlers.Last()
|
||||
routes = append(routes, RouteInfo{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Handler: nameOfFunction(root.handlers.Last()),
|
||||
Method: method,
|
||||
Path: path,
|
||||
Handler: nameOfFunction(handlerFunc),
|
||||
HandlerFunc: handlerFunc,
|
||||
})
|
||||
}
|
||||
for _, child := range root.children {
|
||||
@ -336,7 +339,6 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
func (engine *Engine) HandleContext(c *Context) {
|
||||
c.reset()
|
||||
engine.handleHTTPRequest(c)
|
||||
engine.pool.Put(c)
|
||||
}
|
||||
|
||||
func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||
|
@ -6,12 +6,14 @@ package gin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -19,7 +21,14 @@ import (
|
||||
)
|
||||
|
||||
func testRequest(t *testing.T, url string) {
|
||||
resp, err := http.Get(url)
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
@ -44,6 +53,22 @@ func TestRunEmpty(t *testing.T) {
|
||||
testRequest(t, "http://localhost:8080/example")
|
||||
}
|
||||
|
||||
func TestRunTLS(t *testing.T) {
|
||||
router := New()
|
||||
go func() {
|
||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||
|
||||
assert.NoError(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
|
||||
}()
|
||||
|
||||
// have to wait for the goroutine to start and run the server
|
||||
// otherwise the main thread will complete
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
assert.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
|
||||
testRequest(t, "https://localhost:8443/example")
|
||||
}
|
||||
|
||||
func TestRunEmptyWithEnv(t *testing.T) {
|
||||
os.Setenv("PORT", "3123")
|
||||
router := New()
|
||||
@ -119,6 +144,29 @@ func TestWithHttptestWithAutoSelectedPort(t *testing.T) {
|
||||
testRequest(t, ts.URL+"/example")
|
||||
}
|
||||
|
||||
func TestConcurrentHandleContext(t *testing.T) {
|
||||
router := New()
|
||||
router.GET("/", func(c *Context) {
|
||||
c.Request.URL.Path = "/example"
|
||||
router.HandleContext(c)
|
||||
})
|
||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
iterations := 200
|
||||
wg.Add(iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func() {
|
||||
testRequest(t, ts.URL+"/")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// func TestWithHttptestWithSpecifiedPort(t *testing.T) {
|
||||
// router := New()
|
||||
// router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||
|
252
gin_test.go
252
gin_test.go
@ -10,6 +10,7 @@ import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@ -22,105 +23,105 @@ func formatAsDate(t time.Time) string {
|
||||
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
|
||||
}
|
||||
|
||||
func setupHTMLFiles(t *testing.T, mode string, tls bool) func() {
|
||||
go func() {
|
||||
SetMode(mode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine)) *httptest.Server {
|
||||
SetMode(mode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
loadMethod(router)
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"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", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")
|
||||
} else {
|
||||
router.Run(":8888")
|
||||
}
|
||||
}()
|
||||
t.Log("waiting 1 second for server startup")
|
||||
time.Sleep(1 * time.Second)
|
||||
return func() {}
|
||||
})
|
||||
|
||||
var ts *httptest.Server
|
||||
|
||||
if tls {
|
||||
ts = httptest.NewTLSServer(router)
|
||||
} else {
|
||||
ts = httptest.NewServer(router)
|
||||
}
|
||||
|
||||
return ts
|
||||
}
|
||||
|
||||
func setupHTMLGlob(t *testing.T, mode string, tls bool) func() {
|
||||
go func() {
|
||||
SetMode(mode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"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", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem")
|
||||
} else {
|
||||
router.Run(":8888")
|
||||
}
|
||||
}()
|
||||
t.Log("waiting 1 second for server startup")
|
||||
time.Sleep(1 * time.Second)
|
||||
return func() {}
|
||||
}
|
||||
func TestLoadHTMLGlobDebugMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
DebugMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
func TestLoadHTMLGlob(t *testing.T) {
|
||||
td := setupHTMLGlob(t, DebugMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
func TestLoadHTMLGlob2(t *testing.T) {
|
||||
td := setupHTMLGlob(t, TestMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
func TestLoadHTMLGlobTestMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
TestMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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")
|
||||
func TestLoadHTMLGlobReleaseMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
ReleaseMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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)
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
DebugMode,
|
||||
true,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
// Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
@ -128,29 +129,33 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
res, err := client.Get("https://127.0.0.1:9999/test")
|
||||
res, err := client.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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) {
|
||||
time.Now()
|
||||
td := setupHTMLGlob(t, DebugMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/raw")
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
DebugMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLGlob("./testdata/template/*")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -164,59 +169,77 @@ func TestCreateEngine(t *testing.T) {
|
||||
assert.Empty(t, router.Handlers)
|
||||
}
|
||||
|
||||
// func TestLoadHTMLDebugMode(t *testing.T) {
|
||||
// router := New()
|
||||
// SetMode(DebugMode)
|
||||
// router.LoadHTMLGlob("*.testtmpl")
|
||||
// r := router.HTMLRender.(render.HTMLDebug)
|
||||
// assert.Empty(t, r.Files)
|
||||
// assert.Equal(t, "*.testtmpl", r.Glob)
|
||||
//
|
||||
// router.LoadHTMLFiles("index.html.testtmpl", "login.html.testtmpl")
|
||||
// r = router.HTMLRender.(render.HTMLDebug)
|
||||
// assert.Empty(t, r.Glob)
|
||||
// assert.Equal(t, []string{"index.html", "login.html"}, r.Files)
|
||||
// SetMode(TestMode)
|
||||
// }
|
||||
func TestLoadHTMLFilesTestMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
TestMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
func TestLoadHTMLFiles(t *testing.T) {
|
||||
td := setupHTMLFiles(t, TestMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||
td()
|
||||
}
|
||||
|
||||
func TestLoadHTMLFiles2(t *testing.T) {
|
||||
td := setupHTMLFiles(t, DebugMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
func TestLoadHTMLFilesDebugMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
DebugMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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")
|
||||
func TestLoadHTMLFilesReleaseMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
ReleaseMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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)
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
TestMode,
|
||||
true,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
// Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
@ -224,28 +247,33 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
res, err := client.Get("https://127.0.0.1:9999/test")
|
||||
res, err := client.Get(fmt.Sprintf("%s/test", ts.URL))
|
||||
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) {
|
||||
time.Now()
|
||||
td := setupHTMLFiles(t, TestMode, false)
|
||||
res, err := http.Get("http://127.0.0.1:8888/raw")
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
TestMode,
|
||||
false,
|
||||
func(router *Engine) {
|
||||
router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl")
|
||||
},
|
||||
)
|
||||
defer ts.Close()
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
func TestAddRoute(t *testing.T) {
|
||||
|
17
go.mod
17
go.mod
@ -1,25 +1,28 @@
|
||||
module github.com/gin-gonic/gin
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 // indirect
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15
|
||||
github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b
|
||||
github.com/json-iterator/go v1.1.5
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227
|
||||
github.com/mattn/go-isatty v0.0.3
|
||||
github.com/mattn/go-isatty v0.0.4
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6
|
||||
github.com/thinkerou/favicon v0.1.0
|
||||
github.com/ugorji/go v1.1.1
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e // indirect
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba // indirect
|
||||
google.golang.org/grpc v1.15.0
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2
|
||||
|
33
go.sum
33
go.sum
@ -1,7 +1,10 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296 h1:tRsilif6pbtt+PX6uRoyGd+qR+4ZPucFZLHlc3Ak6z8=
|
||||
github.com/campoy/embedmd v0.0.0-20171205015432-c59ce00e0296/go.mod h1:/dBk8ICkslPCmyRdn4azP+QvBxL6Eg3EYxUGI9xMMFw=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 h1:HqkufMBR7waVfFFRABWqHa1WgTvjtVDJTLJe3CR576A=
|
||||
github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY=
|
||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
|
||||
@ -16,13 +19,13 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
|
||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
|
||||
github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b h1:X61dhFTE1Au92SvyF8HyAwdjWqiSdfBgFR7wTxC0+uU=
|
||||
github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo=
|
||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
@ -31,23 +34,27 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6 h1:d/LEgOfWe+AlOCz/kzmkvlO+gq9LRGhjSHqt2nx8Muc=
|
||||
github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s=
|
||||
github.com/thinkerou/favicon v0.1.0 h1:eWMISKTpHq2G8HOuKn7ydD55j5DDehx94b0C2y8ABMs=
|
||||
github.com/thinkerou/favicon v0.1.0/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s=
|
||||
github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w=
|
||||
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU=
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd h1:cgsAvzdqkDKdI02tIvDjO225vDPHMDCgfKqx5KEVI7U=
|
||||
golang.org/x/lint v0.0.0-20181011164241-5906bd5c48cd/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 h1:Y/KGZSOdz/2r0WJ9Mkmz6NJBusp0kiNx1Cn82lzJQ6w=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba h1:nZJIJPGow0Kf9bU9QTc1U6OXbs/7Hu4e+cNv+hxH+Zc=
|
||||
golang.org/x/sys v0.0.0-20181011152604-fa43e7bc11ba/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
var (
|
||||
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
||||
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
||||
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
||||
yellow = string([]byte{27, 91, 57, 48, 59, 52, 51, 109})
|
||||
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
||||
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
|
||||
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
||||
|
@ -85,7 +85,7 @@ func TestLogger(t *testing.T) {
|
||||
func TestColorForMethod(t *testing.T) {
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 52, 109}), colorForMethod("GET"), "get should be blue")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 54, 109}), colorForMethod("POST"), "post should be cyan")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForMethod("PUT"), "put should be yellow")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForMethod("DELETE"), "delete should be red")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForMethod("PATCH"), "patch should be green")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 53, 109}), colorForMethod("HEAD"), "head should be magenta")
|
||||
@ -96,7 +96,7 @@ func TestColorForMethod(t *testing.T) {
|
||||
func TestColorForStatus(t *testing.T) {
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 50, 109}), colorForStatus(http.StatusOK), "2xx should be green")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 55, 109}), colorForStatus(http.StatusMovedPermanently), "3xx should be white")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 48, 59, 52, 51, 109}), colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
||||
assert.Equal(t, string([]byte{27, 91, 57, 55, 59, 52, 49, 109}), colorForStatus(2), "other things should be red")
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,12 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
|
||||
if err := recover(); err != nil {
|
||||
if logger != nil {
|
||||
stack := stack(3)
|
||||
httprequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
||||
if IsDebugging() {
|
||||
httprequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
||||
} else {
|
||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s", timeFormat(time.Now()), err, stack, reset)
|
||||
}
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -24,9 +24,19 @@ func TestPanicInHandler(t *testing.T) {
|
||||
w := performRequest(router, "GET", "/recovery")
|
||||
// TEST
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
assert.Contains(t, buffer.String(), "GET /recovery")
|
||||
assert.Contains(t, buffer.String(), "panic recovered")
|
||||
assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
|
||||
assert.Contains(t, buffer.String(), "TestPanicInHandler")
|
||||
assert.NotContains(t, buffer.String(), "GET /recovery")
|
||||
|
||||
// Debug mode prints the request
|
||||
SetMode(DebugMode)
|
||||
// RUN
|
||||
w = performRequest(router, "GET", "/recovery")
|
||||
// TEST
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
assert.Contains(t, buffer.String(), "GET /recovery")
|
||||
|
||||
}
|
||||
|
||||
// TestPanicWithAbort assert that panic has been recovered even if context.Abort was used.
|
||||
|
@ -480,7 +480,7 @@ func TestRenderReader(t *testing.T) {
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, w.Body.String())
|
||||
assert.Equal(t, "image/png", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, strconv.Itoa(len(body)), w.HeaderMap.Get("Content-Length"))
|
||||
assert.Equal(t, headers["Content-Disposition"], w.HeaderMap.Get("Content-Disposition"))
|
||||
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length"))
|
||||
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ type IRouter interface {
|
||||
Group(string, ...HandlerFunc) *RouterGroup
|
||||
}
|
||||
|
||||
// Iroutes defins all router handle interface.
|
||||
// IRoutes defines all router handle interface.
|
||||
type IRoutes interface {
|
||||
Use(...HandlerFunc) IRoutes
|
||||
|
||||
|
@ -267,7 +267,7 @@ func TestRouteStaticFile(t *testing.T) {
|
||||
assert.Equal(t, w, w2)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "Gin Web Framework", w.Body.String())
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
|
||||
w3 := performRequest(router, "HEAD", "/using_static/"+filename)
|
||||
w4 := performRequest(router, "HEAD", "/result")
|
||||
@ -285,7 +285,7 @@ func TestRouteStaticListingDir(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "gin.go")
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
// TestHandleHeadToDir - ensure the root/sub dir handles properly
|
||||
@ -312,10 +312,10 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "package gin")
|
||||
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.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.HeaderMap.Get("Expires"))
|
||||
assert.Equal(t, "Gin Framework", w.HeaderMap.Get("x-GIN"))
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
|
||||
assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
|
||||
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
||||
}
|
||||
|
||||
func TestRouteNotAllowedEnabled(t *testing.T) {
|
||||
|
25
tools.go
Normal file
25
tools.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build tools
|
||||
|
||||
// This file exists to cause `go mod` and `go get` to believe these tools
|
||||
// are dependencies, even though they are not runtime dependencies of any
|
||||
// gin package. This means they will appear in `go.mod` file, but will not
|
||||
// be a part of the build.
|
||||
|
||||
package gin
|
||||
|
||||
import (
|
||||
_ "github.com/campoy/embedmd"
|
||||
_ "github.com/client9/misspell/cmd/misspell"
|
||||
_ "github.com/dustin/go-broadcast"
|
||||
_ "github.com/gin-gonic/autotls"
|
||||
_ "github.com/jessevdk/go-assets"
|
||||
_ "github.com/manucorporat/stats"
|
||||
_ "github.com/thinkerou/favicon"
|
||||
_ "golang.org/x/crypto/acme/autocert"
|
||||
_ "golang.org/x/lint/golint"
|
||||
_ "google.golang.org/grpc"
|
||||
)
|
11
tree.go
11
tree.go
@ -193,9 +193,16 @@ func (n *node) addRoute(path string, handlers HandlersChain) {
|
||||
}
|
||||
}
|
||||
|
||||
panic("path segment '" + path +
|
||||
pathSeg := path
|
||||
if n.nType != catchAll {
|
||||
pathSeg = strings.SplitN(path, "/", 2)[0]
|
||||
}
|
||||
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
|
||||
panic("'" + pathSeg +
|
||||
"' in new path '" + fullPath +
|
||||
"' conflicts with existing wildcard '" + n.path +
|
||||
"' in path '" + fullPath + "'")
|
||||
"' in existing prefix '" + prefix +
|
||||
"'")
|
||||
}
|
||||
|
||||
c := path[0]
|
||||
|
42
tree_test.go
42
tree_test.go
@ -5,7 +5,9 @@
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -656,3 +658,43 @@ func TestTreeInvalidNodeType(t *testing.T) {
|
||||
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeWildcardConflictEx(t *testing.T) {
|
||||
conflicts := [...]struct {
|
||||
route string
|
||||
segPath string
|
||||
existPath string
|
||||
existSegPath string
|
||||
}{
|
||||
{"/who/are/foo", "/foo", `/who/are/\*you`, `/\*you`},
|
||||
{"/who/are/foo/", "/foo/", `/who/are/\*you`, `/\*you`},
|
||||
{"/who/are/foo/bar", "/foo/bar", `/who/are/\*you`, `/\*you`},
|
||||
{"/conxxx", "xxx", `/con:tact`, `:tact`},
|
||||
{"/conooo/xxx", "ooo", `/con:tact`, `:tact`},
|
||||
}
|
||||
|
||||
for _, conflict := range conflicts {
|
||||
// I have to re-create a 'tree', because the 'tree' will be
|
||||
// in an inconsistent state when the loop recovers from the
|
||||
// panic which threw by 'addRoute' function.
|
||||
tree := &node{}
|
||||
routes := [...]string{
|
||||
"/con:tact",
|
||||
"/who/are/*you",
|
||||
"/who/foo/hello",
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
}
|
||||
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(conflict.route, fakeHandler(conflict.route))
|
||||
})
|
||||
|
||||
if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'",
|
||||
conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) {
|
||||
t.Fatalf("invalid wildcard conflict error (%v)", recv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
vendor/vendor.json
vendored
43
vendor/vendor.json
vendored
@ -6,7 +6,9 @@
|
||||
"checksumSHA1": "CSPbwbyzqA6sfORicn4HFtIhF/c=",
|
||||
"path": "github.com/davecgh/go-spew/spew",
|
||||
"revision": "8991bc29aa16c548c550c7ff78260e27b9ab7c73",
|
||||
"revisionTime": "2018-02-21T22:46:20Z"
|
||||
"revisionTime": "2018-02-21T22:46:20Z",
|
||||
"version": "v1.1",
|
||||
"versionExact": "v1.1.1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QeKwBtN2df+j+4stw3bQJ6yO4EY=",
|
||||
@ -15,12 +17,12 @@
|
||||
"revisionTime": "2017-01-09T09:34:21Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Pyou8mceOASSFxc7GeXZuVdSMi0=",
|
||||
"checksumSHA1": "mE9XW26JSpe4meBObM6J/Oeq0eg=",
|
||||
"path": "github.com/golang/protobuf/proto",
|
||||
"revision": "b4deda0973fb4c70b50d226b1af49f3da59f5265",
|
||||
"revisionTime": "2018-04-30T18:52:41Z",
|
||||
"version": "v1.1.0",
|
||||
"versionExact": "v1.1.0"
|
||||
"revision": "aa810b61a9c79d51363740d207bb46cf8e620ed5",
|
||||
"revisionTime": "2018-08-14T21:14:27Z",
|
||||
"version": "v1.2",
|
||||
"versionExact": "v1.2.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "WqeEgS7pqqkwK8mlrAZmDgtWJMY=",
|
||||
@ -31,19 +33,19 @@
|
||||
"versionExact": "v1.1.5"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "y/A5iuvwjytQE2CqVuphQRXR2nI=",
|
||||
"checksumSHA1": "w5RcOnfv5YDr3j2bd1YydkPiZx4=",
|
||||
"path": "github.com/mattn/go-isatty",
|
||||
"revision": "0360b2af4f38e8d38c7fce2a9f4e702702d73a39",
|
||||
"revisionTime": "2017-09-25T05:34:41Z",
|
||||
"version": "v0.0.3",
|
||||
"versionExact": "v0.0.3"
|
||||
"revision": "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c",
|
||||
"revisionTime": "2017-11-07T05:05:31Z",
|
||||
"version": "v0.0",
|
||||
"versionExact": "v0.0.4"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=",
|
||||
"path": "github.com/stretchr/testify/assert",
|
||||
"revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686",
|
||||
"revisionTime": "2018-05-06T18:05:49Z",
|
||||
"version": "v1.2.2",
|
||||
"version": "v1.2",
|
||||
"versionExact": "v1.2.2"
|
||||
},
|
||||
{
|
||||
@ -51,15 +53,20 @@
|
||||
"path": "github.com/ugorji/go/codec",
|
||||
"revision": "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab",
|
||||
"revisionTime": "2018-04-07T10:07:33Z",
|
||||
"version": "v1.1.1",
|
||||
"version": "v1.1",
|
||||
"versionExact": "v1.1.1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
||||
"comment": "release-branch.go1.7",
|
||||
"checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=",
|
||||
"path": "golang.org/x/net/context",
|
||||
"revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a",
|
||||
"revisionTime": "2016-10-18T08:54:36Z"
|
||||
"revision": "49bb7cea24b1df9410e1712aa6433dae904ff66a",
|
||||
"revisionTime": "2018-10-11T05:27:23Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "SiJNkx+YGtq3Gtr6Ldu6OW83O+U=",
|
||||
"path": "golang.org/x/sys/unix",
|
||||
"revision": "fa43e7bc11baaae89f3f902b2b4d832b68234844",
|
||||
"revisionTime": "2018-10-11T14:35:51Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=",
|
||||
@ -74,7 +81,7 @@
|
||||
"path": "gopkg.in/yaml.v2",
|
||||
"revision": "5420a8b6744d3b0345ab293f6fcba19c978f1183",
|
||||
"revisionTime": "2018-03-28T19:50:20Z",
|
||||
"version": "v2.2.1",
|
||||
"version": "v2.2",
|
||||
"versionExact": "v2.2.1"
|
||||
}
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user