2 Package echo implements high performance, minimalist Go web framework.
11 "github.com/labstack/echo"
12 "github.com/labstack/echo/middleware"
16 func hello(c echo.Context) error {
17 return c.String(http.StatusOK, "Hello, World!")
25 e.Use(middleware.Logger())
26 e.Use(middleware.Recover())
32 e.Logger.Fatal(e.Start(":1323"))
35 Learn more at https://echo.labstack.com
57 "github.com/labstack/gommon/color"
58 "github.com/labstack/gommon/log"
59 "golang.org/x/crypto/acme/autocert"
63 // Echo is the top-level framework instance.
65 StdLogger *stdLog.Logger
67 premiddleware []MiddlewareFunc
68 middleware []MiddlewareFunc
71 notFoundHandler HandlerFunc
74 TLSServer *http.Server
76 TLSListener net.Listener
77 AutoTLSManager autocert.Manager
82 HTTPErrorHandler HTTPErrorHandler
89 // Route contains a handler and information for matching against requests.
91 Method string `json:"method"`
92 Path string `json:"path"`
93 Name string `json:"name"`
96 // HTTPError represents an error that occurred while handling a request.
100 Internal error // Stores the error returned by an external dependency
103 // MiddlewareFunc defines a function to process middleware.
104 MiddlewareFunc func(HandlerFunc) HandlerFunc
106 // HandlerFunc defines a function to serve HTTP requests.
107 HandlerFunc func(Context) error
109 // HTTPErrorHandler is a centralized HTTP error handler.
110 HTTPErrorHandler func(error, Context)
112 // Validator is the interface that wraps the Validate function.
113 Validator interface {
114 Validate(i interface{}) error
117 // Renderer is the interface that wraps the Render function.
119 Render(io.Writer, string, interface{}, Context) error
122 // Map defines a generic map of type `map[string]interface{}`.
123 Map map[string]interface{}
125 // i is the interface for Echo and Group.
127 GET(string, HandlerFunc, ...MiddlewareFunc) *Route
132 // NOTE: Deprecated, please use the stdlib constants directly instead.
134 CONNECT = http.MethodConnect
135 DELETE = http.MethodDelete
137 HEAD = http.MethodHead
138 OPTIONS = http.MethodOptions
139 PATCH = http.MethodPatch
140 POST = http.MethodPost
141 // PROPFIND = "PROPFIND"
143 TRACE = http.MethodTrace
148 MIMEApplicationJSON = "application/json"
149 MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8
150 MIMEApplicationJavaScript = "application/javascript"
151 MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
152 MIMEApplicationXML = "application/xml"
153 MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + charsetUTF8
154 MIMETextXML = "text/xml"
155 MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + charsetUTF8
156 MIMEApplicationForm = "application/x-www-form-urlencoded"
157 MIMEApplicationProtobuf = "application/protobuf"
158 MIMEApplicationMsgpack = "application/msgpack"
159 MIMETextHTML = "text/html"
160 MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + charsetUTF8
161 MIMETextPlain = "text/plain"
162 MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + charsetUTF8
163 MIMEMultipartForm = "multipart/form-data"
164 MIMEOctetStream = "application/octet-stream"
168 charsetUTF8 = "charset=UTF-8"
169 // PROPFIND Method can be used on collection and property resources.
170 PROPFIND = "PROPFIND"
175 HeaderAccept = "Accept"
176 HeaderAcceptEncoding = "Accept-Encoding"
177 HeaderAllow = "Allow"
178 HeaderAuthorization = "Authorization"
179 HeaderContentDisposition = "Content-Disposition"
180 HeaderContentEncoding = "Content-Encoding"
181 HeaderContentLength = "Content-Length"
182 HeaderContentType = "Content-Type"
183 HeaderCookie = "Cookie"
184 HeaderSetCookie = "Set-Cookie"
185 HeaderIfModifiedSince = "If-Modified-Since"
186 HeaderLastModified = "Last-Modified"
187 HeaderLocation = "Location"
188 HeaderUpgrade = "Upgrade"
190 HeaderWWWAuthenticate = "WWW-Authenticate"
191 HeaderXForwardedFor = "X-Forwarded-For"
192 HeaderXForwardedProto = "X-Forwarded-Proto"
193 HeaderXForwardedProtocol = "X-Forwarded-Protocol"
194 HeaderXForwardedSsl = "X-Forwarded-Ssl"
195 HeaderXUrlScheme = "X-Url-Scheme"
196 HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
197 HeaderXRealIP = "X-Real-IP"
198 HeaderXRequestID = "X-Request-ID"
199 HeaderXRequestedWith = "X-Requested-With"
200 HeaderServer = "Server"
201 HeaderOrigin = "Origin"
204 HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
205 HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
206 HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
207 HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
208 HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
209 HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
210 HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
211 HeaderAccessControlMaxAge = "Access-Control-Max-Age"
214 HeaderStrictTransportSecurity = "Strict-Transport-Security"
215 HeaderXContentTypeOptions = "X-Content-Type-Options"
216 HeaderXXSSProtection = "X-XSS-Protection"
217 HeaderXFrameOptions = "X-Frame-Options"
218 HeaderContentSecurityPolicy = "Content-Security-Policy"
219 HeaderXCSRFToken = "X-CSRF-Token"
224 Version = "3.3.10-dev"
225 website = "https://echo.labstack.com"
226 // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
231 /___/\__/_//_/\___/ %s
232 High performance, minimalist Go web framework
234 ____________________________________O/_______
240 methods = [...]string{
256 ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
257 ErrNotFound = NewHTTPError(http.StatusNotFound)
258 ErrUnauthorized = NewHTTPError(http.StatusUnauthorized)
259 ErrForbidden = NewHTTPError(http.StatusForbidden)
260 ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
261 ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
262 ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests)
263 ErrBadRequest = NewHTTPError(http.StatusBadRequest)
264 ErrBadGateway = NewHTTPError(http.StatusBadGateway)
265 ErrInternalServerError = NewHTTPError(http.StatusInternalServerError)
266 ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout)
267 ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable)
268 ErrValidatorNotRegistered = errors.New("validator not registered")
269 ErrRendererNotRegistered = errors.New("renderer not registered")
270 ErrInvalidRedirectCode = errors.New("invalid redirect status code")
271 ErrCookieNotFound = errors.New("cookie not found")
276 NotFoundHandler = func(c Context) error {
280 MethodNotAllowedHandler = func(c Context) error {
281 return ErrMethodNotAllowed
285 // New creates an instance of Echo.
286 func New() (e *Echo) {
288 Server: new(http.Server),
289 TLSServer: new(http.Server),
290 AutoTLSManager: autocert.Manager{
291 Prompt: autocert.AcceptTOS,
293 Logger: log.New("echo"),
294 colorer: color.New(),
298 e.TLSServer.Handler = e
299 e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
300 e.Binder = &DefaultBinder{}
301 e.Logger.SetLevel(log.ERROR)
302 e.StdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
303 e.pool.New = func() interface{} {
304 return e.NewContext(nil, nil)
306 e.router = NewRouter(e)
310 // NewContext returns a Context instance.
311 func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context {
314 response: NewResponse(w, e),
317 pvalues: make([]string, *e.maxParam),
318 handler: NotFoundHandler,
322 // Router returns router.
323 func (e *Echo) Router() *Router {
327 // DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response
329 func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
331 code = http.StatusInternalServerError
335 if he, ok := err.(*HTTPError); ok {
338 if he.Internal != nil {
339 err = fmt.Errorf("%v, %v", err, he.Internal)
344 msg = http.StatusText(code)
346 if _, ok := msg.(string); ok {
347 msg = Map{"message": msg}
351 if !c.Response().Committed {
352 if c.Request().Method == http.MethodHead { // Issue #608
353 err = c.NoContent(code)
355 err = c.JSON(code, msg)
363 // Pre adds middleware to the chain which is run before router.
364 func (e *Echo) Pre(middleware ...MiddlewareFunc) {
365 e.premiddleware = append(e.premiddleware, middleware...)
368 // Use adds middleware to the chain which is run after router.
369 func (e *Echo) Use(middleware ...MiddlewareFunc) {
370 e.middleware = append(e.middleware, middleware...)
373 // CONNECT registers a new CONNECT route for a path with matching handler in the
374 // router with optional route-level middleware.
375 func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
376 return e.Add(http.MethodConnect, path, h, m...)
379 // DELETE registers a new DELETE route for a path with matching handler in the router
380 // with optional route-level middleware.
381 func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
382 return e.Add(http.MethodDelete, path, h, m...)
385 // GET registers a new GET route for a path with matching handler in the router
386 // with optional route-level middleware.
387 func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
388 return e.Add(http.MethodGet, path, h, m...)
391 // HEAD registers a new HEAD route for a path with matching handler in the
392 // router with optional route-level middleware.
393 func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
394 return e.Add(http.MethodHead, path, h, m...)
397 // OPTIONS registers a new OPTIONS route for a path with matching handler in the
398 // router with optional route-level middleware.
399 func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
400 return e.Add(http.MethodOptions, path, h, m...)
403 // PATCH registers a new PATCH route for a path with matching handler in the
404 // router with optional route-level middleware.
405 func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
406 return e.Add(http.MethodPatch, path, h, m...)
409 // POST registers a new POST route for a path with matching handler in the
410 // router with optional route-level middleware.
411 func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
412 return e.Add(http.MethodPost, path, h, m...)
415 // PUT registers a new PUT route for a path with matching handler in the
416 // router with optional route-level middleware.
417 func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
418 return e.Add(http.MethodPut, path, h, m...)
421 // TRACE registers a new TRACE route for a path with matching handler in the
422 // router with optional route-level middleware.
423 func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
424 return e.Add(http.MethodTrace, path, h, m...)
427 // Any registers a new route for all HTTP methods and path with matching handler
428 // in the router with optional route-level middleware.
429 func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
430 routes := make([]*Route, len(methods))
431 for i, m := range methods {
432 routes[i] = e.Add(m, path, handler, middleware...)
437 // Match registers a new route for multiple HTTP methods and path with matching
438 // handler in the router with optional route-level middleware.
439 func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
440 routes := make([]*Route, len(methods))
441 for i, m := range methods {
442 routes[i] = e.Add(m, path, handler, middleware...)
447 // Static registers a new route with path prefix to serve static files from the
448 // provided root directory.
449 func (e *Echo) Static(prefix, root string) *Route {
451 root = "." // For security we want to restrict to CWD.
453 return static(e, prefix, root)
456 func static(i i, prefix, root string) *Route {
457 h := func(c Context) error {
458 p, err := url.PathUnescape(c.Param("*"))
462 name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
467 return i.GET(prefix+"*", h)
470 return i.GET(prefix+"/*", h)
473 // File registers a new route with path to serve a static file with optional route-level middleware.
474 func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {
475 return e.GET(path, func(c Context) error {
480 // Add registers a new route for an HTTP method and path with matching handler
481 // in the router with optional route-level middleware.
482 func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
483 name := handlerName(handler)
484 e.router.Add(method, path, func(c Context) error {
487 for i := len(middleware) - 1; i >= 0; i-- {
497 e.router.routes[method+path] = r
501 // Group creates a new router group with prefix and optional group-level middleware.
502 func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
503 g = &Group{prefix: prefix, echo: e}
508 // URI generates a URI from handler.
509 func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
510 name := handlerName(handler)
511 return e.Reverse(name, params...)
514 // URL is an alias for `URI` function.
515 func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
516 return e.URI(h, params...)
519 // Reverse generates an URL from route name and provided parameters.
520 func (e *Echo) Reverse(name string, params ...interface{}) string {
521 uri := new(bytes.Buffer)
524 for _, r := range e.router.routes {
526 for i, l := 0, len(r.Path); i < l; i++ {
527 if r.Path[i] == ':' && n < ln {
528 for ; i < l && r.Path[i] != '/'; i++ {
530 uri.WriteString(fmt.Sprintf("%v", params[n]))
534 uri.WriteByte(r.Path[i])
543 // Routes returns the registered routes.
544 func (e *Echo) Routes() []*Route {
545 routes := make([]*Route, 0, len(e.router.routes))
546 for _, v := range e.router.routes {
547 routes = append(routes, v)
552 // AcquireContext returns an empty `Context` instance from the pool.
553 // You must return the context by calling `ReleaseContext()`.
554 func (e *Echo) AcquireContext() Context {
555 return e.pool.Get().(Context)
558 // ReleaseContext returns the `Context` instance back to the pool.
559 // You must call it after `AcquireContext()`.
560 func (e *Echo) ReleaseContext(c Context) {
564 // ServeHTTP implements `http.Handler` interface, which serves HTTP requests.
565 func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
567 c := e.pool.Get().(*context)
572 if e.premiddleware == nil {
573 e.router.Find(r.Method, getPath(r), c)
575 for i := len(e.middleware) - 1; i >= 0; i-- {
576 h = e.middleware[i](h)
579 h = func(c Context) error {
580 e.router.Find(r.Method, getPath(r), c)
582 for i := len(e.middleware) - 1; i >= 0; i-- {
583 h = e.middleware[i](h)
587 for i := len(e.premiddleware) - 1; i >= 0; i-- {
588 h = e.premiddleware[i](h)
593 if err := h(c); err != nil {
594 e.HTTPErrorHandler(err, c)
601 // Start starts an HTTP server.
602 func (e *Echo) Start(address string) error {
603 e.Server.Addr = address
604 return e.StartServer(e.Server)
607 // StartTLS starts an HTTPS server.
608 func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) {
609 if certFile == "" || keyFile == "" {
610 return errors.New("invalid tls configuration")
613 s.TLSConfig = new(tls.Config)
614 s.TLSConfig.Certificates = make([]tls.Certificate, 1)
615 s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
619 return e.startTLS(address)
622 // StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
623 func (e *Echo) StartAutoTLS(address string) error {
625 s.TLSConfig = new(tls.Config)
626 s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
627 return e.startTLS(address)
630 func (e *Echo) startTLS(address string) error {
634 s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")
636 return e.StartServer(e.TLSServer)
639 // StartServer starts a custom http server.
640 func (e *Echo) StartServer(s *http.Server) (err error) {
642 e.colorer.SetOutput(e.Logger.Output())
643 s.ErrorLog = e.StdLogger
646 e.Logger.SetLevel(log.DEBUG)
650 e.colorer.Printf(banner, e.colorer.Red("v"+Version), e.colorer.Blue(website))
653 if s.TLSConfig == nil {
654 if e.Listener == nil {
655 e.Listener, err = newListener(s.Addr)
661 e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
663 return s.Serve(e.Listener)
665 if e.TLSListener == nil {
666 l, err := newListener(s.Addr)
670 e.TLSListener = tls.NewListener(l, s.TLSConfig)
673 e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
675 return s.Serve(e.TLSListener)
678 // Close immediately stops the server.
679 // It internally calls `http.Server#Close()`.
680 func (e *Echo) Close() error {
681 if err := e.TLSServer.Close(); err != nil {
684 return e.Server.Close()
687 // Shutdown stops the server gracefully.
688 // It internally calls `http.Server#Shutdown()`.
689 func (e *Echo) Shutdown(ctx stdContext.Context) error {
690 if err := e.TLSServer.Shutdown(ctx); err != nil {
693 return e.Server.Shutdown(ctx)
696 // NewHTTPError creates a new HTTPError instance.
697 func NewHTTPError(code int, message ...interface{}) *HTTPError {
698 he := &HTTPError{Code: code, Message: http.StatusText(code)}
699 if len(message) > 0 {
700 he.Message = message[0]
705 // Error makes it compatible with `error` interface.
706 func (he *HTTPError) Error() string {
707 return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
710 // SetInternal sets error to HTTPError.Internal
711 func (he *HTTPError) SetInternal(err error) *HTTPError {
716 // WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
717 func WrapHandler(h http.Handler) HandlerFunc {
718 return func(c Context) error {
719 h.ServeHTTP(c.Response(), c.Request())
724 // WrapMiddleware wraps `func(http.Handler) http.Handler` into `echo.MiddlewareFunc`
725 func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
726 return func(next HandlerFunc) HandlerFunc {
727 return func(c Context) (err error) {
728 m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
731 })).ServeHTTP(c.Response(), c.Request())
737 func getPath(r *http.Request) string {
738 path := r.URL.RawPath
745 func handlerName(h HandlerFunc) string {
746 t := reflect.ValueOf(h).Type()
747 if t.Kind() == reflect.Func {
748 return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
753 // // PathUnescape is wraps `url.PathUnescape`
754 // func PathUnescape(s string) (string, error) {
755 // return url.PathUnescape(s)
758 // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
759 // connections. It's used by ListenAndServe and ListenAndServeTLS so
760 // dead TCP connections (e.g. closing laptop mid-download) eventually
762 type tcpKeepAliveListener struct {
766 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
767 tc, err := ln.AcceptTCP()
771 tc.SetKeepAlive(true)
772 tc.SetKeepAlivePeriod(3 * time.Minute)
776 func newListener(address string) (*tcpKeepAliveListener, error) {
777 l, err := net.Listen("tcp", address)
781 return &tcpKeepAliveListener{l.(*net.TCPListener)}, nil