| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 | /*Package echo implements high performance, minimalist Go web framework.Example:  package main  import (    "net/http"    "github.com/labstack/echo"    "github.com/labstack/echo/middleware"  )  // Handler  func hello(c echo.Context) error {    return c.String(http.StatusOK, "Hello, World!")  }  func main() {    // Echo instance    e := echo.New()    // Middleware    e.Use(middleware.Logger())    e.Use(middleware.Recover())    // Routes    e.GET("/", hello)    // Start server    e.Logger.Fatal(e.Start(":1323"))  }Learn more at https://echo.labstack.com*/package echoimport (	"bytes"	stdContext "context"	"crypto/tls"	"errors"	"fmt"	"io"	stdLog "log"	"net"	"net/http"	"net/url"	"path"	"path/filepath"	"reflect"	"runtime"	"sync"	"time"	"github.com/labstack/gommon/color"	"github.com/labstack/gommon/log"	"golang.org/x/crypto/acme/autocert")type (	// Echo is the top-level framework instance.	Echo struct {		stdLogger        *stdLog.Logger		colorer          *color.Color		premiddleware    []MiddlewareFunc		middleware       []MiddlewareFunc		maxParam         *int		router           *Router		notFoundHandler  HandlerFunc		pool             sync.Pool		Server           *http.Server		TLSServer        *http.Server		Listener         net.Listener		TLSListener      net.Listener		AutoTLSManager   autocert.Manager		DisableHTTP2     bool		Debug            bool		HideBanner       bool		HidePort         bool		HTTPErrorHandler HTTPErrorHandler		Binder           Binder		Validator        Validator		Renderer         Renderer		Logger           Logger	}	// Route contains a handler and information for matching against requests.	Route struct {		Method string `json:"method"`		Path   string `json:"path"`		Name   string `json:"name"`	}	// HTTPError represents an error that occurred while handling a request.	HTTPError struct {		Code     int		Message  interface{}		Internal error // Stores the error returned by an external dependency	}	// MiddlewareFunc defines a function to process middleware.	MiddlewareFunc func(HandlerFunc) HandlerFunc	// HandlerFunc defines a function to serve HTTP requests.	HandlerFunc func(Context) error	// HTTPErrorHandler is a centralized HTTP error handler.	HTTPErrorHandler func(error, Context)	// Validator is the interface that wraps the Validate function.	Validator interface {		Validate(i interface{}) error	}	// Renderer is the interface that wraps the Render function.	Renderer interface {		Render(io.Writer, string, interface{}, Context) error	}	// Map defines a generic map of type `map[string]interface{}`.	Map map[string]interface{}	// i is the interface for Echo and Group.	i interface {		GET(string, HandlerFunc, ...MiddlewareFunc) *Route	})// HTTP methodsconst (	CONNECT  = "CONNECT"	DELETE   = "DELETE"	GET      = "GET"	HEAD     = "HEAD"	OPTIONS  = "OPTIONS"	PATCH    = "PATCH"	POST     = "POST"	PROPFIND = "PROPFIND"	PUT      = "PUT"	TRACE    = "TRACE")// MIME typesconst (	MIMEApplicationJSON                  = "application/json"	MIMEApplicationJSONCharsetUTF8       = MIMEApplicationJSON + "; " + charsetUTF8	MIMEApplicationJavaScript            = "application/javascript"	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"	MIMETextHTML                         = "text/html"	MIMETextHTMLCharsetUTF8              = MIMETextHTML + "; " + charsetUTF8	MIMETextPlain                        = "text/plain"	MIMETextPlainCharsetUTF8             = MIMETextPlain + "; " + charsetUTF8	MIMEMultipartForm                    = "multipart/form-data"	MIMEOctetStream                      = "application/octet-stream")const (	charsetUTF8 = "charset=UTF-8")// Headersconst (	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"	HeaderXRequestedWith      = "X-Requested-With"	HeaderServer              = "Server"	HeaderOrigin              = "Origin"	// Access control	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"	// Security	HeaderStrictTransportSecurity = "Strict-Transport-Security"	HeaderXContentTypeOptions     = "X-Content-Type-Options"	HeaderXXSSProtection          = "X-XSS-Protection"	HeaderXFrameOptions           = "X-Frame-Options"	HeaderContentSecurityPolicy   = "Content-Security-Policy"	HeaderXCSRFToken              = "X-CSRF-Token")const (        Version = "3.3.6"        website = "https://zi-tel.com"        // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo        banner = `FDIACS-API %sHigh performance, minimalist API written with Go web framework%s____________________________________O/_______                                    O\`)var (	methods = [...]string{		CONNECT,		DELETE,		GET,		HEAD,		OPTIONS,		PATCH,		POST,		PROPFIND,		PUT,		TRACE,	})// Errorsvar (	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")	ErrRendererNotRegistered       = errors.New("renderer not registered")	ErrInvalidRedirectCode         = errors.New("invalid redirect status code")	ErrCookieNotFound              = errors.New("cookie not found"))// Error handlersvar (	NotFoundHandler = func(c Context) error {		return ErrNotFound	}	MethodNotAllowedHandler = func(c Context) error {		return ErrMethodNotAllowed	})// New creates an instance of Echo.func New() (e *Echo) {	e = &Echo{		Server:    new(http.Server),		TLSServer: new(http.Server),		AutoTLSManager: autocert.Manager{			Prompt: autocert.AcceptTOS,		},		Logger:   log.New("echo"),		colorer:  color.New(),		maxParam: new(int),	}	e.Server.Handler = e	e.TLSServer.Handler = e	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler	e.Binder = &DefaultBinder{}	e.Logger.SetLevel(log.ERROR)	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)	e.pool.New = func() interface{} {		return e.NewContext(nil, nil)	}	e.router = NewRouter(e)	return}// NewContext returns a Context instance.func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context {	return &context{		request:  r,		response: NewResponse(w, e),		store:    make(Map),		echo:     e,		pvalues:  make([]string, *e.maxParam),		handler:  NotFoundHandler,	}}// Router returns router.func (e *Echo) Router() *Router {	return e.router}// DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response// with status code.func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {	var (		code = http.StatusInternalServerError		msg  interface{}	)	if he, ok := err.(*HTTPError); ok {		code = he.Code		msg = he.Message		if he.Internal != nil {			err = fmt.Errorf("%v, %v", err, he.Internal)		}	} else if e.Debug {		msg = err.Error()	} else {		msg = http.StatusText(code)	}	if _, ok := msg.(string); ok {		msg = Map{"message": msg}	}	// Send response	if !c.Response().Committed {		if c.Request().Method == HEAD { // Issue #608			err = c.NoContent(code)		} else {			err = c.JSON(code, msg)		}		if err != nil {			e.Logger.Error(err)		}	}}// Pre adds middleware to the chain which is run before router.func (e *Echo) Pre(middleware ...MiddlewareFunc) {	e.premiddleware = append(e.premiddleware, middleware...)}// Use adds middleware to the chain which is run after router.func (e *Echo) Use(middleware ...MiddlewareFunc) {	e.middleware = append(e.middleware, middleware...)}// CONNECT registers a new CONNECT route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(CONNECT, path, h, m...)}// DELETE registers a new DELETE route for a path with matching handler in the router// with optional route-level middleware.func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(DELETE, path, h, m...)}// GET registers a new GET route for a path with matching handler in the router// with optional route-level middleware.func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(GET, path, h, m...)}// HEAD registers a new HEAD route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(HEAD, path, h, m...)}// OPTIONS registers a new OPTIONS route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(OPTIONS, path, h, m...)}// PATCH registers a new PATCH route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(PATCH, path, h, m...)}// POST registers a new POST route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(POST, path, h, m...)}// PUT registers a new PUT route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(PUT, path, h, m...)}// TRACE registers a new TRACE route for a path with matching handler in the// router with optional route-level middleware.func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {	return e.Add(TRACE, path, h, m...)}// Any registers a new route for all HTTP methods and path with matching handler// in the router with optional route-level middleware.func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {	routes := make([]*Route, len(methods))	for i, m := range methods {		routes[i] = e.Add(m, path, handler, middleware...)	}	return routes}// Match registers a new route for multiple HTTP methods and path with matching// handler in the router with optional route-level middleware.func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {	routes := make([]*Route, len(methods))	for i, m := range methods {		routes[i] = e.Add(m, path, handler, middleware...)	}	return routes}// Static registers a new route with path prefix to serve static files from the// provided root directory.func (e *Echo) Static(prefix, root string) *Route {	if root == "" {		root = "." // For security we want to restrict to CWD.	}	return static(e, prefix, root)}func static(i i, prefix, root string) *Route {	h := func(c Context) error {		p, err := url.PathUnescape(c.Param("*"))		if err != nil {			return err		}		name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security		return c.File(name)	}	i.GET(prefix, h)	if prefix == "/" {		return i.GET(prefix+"*", h)	}	return i.GET(prefix+"/*", h)}// File registers a new route with path to serve a static file with optional route-level middleware.func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {	return e.GET(path, func(c Context) error {		return c.File(file)	}, m...)}// 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)	e.router.Add(method, path, func(c Context) error {		h := handler		// Chain middleware		for i := len(middleware) - 1; i >= 0; i-- {			h = middleware[i](h)		}		return h(c)	})	r := &Route{		Method: method,		Path:   path,		Name:   name,	}	e.router.routes[method+path] = r	return r}// Group creates a new router group with prefix and optional group-level middleware.func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {	g = &Group{prefix: prefix, echo: e}	g.Use(m...)	return}// URI generates a URI from handler.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)	ln := len(params)	n := 0	for _, r := range e.router.routes {		if r.Name == name {			for i, l := 0, len(r.Path); i < l; i++ {				if r.Path[i] == ':' && n < ln {					for ; i < l && r.Path[i] != '/'; i++ {					}					uri.WriteString(fmt.Sprintf("%v", params[n]))					n++				}				if i < l {					uri.WriteByte(r.Path[i])				}			}			break		}	}	return uri.String()}// Routes returns the registered routes.func (e *Echo) Routes() []*Route {	routes := make([]*Route, 0, len(e.router.routes))	for _, v := range e.router.routes {		routes = append(routes, v)	}	return routes}// AcquireContext returns an empty `Context` instance from the pool.// You must return the context by calling `ReleaseContext()`.func (e *Echo) AcquireContext() Context {	return e.pool.Get().(Context)}// ReleaseContext returns the `Context` instance back to the pool.// You must call it after `AcquireContext()`.func (e *Echo) ReleaseContext(c Context) {	e.pool.Put(c)}// ServeHTTP implements `http.Handler` interface, which serves HTTP requests.func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {	// Acquire context	c := e.pool.Get().(*context)	c.Reset(r, w)	h := NotFoundHandler	if e.premiddleware == nil {		e.router.Find(r.Method, getPath(r), c)		h = c.Handler()		for i := len(e.middleware) - 1; i >= 0; i-- {			h = e.middleware[i](h)		}	} else {		h = func(c Context) error {			e.router.Find(r.Method, getPath(r), c)			h := c.Handler()			for i := len(e.middleware) - 1; i >= 0; i-- {				h = e.middleware[i](h)			}			return h(c)		}		for i := len(e.premiddleware) - 1; i >= 0; i-- {			h = e.premiddleware[i](h)		}	}	// Execute chain	if err := h(c); err != nil {		e.HTTPErrorHandler(err, c)	}	// Release context	e.pool.Put(c)}// Start starts an HTTP server.func (e *Echo) Start(address string) error {	e.Server.Addr = address	return e.StartServer(e.Server)}// StartTLS starts an HTTPS server.func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) {	if certFile == "" || keyFile == "" {		return errors.New("invalid tls configuration")	}	s := e.TLSServer	s.TLSConfig = new(tls.Config)	s.TLSConfig.Certificates = make([]tls.Certificate, 1)	s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)	if err != nil {		return	}	return e.startTLS(address)}// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.func (e *Echo) StartAutoTLS(address string) error {	if e.Listener == nil {		go http.ListenAndServe(":http", e.AutoTLSManager.HTTPHandler(nil))	}	s := e.TLSServer	s.TLSConfig = new(tls.Config)	s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate	return e.startTLS(address)}func (e *Echo) startTLS(address string) error {	s := e.TLSServer	s.Addr = address	if !e.DisableHTTP2 {		s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")	}	return e.StartServer(e.TLSServer)}// StartServer starts a custom http server.func (e *Echo) StartServer(s *http.Server) (err error) {	// Setup	e.colorer.SetOutput(e.Logger.Output())	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 {			e.Listener, err = newListener(s.Addr)			if err != nil {				return err			}		}		if !e.HidePort {			e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))		}		return s.Serve(e.Listener)	}	if e.TLSListener == nil {		l, err := newListener(s.Addr)		if err != nil {			return err		}		e.TLSListener = tls.NewListener(l, s.TLSConfig)	}	if !e.HidePort {		e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))	}	return s.Serve(e.TLSListener)}// 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)}// NewHTTPError creates a new HTTPError instance.func NewHTTPError(code int, message ...interface{}) *HTTPError {	he := &HTTPError{Code: code, Message: http.StatusText(code)}	if len(message) > 0 {		he.Message = message[0]	}	return he}// Error makes it compatible with `error` interface.func (he *HTTPError) Error() string {	return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)}// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.func WrapHandler(h http.Handler) HandlerFunc {	return func(c Context) error {		h.ServeHTTP(c.Response(), c.Request())		return nil	}}// WrapMiddleware wraps `func(http.Handler) http.Handler` into `echo.MiddlewareFunc`func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {	return func(next HandlerFunc) HandlerFunc {		return func(c Context) (err error) {			m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {				c.SetRequest(r)				err = next(c)			})).ServeHTTP(c.Response(), c.Request())			return		}	}}func getPath(r *http.Request) string {	path := r.URL.RawPath	if path == "" {		path = r.URL.Path	}	return path}func handlerName(h HandlerFunc) string {	t := reflect.ValueOf(h).Type()	if t.Kind() == reflect.Func {		return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()	}	return t.String()}// // PathUnescape is wraps `url.PathUnescape`// func PathUnescape(s string) (string, error) {// 	return url.PathUnescape(s)// }// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted// connections. It's used by ListenAndServe and ListenAndServeTLS so// dead TCP connections (e.g. closing laptop mid-download) eventually// go away.type tcpKeepAliveListener struct {	*net.TCPListener}func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {	tc, err := ln.AcceptTCP()	if err != nil {		return	}	tc.SetKeepAlive(true)	tc.SetKeepAlivePeriod(3 * time.Minute)	return tc, nil}func newListener(address string) (*tcpKeepAliveListener, error) {	l, err := net.Listen("tcp", address)	if err != nil {		return nil, err	}	return &tcpKeepAliveListener{l.(*net.TCPListener)}, nil}
 |