diff --git a/api/repos.go b/api/repos.go index 0a549fc..724a29d 100644 --- a/api/repos.go +++ b/api/repos.go @@ -11,7 +11,7 @@ import ( "github.com/lgtmco/lgtm/shared/token" "github.com/lgtmco/lgtm/store" - log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" ) @@ -20,7 +20,7 @@ func GetRepos(c *gin.Context) { user := session.User(c) repos, err := cache.GetRepos(c, user) if err != nil { - log.Errorf("Error getting remote repository list. %s", err) + logrus.Errorf("Error getting remote repository list. %s", err) c.String(500, "Error getting remote repository list") return } @@ -32,7 +32,7 @@ func GetRepos(c *gin.Context) { repom, err := store.GetRepoIntersectMap(c, repos) if err != nil { - log.Errorf("Error getting active repository list. %s", err) + logrus.Errorf("Error getting active repository list. %s", err) c.String(500, "Error getting active repository list") return } @@ -45,7 +45,7 @@ func GetRepos(c *gin.Context) { repoc[i] = repo_ } } - c.IndentedJSON(200, repoc) + c.JSON(200, repoc) } // GetRepo gets the repository by slug. @@ -56,7 +56,7 @@ func GetRepo(c *gin.Context) { ) repo, err := store.GetRepoOwnerName(c, owner, name) if err != nil { - log.Errorf("Error getting repository %s. %s", name, err) + logrus.Errorf("Error getting repository %s. %s", name, err) c.String(404, "Error getting repository %s", name) return } @@ -111,7 +111,7 @@ func PostRepo(c *gin.Context) { c.String(500, "Error activating the repository. %s", err) return } - c.IndentedJSON(200, repo) + c.JSON(200, repo) } // DeleteRepo deletes a repository configuration. @@ -123,13 +123,13 @@ func DeleteRepo(c *gin.Context) { ) repo, err := store.GetRepoOwnerName(c, owner, name) if err != nil { - log.Errorf("Error getting repository %s. %s", name, err) + logrus.Errorf("Error getting repository %s. %s", name, err) c.AbortWithStatus(404) return } err = store.DeleteRepo(c, repo) if err != nil { - log.Errorf("Error deleting repository %s. %s", name, err) + logrus.Errorf("Error deleting repository %s. %s", name, err) c.AbortWithStatus(500) return } @@ -139,7 +139,7 @@ func DeleteRepo(c *gin.Context) { ) err = remote.DelHook(c, user, repo, link) if err != nil { - log.Errorf("Error deleting repository hook for %s. %s", name, err) + logrus.Errorf("Error deleting repository hook for %s. %s", name, err) } c.String(200, "") } diff --git a/api/teams.go b/api/teams.go index a09a3a2..bf67657 100644 --- a/api/teams.go +++ b/api/teams.go @@ -5,7 +5,7 @@ import ( "github.com/lgtmco/lgtm/model" "github.com/lgtmco/lgtm/router/middleware/session" - log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" ) @@ -14,7 +14,7 @@ func GetTeams(c *gin.Context) { user := session.User(c) teams, err := cache.GetTeams(c, user) if err != nil { - log.Errorf("Error getting team list. %s", err) + logrus.Errorf("Error getting teams for user %s. %s", user.Login, err) c.String(500, "Error getting team list") return } diff --git a/api/teams_test.go b/api/teams_test.go new file mode 100644 index 0000000..f61779e --- /dev/null +++ b/api/teams_test.go @@ -0,0 +1,79 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/lgtmco/lgtm/model" + + cache "github.com/lgtmco/lgtm/cache/mock" + remote "github.com/lgtmco/lgtm/remote/mock" + + "github.com/Sirupsen/logrus" + "github.com/franela/goblin" + "github.com/gin-gonic/gin" +) + +func TestTeams(t *testing.T) { + gin.SetMode(gin.TestMode) + logrus.SetOutput(ioutil.Discard) + + g := goblin.Goblin(t) + + g.Describe("Team endpoint", func() { + g.It("Should return the team list", func() { + cache := new(cache.Cache) + cache.On("Get", "teams:octocat").Return(fakeTeams, nil).Once() + + e := gin.New() + e.NoRoute(GetTeams) + e.Use(func(c *gin.Context) { + c.Set("user", fakeUser) + c.Set("cache", cache) + }) + + w := httptest.NewRecorder() + r, _ := http.NewRequest("GET", "/", nil) + e.ServeHTTP(w, r) + + // the user is appended to the team list so we retrieve a full list of + // accounts to which the user has access. + teams := append(fakeTeams, &model.Team{ + Login: fakeUser.Login, + }) + + want, _ := json.Marshal(teams) + got := strings.TrimSpace(w.Body.String()) + g.Assert(got).Equal(string(want)) + g.Assert(w.Code).Equal(200) + }) + + g.It("Should return a 500 error", func() { + remote := new(remote.Remote) + cache := new(cache.Cache) + cache.On("Get", "teams:octocat").Return(nil, fmt.Errorf("Not Found")).Once() + remote.On("GetTeams", fakeUser).Return(nil, fmt.Errorf("Not Found")).Once() + + e := gin.New() + e.NoRoute(GetTeams) + e.Use(func(c *gin.Context) { + c.Set("user", fakeUser) + c.Set("cache", cache) + c.Set("remote", remote) + }) + + w := httptest.NewRecorder() + r, _ := http.NewRequest("GET", "/", nil) + e.ServeHTTP(w, r) + + got := strings.TrimSpace(w.Body.String()) + g.Assert(got).Equal("Error getting team list") + g.Assert(w.Code).Equal(500) + }) + }) +} diff --git a/api/user_test.go b/api/user_test.go new file mode 100644 index 0000000..b47d681 --- /dev/null +++ b/api/user_test.go @@ -0,0 +1,51 @@ +package api + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/Sirupsen/logrus" + "github.com/lgtmco/lgtm/model" + + "github.com/franela/goblin" + "github.com/gin-gonic/gin" +) + +func TestUsers(t *testing.T) { + gin.SetMode(gin.TestMode) + logrus.SetOutput(ioutil.Discard) + + g := goblin.Goblin(t) + + g.Describe("User endpoint", func() { + g.It("Should return the authenticated user", func() { + + e := gin.New() + e.NoRoute(GetUser) + e.Use(func(c *gin.Context) { + c.Set("user", fakeUser) + }) + + w := httptest.NewRecorder() + r, _ := http.NewRequest("GET", "/", nil) + e.ServeHTTP(w, r) + + want, _ := json.Marshal(fakeUser) + got := strings.TrimSpace(w.Body.String()) + g.Assert(got).Equal(string(want)) + g.Assert(w.Code).Equal(200) + }) + }) +} + +var ( + fakeUser = &model.User{Login: "octocat"} + fakeTeams = []*model.Team{ + {Login: "drone"}, + {Login: "docker"}, + } +) diff --git a/vendor/github.com/gin-gonic/gin/CHANGELOG.md b/vendor/github.com/gin-gonic/gin/CHANGELOG.md index 5b5b6ad..82f1bea 100644 --- a/vendor/github.com/gin-gonic/gin/CHANGELOG.md +++ b/vendor/github.com/gin-gonic/gin/CHANGELOG.md @@ -2,7 +2,7 @@ ###Gin 1.0rc2 (...) -- [PERFORMANCE] Fast path for writting Content-Type. +- [PERFORMANCE] Fast path for writing Content-Type. - [PERFORMANCE] Much faster 404 routing - [PERFORMANCE] Allocation optimizations - [PERFORMANCE] Faster root tree lookup diff --git a/vendor/github.com/gin-gonic/gin/README.md b/vendor/github.com/gin-gonic/gin/README.md index e83952d..99a6d0b 100644 --- a/vendor/github.com/gin-gonic/gin/README.md +++ b/vendor/github.com/gin-gonic/gin/README.md @@ -1,18 +1,19 @@ #Gin Web Framework - + [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![Coverage Status](https://coveralls.io/repos/gin-gonic/gin/badge.svg?branch=master)](https://coveralls.io/r/gin-gonic/gin?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) [![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin) [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Gin is a web framework written in 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. +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. ![Gin console logger](https://gin-gonic.github.io/gin/other/console.png) -``` +```sh $ cat test.go ``` ```go @@ -23,9 +24,11 @@ import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") + c.JSON(200, gin.H{ + "message": "pong", + }) }) - r.Run(":8080") // listen and serve on 0.0.0.0:8080 + r.Run() // listen and server on 0.0.0.0:8080 } ``` @@ -83,14 +86,21 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 ## Start using it 1. Download and install it: -```sh -go get github.com/gin-gonic/gin -``` + ```sh + $ go get github.com/gin-gonic/gin + ``` + 2. Import it in your code: -```go -import "github.com/gin-gonic/gin" -``` + ```go + import "github.com/gin-gonic/gin" + ``` + +3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`. + + ```go + import "net/http" + ``` ##API Examples @@ -110,8 +120,10 @@ func main() { router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options) - // Listen and server on 0.0.0.0:8080 - router.Run(":8080") + // By default it serves on :8080 unless a + // PORT environment variable was defined. + router.Run() + // router.Run(":3000") for a hard coded port } ``` @@ -128,7 +140,7 @@ func main() { }) // However, this one will match /user/john/ and also /user/john/send - // If no other routers match /user/john, it will redirect to /user/join/ + // If no other routers match /user/john, it will redirect to /user/john/ router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") @@ -143,17 +155,17 @@ func main() { #### Querystring parameters ```go func main() { - router := gin.Default() + router := gin.Default() - // Query string parameters are parsed using the existing underlying request object. - // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe - router.GET("/welcome", func(c *gin.Context) { - firstname := c.DefaultQuery("firstname", "Guest") - lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + // Query string parameters are parsed using the existing underlying request object. + // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe + router.GET("/welcome", func(c *gin.Context) { + firstname := c.DefaultQuery("firstname", "Guest") + lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") - c.String(http.StatusOK, "Hello %s %s", firstname, lastname) - }) - router.Run(":8080") + c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + }) + router.Run(":8080") } ``` @@ -161,18 +173,19 @@ func main() { ```go func main() { - router := gin.Default() + router := gin.Default() - router.POST("/form_post", func(c *gin.Context) { - message := c.PostForm("message") - nick := c.DefaultPostForm("nick", "anonymous") + router.POST("/form_post", func(c *gin.Context) { + message := c.PostForm("message") + nick := c.DefaultPostForm("nick", "anonymous") - c.JSON(200, gin.H{ - "status": "posted", - "message": message, - }) - }) - router.Run(":8080") + c.JSON(200, gin.H{ + "status": "posted", + "message": message, + "nick": nick, + }) + }) + router.Run(":8080") } ``` @@ -190,21 +203,48 @@ func main() { router := gin.Default() router.POST("/post", func(c *gin.Context) { - id := c.Query("id") - page := c.DefaultQuery("id", "0") - name := c.PostForm("name") - message := c.PostForm("message") - fmt.Println("id: %s; page: %s; name: %s; message: %s", id, page, name, message) + id := c.Query("id") + page := c.DefaultQuery("page", "0") + name := c.PostForm("name") + message := c.PostForm("message") + + fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080") } ``` ``` -id: 1234; page: 0; name: manu; message: this_is_great +id: 1234; page: 1; name: manu; message: this_is_great ``` +### Another example: upload file + +References issue [#548](https://github.com/gin-gonic/gin/issues/548). + +```go +func main() { + router := gin.Default() + + router.POST("/upload", func(c *gin.Context) { + + file, header , err := c.Request.FormFile("upload") + filename := header.Filename + fmt.Println(header.Filename) + out, err := os.Create("./tmp/"+filename+".png") + if err != nil { + log.Fatal(err) + } + defer out.Close() + _, err = io.Copy(out, file) + if err != nil { + log.Fatal(err) + } + }) + router.Run(":8080") +} +``` #### Grouping routes ```go @@ -261,7 +301,7 @@ func main() { // Authorization group // authorized := r.Group("/", AuthRequired()) - // exactly the same than: + // exactly the same as: authorized := r.Group("/") // per group middleware! in this case we use the custom created // AuthRequired() middleware just in the "authorized" group. @@ -301,30 +341,30 @@ type Login struct { func main() { router := gin.Default() - // Example for binding JSON ({"user": "manu", "password": "123"}) + // Example for binding JSON ({"user": "manu", "password": "123"}) router.POST("/loginJSON", func(c *gin.Context) { var json Login - if c.BindJSON(&json) == nil { - if json.User == "manu" && json.Password == "123" { - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - } else { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - } - } + if c.BindJSON(&json) == nil { + if json.User == "manu" && json.Password == "123" { + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + } + } }) - // Example for binding a HTML form (user=manu&password=123) - router.POST("/loginForm", func(c *gin.Context) { - var form Login - // This will infer what binder to use depending on the content-type header. - if c.Bind(&form) == nil { - if form.User == "manu" && form.Password == "123" { - c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) - } else { - c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) - } - } - }) + // Example for binding a HTML form (user=manu&password=123) + router.POST("/loginForm", func(c *gin.Context) { + var form Login + // This will infer what binder to use depending on the content-type header. + if c.Bind(&form) == nil { + if form.User == "manu" && form.Password == "123" { + c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) + } + } + }) // Listen and server on 0.0.0.0:8080 router.Run(":8080") @@ -353,21 +393,21 @@ func main() { // c.BindWith(&form, binding.Form) // or you can simply use autobinding with Bind method: var form LoginForm - // in this case proper binding will be automatically selected + // in this case proper binding will be automatically selected if c.Bind(&form) == nil { - if form.User == "user" && form.Password == "password" { - c.JSON(200, gin.H{"status": "you are logged in"}) - } else { - c.JSON(401, gin.H{"status": "unauthorized"}) - } - } + if form.User == "user" && form.Password == "password" { + c.JSON(200, gin.H{"status": "you are logged in"}) + } else { + c.JSON(401, gin.H{"status": "unauthorized"}) + } + } }) router.Run(":8080") } ``` Test it with: -```bash +```sh $ curl -v --form user=user --form password=password http://localhost:8080/login ``` @@ -411,13 +451,13 @@ func main() { ```go func main() { - router := gin.Default() - router.Static("/assets", "./assets") - router.StaticFS("/more_static", http.Dir("my_file_system")) - router.StaticFile("/favicon.ico", "./resources/favicon.ico") + router := gin.Default() + router.Static("/assets", "./assets") + router.StaticFS("/more_static", http.Dir("my_file_system")) + router.StaticFile("/favicon.ico", "./resources/favicon.ico") - // Listen and server on 0.0.0.0:8080 - router.Run(":8080") + // Listen and server on 0.0.0.0:8080 + router.Run(":8080") } ``` @@ -438,11 +478,53 @@ func main() { router.Run(":8080") } ``` +templates/index.tmpl ```html + +

