Update vendor labstack/echo

This commit is contained in:
Wim 2017-12-07 23:00:56 +01:00
parent 434393d1c3
commit 5c919e6bff
17 changed files with 335 additions and 143 deletions

View File

@ -494,14 +494,9 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error)
} }
func (c *context) File(file string) (err 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) f, err := os.Open(file)
if err != nil { if err != nil {
return ErrNotFound return NotFoundHandler(c)
} }
defer f.Close() defer f.Close()
@ -510,7 +505,7 @@ func (c *context) File(file string) (err error) {
file = filepath.Join(file, indexPage) file = filepath.Join(file, indexPage)
f, err = os.Open(file) f, err = os.Open(file)
if err != nil { if err != nil {
return ErrNotFound return NotFoundHandler(c)
} }
defer f.Close() defer f.Close()
if fi, err = f.Stat(); err != nil { if fi, err = f.Stat(); err != nil {
@ -530,7 +525,7 @@ func (c *context) Inline(file, name string) (err error) {
} }
func (c *context) contentDisposition(file, name, dispositionType string) (err error) { func (c *context) contentDisposition(file, name, dispositionType string) (err error) {
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%s", dispositionType, name)) c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
c.File(file) c.File(file)
return return
} }

View File

@ -72,6 +72,7 @@ type (
TLSServer *http.Server TLSServer *http.Server
Listener net.Listener Listener net.Listener
TLSListener net.Listener TLSListener net.Listener
AutoTLSManager autocert.Manager
DisableHTTP2 bool DisableHTTP2 bool
Debug bool Debug bool
HideBanner bool HideBanner bool
@ -79,22 +80,22 @@ type (
Binder Binder Binder Binder
Validator Validator Validator Validator
Renderer Renderer Renderer Renderer
AutoTLSManager autocert.Manager
// Mutex sync.RWMutex // Mutex sync.RWMutex
Logger Logger Logger Logger
} }
// Route contains a handler and information for matching against requests. // Route contains a handler and information for matching against requests.
Route struct { Route struct {
Method string `json:"method"` Method string `json:"method"`
Path string `json:"path"` Path string `json:"path"`
Handler string `json:"handler"` Name string `json:"name"`
} }
// HTTPError represents an error that occurred while handling a request. // HTTPError represents an error that occurred while handling a request.
HTTPError struct { HTTPError struct {
Code int Code int
Message interface{} Message interface{}
Inner error // Stores the error returned by an external dependency
} }
// MiddlewareFunc defines a function to process middleware. // MiddlewareFunc defines a function to process middleware.
@ -121,7 +122,7 @@ type (
// i is the interface for Echo and Group. // i is the interface for Echo and Group.
i interface { i interface {
GET(string, HandlerFunc, ...MiddlewareFunc) GET(string, HandlerFunc, ...MiddlewareFunc) *Route
} }
) )
@ -212,7 +213,7 @@ const (
) )
const ( const (
version = "3.1.0" version = "3.2.5"
website = "https://echo.labstack.com" website = "https://echo.labstack.com"
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
banner = ` banner = `
@ -282,7 +283,7 @@ func New() (e *Echo) {
e.TLSServer.Handler = e e.TLSServer.Handler = e
e.HTTPErrorHandler = e.DefaultHTTPErrorHandler e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
e.Binder = &DefaultBinder{} e.Binder = &DefaultBinder{}
e.Logger.SetLevel(log.OFF) e.Logger.SetLevel(log.ERROR)
e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
e.pool.New = func() interface{} { e.pool.New = func() interface{} {
return e.NewContext(nil, nil) return e.NewContext(nil, nil)
@ -319,6 +320,9 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
if he, ok := err.(*HTTPError); ok { if he, ok := err.(*HTTPError); ok {
code = he.Code code = he.Code
msg = he.Message msg = he.Message
if he.Inner != nil {
msg = fmt.Sprintf("%v, %v", err, he.Inner)
}
} else if e.Debug { } else if e.Debug {
msg = err.Error() msg = err.Error()
} else { } else {
@ -328,19 +332,19 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
msg = Map{"message": msg} msg = Map{"message": msg}
} }
e.Logger.Error(err)
// Send response
if !c.Response().Committed { if !c.Response().Committed {
if c.Request().Method == HEAD { // Issue #608 if c.Request().Method == HEAD { // Issue #608
if err := c.NoContent(code); err != nil { err = c.NoContent(code)
goto ERROR
}
} else { } else {
if err := c.JSON(code, msg); err != nil { err = c.JSON(code, msg)
goto ERROR }
} if err != nil {
e.Logger.Error(err)
} }
} }
ERROR:
e.Logger.Error(err)
} }
// Pre adds middleware to the chain which is run before router. // Pre adds middleware to the chain which is run before router.
@ -355,104 +359,114 @@ func (e *Echo) Use(middleware ...MiddlewareFunc) {
// CONNECT registers a new CONNECT route for a path with matching handler in the // CONNECT registers a new CONNECT route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(CONNECT, path, h, m...) return e.Add(CONNECT, path, h, m...)
} }
// DELETE registers a new DELETE route for a path with matching handler in the router // DELETE registers a new DELETE route for a path with matching handler in the router
// with optional route-level middleware. // with optional route-level middleware.
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(DELETE, path, h, m...) return e.Add(DELETE, path, h, m...)
} }
// GET registers a new GET route for a path with matching handler in the router // GET registers a new GET route for a path with matching handler in the router
// with optional route-level middleware. // with optional route-level middleware.
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(GET, path, h, m...) return e.Add(GET, path, h, m...)
} }
// HEAD registers a new HEAD route for a path with matching handler in the // HEAD registers a new HEAD route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(HEAD, path, h, m...) return e.Add(HEAD, path, h, m...)
} }
// OPTIONS registers a new OPTIONS route for a path with matching handler in the // OPTIONS registers a new OPTIONS route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(OPTIONS, path, h, m...) return e.Add(OPTIONS, path, h, m...)
} }
// PATCH registers a new PATCH route for a path with matching handler in the // PATCH registers a new PATCH route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(PATCH, path, h, m...) return e.Add(PATCH, path, h, m...)
} }
// POST registers a new POST route for a path with matching handler in the // POST registers a new POST route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(POST, path, h, m...) return e.Add(POST, path, h, m...)
} }
// PUT registers a new PUT route for a path with matching handler in the // PUT registers a new PUT route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(PUT, path, h, m...) return e.Add(PUT, path, h, m...)
} }
// TRACE registers a new TRACE route for a path with matching handler in the // TRACE registers a new TRACE route for a path with matching handler in the
// router with optional route-level middleware. // router with optional route-level middleware.
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) { func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
e.add(TRACE, path, h, m...) return e.Add(TRACE, path, h, m...)
} }
// Any registers a new route for all HTTP methods and path with matching handler // Any registers a new route for all HTTP methods and path with matching handler
// in the router with optional route-level middleware. // in the router with optional route-level middleware.
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) { func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
routes := make([]*Route, 0)
for _, m := range methods { for _, m := range methods {
e.add(m, path, handler, middleware...) routes = append(routes, e.Add(m, path, handler, middleware...))
} }
return routes
} }
// Match registers a new route for multiple HTTP methods and path with matching // Match registers a new route for multiple HTTP methods and path with matching
// handler in the router with optional route-level middleware. // handler in the router with optional route-level middleware.
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
routes := make([]*Route, 0)
for _, m := range methods { for _, m := range methods {
e.add(m, path, handler, middleware...) routes = append(routes, e.Add(m, path, handler, middleware...))
} }
return routes
} }
// Static registers a new route with path prefix to serve static files from the // Static registers a new route with path prefix to serve static files from the
// provided root directory. // provided root directory.
func (e *Echo) Static(prefix, root string) { func (e *Echo) Static(prefix, root string) *Route {
if root == "" { if root == "" {
root = "." // For security we want to restrict to CWD. root = "." // For security we want to restrict to CWD.
} }
static(e, prefix, root) return static(e, prefix, root)
} }
func static(i i, prefix, root string) { func static(i i, prefix, root string) *Route {
h := func(c Context) error { h := func(c Context) error {
name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security p, err := PathUnescape(c.Param("*"))
if err != nil {
return err
}
name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
return c.File(name) return c.File(name)
} }
i.GET(prefix, h) i.GET(prefix, h)
if prefix == "/" { if prefix == "/" {
i.GET(prefix+"*", h) return i.GET(prefix+"*", h)
} else {
i.GET(prefix+"/*", h)
} }
return i.GET(prefix+"/*", h)
} }
// File registers a new route with path to serve a static file. // File registers a new route with path to serve a static file.
func (e *Echo) File(path, file string) { func (e *Echo) File(path, file string) *Route {
e.GET(path, func(c Context) error { return e.GET(path, func(c Context) error {
return c.File(file) return c.File(file)
}) })
} }
func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { // Add registers a new route for an HTTP method and path with matching handler
// in the router with optional route-level middleware.
func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
name := handlerName(handler) name := handlerName(handler)
e.router.Add(method, path, func(c Context) error { e.router.Add(method, path, func(c Context) error {
h := handler h := handler
@ -463,11 +477,12 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl
return h(c) return h(c)
}) })
r := &Route{ r := &Route{
Method: method, Method: method,
Path: path, Path: path,
Handler: name, Name: name,
} }
e.router.routes[method+path] = r e.router.routes[method+path] = r
return r
} }
// Group creates a new router group with prefix and optional group-level middleware. // Group creates a new router group with prefix and optional group-level middleware.
@ -479,12 +494,22 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
// URI generates a URI from handler. // URI generates a URI from handler.
func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
name := handlerName(handler)
return e.Reverse(name, params...)
}
// URL is an alias for `URI` function.
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
return e.URI(h, params...)
}
// Reverse generates an URL from route name and provided parameters.
func (e *Echo) Reverse(name string, params ...interface{}) string {
uri := new(bytes.Buffer) uri := new(bytes.Buffer)
ln := len(params) ln := len(params)
n := 0 n := 0
name := handlerName(handler)
for _, r := range e.router.routes { for _, r := range e.router.routes {
if r.Handler == name { if r.Name == name {
for i, l := 0, len(r.Path); i < l; i++ { for i, l := 0, len(r.Path); i < l; i++ {
if r.Path[i] == ':' && n < ln { if r.Path[i] == ':' && n < ln {
for ; i < l && r.Path[i] != '/'; i++ { for ; i < l && r.Path[i] != '/'; i++ {
@ -502,11 +527,6 @@ func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
return uri.String() return uri.String()
} }
// URL is an alias for `URI` function.
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
return e.URI(h, params...)
}
// Routes returns the registered routes. // Routes returns the registered routes.
func (e *Echo) Routes() []*Route { func (e *Echo) Routes() []*Route {
routes := []*Route{} routes := []*Route{}
@ -653,7 +673,7 @@ func NewHTTPError(code int, message ...interface{}) *HTTPError {
// Error makes it compatible with `error` interface. // Error makes it compatible with `error` interface.
func (he *HTTPError) Error() string { func (he *HTTPError) Error() string {
return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message) return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
} }
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`. // WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.

View File

@ -21,66 +21,66 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
// Allow all requests to reach the group as they might get dropped if router // 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. // doesn't find a match, making none of the group middleware process.
g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error { g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error {
return ErrNotFound return NotFoundHandler(c)
}, g.middleware...) }, g.middleware...)
} }
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(CONNECT, path, h, m...) return g.Add(CONNECT, path, h, m...)
} }
// DELETE implements `Echo#DELETE()` for sub-routes within the Group. // DELETE implements `Echo#DELETE()` for sub-routes within the Group.
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(DELETE, path, h, m...) return g.Add(DELETE, path, h, m...)
} }
// GET implements `Echo#GET()` for sub-routes within the Group. // GET implements `Echo#GET()` for sub-routes within the Group.
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(GET, path, h, m...) return g.Add(GET, path, h, m...)
} }
// HEAD implements `Echo#HEAD()` for sub-routes within the Group. // HEAD implements `Echo#HEAD()` for sub-routes within the Group.
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(HEAD, path, h, m...) return g.Add(HEAD, path, h, m...)
} }
// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group. // OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(OPTIONS, path, h, m...) return g.Add(OPTIONS, path, h, m...)
} }
// PATCH implements `Echo#PATCH()` for sub-routes within the Group. // PATCH implements `Echo#PATCH()` for sub-routes within the Group.
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(PATCH, path, h, m...) return g.Add(PATCH, path, h, m...)
} }
// POST implements `Echo#POST()` for sub-routes within the Group. // POST implements `Echo#POST()` for sub-routes within the Group.
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(POST, path, h, m...) return g.Add(POST, path, h, m...)
} }
// PUT implements `Echo#PUT()` for sub-routes within the Group. // PUT implements `Echo#PUT()` for sub-routes within the Group.
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(PUT, path, h, m...) return g.Add(PUT, path, h, m...)
} }
// TRACE implements `Echo#TRACE()` for sub-routes within the Group. // TRACE implements `Echo#TRACE()` for sub-routes within the Group.
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) { func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
g.add(TRACE, path, h, m...) return g.Add(TRACE, path, h, m...)
} }
// Any implements `Echo#Any()` for sub-routes within the Group. // Any implements `Echo#Any()` for sub-routes within the Group.
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) { func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
for _, m := range methods { for _, m := range methods {
g.add(m, path, handler, middleware...) g.Add(m, path, handler, middleware...)
} }
} }
// Match implements `Echo#Match()` for sub-routes within the Group. // Match implements `Echo#Match()` for sub-routes within the Group.
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
for _, m := range methods { for _, m := range methods {
g.add(m, path, handler, middleware...) g.Add(m, path, handler, middleware...)
} }
} }
@ -102,12 +102,13 @@ func (g *Group) File(path, file string) {
g.echo.File(g.prefix+path, file) g.echo.File(g.prefix+path, file)
} }
func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) { // Add implements `Echo#Add()` for sub-routes within the Group.
func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
// Combine into a new slice to avoid accidentally passing the same slice for // Combine into a new slice to avoid accidentally passing the same slice for
// multiple routes, which would lead to later add() calls overwriting the // multiple routes, which would lead to later add() calls overwriting the
// middleware from earlier calls. // middleware from earlier calls.
m := []MiddlewareFunc{} m := []MiddlewareFunc{}
m = append(m, g.middleware...) m = append(m, g.middleware...)
m = append(m, middleware...) m = append(m, middleware...)
g.echo.add(method, g.prefix+path, handler, m...) return g.echo.Add(method, g.prefix+path, handler, m...)
} }

