diff --git a/vendor/github.com/labstack/echo/bind.go b/vendor/github.com/labstack/echo/bind.go index f2393ea6..186bd83d 100644 --- a/vendor/github.com/labstack/echo/bind.go +++ b/vendor/github.com/labstack/echo/bind.go @@ -30,16 +30,16 @@ type ( // Bind implements the `Binder#Bind` function. func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { req := c.Request() - if req.Method == GET { - if err = b.bindData(i, c.QueryParams(), "query"); err != nil { - return NewHTTPError(http.StatusBadRequest, err.Error()) - } - return - } - ctype := req.Header.Get(HeaderContentType) if req.ContentLength == 0 { + if req.Method == GET || req.Method == DELETE { + if err = b.bindData(i, c.QueryParams(), "query"); err != nil { + return NewHTTPError(http.StatusBadRequest, err.Error()) + } + return + } return NewHTTPError(http.StatusBadRequest, "Request body can't be empty") } + ctype := req.Header.Get(HeaderContentType) switch { case strings.HasPrefix(ctype, MIMEApplicationJSON): if err = json.NewDecoder(req.Body).Decode(i); err != nil { @@ -51,7 +51,7 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { return NewHTTPError(http.StatusBadRequest, err.Error()) } } - case strings.HasPrefix(ctype, MIMEApplicationXML): + case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML): if err = xml.NewDecoder(req.Body).Decode(i); err != nil { if ute, ok := err.(*xml.UnsupportedTypeError); ok { return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())) @@ -142,6 +142,8 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V } switch valueKind { + case reflect.Ptr: + return setWithProperType(structField.Elem().Kind(), val, structField.Elem()) case reflect.Int: return setIntField(val, 0, structField) case reflect.Int8: diff --git a/vendor/github.com/labstack/echo/context.go b/vendor/github.com/labstack/echo/context.go index 1a6ebf47..242eec26 100644 --- a/vendor/github.com/labstack/echo/context.go +++ b/vendor/github.com/labstack/echo/context.go @@ -31,6 +31,9 @@ type ( // IsTLS returns true if HTTP connection is TLS otherwise false. IsTLS() bool + // IsWebSocket returns true if HTTP connection is WebSocket otherwise false. + IsWebSocket() bool + // Scheme returns the HTTP protocol scheme, `http` or `https`. Scheme() string @@ -219,19 +222,36 @@ func (c *context) IsTLS() bool { return c.request.TLS != nil } +func (c *context) IsWebSocket() bool { + upgrade := c.request.Header.Get(HeaderUpgrade) + return upgrade == "websocket" || upgrade == "Websocket" +} + func (c *context) Scheme() string { // Can't use `r.Request.URL.Scheme` // See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0 if c.IsTLS() { return "https" } + if scheme := c.request.Header.Get(HeaderXForwardedProto); scheme != "" { + return scheme + } + if scheme := c.request.Header.Get(HeaderXForwardedProtocol); scheme != "" { + return scheme + } + if ssl := c.request.Header.Get(HeaderXForwardedSsl); ssl == "on" { + return "https" + } + if scheme := c.request.Header.Get(HeaderXUrlScheme); scheme != "" { + return scheme + } return "http" } func (c *context) RealIP() string { ra := c.request.RemoteAddr if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { - ra = ip + ra = strings.Split(ip, ", ")[0] } else if ip := c.request.Header.Get(HeaderXRealIP); ip != "" { ra = ip } else { @@ -275,7 +295,7 @@ func (c *context) SetParamNames(names ...string) { } func (c *context) ParamValues() []string { - return c.pvalues + return c.pvalues[:len(c.pnames)] } func (c *context) SetParamValues(values ...string) { @@ -385,7 +405,8 @@ func (c *context) String(code int, s string) (err error) { } func (c *context) JSON(code int, i interface{}) (err error) { - if c.echo.Debug { + _, pretty := c.QueryParams()["pretty"] + if c.echo.Debug || pretty { return c.JSONPretty(code, i, " ") } b, err := json.Marshal(i) @@ -429,7 +450,8 @@ func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) { } func (c *context) XML(code int, i interface{}) (err error) { - if c.echo.Debug { + _, pretty := c.QueryParams()["pretty"] + if c.echo.Debug || pretty { return c.XMLPretty(code, i, " ") } b, err := xml.Marshal(i) @@ -471,7 +493,12 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error) return } -func (c *context) File(file string) error { +func (c *context) File(file string) (err error) { + file, err = url.QueryUnescape(file) // Issue #839 + if err != nil { + return + } + f, err := os.Open(file) if err != nil { return ErrNotFound @@ -487,11 +514,11 @@ func (c *context) File(file string) error { } defer f.Close() if fi, err = f.Stat(); err != nil { - return err + return } } http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f) - return nil + return } func (c *context) Attachment(file, name string) (err error) { @@ -514,7 +541,7 @@ func (c *context) NoContent(code int) error { } func (c *context) Redirect(code int, url string) error { - if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect { + if code < 300 || code > 308 { return ErrInvalidRedirectCode } c.response.Header().Set(HeaderLocation, url) @@ -548,4 +575,8 @@ func (c *context) Reset(r *http.Request, w http.ResponseWriter) { c.query = nil c.handler = NotFoundHandler c.store = nil + c.path = "" + c.pnames = nil + // NOTE: Don't reset because it has to have length c.echo.maxParam at all times + // c.pvalues = nil } diff --git a/vendor/github.com/labstack/echo/cookbook/auto-tls/server.go b/vendor/github.com/labstack/echo/cookbook/auto-tls/server.go deleted file mode 100644 index 4a8bbdfd..00000000 --- a/vendor/github.com/labstack/echo/cookbook/auto-tls/server.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "net/http" - - "golang.org/x/crypto/acme/autocert" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func main() { - e := echo.New() - // e.AutoTLSManager.HostPolicy = autocert.HostWhitelist("") - // Cache certificates - e.AutoTLSManager.Cache = autocert.DirCache("/var/www/.cache") - e.Use(middleware.Recover()) - e.Use(middleware.Logger()) - e.GET("/", func(c echo.Context) error { - return c.HTML(http.StatusOK, ` -

Welcome to Echo!

-

TLS certificates automatically installed from Let's Encrypt :)

- `) - }) - e.Logger.Fatal(e.StartAutoTLS(":443")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/cors/server.go b/vendor/github.com/labstack/echo/cookbook/cors/server.go deleted file mode 100644 index 0cc5c345..00000000 --- a/vendor/github.com/labstack/echo/cookbook/cors/server.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -var ( - users = []string{"Joe", "Veer", "Zion"} -) - -func getUsers(c echo.Context) error { - return c.JSON(http.StatusOK, users) -} - -func main() { - e := echo.New() - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - // CORS default - // Allows requests from any origin wth GET, HEAD, PUT, POST or DELETE method. - // e.Use(middleware.CORS()) - - // CORS restricted - // Allows requests from any `https://labstack.com` or `https://labstack.net` origin - // wth GET, PUT, POST or DELETE method. - e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ - AllowOrigins: []string{"https://labstack.com", "https://labstack.net"}, - AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE}, - })) - - e.GET("/api/users", getUsers) - - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/crud/server.go b/vendor/github.com/labstack/echo/cookbook/crud/server.go deleted file mode 100644 index fbb5c754..00000000 --- a/vendor/github.com/labstack/echo/cookbook/crud/server.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "net/http" - "strconv" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -type ( - user struct { - ID int `json:"id"` - Name string `json:"name"` - } -) - -var ( - users = map[int]*user{} - seq = 1 -) - -//---------- -// Handlers -//---------- - -func createUser(c echo.Context) error { - u := &user{ - ID: seq, - } - if err := c.Bind(u); err != nil { - return err - } - users[u.ID] = u - seq++ - return c.JSON(http.StatusCreated, u) -} - -func getUser(c echo.Context) error { - id, _ := strconv.Atoi(c.Param("id")) - return c.JSON(http.StatusOK, users[id]) -} - -func updateUser(c echo.Context) error { - u := new(user) - if err := c.Bind(u); err != nil { - return err - } - id, _ := strconv.Atoi(c.Param("id")) - users[id].Name = u.Name - return c.JSON(http.StatusOK, users[id]) -} - -func deleteUser(c echo.Context) error { - id, _ := strconv.Atoi(c.Param("id")) - delete(users, id) - return c.NoContent(http.StatusNoContent) -} - -func main() { - e := echo.New() - - // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - // Routes - e.POST("/users", createUser) - e.GET("/users/:id", getUser) - e.PUT("/users/:id", updateUser) - e.DELETE("/users/:id", deleteUser) - - // Start server - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/embed-resources/server.go b/vendor/github.com/labstack/echo/cookbook/embed-resources/server.go deleted file mode 100644 index e5b0c3db..00000000 --- a/vendor/github.com/labstack/echo/cookbook/embed-resources/server.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "net/http" - - rice "github.com/GeertJohan/go.rice" - "github.com/labstack/echo" -) - -func main() { - e := echo.New() - // the file server for rice. "app" is the folder where the files come from. - assetHandler := http.FileServer(rice.MustFindBox("app").HTTPBox()) - // serves the index.html from rice - e.GET("/", echo.WrapHandler(assetHandler)) - - // servers other static files - e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler))) - - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/file-upload/multiple/server.go b/vendor/github.com/labstack/echo/cookbook/file-upload/multiple/server.go deleted file mode 100644 index cd0f54d3..00000000 --- a/vendor/github.com/labstack/echo/cookbook/file-upload/multiple/server.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func upload(c echo.Context) error { - // Read form fields - name := c.FormValue("name") - email := c.FormValue("email") - - //------------ - // Read files - //------------ - - // Multipart form - form, err := c.MultipartForm() - if err != nil { - return err - } - files := form.File["files"] - - for _, file := range files { - // Source - src, err := file.Open() - if err != nil { - return err - } - defer src.Close() - - // Destination - dst, err := os.Create(file.Filename) - if err != nil { - return err - } - defer dst.Close() - - // Copy - if _, err = io.Copy(dst, src); err != nil { - return err - } - - } - - return c.HTML(http.StatusOK, fmt.Sprintf("

Uploaded successfully %d files with fields name=%s and email=%s.

", len(files), name, email)) -} - -func main() { - e := echo.New() - - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - e.Static("/", "public") - e.POST("/upload", upload) - - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/file-upload/single/server.go b/vendor/github.com/labstack/echo/cookbook/file-upload/single/server.go deleted file mode 100644 index 1b84f220..00000000 --- a/vendor/github.com/labstack/echo/cookbook/file-upload/single/server.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func upload(c echo.Context) error { - // Read form fields - name := c.FormValue("name") - email := c.FormValue("email") - - //----------- - // Read file - //----------- - - // Source - file, err := c.FormFile("file") - if err != nil { - return err - } - src, err := file.Open() - if err != nil { - return err - } - defer src.Close() - - // Destination - dst, err := os.Create(file.Filename) - if err != nil { - return err - } - defer dst.Close() - - // Copy - if _, err = io.Copy(dst, src); err != nil { - return err - } - - return c.HTML(http.StatusOK, fmt.Sprintf("

File %s uploaded successfully with fields name=%s and email=%s.

", file.Filename, name, email)) -} - -func main() { - e := echo.New() - - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - e.Static("/", "public") - e.POST("/upload", upload) - - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-engine.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-engine.go deleted file mode 100644 index 0c1db087..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-engine.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build appengine - -package main - -import ( - "net/http" - - "github.com/labstack/echo" -) - -func createMux() *echo.Echo { - e := echo.New() - // note: we don't need to provide the middleware or static handlers, that's taken care of by the platform - // app engine has it's own "main" wrapper - we just need to hook echo into the default handler - http.Handle("/", e) - return e -} diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-managed.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-managed.go deleted file mode 100644 index 7b8eacf8..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-managed.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build appenginevm - -package main - -import ( - "net/http" - - "github.com/labstack/echo" - "google.golang.org/appengine" -) - -func createMux() *echo.Echo { - e := echo.New() - // note: we don't need to provide the middleware or static handlers - // for the appengine vm version - that's taken care of by the platform - return e -} - -func main() { - // the appengine package provides a convenient method to handle the health-check requests - // and also run the app on the correct port. We just need to add Echo to the default handler - e := echo.New(":8080") - http.Handle("/", e) - appengine.Main() -} diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-standalone.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-standalone.go deleted file mode 100644 index c3b44dc0..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app-standalone.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !appengine,!appenginevm - -package main - -import ( - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func createMux() *echo.Echo { - e := echo.New() - - e.Use(middleware.Recover()) - e.Use(middleware.Logger()) - e.Use(middleware.Gzip()) - - e.Static("/", "public") - - return e -} - -func main() { - e.Logger.Fatal(e.Start(":8080")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/app.go deleted file mode 100644 index 8d4d97a2..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/app.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -// reference our echo instance and create it early -var e = createMux() diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/users.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/users.go deleted file mode 100644 index 19533e51..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/users.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -type ( - user struct { - ID string `json:"id"` - Name string `json:"name"` - } -) - -var ( - users map[string]user -) - -func init() { - users = map[string]user{ - "1": user{ - ID: "1", - Name: "Wreck-It Ralph", - }, - } - - // hook into the echo instance to create an endpoint group - // and add specific middleware to it plus handlers - g := e.Group("/users") - g.Use(middleware.CORS()) - - g.POST("", createUser) - g.GET("", getUsers) - g.GET("/:id", getUser) -} - -func createUser(c echo.Context) error { - u := new(user) - if err := c.Bind(u); err != nil { - return err - } - users[u.ID] = *u - return c.JSON(http.StatusCreated, u) -} - -func getUsers(c echo.Context) error { - return c.JSON(http.StatusOK, users) -} - -func getUser(c echo.Context) error { - return c.JSON(http.StatusOK, users[c.Param("id")]) -} diff --git a/vendor/github.com/labstack/echo/cookbook/google-app-engine/welcome.go b/vendor/github.com/labstack/echo/cookbook/google-app-engine/welcome.go deleted file mode 100644 index 2639b209..00000000 --- a/vendor/github.com/labstack/echo/cookbook/google-app-engine/welcome.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "html/template" - "io" - "net/http" - - "github.com/labstack/echo" -) - -type ( - Template struct { - templates *template.Template - } -) - -func init() { - t := &Template{ - templates: template.Must(template.ParseFiles("templates/welcome.html")), - } - e.Renderer = t - e.GET("/welcome", welcome) -} - -func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { - return t.templates.ExecuteTemplate(w, name, data) -} - -func welcome(c echo.Context) error { - return c.Render(http.StatusOK, "welcome", "Joe") -} diff --git a/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/grace/server.go b/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/grace/server.go deleted file mode 100644 index 1f9937b0..00000000 --- a/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/grace/server.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/facebookgo/grace/gracehttp" - "github.com/labstack/echo" -) - -func main() { - // Setup - e := echo.New() - e.GET("/", func(c echo.Context) error { - return c.String(http.StatusOK, "Six sick bricks tick") - }) - e.Server.Addr = ":1323" - - // Serve it like a boss - e.Logger.Fatal(gracehttp.Serve(e.Server)) -} diff --git a/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/graceful/server.go b/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/graceful/server.go deleted file mode 100644 index 39e7b634..00000000 --- a/vendor/github.com/labstack/echo/cookbook/graceful-shutdown/graceful/server.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "net/http" - "time" - - "github.com/labstack/echo" - "github.com/tylerb/graceful" -) - -func main() { - // Setup - e := echo.New() - e.GET("/", func(c echo.Context) error { - return c.String(http.StatusOK, "Sue sews rose on slow joe crows nose") - }) - e.Server.Addr = ":1323" - - // Serve it like a boss - graceful.ListenAndServe(e.Server, 5*time.Second) -} diff --git a/vendor/github.com/labstack/echo/cookbook/hello-world/server.go b/vendor/github.com/labstack/echo/cookbook/hello-world/server.go deleted file mode 100644 index 06e0718b..00000000 --- a/vendor/github.com/labstack/echo/cookbook/hello-world/server.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func main() { - // Echo instance - e := echo.New() - - // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - // Route => handler - e.GET("/", func(c echo.Context) error { - return c.String(http.StatusOK, "Hello, World!\n") - }) - - // Start server - e.Logger.Fatal(e.Start(":1323")) -} diff --git a/vendor/github.com/labstack/echo/cookbook/http2/server.go b/vendor/github.com/labstack/echo/cookbook/http2/server.go deleted file mode 100644 index 8db989c4..00000000 --- a/vendor/github.com/labstack/echo/cookbook/http2/server.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "time" - - "github.com/labstack/echo" -) - -func request(c echo.Context) error { - req := c.Request() - format := "
Request Information\n\nProtocol: %s\nHost: %s\nRemote Address: %s\nMethod: %s\nPath: %s\n
" - return c.HTML(http.StatusOK, fmt.Sprintf(format, req.Proto, req.Host, req.RemoteAddr, req.Method, req.URL.Path)) -} - -func stream(c echo.Context) error { - res := c.Response() - gone := res.CloseNotify() - res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8) - res.WriteHeader(http.StatusOK) - ticker := time.NewTicker(1 * time.Second) - defer ticker.Stop() - - fmt.Fprint(res, "
Clock Stream\n\n")
-	for {
-		fmt.Fprintf(res, "%v\n", time.Now())
-		res.Flush()
-		select {
-		case <-ticker.C:
-		case <-gone:
-			break
-		}
-	}
-}
-
-func main() {
-	e := echo.New()
-	e.GET("/request", request)
-	e.GET("/stream", stream)
-	e.Logger.Fatal(e.StartTLS(":1323", "cert.pem", "key.pem"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/jsonp/server.go b/vendor/github.com/labstack/echo/cookbook/jsonp/server.go
deleted file mode 100644
index ba46bab0..00000000
--- a/vendor/github.com/labstack/echo/cookbook/jsonp/server.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package main
-
-import (
-	"math/rand"
-	"net/http"
-	"time"
-
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/middleware"
-)
-
-func main() {
-	e := echo.New()
-	e.Use(middleware.Logger())
-	e.Use(middleware.Recover())
-
-	e.Static("/", "public")
-
-	// JSONP
-	e.GET("/jsonp", func(c echo.Context) error {
-		callback := c.QueryParam("callback")
-		var content struct {
-			Response  string    `json:"response"`
-			Timestamp time.Time `json:"timestamp"`
-			Random    int       `json:"random"`
-		}
-		content.Response = "Sent via JSONP"
-		content.Timestamp = time.Now().UTC()
-		content.Random = rand.Intn(1000)
-		return c.JSONP(http.StatusOK, callback, &content)
-	})
-
-	// Start server
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/jwt/custom-claims/server.go b/vendor/github.com/labstack/echo/cookbook/jwt/custom-claims/server.go
deleted file mode 100644
index b3a13205..00000000
--- a/vendor/github.com/labstack/echo/cookbook/jwt/custom-claims/server.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"time"
-
-	jwt "github.com/dgrijalva/jwt-go"
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/middleware"
-)
-
-// jwtCustomClaims are custom claims extending default ones.
-type jwtCustomClaims struct {
-	Name  string `json:"name"`
-	Admin bool   `json:"admin"`
-	jwt.StandardClaims
-}
-
-func login(c echo.Context) error {
-	username := c.FormValue("username")
-	password := c.FormValue("password")
-
-	if username == "jon" && password == "shhh!" {
-
-		// Set custom claims
-		claims := &jwtCustomClaims{
-			"Jon Snow",
-			true,
-			jwt.StandardClaims{
-				ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
-			},
-		}
-
-		// Create token with claims
-		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
-
-		// Generate encoded token and send it as response.
-		t, err := token.SignedString([]byte("secret"))
-		if err != nil {
-			return err
-		}
-		return c.JSON(http.StatusOK, echo.Map{
-			"token": t,
-		})
-	}
-
-	return echo.ErrUnauthorized
-}
-
-func accessible(c echo.Context) error {
-	return c.String(http.StatusOK, "Accessible")
-}
-
-func restricted(c echo.Context) error {
-	user := c.Get("user").(*jwt.Token)
-	claims := user.Claims.(*jwtCustomClaims)
-	name := claims.Name
-	return c.String(http.StatusOK, "Welcome "+name+"!")
-}
-
-func main() {
-	e := echo.New()
-
-	// Middleware
-	e.Use(middleware.Logger())
-	e.Use(middleware.Recover())
-
-	// Login route
-	e.POST("/login", login)
-
-	// Unauthenticated route
-	e.GET("/", accessible)
-
-	// Restricted group
-	r := e.Group("/restricted")
-
-	// Configure middleware with the custom claims type
-	config := middleware.JWTConfig{
-		Claims:     &jwtCustomClaims{},
-		SigningKey: []byte("secret"),
-	}
-	r.Use(middleware.JWTWithConfig(config))
-	r.GET("", restricted)
-
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/jwt/map-claims/server.go b/vendor/github.com/labstack/echo/cookbook/jwt/map-claims/server.go
deleted file mode 100644
index 678be490..00000000
--- a/vendor/github.com/labstack/echo/cookbook/jwt/map-claims/server.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"time"
-
-	jwt "github.com/dgrijalva/jwt-go"
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/middleware"
-)
-
-func login(c echo.Context) error {
-	username := c.FormValue("username")
-	password := c.FormValue("password")
-
-	if username == "jon" && password == "shhh!" {
-		// Create token
-		token := jwt.New(jwt.SigningMethodHS256)
-
-		// Set claims
-		claims := token.Claims.(jwt.MapClaims)
-		claims["name"] = "Jon Snow"
-		claims["admin"] = true
-		claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
-
-		// Generate encoded token and send it as response.
-		t, err := token.SignedString([]byte("secret"))
-		if err != nil {
-			return err
-		}
-		return c.JSON(http.StatusOK, map[string]string{
-			"token": t,
-		})
-	}
-
-	return echo.ErrUnauthorized
-}
-
-func accessible(c echo.Context) error {
-	return c.String(http.StatusOK, "Accessible")
-}
-
-func restricted(c echo.Context) error {
-	user := c.Get("user").(*jwt.Token)
-	claims := user.Claims.(jwt.MapClaims)
-	name := claims["name"].(string)
-	return c.String(http.StatusOK, "Welcome "+name+"!")
-}
-
-func main() {
-	e := echo.New()
-
-	// Middleware
-	e.Use(middleware.Logger())
-	e.Use(middleware.Recover())
-
-	// Login route
-	e.POST("/login", login)
-
-	// Unauthenticated route
-	e.GET("/", accessible)
-
-	// Restricted group
-	r := e.Group("/restricted")
-	r.Use(middleware.JWT([]byte("secret")))
-	r.GET("", restricted)
-
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/middleware/server.go b/vendor/github.com/labstack/echo/cookbook/middleware/server.go
deleted file mode 100644
index 2f21df50..00000000
--- a/vendor/github.com/labstack/echo/cookbook/middleware/server.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"strconv"
-	"sync"
-	"time"
-
-	"github.com/labstack/echo"
-)
-
-type (
-	Stats struct {
-		Uptime       time.Time      `json:"uptime"`
-		RequestCount uint64         `json:"requestCount"`
-		Statuses     map[string]int `json:"statuses"`
-		mutex        sync.RWMutex
-	}
-)
-
-func NewStats() *Stats {
-	return &Stats{
-		Uptime:   time.Now(),
-		Statuses: make(map[string]int),
-	}
-}
-
-// Process is the middleware function.
-func (s *Stats) Process(next echo.HandlerFunc) echo.HandlerFunc {
-	return func(c echo.Context) error {
-		if err := next(c); err != nil {
-			c.Error(err)
-		}
-		s.mutex.Lock()
-		defer s.mutex.Unlock()
-		s.RequestCount++
-		status := strconv.Itoa(c.Response().Status)
-		s.Statuses[status]++
-		return nil
-	}
-}
-
-// Handle is the endpoint to get stats.
-func (s *Stats) Handle(c echo.Context) error {
-	s.mutex.RLock()
-	defer s.mutex.RUnlock()
-	return c.JSON(http.StatusOK, s)
-}
-
-// ServerHeader middleware adds a `Server` header to the response.
-func ServerHeader(next echo.HandlerFunc) echo.HandlerFunc {
-	return func(c echo.Context) error {
-		c.Response().Header().Set(echo.HeaderServer, "Echo/3.0")
-		return next(c)
-	}
-}
-
-func main() {
-	e := echo.New()
-
-	// Debug mode
-	e.Debug = true
-
-	//-------------------
-	// Custom middleware
-	//-------------------
-	// Stats
-	s := NewStats()
-	e.Use(s.Process)
-	e.GET("/stats", s.Handle) // Endpoint to get stats
-
-	// Server header
-	e.Use(ServerHeader)
-
-	// Handler
-	e.GET("/", func(c echo.Context) error {
-		return c.String(http.StatusOK, "Hello, World!")
-	})
-
-	// Start server
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/streaming-response/server.go b/vendor/github.com/labstack/echo/cookbook/streaming-response/server.go
deleted file mode 100644
index a3a679ef..00000000
--- a/vendor/github.com/labstack/echo/cookbook/streaming-response/server.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"time"
-
-	"encoding/json"
-
-	"github.com/labstack/echo"
-)
-
-type (
-	Geolocation struct {
-		Altitude  float64
-		Latitude  float64
-		Longitude float64
-	}
-)
-
-var (
-	locations = []Geolocation{
-		{-97, 37.819929, -122.478255},
-		{1899, 39.096849, -120.032351},
-		{2619, 37.865101, -119.538329},
-		{42, 33.812092, -117.918974},
-		{15, 37.77493, -122.419416},
-	}
-)
-
-func main() {
-	e := echo.New()
-	e.GET("/", func(c echo.Context) error {
-		c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
-		c.Response().WriteHeader(http.StatusOK)
-		for _, l := range locations {
-			if err := json.NewEncoder(c.Response()).Encode(l); err != nil {
-				return err
-			}
-			c.Response().Flush()
-			time.Sleep(1 * time.Second)
-		}
-		return nil
-	})
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/subdomains/server.go b/vendor/github.com/labstack/echo/cookbook/subdomains/server.go
deleted file mode 100644
index ef4f65f9..00000000
--- a/vendor/github.com/labstack/echo/cookbook/subdomains/server.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package main
-
-import (
-	"net/http"
-
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/middleware"
-)
-
-type (
-	Host struct {
-		Echo *echo.Echo
-	}
-)
-
-func main() {
-	// Hosts
-	hosts := make(map[string]*Host)
-
-	//-----
-	// API
-	//-----
-
-	api := echo.New()
-	api.Use(middleware.Logger())
-	api.Use(middleware.Recover())
-
-	hosts["api.localhost:1323"] = &Host{api}
-
-	api.GET("/", func(c echo.Context) error {
-		return c.String(http.StatusOK, "API")
-	})
-
-	//------
-	// Blog
-	//------
-
-	blog := echo.New()
-	blog.Use(middleware.Logger())
-	blog.Use(middleware.Recover())
-
-	hosts["blog.localhost:1323"] = &Host{blog}
-
-	blog.GET("/", func(c echo.Context) error {
-		return c.String(http.StatusOK, "Blog")
-	})
-
-	//---------
-	// Website
-	//---------
-
-	site := echo.New()
-	site.Use(middleware.Logger())
-	site.Use(middleware.Recover())
-
-	hosts["localhost:1323"] = &Host{site}
-
-	site.GET("/", func(c echo.Context) error {
-		return c.String(http.StatusOK, "Website")
-	})
-
-	// Server
-	e := echo.New()
-	e.Any("/*", func(c echo.Context) (err error) {
-		req := c.Request()
-		res := c.Response()
-		host := hosts[req.Host]
-
-		if host == nil {
-			err = echo.ErrNotFound
-		} else {
-			host.Echo.ServeHTTP(res, req)
-		}
-
-		return
-	})
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go
deleted file mode 100644
index 263c5e21..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package handler
-
-import mgo "gopkg.in/mgo.v2"
-
-type (
-	Handler struct {
-		DB *mgo.Session
-	}
-)
-
-const (
-	// Key (Should come from somewhere else).
-	Key = "secret"
-)
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go
deleted file mode 100644
index b1428a30..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package handler
-
-import (
-	"net/http"
-	"strconv"
-
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/cookbook/twitter/model"
-	mgo "gopkg.in/mgo.v2"
-	"gopkg.in/mgo.v2/bson"
-)
-
-func (h *Handler) CreatePost(c echo.Context) (err error) {
-	u := &model.User{
-		ID: bson.ObjectIdHex(userIDFromToken(c)),
-	}
-	p := &model.Post{
-		ID:   bson.NewObjectId(),
-		From: u.ID.Hex(),
-	}
-	if err = c.Bind(p); err != nil {
-		return
-	}
-
-	// Validation
-	if p.To == "" || p.Message == "" {
-		return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"}
-	}
-
-	// Find user from database
-	db := h.DB.Clone()
-	defer db.Close()
-	if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil {
-		if err == mgo.ErrNotFound {
-			return echo.ErrNotFound
-		}
-		return
-	}
-
-	// Save post in database
-	if err = db.DB("twitter").C("posts").Insert(p); err != nil {
-		return
-	}
-	return c.JSON(http.StatusCreated, p)
-}
-
-func (h *Handler) FetchPost(c echo.Context) (err error) {
-	userID := userIDFromToken(c)
-	page, _ := strconv.Atoi(c.QueryParam("page"))
-	limit, _ := strconv.Atoi(c.QueryParam("limit"))
-
-	// Defaults
-	if page == 0 {
-		page = 1
-	}
-	if limit == 0 {
-		limit = 100
-	}
-
-	// Retrieve posts from database
-	posts := []*model.Post{}
-	db := h.DB.Clone()
-	if err = db.DB("twitter").C("posts").
-		Find(bson.M{"to": userID}).
-		Skip((page - 1) * limit).
-		Limit(limit).
-		All(&posts); err != nil {
-		return
-	}
-	defer db.Close()
-
-	return c.JSON(http.StatusOK, posts)
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go
deleted file mode 100644
index a34d2f4e..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package handler
-
-import (
-	"net/http"
-	"time"
-
-	jwt "github.com/dgrijalva/jwt-go"
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/cookbook/twitter/model"
-	mgo "gopkg.in/mgo.v2"
-	"gopkg.in/mgo.v2/bson"
-)
-
-func (h *Handler) Signup(c echo.Context) (err error) {
-	// Bind
-	u := &model.User{ID: bson.NewObjectId()}
-	if err = c.Bind(u); err != nil {
-		return
-	}
-
-	// Validate
-	if u.Email == "" || u.Password == "" {
-		return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid email or password"}
-	}
-
-	// Save user
-	db := h.DB.Clone()
-	defer db.Close()
-	if err = db.DB("twitter").C("users").Insert(u); err != nil {
-		return
-	}
-
-	return c.JSON(http.StatusCreated, u)
-}
-
-func (h *Handler) Login(c echo.Context) (err error) {
-	// Bind
-	u := new(model.User)
-	if err = c.Bind(u); err != nil {
-		return
-	}
-
-	// Find user
-	db := h.DB.Clone()
-	defer db.Close()
-	if err = db.DB("twitter").C("users").
-		Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil {
-		if err == mgo.ErrNotFound {
-			return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"}
-		}
-		return
-	}
-
-	//-----
-	// JWT
-	//-----
-
-	// Create token
-	token := jwt.New(jwt.SigningMethodHS256)
-
-	// Set claims
-	claims := token.Claims.(jwt.MapClaims)
-	claims["id"] = u.ID
-	claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
-
-	// Generate encoded token and send it as response
-	u.Token, err = token.SignedString([]byte(Key))
-	if err != nil {
-		return err
-	}
-
-	u.Password = "" // Don't send password
-	return c.JSON(http.StatusOK, u)
-}
-
-func (h *Handler) Follow(c echo.Context) (err error) {
-	userID := userIDFromToken(c)
-	id := c.Param("id")
-
-	// Add a follower to user
-	db := h.DB.Clone()
-	defer db.Close()
-	if err = db.DB("twitter").C("users").
-		UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil {
-		if err == mgo.ErrNotFound {
-			return echo.ErrNotFound
-		}
-	}
-
-	return
-}
-
-func userIDFromToken(c echo.Context) string {
-	user := c.Get("user").(*jwt.Token)
-	claims := user.Claims.(jwt.MapClaims)
-	return claims["id"].(string)
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go b/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go
deleted file mode 100644
index 7344296e..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package model
-
-import "gopkg.in/mgo.v2/bson"
-
-type (
-	Post struct {
-		ID      bson.ObjectId `json:"id" bson:"_id,omitempty"`
-		To      string        `json:"to" bson:"to"`
-		From    string        `json:"from" bson:"from"`
-		Message string        `json:"message" bson:"message"`
-	}
-)
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go b/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go
deleted file mode 100644
index e063c89b..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package model
-
-import "gopkg.in/mgo.v2/bson"
-
-type (
-	User struct {
-		ID        bson.ObjectId `json:"id" bson:"_id,omitempty"`
-		Email     string        `json:"email" bson:"email"`
-		Password  string        `json:"password,omitempty" bson:"password"`
-		Token     string        `json:"token,omitempty" bson:"-"`
-		Followers []string      `json:"followers,omitempty" bson:"followers,omitempty"`
-	}
-)
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/server.go b/vendor/github.com/labstack/echo/cookbook/twitter/server.go
deleted file mode 100644
index 22db7aa0..00000000
--- a/vendor/github.com/labstack/echo/cookbook/twitter/server.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package main
-
-import (
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/cookbook/twitter/handler"
-	"github.com/labstack/echo/middleware"
-	"github.com/labstack/gommon/log"
-	mgo "gopkg.in/mgo.v2"
-)
-
-func main() {
-	e := echo.New()
-	e.Logger.SetLevel(log.ERROR)
-	e.Use(middleware.Logger())
-	e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
-		SigningKey: []byte(handler.Key),
-		Skipper: func(c echo.Context) bool {
-			// Skip authentication for and signup login requests
-			if c.Path() == "/login" || c.Path() == "/signup" {
-				return true
-			}
-			return false
-		},
-	}))
-
-	// Database connection
-	db, err := mgo.Dial("localhost")
-	if err != nil {
-		e.Logger.Fatal(err)
-	}
-
-	// Create indices
-	if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{
-		Key:    []string{"email"},
-		Unique: true,
-	}); err != nil {
-		log.Fatal(err)
-	}
-
-	// Initialize handler
-	h := &handler.Handler{DB: db}
-
-	// Routes
-	e.POST("/signup", h.Signup)
-	e.POST("/login", h.Login)
-	e.POST("/follow/:id", h.Follow)
-	e.POST("/posts", h.CreatePost)
-	e.GET("/feed", h.FetchPost)
-
-	// Start server
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/websocket/gorilla/server.go b/vendor/github.com/labstack/echo/cookbook/websocket/gorilla/server.go
deleted file mode 100644
index e9d52dbb..00000000
--- a/vendor/github.com/labstack/echo/cookbook/websocket/gorilla/server.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"log"
-
-	"github.com/labstack/echo"
-
-	"github.com/gorilla/websocket"
-	"github.com/labstack/echo/middleware"
-)
-
-var (
-	upgrader = websocket.Upgrader{}
-)
-
-func hello(c echo.Context) error {
-	ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
-	if err != nil {
-		return err
-	}
-	defer ws.Close()
-
-	for {
-		// Write
-		err := ws.WriteMessage(websocket.TextMessage, []byte("Hello, Client!"))
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		// Read
-		_, msg, err := ws.ReadMessage()
-		if err != nil {
-			log.Fatal(err)
-		}
-		fmt.Printf("%s\n", msg)
-	}
-}
-
-func main() {
-	e := echo.New()
-	e.Use(middleware.Logger())
-	e.Use(middleware.Recover())
-	e.Static("/", "../public")
-	e.GET("/ws", hello)
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/cookbook/websocket/net/server.go b/vendor/github.com/labstack/echo/cookbook/websocket/net/server.go
deleted file mode 100644
index aa746030..00000000
--- a/vendor/github.com/labstack/echo/cookbook/websocket/net/server.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"log"
-
-	"github.com/labstack/echo"
-	"github.com/labstack/echo/middleware"
-	"golang.org/x/net/websocket"
-)
-
-func hello(c echo.Context) error {
-	websocket.Handler(func(ws *websocket.Conn) {
-		defer ws.Close()
-		for {
-			// Write
-			err := websocket.Message.Send(ws, "Hello, Client!")
-			if err != nil {
-				log.Fatal(err)
-			}
-
-			// Read
-			msg := ""
-			err = websocket.Message.Receive(ws, &msg)
-			if err != nil {
-				log.Fatal(err)
-			}
-			fmt.Printf("%s\n", msg)
-		}
-	}).ServeHTTP(c.Response(), c.Request())
-	return nil
-}
-
-func main() {
-	e := echo.New()
-	e.Use(middleware.Logger())
-	e.Use(middleware.Recover())
-	e.Static("/", "../public")
-	e.GET("/ws", hello)
-	e.Logger.Fatal(e.Start(":1323"))
-}
diff --git a/vendor/github.com/labstack/echo/echo.go b/vendor/github.com/labstack/echo/echo.go
index f0c1b72e..baa92fde 100644
--- a/vendor/github.com/labstack/echo/echo.go
+++ b/vendor/github.com/labstack/echo/echo.go
@@ -42,10 +42,11 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	slog "log"
+	stdLog "log"
 	"net"
 	"net/http"
 	"path"
+	"path/filepath"
 	"reflect"
 	"runtime"
 	"sync"
@@ -59,7 +60,7 @@ import (
 type (
 	// Echo is the top-level framework instance.
 	Echo struct {
-		stdLogger        *slog.Logger
+		stdLogger        *stdLog.Logger
 		colorer          *color.Color
 		premiddleware    []MiddlewareFunc
 		middleware       []MiddlewareFunc
@@ -73,20 +74,21 @@ type (
 		TLSListener      net.Listener
 		DisableHTTP2     bool
 		Debug            bool
+		HideBanner       bool
 		HTTPErrorHandler HTTPErrorHandler
 		Binder           Binder
 		Validator        Validator
 		Renderer         Renderer
 		AutoTLSManager   autocert.Manager
-		Mutex            sync.RWMutex
-		Logger           Logger
+		// Mutex            sync.RWMutex
+		Logger Logger
 	}
 
 	// Route contains a handler and information for matching against requests.
 	Route struct {
-		Method  string
-		Path    string
-		Handler string
+		Method  string `json:"method"`
+		Path    string `json:"path"`
+		Handler string `json:"handler"`
 	}
 
 	// HTTPError represents an error that occurred while handling a request.
@@ -144,6 +146,8 @@ const (
 	MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
 	MIMEApplicationXML                   = "application/xml"
 	MIMEApplicationXMLCharsetUTF8        = MIMEApplicationXML + "; " + charsetUTF8
+	MIMETextXML                          = "text/xml"
+	MIMETextXMLCharsetUTF8               = MIMETextXML + "; " + charsetUTF8
 	MIMEApplicationForm                  = "application/x-www-form-urlencoded"
 	MIMEApplicationProtobuf              = "application/protobuf"
 	MIMEApplicationMsgpack               = "application/msgpack"
@@ -161,27 +165,34 @@ const (
 
 // Headers
 const (
-	HeaderAcceptEncoding                = "Accept-Encoding"
-	HeaderAllow                         = "Allow"
-	HeaderAuthorization                 = "Authorization"
-	HeaderContentDisposition            = "Content-Disposition"
-	HeaderContentEncoding               = "Content-Encoding"
-	HeaderContentLength                 = "Content-Length"
-	HeaderContentType                   = "Content-Type"
-	HeaderCookie                        = "Cookie"
-	HeaderSetCookie                     = "Set-Cookie"
-	HeaderIfModifiedSince               = "If-Modified-Since"
-	HeaderLastModified                  = "Last-Modified"
-	HeaderLocation                      = "Location"
-	HeaderUpgrade                       = "Upgrade"
-	HeaderVary                          = "Vary"
-	HeaderWWWAuthenticate               = "WWW-Authenticate"
-	HeaderXForwardedProto               = "X-Forwarded-Proto"
-	HeaderXHTTPMethodOverride           = "X-HTTP-Method-Override"
-	HeaderXForwardedFor                 = "X-Forwarded-For"
-	HeaderXRealIP                       = "X-Real-IP"
-	HeaderServer                        = "Server"
-	HeaderOrigin                        = "Origin"
+	HeaderAccept              = "Accept"
+	HeaderAcceptEncoding      = "Accept-Encoding"
+	HeaderAllow               = "Allow"
+	HeaderAuthorization       = "Authorization"
+	HeaderContentDisposition  = "Content-Disposition"
+	HeaderContentEncoding     = "Content-Encoding"
+	HeaderContentLength       = "Content-Length"
+	HeaderContentType         = "Content-Type"
+	HeaderCookie              = "Cookie"
+	HeaderSetCookie           = "Set-Cookie"
+	HeaderIfModifiedSince     = "If-Modified-Since"
+	HeaderLastModified        = "Last-Modified"
+	HeaderLocation            = "Location"
+	HeaderUpgrade             = "Upgrade"
+	HeaderVary                = "Vary"
+	HeaderWWWAuthenticate     = "WWW-Authenticate"
+	HeaderXForwardedFor       = "X-Forwarded-For"
+	HeaderXForwardedProto     = "X-Forwarded-Proto"
+	HeaderXForwardedProtocol  = "X-Forwarded-Protocol"
+	HeaderXForwardedSsl       = "X-Forwarded-Ssl"
+	HeaderXUrlScheme          = "X-Url-Scheme"
+	HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
+	HeaderXRealIP             = "X-Real-IP"
+	HeaderXRequestID          = "X-Request-ID"
+	HeaderServer              = "Server"
+	HeaderOrigin              = "Origin"
+
+	// Access control
 	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
 	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
 	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
@@ -200,6 +211,22 @@ const (
 	HeaderXCSRFToken              = "X-CSRF-Token"
 )
 
+const (
+	version = "3.1.0"
+	website = "https://echo.labstack.com"
+	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
+	banner = `
+   ____    __
+  / __/___/ /  ___
+ / _// __/ _ \/ _ \
+/___/\__/_//_/\___/ %s
+High performance, minimalist Go web framework
+%s
+____________________________________O/_______
+                                    O\
+`
+)
+
 var (
 	methods = [...]string{
 		CONNECT,
@@ -219,6 +246,7 @@ var (
 	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)
 	ErrNotFound                    = NewHTTPError(http.StatusNotFound)
 	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)
+	ErrForbidden                   = NewHTTPError(http.StatusForbidden)
 	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)
 	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
 	ErrValidatorNotRegistered      = errors.New("Validator not registered")
@@ -255,7 +283,7 @@ func New() (e *Echo) {
 	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
 	e.Binder = &DefaultBinder{}
 	e.Logger.SetLevel(log.OFF)
-	e.stdLogger = slog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
+	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
 	e.pool.New = func() interface{} {
 		return e.NewContext(nil, nil)
 	}
@@ -398,12 +426,16 @@ func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middlew
 // Static registers a new route with path prefix to serve static files from the
 // provided root directory.
 func (e *Echo) Static(prefix, root string) {
+	if root == "" {
+		root = "." // For security we want to restrict to CWD.
+	}
 	static(e, prefix, root)
 }
 
 func static(i i, prefix, root string) {
 	h := func(c Context) error {
-		return c.File(path.Join(root, c.Param("*")))
+		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security
+		return c.File(name)
 	}
 	i.GET(prefix, h)
 	if prefix == "/" {
@@ -430,7 +462,7 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl
 		}
 		return h(c)
 	})
-	r := Route{
+	r := &Route{
 		Method:  method,
 		Path:    path,
 		Handler: name,
@@ -476,8 +508,8 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
 }
 
 // Routes returns the registered routes.
-func (e *Echo) Routes() []Route {
-	routes := []Route{}
+func (e *Echo) Routes() []*Route {
+	routes := []*Route{}
 	for _, v := range e.router.routes {
 		routes = append(routes, v)
 	}
@@ -499,8 +531,8 @@ func (e *Echo) ReleaseContext(c Context) {
 // ServeHTTP implements `http.Handler` interface, which serves HTTP requests.
 func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	// Acquire lock
-	e.Mutex.RLock()
-	defer e.Mutex.RUnlock()
+	// e.Mutex.RLock()
+	// defer e.Mutex.RUnlock()
 
 	// Acquire context
 	c := e.pool.Get().(*context)
@@ -510,7 +542,10 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	// Middleware
 	h := func(c Context) error {
 		method := r.Method
-		path := r.URL.EscapedPath()
+		path := r.URL.RawPath
+		if path == "" {
+			path = r.URL.Path
+		}
 		e.router.Find(method, path, c)
 		h := c.Handler()
 		for i := len(e.middleware) - 1; i >= 0; i-- {
@@ -572,8 +607,15 @@ func (e *Echo) startTLS(address string) error {
 func (e *Echo) StartServer(s *http.Server) (err error) {
 	// Setup
 	e.colorer.SetOutput(e.Logger.Output())
-	s.Handler = e
 	s.ErrorLog = e.stdLogger
+	s.Handler = e
+	if e.Debug {
+		e.Logger.SetLevel(log.DEBUG)
+	}
+
+	if !e.HideBanner {
+		e.colorer.Printf(banner, e.colorer.Red("v"+version), e.colorer.Blue(website))
+	}
 
 	if s.TLSConfig == nil {
 		if e.Listener == nil {
@@ -582,7 +624,9 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
 				return err
 			}
 		}
-		e.colorer.Printf("⇛ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
+		if !e.HideBanner {
+			e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
+		}
 		return s.Serve(e.Listener)
 	}
 	if e.TLSListener == nil {
@@ -592,7 +636,9 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
 		}
 		e.TLSListener = tls.NewListener(l, s.TLSConfig)
 	}
-	e.colorer.Printf("⇛ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
+	if !e.HideBanner {
+		e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
+	}
 	return s.Serve(e.TLSListener)
 }
 
diff --git a/vendor/github.com/labstack/echo/echo_go1.8.go b/vendor/github.com/labstack/echo/echo_go1.8.go
new file mode 100644
index 00000000..340bed70
--- /dev/null
+++ b/vendor/github.com/labstack/echo/echo_go1.8.go
@@ -0,0 +1,25 @@
+// +build go1.8
+
+package echo
+
+import (
+	stdContext "context"
+)
+
+// Close immediately stops the server.
+// It internally calls `http.Server#Close()`.
+func (e *Echo) Close() error {
+	if err := e.TLSServer.Close(); err != nil {
+		return err
+	}
+	return e.Server.Close()
+}
+
+// Shutdown stops server the gracefully.
+// It internally calls `http.Server#Shutdown()`.
+func (e *Echo) Shutdown(ctx stdContext.Context) error {
+	if err := e.TLSServer.Shutdown(ctx); err != nil {
+		return err
+	}
+	return e.Server.Shutdown(ctx)
+}
diff --git a/vendor/github.com/labstack/echo/group.go b/vendor/github.com/labstack/echo/group.go
index 9767bb19..799a8f90 100644
--- a/vendor/github.com/labstack/echo/group.go
+++ b/vendor/github.com/labstack/echo/group.go
@@ -1,8 +1,12 @@
 package echo
 
+import (
+	"path"
+)
+
 type (
 	// Group is a set of sub-routes for a specified route. It can be used for inner
-	// routes that share a common middlware or functionality that should be separate
+	// routes that share a common middleware or functionality that should be separate
 	// from the parent echo instance while still inheriting from it.
 	Group struct {
 		prefix     string
@@ -14,6 +18,11 @@ type (
 // Use implements `Echo#Use()` for sub-routes within the Group.
 func (g *Group) Use(middleware ...MiddlewareFunc) {
 	g.middleware = append(g.middleware, middleware...)
+	// Allow all requests to reach the group as they might get dropped if router
+	// doesn't find a match, making none of the group middleware process.
+	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error {
+		return ErrNotFound
+	}, g.middleware...)
 }
 
 // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
diff --git a/vendor/github.com/labstack/echo/middleware/basic_auth.go b/vendor/github.com/labstack/echo/middleware/basic_auth.go
index e98a87e3..c1f34c8f 100644
--- a/vendor/github.com/labstack/echo/middleware/basic_auth.go
+++ b/vendor/github.com/labstack/echo/middleware/basic_auth.go
@@ -2,6 +2,7 @@ package middleware
 
 import (
 	"encoding/base64"
+	"strconv"
 
 	"github.com/labstack/echo"
 )
@@ -15,20 +16,26 @@ type (
 		// Validator is a function to validate BasicAuth credentials.
 		// Required.
 		Validator BasicAuthValidator
+
+		// Realm is a string to define realm attribute of BasicAuth.
+		// Default value "Restricted".
+		Realm string
 	}
 
 	// BasicAuthValidator defines a function to validate BasicAuth credentials.
-	BasicAuthValidator func(string, string, echo.Context) bool
+	BasicAuthValidator func(string, string, echo.Context) (bool, error)
 )
 
 const (
-	basic = "Basic"
+	basic        = "Basic"
+	defaultRealm = "Restricted"
 )
 
 var (
 	// DefaultBasicAuthConfig is the default BasicAuth middleware config.
 	DefaultBasicAuthConfig = BasicAuthConfig{
 		Skipper: DefaultSkipper,
+		Realm:   defaultRealm,
 	}
 )
 
@@ -52,6 +59,9 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 	if config.Skipper == nil {
 		config.Skipper = DefaultBasicAuthConfig.Skipper
 	}
+	if config.Realm == "" {
+		config.Realm = defaultRealm
+	}
 
 	return func(next echo.HandlerFunc) echo.HandlerFunc {
 		return func(c echo.Context) error {
@@ -71,15 +81,25 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 				for i := 0; i < len(cred); i++ {
 					if cred[i] == ':' {
 						// Verify credentials
-						if config.Validator(cred[:i], cred[i+1:], c) {
+						valid, err := config.Validator(cred[:i], cred[i+1:], c)
+						if err != nil {
+							return err
+						} else if valid {
 							return next(c)
 						}
 					}
 				}
 			}
 
+			realm := ""
+			if config.Realm == defaultRealm {
+				realm = defaultRealm
+			} else {
+				realm = strconv.Quote(config.Realm)
+			}
+
 			// Need to return `401` for browsers to pop-up login box.
-			c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm=Restricted")
+			c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm)
 			return echo.ErrUnauthorized
 		}
 	}
diff --git a/vendor/github.com/labstack/echo/middleware/compress.go b/vendor/github.com/labstack/echo/middleware/compress.go
index eee67b37..cffadbd1 100644
--- a/vendor/github.com/labstack/echo/middleware/compress.go
+++ b/vendor/github.com/labstack/echo/middleware/compress.go
@@ -108,8 +108,8 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) {
 	return w.Writer.Write(b)
 }
 
-func (w *gzipResponseWriter) Flush() error {
-	return w.Writer.(*gzip.Writer).Flush()
+func (w *gzipResponseWriter) Flush() {
+	w.Writer.(*gzip.Writer).Flush()
 }
 
 func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
diff --git a/vendor/github.com/labstack/echo/middleware/jwt.go b/vendor/github.com/labstack/echo/middleware/jwt.go
index b2658739..5d2072e7 100644
--- a/vendor/github.com/labstack/echo/middleware/jwt.go
+++ b/vendor/github.com/labstack/echo/middleware/jwt.go
@@ -91,7 +91,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 		config.Skipper = DefaultJWTConfig.Skipper
 	}
 	if config.SigningKey == nil {
-		panic("jwt middleware requires signing key")
+		panic("echo: jwt middleware requires signing key")
 	}
 	if config.SigningMethod == "" {
 		config.SigningMethod = DefaultJWTConfig.SigningMethod
diff --git a/vendor/github.com/labstack/echo/middleware/key_auth.go b/vendor/github.com/labstack/echo/middleware/key_auth.go
index 4d4cb940..5ef87e3d 100644
--- a/vendor/github.com/labstack/echo/middleware/key_auth.go
+++ b/vendor/github.com/labstack/echo/middleware/key_auth.go
@@ -32,7 +32,7 @@ type (
 	}
 
 	// KeyAuthValidator defines a function to validate KeyAuth credentials.
-	KeyAuthValidator func(string, echo.Context) bool
+	KeyAuthValidator func(string, echo.Context) (bool, error)
 
 	keyExtractor func(echo.Context) (string, error)
 )
@@ -94,7 +94,10 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
 			if err != nil {
 				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
 			}
-			if config.Validator(key, c) {
+			valid, err := config.Validator(key, c)
+			if err != nil {
+				return err
+			} else if valid {
 				return next(c)
 			}
 
diff --git a/vendor/github.com/labstack/echo/middleware/logger.go b/vendor/github.com/labstack/echo/middleware/logger.go
index e26b5496..b9c54468 100644
--- a/vendor/github.com/labstack/echo/middleware/logger.go
+++ b/vendor/github.com/labstack/echo/middleware/logger.go
@@ -26,7 +26,7 @@ type (
 		// - time_unix_nano
 		// - time_rfc3339
 		// - time_rfc3339_nano
-		// - id (Request ID - Not implemented)
+		// - id (Request ID)
 		// - remote_ip
 		// - uri
 		// - host
@@ -62,7 +62,7 @@ var (
 	// DefaultLoggerConfig is the default Logger middleware config.
 	DefaultLoggerConfig = LoggerConfig{
 		Skipper: DefaultSkipper,
-		Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` +
+		Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
 			`"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` +
 			`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
 			`"bytes_out":${bytes_out}}` + "\n",
@@ -126,6 +126,12 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
 					return buf.WriteString(time.Now().Format(time.RFC3339))
 				case "time_rfc3339_nano":
 					return buf.WriteString(time.Now().Format(time.RFC3339Nano))
+				case "id":
+					id := req.Header.Get(echo.HeaderXRequestID)
+					if id == "" {
+						id = res.Header().Get(echo.HeaderXRequestID)
+					}
+					return buf.WriteString(id)
 				case "remote_ip":
 					return buf.WriteString(c.RealIP())
 				case "host":
@@ -177,6 +183,11 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
 						return buf.Write([]byte(c.QueryParam(tag[6:])))
 					case strings.HasPrefix(tag, "form:"):
 						return buf.Write([]byte(c.FormValue(tag[5:])))
+					case strings.HasPrefix(tag, "cookie:"):
+						cookie, err := c.Cookie(tag[7:])
+						if err == nil {
+							return buf.Write([]byte(cookie.Value))
+						}
 					}
 				}
 				return 0, nil
diff --git a/vendor/github.com/labstack/echo/middleware/middleware.go b/vendor/github.com/labstack/echo/middleware/middleware.go
index 7edccc1d..efcbab91 100644
--- a/vendor/github.com/labstack/echo/middleware/middleware.go
+++ b/vendor/github.com/labstack/echo/middleware/middleware.go
@@ -9,6 +9,6 @@ type (
 )
 
 // DefaultSkipper returns false which processes the middleware.
-func DefaultSkipper(c echo.Context) bool {
+func DefaultSkipper(echo.Context) bool {
 	return false
 }
diff --git a/vendor/github.com/labstack/echo/middleware/proxy.go b/vendor/github.com/labstack/echo/middleware/proxy.go
new file mode 100644
index 00000000..7eb24abf
--- /dev/null
+++ b/vendor/github.com/labstack/echo/middleware/proxy.go
@@ -0,0 +1,160 @@
+package middleware
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"math/rand"
+	"net"
+	"net/http"
+	"net/http/httputil"
+	"net/url"
+	"sync/atomic"
+	"time"
+
+	"github.com/labstack/echo"
+)
+
+// TODO: Handle TLS proxy
+
+type (
+	// ProxyConfig defines the config for Proxy middleware.
+	ProxyConfig struct {
+		// Skipper defines a function to skip middleware.
+		Skipper Skipper
+
+		// Balancer defines a load balancing technique.
+		// Required.
+		// Possible values:
+		// - RandomBalancer
+		// - RoundRobinBalancer
+		Balancer ProxyBalancer
+	}
+
+	// ProxyTarget defines the upstream target.
+	ProxyTarget struct {
+		URL *url.URL
+	}
+
+	// RandomBalancer implements a random load balancing technique.
+	RandomBalancer struct {
+		Targets []*ProxyTarget
+		random  *rand.Rand
+	}
+
+	// RoundRobinBalancer implements a round-robin load balancing technique.
+	RoundRobinBalancer struct {
+		Targets []*ProxyTarget
+		i       uint32
+	}
+
+	// ProxyBalancer defines an interface to implement a load balancing technique.
+	ProxyBalancer interface {
+		Next() *ProxyTarget
+	}
+)
+
+func proxyHTTP(t *ProxyTarget) http.Handler {
+	return httputil.NewSingleHostReverseProxy(t.URL)
+}
+
+func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		h, ok := w.(http.Hijacker)
+		if !ok {
+			c.Error(errors.New("proxy raw, not a hijacker"))
+			return
+		}
+		in, _, err := h.Hijack()
+		if err != nil {
+			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err))
+			return
+		}
+		defer in.Close()
+
+		out, err := net.Dial("tcp", t.URL.Host)
+		if err != nil {
+			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err))
+			c.Error(he)
+			return
+		}
+		defer out.Close()
+
+		err = r.Write(out)
+		if err != nil {
+			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err))
+			c.Error(he)
+			return
+		}
+
+		errc := make(chan error, 2)
+		cp := func(dst io.Writer, src io.Reader) {
+			_, err := io.Copy(dst, src)
+			errc <- err
+		}
+
+		go cp(out, in)
+		go cp(in, out)
+		err = <-errc
+		if err != nil && err != io.EOF {
+			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err)
+		}
+	})
+}
+
+// Next randomly returns an upstream target.
+func (r *RandomBalancer) Next() *ProxyTarget {
+	if r.random == nil {
+		r.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
+	}
+	return r.Targets[r.random.Intn(len(r.Targets))]
+}
+
+// Next returns an upstream target using round-robin technique.
+func (r *RoundRobinBalancer) Next() *ProxyTarget {
+	r.i = r.i % uint32(len(r.Targets))
+	t := r.Targets[r.i]
+	atomic.AddUint32(&r.i, 1)
+	return t
+}
+
+// Proxy returns an HTTP/WebSocket reverse proxy middleware.
+func Proxy(config ProxyConfig) echo.MiddlewareFunc {
+	// Defaults
+	if config.Skipper == nil {
+		config.Skipper = DefaultLoggerConfig.Skipper
+	}
+	if config.Balancer == nil {
+		panic("echo: proxy middleware requires balancer")
+	}
+
+	return func(next echo.HandlerFunc) echo.HandlerFunc {
+		return func(c echo.Context) (err error) {
+			req := c.Request()
+			res := c.Response()
+			tgt := config.Balancer.Next()
+
+			// Fix header
+			if req.Header.Get(echo.HeaderXRealIP) == "" {
+				req.Header.Set(echo.HeaderXRealIP, c.RealIP())
+			}
+			if req.Header.Get(echo.HeaderXForwardedProto) == "" {
+				req.Header.Set(echo.HeaderXForwardedProto, c.Scheme())
+			}
+			if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" { // For HTTP, it is automatically set by Go HTTP reverse proxy.
+				req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
+			}
+
+			// Proxy
+			switch {
+			case c.IsWebSocket():
+				proxyRaw(tgt, c).ServeHTTP(res, req)
+			case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
+			default:
+				proxyHTTP(tgt).ServeHTTP(res, req)
+			}
+
+			return
+		}
+	}
+}
diff --git a/vendor/github.com/labstack/echo/middleware/request_id.go b/vendor/github.com/labstack/echo/middleware/request_id.go
new file mode 100644
index 00000000..f376c296
--- /dev/null
+++ b/vendor/github.com/labstack/echo/middleware/request_id.go
@@ -0,0 +1,64 @@
+package middleware
+
+import (
+	"github.com/labstack/echo"
+	"github.com/labstack/gommon/random"
+)
+
+type (
+	// RequestIDConfig defines the config for RequestID middleware.
+	RequestIDConfig struct {
+		// Skipper defines a function to skip middleware.
+		Skipper Skipper
+
+		// Generator defines a function to generate an ID.
+		// Optional. Default value random.String(32).
+		Generator func() string
+	}
+)
+
+var (
+	// DefaultRequestIDConfig is the default RequestID middleware config.
+	DefaultRequestIDConfig = RequestIDConfig{
+		Skipper:   DefaultSkipper,
+		Generator: generator,
+	}
+)
+
+// RequestID returns a X-Request-ID middleware.
+func RequestID() echo.MiddlewareFunc {
+	return RequestIDWithConfig(DefaultRequestIDConfig)
+}
+
+// RequestIDWithConfig returns a X-Request-ID middleware with config.
+func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
+	// Defaults
+	if config.Skipper == nil {
+		config.Skipper = DefaultRequestIDConfig.Skipper
+	}
+	if config.Generator == nil {
+		config.Generator = generator
+	}
+
+	return func(next echo.HandlerFunc) echo.HandlerFunc {
+		return func(c echo.Context) error {
+			if config.Skipper(c) {
+				return next(c)
+			}
+
+			req := c.Request()
+			res := c.Response()
+			rid := req.Header.Get(echo.HeaderXRequestID)
+			if rid == "" {
+				rid = config.Generator()
+			}
+			res.Header().Set(echo.HeaderXRequestID, rid)
+
+			return next(c)
+		}
+	}
+}
+
+func generator() string {
+	return random.String(32)
+}
diff --git a/vendor/github.com/labstack/echo/middleware/static.go b/vendor/github.com/labstack/echo/middleware/static.go
index 793c1445..e715c1c4 100644
--- a/vendor/github.com/labstack/echo/middleware/static.go
+++ b/vendor/github.com/labstack/echo/middleware/static.go
@@ -3,7 +3,9 @@ package middleware
 import (
 	"fmt"
 	"os"
+	"path"
 	"path/filepath"
+	"strings"
 
 	"github.com/labstack/echo"
 )
@@ -53,6 +55,9 @@ func Static(root string) echo.MiddlewareFunc {
 // See `Static()`.
 func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 	// Defaults
+	if config.Root == "" {
+		config.Root = "." // For security we want to restrict to CWD.
+	}
 	if config.Skipper == nil {
 		config.Skipper = DefaultStaticConfig.Skipper
 	}
@@ -62,26 +67,44 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 
 	return func(next echo.HandlerFunc) echo.HandlerFunc {
 		return func(c echo.Context) error {
-			p := c.Param("*")
-			name := filepath.Join(config.Root, p)
-			fi, err := os.Stat(name)
+			if config.Skipper(c) {
+				return next(c)
+			}
 
+			p := c.Request().URL.Path
+			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
+				p = c.Param("*")
+			}
+			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
+
+			fi, err := os.Stat(name)
 			if err != nil {
 				if os.IsNotExist(err) {
-					if config.HTML5 {
+					if config.HTML5 && path.Ext(p) == "" {
 						return c.File(filepath.Join(config.Root, config.Index))
 					}
-					return echo.ErrNotFound
+					return next(c)
 				}
 				return err
 			}
 
 			if fi.IsDir() {
-				if config.Browse {
-					return listDir(name, c.Response())
+				index := filepath.Join(name, config.Index)
+				fi, err = os.Stat(index)
+
+				if err != nil {
+					if config.Browse {
+						return listDir(name, c.Response())
+					}
+					if os.IsNotExist(err) {
+						return next(c)
+					}
+					return err
 				}
-				return c.File(filepath.Join(name, config.Index))
+
+				return c.File(index)
 			}
+
 			return c.File(name)
 		}
 	}
diff --git a/vendor/github.com/labstack/echo/router.go b/vendor/github.com/labstack/echo/router.go
index 5cb47116..2ef904e0 100644
--- a/vendor/github.com/labstack/echo/router.go
+++ b/vendor/github.com/labstack/echo/router.go
@@ -7,7 +7,7 @@ type (
 	// request matching and URL path parameter parsing.
 	Router struct {
 		tree   *node
-		routes map[string]Route
+		routes map[string]*Route
 		echo   *Echo
 	}
 	node struct {
@@ -47,7 +47,7 @@ func NewRouter(e *Echo) *Router {
 		tree: &node{
 			methodHandler: new(methodHandler),
 		},
-		routes: make(map[string]Route),
+		routes: map[string]*Route{},
 		echo:   e,
 	}
 }
@@ -101,7 +101,7 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
 
 	cn := r.tree // Current node as root
 	if cn == nil {
-		panic("echo ⇛ invalid method")
+		panic("echo: invalid method")
 	}
 	search := path
 
@@ -296,18 +296,19 @@ func (n *node) checkMethodNotAllowed() HandlerFunc {
 // - Get context from `Echo#AcquireContext()`
 // - Reset it `Context#Reset()`
 // - Return it `Echo#ReleaseContext()`.
-func (r *Router) Find(method, path string, context Context) {
-	context.SetPath(path)
+func (r *Router) Find(method, path string, c Context) {
+	ctx := c.(*context)
+	ctx.path = path
 	cn := r.tree // Current node as root
 
 	var (
 		search  = path
-		c       *node  // Child node
-		n       int    // Param counter
-		nk      kind   // Next kind
-		nn      *node  // Next node
-		ns      string // Next search
-		pvalues = context.ParamValues()
+		child   *node         // Child node
+		n       int           // Param counter
+		nk      kind          // Next kind
+		nn      *node         // Next node
+		ns      string        // Next search
+		pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
 	)
 
 	// Search order static > param > any
@@ -352,20 +353,20 @@ func (r *Router) Find(method, path string, context Context) {
 		}
 
 		// Static node
-		if c = cn.findChild(search[0], skind); c != nil {
+		if child = cn.findChild(search[0], skind); child != nil {
 			// Save next
 			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
 				nk = pkind
 				nn = cn
 				ns = search
 			}
-			cn = c
+			cn = child
 			continue
 		}
 
 		// Param node
 	Param:
-		if c = cn.findChildByKind(pkind); c != nil {
+		if child = cn.findChildByKind(pkind); child != nil {
 			// Issue #378
 			if len(pvalues) == n {
 				continue
@@ -378,7 +379,7 @@ func (r *Router) Find(method, path string, context Context) {
 				ns = search
 			}
 
-			cn = c
+			cn = child
 			i, l := 0, len(search)
 			for ; i < l && search[i] != '/'; i++ {
 			}
@@ -409,13 +410,13 @@ func (r *Router) Find(method, path string, context Context) {
 	}
 
 End:
-	context.SetHandler(cn.findHandler(method))
-	context.SetPath(cn.ppath)
-	context.SetParamNames(cn.pnames...)
+	ctx.handler = cn.findHandler(method)
+	ctx.path = cn.ppath
+	ctx.pnames = cn.pnames
 
 	// NOTE: Slow zone...
-	if context.Handler() == nil {
-		context.SetHandler(cn.checkMethodNotAllowed())
+	if ctx.handler == nil {
+		ctx.handler = cn.checkMethodNotAllowed()
 
 		// Dig further for any, might have an empty value for *, e.g.
 		// serving a directory. Issue #207.
@@ -423,12 +424,12 @@ End:
 			return
 		}
 		if h := cn.findHandler(method); h != nil {
-			context.SetHandler(h)
+			ctx.handler = h
 		} else {
-			context.SetHandler(cn.checkMethodNotAllowed())
+			ctx.handler = cn.checkMethodNotAllowed()
 		}
-		context.SetPath(cn.ppath)
-		context.SetParamNames(cn.pnames...)
+		ctx.path = cn.ppath
+		ctx.pnames = cn.pnames
 		pvalues[len(cn.pnames)-1] = ""
 	}
 
diff --git a/vendor/github.com/labstack/gommon/random/random.go b/vendor/github.com/labstack/gommon/random/random.go
index b76bd9b3..b1ae864a 100644
--- a/vendor/github.com/labstack/gommon/random/random.go
+++ b/vendor/github.com/labstack/gommon/random/random.go
@@ -2,22 +2,24 @@ package random
 
 import (
 	"math/rand"
+	"strings"
 	"time"
 )
 
 type (
 	Random struct {
-		charset Charset
 	}
-
-	Charset string
 )
 
+// Charsets
 const (
-	Alphanumeric Charset = Alphabetic + Numeric
-	Alphabetic   Charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-	Numeric      Charset = "0123456789"
-	Hex          Charset = Numeric + "abcdef"
+	Uppercase    string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	Lowercase           = "abcdefghijklmnopqrstuvwxyz"
+	Alphabetic          = Uppercase + Lowercase
+	Numeric             = "0123456789"
+	Alphanumeric        = Alphabetic + Numeric
+	Symbols             = "`" + `~!@#$%^&*()-_+={}[]|\;:"<>,./?`
+	Hex                 = Numeric + "abcdef"
 )
 
 var (
@@ -26,27 +28,21 @@ var (
 
 func New() *Random {
 	rand.Seed(time.Now().UnixNano())
-	return &Random{
-		charset: Alphanumeric,
+	return new(Random)
+}
+
+func (r *Random) String(length uint8, charsets ...string) string {
+	charset := strings.Join(charsets, "")
+	if charset == "" {
+		charset = Alphanumeric
 	}
-}
-
-func (r *Random) SetCharset(c Charset) {
-	r.charset = c
-}
-
-func (r *Random) String(length uint8) string {
 	b := make([]byte, length)
 	for i := range b {
-		b[i] = r.charset[rand.Int63()%int64(len(r.charset))]
+		b[i] = charset[rand.Int63()%int64(len(charset))]
 	}
 	return string(b)
 }
 
-func SetCharset(c Charset) {
-	global.SetCharset(c)
-}
-
-func String(length uint8) string {
-	return global.String(length)
+func String(length uint8, charsets ...string) string {
+	return global.String(length, charsets...)
 }
diff --git a/vendor/github.com/valyala/fasttemplate/unsafe.go b/vendor/github.com/valyala/fasttemplate/unsafe.go
index 5e25b0ff..0498248f 100644
--- a/vendor/github.com/valyala/fasttemplate/unsafe.go
+++ b/vendor/github.com/valyala/fasttemplate/unsafe.go
@@ -1,3 +1,5 @@
+// +build !appengine
+
 package fasttemplate
 
 import (
diff --git a/vendor/github.com/valyala/fasttemplate/unsafe_gae.go b/vendor/github.com/valyala/fasttemplate/unsafe_gae.go
new file mode 100644
index 00000000..cc4ce151
--- /dev/null
+++ b/vendor/github.com/valyala/fasttemplate/unsafe_gae.go
@@ -0,0 +1,11 @@
+// +build appengine
+
+package fasttemplate
+
+func unsafeBytes2String(b []byte) string {
+	return string(b)
+}
+
+func unsafeString2Bytes(s string) []byte {
+	return []byte(s)
+}
diff --git a/vendor/manifest b/vendor/manifest
index 45903beb..8eefc915 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -53,7 +53,7 @@
 			"importpath": "github.com/dgrijalva/jwt-go",
 			"repository": "https://github.com/dgrijalva/jwt-go",
 			"vcs": "git",
-			"revision": "2268707a8f0843315e2004ee4f1d021dc08baedf",
+			"revision": "6c8dedd55f8a2e41f605de6d5d66e51ed1f299fc",
 			"branch": "master",
 			"notests": true
 		},
@@ -170,7 +170,7 @@
 			"importpath": "github.com/labstack/echo",
 			"repository": "https://github.com/labstack/echo",
 			"vcs": "git",
-			"revision": "0b53f397ad7709a27d37500a67735c0a639b5c38",
+			"revision": "c3887ebb131d996411cf13a9688ab02c8dba599e",
 			"branch": "master",
 			"notests": true
 		},
@@ -178,7 +178,7 @@
 			"importpath": "github.com/labstack/gommon/bytes",
 			"repository": "https://github.com/labstack/gommon",
 			"vcs": "git",
-			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1",
+			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a",
 			"branch": "master",
 			"path": "/bytes",
 			"notests": true
@@ -187,7 +187,7 @@
 			"importpath": "github.com/labstack/gommon/color",
 			"repository": "https://github.com/labstack/gommon",
 			"vcs": "git",
-			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1",
+			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a",
 			"branch": "master",
 			"path": "/color",
 			"notests": true
@@ -196,7 +196,7 @@
 			"importpath": "github.com/labstack/gommon/log",
 			"repository": "https://github.com/labstack/gommon",
 			"vcs": "git",
-			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1",
+			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a",
 			"branch": "master",
 			"path": "/log",
 			"notests": true
@@ -205,7 +205,7 @@
 			"importpath": "github.com/labstack/gommon/random",
 			"repository": "https://github.com/labstack/gommon",
 			"vcs": "git",
-			"revision": "f72d3c883f8ea180da8f085dd320804c41332ad1",
+			"revision": "1121fd3e243c202482226a7afe4dcd07ffc4139a",
 			"branch": "master",
 			"path": "/random",
 			"notests": true
@@ -424,7 +424,7 @@
 			"importpath": "github.com/valyala/fasttemplate",
 			"repository": "https://github.com/valyala/fasttemplate",
 			"vcs": "git",
-			"revision": "d090d65668a286d9a180d43a19dfdc5dcad8fe88",
+			"revision": "dcecefd839c4193db0d35b88ec65b4c12d360ab0",
 			"branch": "master",
 			"notests": true
 		},