+ {{ .title }} +

+ +``` + +Using templates with same name in different directories + +```go +func main() { + router := gin.Default() + router.LoadHTMLGlob("templates/**/*") + router.GET("/posts/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ + "title": "Posts", + }) + }) + router.GET("/users/index", func(c *gin.Context) { + c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ + "title": "Users", + }) + }) + router.Run(":8080") +} +``` +templates/posts/index.tmpl +```html +{{ define "posts/index.tmpl" }}

{{ .title }}

+

Using posts/index.tmpl

+{{ end }} +``` +templates/users/index.tmpl +```html +{{ define "users/index.tmpl" }} +

+ {{ .title }} +

+

Using users/index.tmpl

+ +{{ end }} ``` You can also use your own html template render @@ -535,7 +617,7 @@ func main() { // /admin/secrets endpoint // hit "localhost:8080/admin/secrets authorized.GET("/secrets", func(c *gin.Context) { - // get user, it was setted by the BasicAuth middleware + // get user, it was set by the BasicAuth middleware user := c.MustGet(gin.AuthUserKey).(string) if secret, ok := secrets[user]; ok { c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) @@ -559,17 +641,16 @@ func main() { r.GET("/long_async", func(c *gin.Context) { // create copy to be used inside the goroutine - c_cp := c.Copy() + cCp := c.Copy() go func() { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) - // note than you are using the copied context "c_cp", IMPORTANT - log.Println("Done! in path " + c_cp.Request.URL.Path) + // note that you are using the copied context "cCp", IMPORTANT + log.Println("Done! in path " + cCp.Request.URL.Path) }() }) - r.GET("/long_sync", func(c *gin.Context) { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) @@ -578,8 +659,8 @@ func main() { log.Println("Done! in path " + c.Request.URL.Path) }) - // Listen and server on 0.0.0.0:8080 - r.Run(":8080") + // Listen and server on 0.0.0.0:8080 + r.Run(":8080") } ``` @@ -609,3 +690,21 @@ func main() { s.ListenAndServe() } ``` + +#### Graceful restart or stop + +Do you want to graceful restart or stop your web server? +There are some ways this can be done. + +We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details. + +```go +router := gin.Default() +router.GET("/", handler) +// [...] +endless.ListenAndServe(":4242", router) +``` + +An alternative to endless: + +* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. diff --git a/vendor/github.com/gin-gonic/gin/auth.go b/vendor/github.com/gin-gonic/gin/auth.go index ab4e35d..125e659 100644 --- a/vendor/github.com/gin-gonic/gin/auth.go +++ b/vendor/github.com/gin-gonic/gin/auth.go @@ -65,14 +65,10 @@ func BasicAuth(accounts Accounts) HandlerFunc { } func processAccounts(accounts Accounts) authPairs { - if len(accounts) == 0 { - panic("Empty list of authorized credentials") - } + assert1(len(accounts) > 0, "Empty list of authorized credentials") pairs := make(authPairs, 0, len(accounts)) for user, password := range accounts { - if len(user) == 0 { - panic("User can not be empty") - } + assert1(len(user) > 0, "User can not be empty") value := authorizationHeader(user, password) pairs = append(pairs, authPair{ Value: value, diff --git a/vendor/github.com/gin-gonic/gin/binding/binding.go b/vendor/github.com/gin-gonic/gin/binding/binding.go index 9cf701d..dc7397f 100644 --- a/vendor/github.com/gin-gonic/gin/binding/binding.go +++ b/vendor/github.com/gin-gonic/gin/binding/binding.go @@ -14,6 +14,7 @@ const ( MIMEPlain = "text/plain" MIMEPOSTForm = "application/x-www-form-urlencoded" MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" ) type Binding interface { @@ -38,6 +39,7 @@ var ( Form = formBinding{} FormPost = formPostBinding{} FormMultipart = formMultipartBinding{} + ProtoBuf = protobufBinding{} ) func Default(method, contentType string) Binding { @@ -49,6 +51,8 @@ func Default(method, contentType string) Binding { return JSON case MIMEXML, MIMEXML2: return XML + case MIMEPROTOBUF: + return ProtoBuf default: //case MIMEPOSTForm, MIMEMultipartPOSTForm: return Form } diff --git a/vendor/github.com/gin-gonic/gin/binding/default_validator.go b/vendor/github.com/gin-gonic/gin/binding/default_validator.go index 7f12152..760728b 100644 --- a/vendor/github.com/gin-gonic/gin/binding/default_validator.go +++ b/vendor/github.com/gin-gonic/gin/binding/default_validator.go @@ -4,7 +4,7 @@ import ( "reflect" "sync" - "gopkg.in/bluesuncorp/validator.v5" + "gopkg.in/go-playground/validator.v8" ) type defaultValidator struct { @@ -26,7 +26,8 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error { func (v *defaultValidator) lazyinit() { v.once.Do(func() { - v.validate = validator.New("binding", validator.BakedInValidators) + config := &validator.Config{TagName: "binding"} + v.validate = validator.New(config) }) } diff --git a/vendor/github.com/gin-gonic/gin/binding/protobuf.go b/vendor/github.com/gin-gonic/gin/binding/protobuf.go new file mode 100644 index 0000000..9f95622 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/binding/protobuf.go @@ -0,0 +1,35 @@ +// 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 binding + +import ( + "github.com/golang/protobuf/proto" + + "io/ioutil" + "net/http" +) + +type protobufBinding struct{} + +func (protobufBinding) Name() string { + return "protobuf" +} + +func (protobufBinding) Bind(req *http.Request, obj interface{}) error { + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + + if err = proto.Unmarshal(buf, obj.(proto.Message)); err != nil { + return err + } + + //Here it's same to return validate(obj), but util now we cann't add `binding:""` to the struct + //which automatically generate by gen-proto + return nil + //return validate(obj) +} diff --git a/vendor/github.com/gin-gonic/gin/context.go b/vendor/github.com/gin-gonic/gin/context.go index b784c14..5d3b6a4 100644 --- a/vendor/github.com/gin-gonic/gin/context.go +++ b/vendor/github.com/gin-gonic/gin/context.go @@ -8,7 +8,9 @@ import ( "errors" "io" "math" + "net" "net/http" + "net/url" "strings" "time" @@ -67,7 +69,7 @@ func (c *Context) reset() { // Copy returns a copy of the current context that can be safely used outside the request's scope. // This have to be used then the context has to be passed to a goroutine. func (c *Context) Copy() *Context { - var cp Context = *c + var cp = *c cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem cp.index = abortIndex @@ -75,7 +77,7 @@ func (c *Context) Copy() *Context { return &cp } -// HandlerName returns the main handle's name. For example if the handler is "handleGetUsers()", this +// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this // function will return "main.handleGetUsers" func (c *Context) HandlerName() string { return nameOfFunction(c.handlers.Last()) @@ -96,15 +98,15 @@ func (c *Context) Next() { } } -// IsAborted returns true if the currect context was aborted. +// IsAborted returns true if the current context was aborted. func (c *Context) IsAborted() bool { return c.index >= abortIndex } -// Abort stops the system to continue calling the pending handlers in the chain. -// Let's say you have an authorization middleware that validates if the request is authorized -// if the authorization fails (the password does not match). This method (Abort()) should be called -// in order to stop the execution of the actual handler. +// Abort prevents pending handlers from being called. Note that this will not stop the current handler. +// Let's say you have an authorization middleware that validates that the current request is authorized. If the +// authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers +// for this request are not called. func (c *Context) Abort() { c.index = abortIndex } @@ -112,7 +114,8 @@ func (c *Context) Abort() { // AbortWithStatus calls `Abort()` and writes the headers with the specified status code. // For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401). func (c *Context) AbortWithStatus(code int) { - c.Writer.WriteHeader(code) + c.Status(code) + c.Writer.WriteHeaderNow() c.Abort() } @@ -169,7 +172,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) { return } -// Returns the value for the given key if it exists, otherwise it panics. +// MustGet returns the value for the given key if it exists, otherwise it panics. func (c *Context) MustGet(key string) interface{} { if value, exists := c.Get(key); exists { return value @@ -181,50 +184,52 @@ func (c *Context) MustGet(key string) interface{} { /************ INPUT DATA ************/ /************************************/ -// Query is a shortcut for c.Request.URL.Query().Get(key) -// It is used to return the url query values. -// ?id=1234&name=Manu -// c.Query("id") == "1234" -// c.Query("name") == "Manu" -// c.Query("wtf") == "" -func (c *Context) Query(key string) (va string) { - va, _ = c.query(key) - return -} - -// PostForm is a shortcut for c.Request.PostFormValue(key) -func (c *Context) PostForm(key string) (va string) { - va, _ = c.postForm(key) - return -} - -// Param is a shortcut for c.Params.ByName(key) +// Param returns the value of the URL param. +// It is a shortcut for c.Params.ByName(key) +// router.GET("/user/:id", func(c *gin.Context) { +// // a GET request to /user/john +// id := c.Param("id") // id == "john" +// }) func (c *Context) Param(key string) string { return c.Params.ByName(key) } -func (c *Context) DefaultPostForm(key, defaultValue string) string { - if va, ok := c.postForm(key); ok { - return va - } - return defaultValue +// Query returns the keyed url query value if it exists, +// othewise it returns an empty string `("")`. +// It is shortcut for `c.Request.URL.Query().Get(key)` +// GET /path?id=1234&name=Manu&value= +// c.Query("id") == "1234" +// c.Query("name") == "Manu" +// c.Query("value") == "" +// c.Query("wtf") == "" +func (c *Context) Query(key string) string { + value, _ := c.GetQuery(key) + return value } -// DefaultQuery returns the keyed url query value if it exists, othewise it returns the -// specified defaultValue. -// ``` -// /?name=Manu -// c.DefaultQuery("name", "unknown") == "Manu" -// c.DefaultQuery("id", "none") == "none" -// ``` +// DefaultQuery returns the keyed url query value if it exists, +// othewise it returns the specified defaultValue string. +// See: Query() and GetQuery() for further information. +// GET /?name=Manu&lastname= +// c.DefaultQuery("name", "unknown") == "Manu" +// c.DefaultQuery("id", "none") == "none" +// c.DefaultQuery("lastname", "none") == "" func (c *Context) DefaultQuery(key, defaultValue string) string { - if va, ok := c.query(key); ok { - return va + if value, ok := c.GetQuery(key); ok { + return value } return defaultValue } -func (c *Context) query(key string) (string, bool) { +// GetQuery is like Query(), it returns the keyed url query value +// if it exists `(value, true)` (even when the value is an empty string), +// othewise it returns `("", false)`. +// It is shortcut for `c.Request.URL.Query().Get(key)` +// GET /?name=Manu&lastname= +// ("Manu", true) == c.GetQuery("name") +// ("", false) == c.GetQuery("id") +// ("", true) == c.GetQuery("lastname") +func (c *Context) GetQuery(key string) (string, bool) { req := c.Request if values, ok := req.URL.Query()[key]; ok && len(values) > 0 { return values[0], true @@ -232,7 +237,31 @@ func (c *Context) query(key string) (string, bool) { return "", false } -func (c *Context) postForm(key string) (string, bool) { +// PostForm returns the specified key from a POST urlencoded form or multipart form +// when it exists, otherwise it returns an empty string `("")`. +func (c *Context) PostForm(key string) string { + value, _ := c.GetPostForm(key) + return value +} + +// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form +// when it exists, otherwise it returns the specified defaultValue string. +// See: PostForm() and GetPostForm() for further information. +func (c *Context) DefaultPostForm(key, defaultValue string) string { + if value, ok := c.GetPostForm(key); ok { + return value + } + return defaultValue +} + +// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded +// form or multipart form when it exists `(value, true)` (even when the value is an empty string), +// otherwise it returns ("", false). +// For example, during a PATCH request to update the user's email: +// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" +// email= --> ("", true) := GetPostForm("email") // set email to "" +// --> ("", false) := GetPostForm("email") // do nothing with email +func (c *Context) GetPostForm(key string) (string, bool) { req := c.Request req.ParseMultipartForm(32 << 20) // 32 MB if values := req.PostForm[key]; len(values) > 0 { @@ -248,10 +277,10 @@ func (c *Context) postForm(key string) (string, bool) { // Bind checks the Content-Type to select a binding engine automatically, // Depending the "Content-Type" header different bindings are used: -// "application/json" --> JSON binding -// "application/xml" --> XML binding +// "application/json" --> JSON binding +// "application/xml" --> XML binding // otherwise --> returns an error -// If 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. // Like ParseBody() but this method also writes a 400 error if the json is not valid. func (c *Context) Bind(obj interface{}) error { @@ -291,7 +320,10 @@ func (c *Context) ClientIP() string { return clientIP } } - return strings.TrimSpace(c.Request.RemoteAddr) + if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil { + return ip + } + return "" } // ContentType returns the Content-Type header of the request. @@ -310,6 +342,10 @@ func (c *Context) requestHeader(key string) string { /******** RESPONSE RENDERING ********/ /************************************/ +func (c *Context) Status(code int) { + c.writermem.WriteHeader(code) +} + // Header is a intelligent shortcut for c.Writer.Header().Set(key, value) // It writes a header in the response. // If value == "", this method removes the header `c.Writer.Header().Del(key)` @@ -321,16 +357,43 @@ func (c *Context) Header(key, value string) { } } -func (c *Context) Render(code int, r render.Render) { - c.writermem.WriteHeader(code) - if err := r.Render(c.Writer); err != nil { - c.renderError(err) +func (c *Context) SetCookie( + name string, + value string, + maxAge int, + path string, + domain string, + secure bool, + httpOnly bool, +) { + if path == "" { + path = "/" } + http.SetCookie(c.Writer, &http.Cookie{ + Name: name, + Value: url.QueryEscape(value), + MaxAge: maxAge, + Path: path, + Domain: domain, + Secure: secure, + HttpOnly: httpOnly, + }) } -func (c *Context) renderError(err error) { - debugPrintError(err) - c.AbortWithError(500, err).SetType(ErrorTypeRender) +func (c *Context) Cookie(name string) (string, error) { + cookie, err := c.Request.Cookie(name) + if err != nil { + return "", err + } + val, _ := url.QueryUnescape(cookie.Value) + return val, nil +} + +func (c *Context) Render(code int, r render.Render) { + c.Status(code) + if err := r.Render(c.Writer); err != nil { + panic(err) + } } // HTML renders the HTTP template specified by its file name. @@ -352,9 +415,9 @@ func (c *Context) IndentedJSON(code int, obj interface{}) { // JSON serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". func (c *Context) JSON(code int, obj interface{}) { - c.writermem.WriteHeader(code) + c.Status(code) if err := render.WriteJSON(c.Writer, obj); err != nil { - c.renderError(err) + panic(err) } } @@ -364,9 +427,14 @@ func (c *Context) XML(code int, obj interface{}) { c.Render(code, render.XML{Data: obj}) } +// YAML serializes the given struct as YAML into the response body. +func (c *Context) YAML(code int, obj interface{}) { + c.Render(code, render.YAML{Data: obj}) +} + // String writes the given string into the response body. func (c *Context) String(code int, format string, values ...interface{}) { - c.writermem.WriteHeader(code) + c.Status(code) render.WriteString(c.Writer, format, values) } @@ -408,9 +476,9 @@ func (c *Context) Stream(step func(w io.Writer) bool) { case <-clientGone: return default: - keepopen := step(w) + keepOpen := step(w) w.Flush() - if !keepopen { + if !keepOpen { return } } @@ -450,9 +518,8 @@ func (c *Context) Negotiate(code int, config Negotiate) { } func (c *Context) NegotiateFormat(offered ...string) string { - if len(offered) == 0 { - panic("you must provide at least one offer") - } + assert1(len(offered) > 0, "you must provide at least one offer") + if c.Accepted == nil { c.Accepted = parseAccept(c.requestHeader("Accept")) } diff --git a/vendor/github.com/gin-gonic/gin/debug.go b/vendor/github.com/gin-gonic/gin/debug.go index 0836fc5..a121591 100644 --- a/vendor/github.com/gin-gonic/gin/debug.go +++ b/vendor/github.com/gin-gonic/gin/debug.go @@ -24,7 +24,7 @@ func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) { if IsDebugging() { nuHandlers := len(handlers) handlerName := nameOfFunction(handlers.Last()) - debugPrint("%-5s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) + debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) } } diff --git a/vendor/github.com/gin-gonic/gin/deprecated.go b/vendor/github.com/gin-gonic/gin/deprecated.go index b2e874f..0488a9b 100644 --- a/vendor/github.com/gin-gonic/gin/deprecated.go +++ b/vendor/github.com/gin-gonic/gin/deprecated.go @@ -3,3 +3,10 @@ // license that can be found in the LICENSE file. package gin + +import "log" + +func (c *Context) GetCookie(name string) (string, error) { + log.Println("GetCookie() method is deprecated. Use Cookie() instead.") + return c.Cookie(name) +} diff --git a/vendor/github.com/gin-gonic/gin/errors.go b/vendor/github.com/gin-gonic/gin/errors.go index e829c88..7716bfa 100644 --- a/vendor/github.com/gin-gonic/gin/errors.go +++ b/vendor/github.com/gin-gonic/gin/errors.go @@ -66,7 +66,7 @@ func (msg *Error) JSON() interface{} { return json } -// Implements the json.Marshaller interface +// MarshalJSON implements the json.Marshaller interface func (msg *Error) MarshalJSON() ([]byte, error) { return json.Marshal(msg.JSON()) } @@ -89,7 +89,7 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs { if typ == ErrorTypeAny { return a } - var result errorMsgs = nil + var result errorMsgs for _, msg := range a { if msg.IsType(typ) { result = append(result, msg) @@ -109,13 +109,11 @@ func (a errorMsgs) Last() *Error { } // Returns an array will all the error messages. -// Example -// ``` -// c.Error(errors.New("first")) -// c.Error(errors.New("second")) -// c.Error(errors.New("third")) -// c.Errors.Errors() // == []string{"first", "second", "third"} -// `` +// Example: +// c.Error(errors.New("first")) +// c.Error(errors.New("second")) +// c.Error(errors.New("third")) +// c.Errors.Errors() // == []string{"first", "second", "third"} func (a errorMsgs) Errors() []string { if len(a) == 0 { return nil diff --git a/vendor/github.com/gin-gonic/gin/gin.go b/vendor/github.com/gin-gonic/gin/gin.go index 3834d67..60f4ab2 100644 --- a/vendor/github.com/gin-gonic/gin/gin.go +++ b/vendor/github.com/gin-gonic/gin/gin.go @@ -14,7 +14,7 @@ import ( "github.com/gin-gonic/gin/render" ) -// Framework's version +// Version is Framework's version const Version = "v1.0rc2" var default404Body = []byte("404 page not found") @@ -113,7 +113,7 @@ func New() *Engine { // Default returns an Engine instance with the Logger and Recovery middleware already attached. func Default() *Engine { engine := New() - engine.Use(Recovery(), Logger()) + engine.Use(Logger(), Recovery()) return engine } @@ -147,19 +147,19 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) { engine.HTMLRender = render.HTMLProduction{Template: templ} } -// Adds handlers for NoRoute. It return a 404 code by default. +// NoRoute adds handlers for NoRoute. It return a 404 code by default. func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.noRoute = handlers engine.rebuild404Handlers() } -// Sets the handlers called when... TODO +// NoMethod sets the handlers called when... TODO func (engine *Engine) NoMethod(handlers ...HandlerFunc) { engine.noMethod = handlers engine.rebuild405Handlers() } -// Attachs a global middleware to the router. ie. the middleware attached though Use() will be +// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be // included in the handlers chain for every single request. Even 404, 405, static files... // For example, this is the right place for a logger or error management middleware. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { @@ -178,25 +178,15 @@ func (engine *Engine) rebuild405Handlers() { } func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { + assert1(path[0] == '/', "path must begin with '/'") + assert1(len(method) > 0, "HTTP method can not be empty") + assert1(len(handlers) > 0, "there must be at least one handler") + debugPrintRoute(method, path, handlers) - - if path[0] != '/' { - panic("path must begin with '/'") - } - if method == "" { - panic("HTTP method can not be empty") - } - if len(handlers) == 0 { - panic("there must be at least one handler") - } - root := engine.trees.get(method) if root == nil { root = new(node) - engine.trees = append(engine.trees, methodTree{ - method: method, - root: root, - }) + engine.trees = append(engine.trees, methodTree{method: method, root: root}) } root.addRoute(path, handlers) } @@ -227,7 +217,7 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { // Run attaches the router to a http.Server and starts listening and serving HTTP requests. // It is a shortcut for http.ListenAndServe(addr, router) -// Note: this method will block the calling goroutine undefinitelly unless an error happens. +// Note: this method will block the calling goroutine indefinitely unless an error happens. func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() @@ -239,7 +229,7 @@ func (engine *Engine) Run(addr ...string) (err error) { // RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests. // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) -// Note: this method will block the calling goroutine undefinitelly unless an error happens. +// Note: this method will block the calling goroutine indefinitely unless an error happens. func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) { debugPrint("Listening and serving HTTPS on %s\n", addr) defer func() { debugPrintError(err) }() @@ -250,7 +240,7 @@ func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err // RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests // through the specified unix socket (ie. a file). -// Note: this method will block the calling goroutine undefinitelly unless an error happens. +// Note: this method will block the calling goroutine indefinitely unless an error happens. func (engine *Engine) RunUnix(file string) (err error) { debugPrint("Listening and serving HTTP on unix:/%s", file) defer func() { debugPrintError(err) }() diff --git a/vendor/github.com/gin-gonic/gin/logger.go b/vendor/github.com/gin-gonic/gin/logger.go index e0f9b36..d56bc62 100644 --- a/vendor/github.com/gin-gonic/gin/logger.go +++ b/vendor/github.com/gin-gonic/gin/logger.go @@ -28,25 +28,32 @@ func ErrorLogger() HandlerFunc { func ErrorLoggerT(typ ErrorType) HandlerFunc { return func(c *Context) { c.Next() - // avoid writting if we already wrote into the response body - if !c.Writer.Written() { - errors := c.Errors.ByType(typ) - if len(errors) > 0 { - c.JSON(-1, errors) - } + errors := c.Errors.ByType(typ) + if len(errors) > 0 { + c.JSON(-1, errors) } } } -// Instances a Logger middleware that will write the logs to gin.DefaultWriter +// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter // By default gin.DefaultWriter = os.Stdout func Logger() HandlerFunc { return LoggerWithWriter(DefaultWriter) } -// Instance a Logger middleware with the specified writter buffer. +// LoggerWithWriter instance a Logger middleware with the specified writter buffer. // Example: os.Stdout, a file opened in write mode, a socket... -func LoggerWithWriter(out io.Writer) HandlerFunc { +func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { + var skip map[string]struct{} + + if length := len(notlogged); length > 0 { + skip = make(map[string]struct{}, length) + + for _, path := range notlogged { + skip[path] = struct{}{} + } + } + return func(c *Context) { // Start timer start := time.Now() @@ -55,26 +62,29 @@ func LoggerWithWriter(out io.Writer) HandlerFunc { // Process request c.Next() - // Stop timer - end := time.Now() - latency := end.Sub(start) + // Log only when path is not being skipped + if _, ok := skip[path]; !ok { + // Stop timer + end := time.Now() + latency := end.Sub(start) - clientIP := c.ClientIP() - method := c.Request.Method - statusCode := c.Writer.Status() - statusColor := colorForStatus(statusCode) - methodColor := colorForMethod(method) - comment := c.Errors.ByType(ErrorTypePrivate).String() + clientIP := c.ClientIP() + method := c.Request.Method + statusCode := c.Writer.Status() + statusColor := colorForStatus(statusCode) + methodColor := colorForMethod(method) + comment := c.Errors.ByType(ErrorTypePrivate).String() - fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s", - end.Format("2006/01/02 - 15:04:05"), - statusColor, statusCode, reset, - latency, - clientIP, - methodColor, reset, method, - path, - comment, - ) + fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s", + end.Format("2006/01/02 - 15:04:05"), + statusColor, statusCode, reset, + latency, + clientIP, + methodColor, reset, method, + path, + comment, + ) + } } } diff --git a/vendor/github.com/gin-gonic/gin/logo.jpg b/vendor/github.com/gin-gonic/gin/logo.jpg new file mode 100644 index 0000000..bb51852 Binary files /dev/null and b/vendor/github.com/gin-gonic/gin/logo.jpg differ diff --git a/vendor/github.com/gin-gonic/gin/mode.go b/vendor/github.com/gin-gonic/gin/mode.go index 15efaeb..fcb8f8f 100644 --- a/vendor/github.com/gin-gonic/gin/mode.go +++ b/vendor/github.com/gin-gonic/gin/mode.go @@ -5,11 +5,9 @@ package gin import ( - "io" "os" "github.com/gin-gonic/gin/binding" - "github.com/mattn/go-colorable" ) const ENV_GIN_MODE = "GIN_MODE" @@ -25,9 +23,18 @@ const ( testCode = iota ) -var DefaultWriter io.Writer = colorable.NewColorableStdout() -var ginMode int = debugCode -var modeName string = DebugMode +// DefaultWriter is the default io.Writer used the Gin for debug output and +// middleware output like Logger() or Recovery(). +// Note that both Logger and Recovery provides custom ways to configure their +// output io.Writer. +// To support coloring in Windows use: +// import "github.com/mattn/go-colorable" +// gin.DefaultWriter = colorable.NewColorableStdout() +var DefaultWriter = os.Stdout +var DefaultErrorWriter = os.Stderr + +var ginMode = debugCode +var modeName = DebugMode func init() { mode := os.Getenv(ENV_GIN_MODE) diff --git a/vendor/github.com/gin-gonic/gin/recovery.go b/vendor/github.com/gin-gonic/gin/recovery.go index e296e33..c502f35 100644 --- a/vendor/github.com/gin-gonic/gin/recovery.go +++ b/vendor/github.com/gin-gonic/gin/recovery.go @@ -10,6 +10,7 @@ import ( "io" "io/ioutil" "log" + "net/http/httputil" "runtime" ) @@ -22,20 +23,21 @@ var ( // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. func Recovery() HandlerFunc { - return RecoveryWithWriter(DefaultWriter) + return RecoveryWithWriter(DefaultErrorWriter) } func RecoveryWithWriter(out io.Writer) HandlerFunc { var logger *log.Logger if out != nil { - logger = log.New(out, "", log.LstdFlags) + logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) } return func(c *Context) { defer func() { if err := recover(); err != nil { if logger != nil { stack := stack(3) - logger.Printf("Panic recovery -> %s\n%s\n", err, stack) + httprequest, _ := httputil.DumpRequest(c.Request, false) + logger.Printf("[Recovery] panic recovered:\n%s\n%s\n%s%s", string(httprequest), err, stack, reset) } c.AbortWithStatus(500) } diff --git a/vendor/github.com/gin-gonic/gin/render/html.go b/vendor/github.com/gin-gonic/gin/render/html.go index 01f6bf2..8bfb23a 100644 --- a/vendor/github.com/gin-gonic/gin/render/html.go +++ b/vendor/github.com/gin-gonic/gin/render/html.go @@ -61,7 +61,6 @@ func (r HTML) Render(w http.ResponseWriter) error { writeContentType(w, htmlContentType) if len(r.Name) == 0 { return r.Template.Execute(w, r.Data) - } else { - return r.Template.ExecuteTemplate(w, r.Name, r.Data) } + return r.Template.ExecuteTemplate(w, r.Name, r.Data) } diff --git a/vendor/github.com/gin-gonic/gin/render/redirect.go b/vendor/github.com/gin-gonic/gin/render/redirect.go index d64e4d7..bd48d7d 100644 --- a/vendor/github.com/gin-gonic/gin/render/redirect.go +++ b/vendor/github.com/gin-gonic/gin/render/redirect.go @@ -16,7 +16,7 @@ type Redirect struct { } func (r Redirect) Render(w http.ResponseWriter) error { - if r.Code < 300 || r.Code > 308 { + if (r.Code < 300 || r.Code > 308) && r.Code != 201 { panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) } http.Redirect(w, r.Request, r.Location, r.Code) diff --git a/vendor/github.com/gin-gonic/gin/render/render.go b/vendor/github.com/gin-gonic/gin/render/render.go index 994fcd7..3808666 100644 --- a/vendor/github.com/gin-gonic/gin/render/render.go +++ b/vendor/github.com/gin-gonic/gin/render/render.go @@ -20,6 +20,7 @@ var ( _ Render = HTML{} _ HTMLRender = HTMLDebug{} _ HTMLRender = HTMLProduction{} + _ Render = YAML{} ) func writeContentType(w http.ResponseWriter, value []string) { diff --git a/vendor/github.com/gin-gonic/gin/render/yaml.go b/vendor/github.com/gin-gonic/gin/render/yaml.go new file mode 100644 index 0000000..46937d8 --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/yaml.go @@ -0,0 +1,29 @@ +// 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 render + +import ( + "net/http" + + "gopkg.in/yaml.v2" +) + +type YAML struct { + Data interface{} +} + +var yamlContentType = []string{"application/x-yaml; charset=utf-8"} + +func (r YAML) Render(w http.ResponseWriter) error { + writeContentType(w, yamlContentType) + + bytes, err := yaml.Marshal(r.Data) + if err != nil { + return err + } + + w.Write(bytes) + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/tree.go b/vendor/github.com/gin-gonic/gin/tree.go index c87e0d8..4f1da27 100644 --- a/vendor/github.com/gin-gonic/gin/tree.go +++ b/vendor/github.com/gin-gonic/gin/tree.go @@ -20,7 +20,7 @@ type Param struct { // It is therefore safe to read values by the index. type Params []Param -// ByName returns the value of the first Param which key matches the given name. +// Get returns the value of the first Param which key matches the given name. // If no matching Param is found, an empty string is returned. func (ps Params) Get(name string) (string, bool) { for _, entry := range ps { @@ -31,6 +31,8 @@ func (ps Params) Get(name string) (string, bool) { return "", false } +// ByName returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. func (ps Params) ByName(name string) (va string) { va, _ = ps.Get(name) return @@ -76,9 +78,10 @@ func countParams(path string) uint8 { type nodeType uint8 const ( - static nodeType = 0 - param nodeType = 1 - catchAll nodeType = 2 + static nodeType = iota // default + root + param + catchAll ) type node struct { @@ -238,6 +241,7 @@ func (n *node) addRoute(path string, handlers HandlersChain) { } } else { // Empty tree n.insertChild(numParams, path, fullPath, handlers) + n.nType = root } } @@ -452,6 +456,11 @@ walk: // Outer loop for walking the tree return } + if path == "/" && n.wildChild && n.nType != root { + tsr = true + return + } + // No handle found. Check if a handle for this path + a // trailing slash exists for trailing slash recommendation for i := 0; i < len(n.indices); i++ { diff --git a/vendor/github.com/gin-gonic/gin/utils.go b/vendor/github.com/gin-gonic/gin/utils.go index 533888d..18064fb 100644 --- a/vendor/github.com/gin-gonic/gin/utils.go +++ b/vendor/github.com/gin-gonic/gin/utils.go @@ -47,7 +47,7 @@ func WrapH(h http.Handler) HandlerFunc { type H map[string]interface{} -// Allows type H to be used with xml.Marshal +// MarshalXML allows type H to be used with xml.Marshal func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { start.Name = xml.Name{ Space: "", @@ -71,6 +71,12 @@ func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { return nil } +func assert1(guard bool, text string) { + if !guard { + panic(text) + } +} + func filterFlags(content string) string { for i, char := range content { if char == ' ' || char == ';' { @@ -137,10 +143,9 @@ func resolveAddress(addr []string) string { if port := os.Getenv("PORT"); len(port) > 0 { debugPrint("Environment variable PORT=\"%s\"", port) return ":" + port - } else { - debugPrint("Environment variable PORT is undefined. Using port :8080 by default") - return ":8080" } + debugPrint("Environment variable PORT is undefined. Using port :8080 by default") + return ":8080" case 1: return addr[0] default: diff --git a/vendor/vendor.json b/vendor/vendor.json index 751b4f8..1167948 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -34,18 +34,18 @@ }, { "path": "github.com/gin-gonic/gin", - "revision": "52fcc5dbf6e94df33ad313858fb94b713e9d1b4a", - "revisionTime": "2015-09-25T12:14:45+02:00" + "revision": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f", + "revisionTime": "2016-04-15T01:39:28+02:00" }, { "path": "github.com/gin-gonic/gin/binding", - "revision": "52fcc5dbf6e94df33ad313858fb94b713e9d1b4a", - "revisionTime": "2015-09-25T12:14:45+02:00" + "revision": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f", + "revisionTime": "2016-04-15T01:39:28+02:00" }, { "path": "github.com/gin-gonic/gin/render", - "revision": "52fcc5dbf6e94df33ad313858fb94b713e9d1b4a", - "revisionTime": "2015-09-25T12:14:45+02:00" + "revision": "5caaac4c5c712a9e7a7de29e6c24ef46c753017f", + "revisionTime": "2016-04-15T01:39:28+02:00" }, { "path": "github.com/go-sql-driver/mysql",