View File

@ -3,6 +3,7 @@ package middleware
import ( import (
"encoding/base64" "encoding/base64"
"strconv" "strconv"
"strings"
"github.com/labstack/echo" "github.com/labstack/echo"
) )
@ -27,7 +28,7 @@ type (
) )
const ( const (
basic = "Basic" basic = "basic"
defaultRealm = "Restricted" defaultRealm = "Restricted"
) )
@ -54,7 +55,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
// Defaults // Defaults
if config.Validator == nil { if config.Validator == nil {
panic("basic-auth middleware requires a validator function") panic("echo: basic-auth middleware requires a validator function")
} }
if config.Skipper == nil { if config.Skipper == nil {
config.Skipper = DefaultBasicAuthConfig.Skipper config.Skipper = DefaultBasicAuthConfig.Skipper
@ -72,7 +73,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
auth := c.Request().Header.Get(echo.HeaderAuthorization) auth := c.Request().Header.Get(echo.HeaderAuthorization)
l := len(basic) l := len(basic)
if len(auth) > l+1 && auth[:l] == basic { if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
b, err := base64.StdEncoding.DecodeString(auth[l+1:]) b, err := base64.StdEncoding.DecodeString(auth[l+1:])
if err != nil { if err != nil {
return err return err

112
vendor/github.com/labstack/echo/middleware/body_dump.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package middleware
import (
"bufio"
"bytes"
"io/ioutil"
"net"
"net/http"
"io"
"github.com/labstack/echo"
)
type (
// BodyDumpConfig defines the config for BodyDump middleware.
BodyDumpConfig struct {
// Skipper defines a function to skip middleware.
Skipper Skipper
// Handler receives request and response payload.
// Required.
Handler BodyDumpHandler
}
// BodyDumpHandler receives the request and response payload.
BodyDumpHandler func(echo.Context, []byte, []byte)
bodyDumpResponseWriter struct {
io.Writer
http.ResponseWriter
}
)
var (
// DefaultBodyDumpConfig is the default Gzip middleware config.
DefaultBodyDumpConfig = BodyDumpConfig{
Skipper: DefaultSkipper,
}
)
// BodyDump returns a BodyDump middleware.
//
// BodyLimit middleware captures the request and response payload and calls the
// registered handler.
func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc {
c := DefaultBodyDumpConfig
c.Handler = handler
return BodyDumpWithConfig(c)
}
// BodyDumpWithConfig returns a BodyDump middleware with config.
// See: `BodyDump()`.
func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc {
// Defaults
if config.Handler == nil {
panic("echo: body-dump middleware requires a handler function")
}
if config.Skipper == nil {
config.Skipper = DefaultBodyDumpConfig.Skipper
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
if config.Skipper(c) {
return next(c)
}
// Request
reqBody := []byte{}
if c.Request().Body != nil { // Read
reqBody, _ = ioutil.ReadAll(c.Request().Body)
}
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset
// Response
resBody := new(bytes.Buffer)
mw := io.MultiWriter(c.Response().Writer, resBody)
writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
c.Response().Writer = writer
if err = next(c); err != nil {
c.Error(err)
}
// Callback
config.Handler(c, reqBody, resBody.Bytes())
return
}
}
}
func (w *bodyDumpResponseWriter) WriteHeader(code int) {
w.ResponseWriter.WriteHeader(code)
}
func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func (w *bodyDumpResponseWriter) Flush() {
w.ResponseWriter.(http.Flusher).Flush()
}
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.ResponseWriter.(http.Hijacker).Hijack()
}
func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool {
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

View File

@ -30,7 +30,7 @@ type (
) )
var ( var (
// DefaultBodyLimitConfig is the default Gzip middleware config. // DefaultBodyLimitConfig is the default BodyLimit middleware config.
DefaultBodyLimitConfig = BodyLimitConfig{ DefaultBodyLimitConfig = BodyLimitConfig{
Skipper: DefaultSkipper, Skipper: DefaultSkipper,
} }
@ -60,7 +60,7 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
limit, err := bytes.Parse(config.Limit) limit, err := bytes.Parse(config.Limit)
if err != nil { if err != nil {
panic(fmt.Errorf("invalid body-limit=%s", config.Limit)) panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit))
} }
config.limit = limit config.limit = limit
pool := limitedReaderPool(config) pool := limitedReaderPool(config)

View File

@ -67,7 +67,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
res := c.Response() res := c.Response()
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding) res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) { if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806 res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
rw := res.Writer rw := res.Writer
w, err := gzip.NewWriterLevel(rw, config.Level) w, err := gzip.NewWriterLevel(rw, config.Level)
if err != nil { if err != nil {
@ -98,6 +98,7 @@ func (w *gzipResponseWriter) WriteHeader(code int) {
if code == http.StatusNoContent { // Issue #489 if code == http.StatusNoContent { // Issue #489
w.ResponseWriter.Header().Del(echo.HeaderContentEncoding) w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
} }
w.Header().Del(echo.HeaderContentLength) // Issue #444
w.ResponseWriter.WriteHeader(code) w.ResponseWriter.WriteHeader(code)
} }

View File

@ -1,7 +1,6 @@
package middleware package middleware
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
@ -57,6 +56,12 @@ const (
AlgorithmHS256 = "HS256" AlgorithmHS256 = "HS256"
) )
// Errors
var (
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "Missing or malformed jwt")
ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt")
)
var ( var (
// DefaultJWTConfig is the default JWT auth middleware config. // DefaultJWTConfig is the default JWT auth middleware config.
DefaultJWTConfig = JWTConfig{ DefaultJWTConfig = JWTConfig{
@ -77,7 +82,7 @@ var (
// //
// See: https://jwt.io/introduction // See: https://jwt.io/introduction
// See `JWTConfig.TokenLookup` // See `JWTConfig.TokenLookup`
func JWT(key []byte) echo.MiddlewareFunc { func JWT(key interface{}) echo.MiddlewareFunc {
c := DefaultJWTConfig c := DefaultJWTConfig
c.SigningKey = key c.SigningKey = key
return JWTWithConfig(c) return JWTWithConfig(c)
@ -134,14 +139,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
auth, err := extractor(c) auth, err := extractor(c)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return err
} }
token := new(jwt.Token) token := new(jwt.Token)
// Issue #647, #656 // Issue #647, #656
if _, ok := config.Claims.(jwt.MapClaims); ok { if _, ok := config.Claims.(jwt.MapClaims); ok {
token, err = jwt.Parse(auth, config.keyFunc) token, err = jwt.Parse(auth, config.keyFunc)
} else { } else {
claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims) t := reflect.ValueOf(config.Claims).Type().Elem()
claims := reflect.New(t).Interface().(jwt.Claims)
token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc) token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
} }
if err == nil && token.Valid { if err == nil && token.Valid {
@ -149,7 +155,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
c.Set(config.ContextKey, token) c.Set(config.ContextKey, token)
return next(c) return next(c)
} }
return echo.ErrUnauthorized return &echo.HTTPError{
Code: ErrJWTInvalid.Code,
Message: ErrJWTInvalid.Message,
Inner: err,
}
} }
} }
} }
@ -162,7 +172,7 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor {
if len(auth) > l+1 && auth[:l] == authScheme { if len(auth) > l+1 && auth[:l] == authScheme {
return auth[l+1:], nil return auth[l+1:], nil
} }
return "", errors.New("Missing or invalid jwt in the request header") return "", ErrJWTMissing
} }
} }
@ -171,7 +181,7 @@ func jwtFromQuery(param string) jwtExtractor {
return func(c echo.Context) (string, error) { return func(c echo.Context) (string, error) {
token := c.QueryParam(param) token := c.QueryParam(param)
if token == "" { if token == "" {
return "", errors.New("Missing jwt in the query string") return "", ErrJWTMissing
} }
return token, nil return token, nil
} }
@ -182,7 +192,7 @@ func jwtFromCookie(name string) jwtExtractor {
return func(c echo.Context) (string, error) { return func(c echo.Context) (string, error) {
cookie, err := c.Cookie(name) cookie, err := c.Cookie(name)
if err != nil { if err != nil {
return "", errors.New("Missing jwt in the cookie") return "", ErrJWTMissing
} }
return cookie.Value, nil return cookie.Value, nil
} }

View File

@ -72,7 +72,7 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
config.KeyLookup = DefaultKeyAuthConfig.KeyLookup config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
} }
if config.Validator == nil { if config.Validator == nil {
panic("key-auth middleware requires a validator function") panic("echo: key-auth middleware requires a validator function")
} }
// Initialize // Initialize

View File

@ -1,7 +1,6 @@
package middleware package middleware
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"math/rand" "math/rand"
@ -54,35 +53,38 @@ type (
} }
) )
var (
// DefaultProxyConfig is the default Proxy middleware config.
DefaultProxyConfig = ProxyConfig{
Skipper: DefaultSkipper,
}
)
func proxyHTTP(t *ProxyTarget) http.Handler { func proxyHTTP(t *ProxyTarget) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL) return httputil.NewSingleHostReverseProxy(t.URL)
} }
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h, ok := w.(http.Hijacker) in, _, err := c.Response().Hijack()
if !ok {
c.Error(errors.New("proxy raw, not a hijacker"))
return
}
in, _, err := h.Hijack()
if err != nil { if err != nil {
c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err)) c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
return return
} }
defer in.Close() defer in.Close()
out, err := net.Dial("tcp", t.URL.Host) out, err := net.Dial("tcp", t.URL.Host)
if err != nil { if err != nil {
he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err)) he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))
c.Error(he) c.Error(he)
return return
} }
defer out.Close() defer out.Close()
// Write header
err = r.Write(out) err = r.Write(out)
if err != nil { if err != nil {
he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err)) he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))
c.Error(he) c.Error(he)
return return
} }
@ -97,7 +99,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
go cp(in, out) go cp(in, out)
err = <-errc err = <-errc
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err) c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)
} }
}) })
} }
@ -118,8 +120,18 @@ func (r *RoundRobinBalancer) Next() *ProxyTarget {
return t return t
} }
// Proxy returns an HTTP/WebSocket reverse proxy middleware. // Proxy returns a Proxy middleware.
func Proxy(config ProxyConfig) echo.MiddlewareFunc { //
// Proxy middleware forwards the request to upstream server using a configured load balancing technique.
func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
c := DefaultProxyConfig
c.Balancer = balancer
return ProxyWithConfig(c)
}
// ProxyWithConfig returns a Proxy middleware with config.
// See: `Proxy()`
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
// Defaults // Defaults
if config.Skipper == nil { if config.Skipper == nil {
config.Skipper = DefaultLoggerConfig.Skipper config.Skipper = DefaultLoggerConfig.Skipper
@ -130,6 +142,10 @@ func Proxy(config ProxyConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) { return func(c echo.Context) (err error) {
if config.Skipper(c) {
return next(c)
}
req := c.Request() req := c.Request()
res := c.Response() res := c.Response()
tgt := config.Balancer.Next() tgt := config.Balancer.Next()

View File

@ -5,7 +5,6 @@ import (
"runtime" "runtime"
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/gommon/color"
) )
type ( type (
@ -64,17 +63,14 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
var err error err, ok := r.(error)
switch r := r.(type) { if !ok {
case error:
err = r
default:
err = fmt.Errorf("%v", r) err = fmt.Errorf("%v", r)
} }
stack := make([]byte, config.StackSize) stack := make([]byte, config.StackSize)
length := runtime.Stack(stack, !config.DisableStackAll) length := runtime.Stack(stack, !config.DisableStackAll)
if !config.DisablePrintStack { if !config.DisablePrintStack {
c.Logger().Printf("[%s] %s %s\n", color.Red("PANIC RECOVER"), err, stack[:length]) c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
} }
c.Error(err) c.Error(err)
} }

View File

@ -2,6 +2,7 @@ package middleware
import ( import (
"fmt" "fmt"
"net/http"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -66,7 +67,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
} }
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) (err error) {
if config.Skipper(c) { if config.Skipper(c) {
return next(c) return next(c)
} }
@ -75,17 +76,25 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`. if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
p = c.Param("*") p = c.Param("*")
} }
p, err = echo.PathUnescape(p)
if err != nil {
return
}
name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
fi, err := os.Stat(name) fi, err := os.Stat(name)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
if config.HTML5 && path.Ext(p) == "" { if err = next(c); err != nil {
return c.File(filepath.Join(config.Root, config.Index)) if he, ok := err.(*echo.HTTPError); ok {
if config.HTML5 && he.Code == http.StatusNotFound {
return c.File(filepath.Join(config.Root, config.Index))
}
}
return
} }
return next(c)
} }
return err return
} }
if fi.IsDir() { if fi.IsDir() {
@ -99,7 +108,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return next(c) return next(c)
} }
return err return
} }
return c.File(index) return c.File(index)
@ -110,20 +119,20 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
} }
} }
func listDir(name string, res *echo.Response) error { func listDir(name string, res *echo.Response) (err error) {
dir, err := os.Open(name) dir, err := os.Open(name)
if err != nil { if err != nil {
return err return
} }
dirs, err := dir.Readdir(-1) dirs, err := dir.Readdir(-1)
if err != nil { if err != nil {
return err return
} }
// Create a directory index // Create a directory index
res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8) res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil { if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil {
return err return
} }
for _, d := range dirs { for _, d := range dirs {
name := d.Name() name := d.Name()
@ -133,9 +142,9 @@ func listDir(name string, res *echo.Response) error {
name += "/" name += "/"
} }
if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil { if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil {
return err return
} }
} }
_, err = fmt.Fprintf(res, "</pre>\n") _, err = fmt.Fprintf(res, "</pre>\n")
return err return
} }

View File

@ -11,11 +11,12 @@ type (
// by an HTTP handler to construct an HTTP response. // by an HTTP handler to construct an HTTP response.
// See: https://golang.org/pkg/net/http/#ResponseWriter // See: https://golang.org/pkg/net/http/#ResponseWriter
Response struct { Response struct {
Writer http.ResponseWriter echo *Echo
Status int beforeFuncs []func()
Size int64 Writer http.ResponseWriter
Committed bool Status int
echo *Echo Size int64
Committed bool
} }
) )
@ -34,6 +35,11 @@ func (r *Response) Header() http.Header {
return r.Writer.Header() return r.Writer.Header()
} }
// Before registers a function which is called just before the response is written.
func (r *Response) Before(fn func()) {
r.beforeFuncs = append(r.beforeFuncs, fn)
}
// WriteHeader sends an HTTP response header with status code. If WriteHeader is // WriteHeader sends an HTTP response header with status code. If WriteHeader is
// not called explicitly, the first call to Write will trigger an implicit // not called explicitly, the first call to Write will trigger an implicit
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly // WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
@ -43,6 +49,9 @@ func (r *Response) WriteHeader(code int) {
r.echo.Logger.Warn("response already committed") r.echo.Logger.Warn("response already committed")
return return
} }
for _, fn := range r.beforeFuncs {
fn()
}
r.Status = code r.Status = code
r.Writer.WriteHeader(code) r.Writer.WriteHeader(code)
r.Committed = true r.Committed = true

View File

@ -394,7 +394,7 @@ func (r *Router) Find(method, path string, c Context) {
if cn = cn.findChildByKind(akind); cn == nil { if cn = cn.findChildByKind(akind); cn == nil {
if nn != nil { if nn != nil {
cn = nn cn = nn
nn = nil // Next nn = cn.parent // Next (Issue #954)
search = ns search = ns
if nk == pkind { if nk == pkind {
goto Param goto Param

12
vendor/github.com/labstack/echo/util_go17.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build go1.7, !go1.8
package echo
import (
"net/url"
)
// PathUnescape is wraps `url.QueryUnescape`
func PathUnescape(s string) (string, error) {
return url.QueryUnescape(s)
}

10
vendor/github.com/labstack/echo/util_go18.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
// +build go1.8
package echo
import "net/url"
// PathUnescape is wraps `url.PathUnescape`
func PathUnescape(s string) (string, error) {
return url.PathUnescape(s)
}

2
vendor/manifest vendored
View File

@ -238,7 +238,7 @@
"importpath": "github.com/labstack/echo", "importpath": "github.com/labstack/echo",
"repository": "https://github.com/labstack/echo", "repository": "https://github.com/labstack/echo",
"vcs": "git", "vcs": "git",
"revision": "c3887ebb131d996411cf13a9688ab02c8dba599e", "revision": "0473c51f1dbd83487effce00702571d19033a6e5",
"branch": "master", "branch": "master",
"notests": true "notests": true
}, },