diff --git a/context.go b/context.go index 31df4c56..78eb197a 100644 --- a/context.go +++ b/context.go @@ -22,40 +22,108 @@ import ( ) type ( - // Context represents context for the current request. It holds request and - // response objects, path parameters, data and registered handler. + // Context represents the context of the current HTTP request. It holds request and + // response objects, path, path parameters, data and registered handler. Context interface { netContext.Context + + // NetContext returns `http://blog.golang.org/context.Context` interface. NetContext() netContext.Context + + // SetNetContext sets `http://blog.golang.org/context.Context` interface. SetNetContext(netContext.Context) + + // Request returns `engine.Request` interface. Request() engine.Request + + // Request returns `engine.Response` interface. Response() engine.Response + + // Path returns the registered path for the handler. Path() string + + // P returns path parameter by index. P(int) string + + // Param returns path parameter by name. Param(string) string + + // ParamNames returns path parameter names. ParamNames() []string + + // Query returns query parameter by name. Query(string) string + + // Form returns form parameter by name. Form(string) string + + // Get retrieves data from the context. Get(string) interface{} + + // Set saves data in the context. Set(string, interface{}) + + // Bind binds the request body into provided type `i`. The default binder does + // it based on Content-Type header. Bind(interface{}) error + + // Render renders a template with data and sends a text/html response with status + // code. Templates can be registered using `Echo.SetRenderer()`. Render(int, string, interface{}) error + + // HTML sends an HTTP response with status code. HTML(int, string) error + + // String sends a string response with status code. String(int, string) error + + // JSON sends a JSON response with status code. JSON(int, interface{}) error + + // JSONBlob sends a JSON blob response with status code. JSONBlob(int, []byte) error + + // JSONP sends a JSONP response with status code. It uses `callback` to construct + // the JSONP payload. JSONP(int, string, interface{}) error + + // XML sends an XML response with status code. XML(int, interface{}) error + + // XMLBlob sends a XML blob response with status code. XMLBlob(int, []byte) error + + // File sends a response with the content of the file. File(string) error + + // Attachment sends a response from `io.Reader` as attachment, prompting client + // to save the file. Attachment(io.Reader, string) error + + // NoContent sends a response with no body and a status code. NoContent(int) error + + // Redirect redirects the request with status code. Redirect(int, string) error + + // Error invokes the registered HTTP error handler. Generally used by middleware. Error(err error) + + // Handler implements `Handler` interface. Handle(Context) error + + // Logger returns the `Logger` instance. Logger() *log.Logger + + // Echo returns the `Echo` instance. Echo() *Echo + + // Object returns the `context` instance. Object() *context + + // Reset resets the context after request completes. It must be called along + // with `Echo#GetContext()` and `Echo#PutContext()`. See `Echo#ServeHTTP()` + Reset(engine.Request, engine.Response) } context struct { @@ -118,22 +186,18 @@ func (c *context) Handle(ctx Context) error { return c.handler.Handle(ctx) } -// Request returns *http.Request. func (c *context) Request() engine.Request { return c.request } -// Response returns `engine.Response`. func (c *context) Response() engine.Response { return c.response } -// Path returns the registered path for the handler. func (c *context) Path() string { return c.path } -// P returns path parameter by index. func (c *context) P(i int) (value string) { l := len(c.pnames) if i < l { @@ -142,7 +206,6 @@ func (c *context) P(i int) (value string) { return } -// Param returns path parameter by name. func (c *context) Param(name string) (value string) { l := len(c.pnames) for i, n := range c.pnames { @@ -154,22 +217,18 @@ func (c *context) Param(name string) (value string) { return } -// ParamNames returns path parameter names. func (c *context) ParamNames() []string { return c.pnames } -// Query returns query parameter by name. func (c *context) Query(name string) string { return c.request.URL().QueryValue(name) } -// Form returns form parameter by name. func (c *context) Form(name string) string { return c.request.FormValue(name) } -// Set saves data in the context. func (c *context) Set(key string, val interface{}) { if c.store == nil { c.store = make(store) @@ -177,19 +236,14 @@ func (c *context) Set(key string, val interface{}) { c.store[key] = val } -// Get retrieves data from the context. func (c *context) Get(key string) interface{} { return c.store[key] } -// Bind binds the request body into provided type `i`. The default binder does -// it based on Content-Type header. func (c *context) Bind(i interface{}) error { return c.echo.binder.Bind(i, c) } -// Render renders a template with data and sends a text/html response with status -// code. Templates can be registered using `Echo.SetRenderer()`. func (c *context) Render(code int, name string, data interface{}) (err error) { if c.echo.renderer == nil { return ErrRendererNotRegistered @@ -204,7 +258,6 @@ func (c *context) Render(code int, name string, data interface{}) (err error) { return } -// HTML sends an HTTP response with status code. func (c *context) HTML(code int, html string) (err error) { c.response.Header().Set(ContentType, TextHTMLCharsetUTF8) c.response.WriteHeader(code) @@ -212,7 +265,6 @@ func (c *context) HTML(code int, html string) (err error) { return } -// String sends a string response with status code. func (c *context) String(code int, s string) (err error) { c.response.Header().Set(ContentType, TextPlainCharsetUTF8) c.response.WriteHeader(code) @@ -220,7 +272,6 @@ func (c *context) String(code int, s string) (err error) { return } -// JSON sends a JSON response with status code. func (c *context) JSON(code int, i interface{}) (err error) { b, err := json.Marshal(i) if c.echo.Debug() { @@ -232,7 +283,6 @@ func (c *context) JSON(code int, i interface{}) (err error) { return c.JSONBlob(code, b) } -// JSONBlob sends a JSON blob response with status code. func (c *context) JSONBlob(code int, b []byte) (err error) { c.response.Header().Set(ContentType, ApplicationJSONCharsetUTF8) c.response.WriteHeader(code) @@ -240,8 +290,6 @@ func (c *context) JSONBlob(code int, b []byte) (err error) { return } -// JSONP sends a JSONP response with status code. It uses `callback` to construct -// the JSONP payload. func (c *context) JSONP(code int, callback string, i interface{}) (err error) { b, err := json.Marshal(i) if err != nil { @@ -259,7 +307,6 @@ func (c *context) JSONP(code int, callback string, i interface{}) (err error) { return } -// XML sends an XML response with status code. func (c *context) XML(code int, i interface{}) (err error) { b, err := xml.Marshal(i) if c.echo.Debug() { @@ -271,7 +318,6 @@ func (c *context) XML(code int, i interface{}) (err error) { return c.XMLBlob(code, b) } -// XMLBlob sends a XML blob response with status code. func (c *context) XMLBlob(code int, b []byte) (err error) { c.response.Header().Set(ContentType, ApplicationXMLCharsetUTF8) c.response.WriteHeader(code) @@ -282,7 +328,6 @@ func (c *context) XMLBlob(code int, b []byte) (err error) { return } -// File sends a response with the content of the file. func (c *context) File(file string) error { root, file := filepath.Split(file) fs := http.Dir(root) @@ -305,8 +350,6 @@ func (c *context) File(file string) error { return ServeContent(c.Request(), c.Response(), f, fi) } -// Attachment sends a response from `io.Reader` as attachment, prompting client -// to save the file. func (c *context) Attachment(r io.Reader, name string) (err error) { c.response.Header().Set(ContentType, detectContentType(name)) c.response.Header().Set(ContentDisposition, "attachment; filename="+name) @@ -315,13 +358,11 @@ func (c *context) Attachment(r io.Reader, name string) (err error) { return } -// NoContent sends a response with no body and a status code. func (c *context) NoContent(code int) error { c.response.WriteHeader(code) return nil } -// Redirect redirects the request with status code. func (c *context) Redirect(code int, url string) error { if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect { return ErrInvalidRedirectCode @@ -331,26 +372,24 @@ func (c *context) Redirect(code int, url string) error { return nil } -// Error invokes the registered HTTP error handler. Generally used by middleware. func (c *context) Error(err error) { c.echo.httpErrorHandler(err, c) } -// Echo returns the `Echo` instance. func (c *context) Echo() *Echo { return c.echo } -// Logger returns the `Logger` instance. func (c *context) Logger() *log.Logger { return c.echo.logger } -// Object returns the `context` object. func (c *context) Object() *context { return c } +// ServeContent sends a response from `io.Reader`. It automatically sets the `Content-Type` +// and `Last-Modified` headers. func ServeContent(req engine.Request, res engine.Response, f http.File, fi os.FileInfo) error { res.Header().Set(ContentType, detectContentType(fi.Name())) res.Header().Set(LastModified, fi.ModTime().UTC().Format(http.TimeFormat)) @@ -366,7 +405,7 @@ func detectContentType(name string) (t string) { return } -func (c *context) reset(req engine.Request, res engine.Response) { +func (c *context) Reset(req engine.Request, res engine.Response) { c.netContext = nil c.request = req c.response = res diff --git a/context_test.go b/context_test.go index 4aee5342..1c4f7ed8 100644 --- a/context_test.go +++ b/context_test.go @@ -191,8 +191,8 @@ func TestContext(t *testing.T) { c.Error(errors.New("error")) assert.Equal(t, http.StatusInternalServerError, rec.Status()) - // reset - c.Object().reset(req, test.NewResponseRecorder()) + // Reset + c.Object().Reset(req, test.NewResponseRecorder()) } func TestContextPath(t *testing.T) { diff --git a/echo.go b/echo.go index 128ae1c3..ec7d04a5 100644 --- a/echo.go +++ b/echo.go @@ -1,3 +1,42 @@ +/* +Package echo implements a fast and unfancy micro web framework for Go. + +Example: + + package main + + import ( + "net/http" + + "github.com/labstack/echo" + "github.com/labstack/echo/engine/standard" + "github.com/labstack/echo/middleware" + ) + + // Handler + func hello() echo.HandlerFunc { + return func(c echo.Context) error { + return c.String(http.StatusOK, "Hello, World!\n") + } + } + + func main() { + // Echo instance + e := echo.New() + + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + // Routes + e.Get("/", hello()) + + // Start server + e.Run(standard.New(":1323")) + } + +Learn more at https://labstack.com/echo +*/ package echo import ( @@ -20,6 +59,7 @@ import ( ) type ( + // Echo is the top-level framework instance. Echo struct { prefix string middleware []Middleware @@ -36,33 +76,43 @@ type ( logger *log.Logger } + // Route contains a handler and information for matching against requests. Route struct { Method string Path string Handler string } + // HTTPError represents an error that occured while handling a request. HTTPError struct { Code int Message string } + // Middleware defines an interface for middleware via `Handle(Handler) Handler` + // function. Middleware interface { Handle(Handler) Handler } + // MiddlewareFunc is an adapter to allow the use of `func(Handler) Handler` as + // middleware. MiddlewareFunc func(Handler) Handler + // Handler defines an interface to server HTTP requests via `Handle(Context)` + // function. Handler interface { Handle(Context) error } + // HandlerFunc is an adapter to allow the use of `func(Context)` as an HTTP + // handler. HandlerFunc func(Context) error // HTTPErrorHandler is a centralized HTTP error handler. HTTPErrorHandler func(error, Context) - // Binder is the interface that wraps the Bind method. + // Binder is the interface that wraps the Bind function. Binder interface { Bind(interface{}, Context) error } @@ -70,41 +120,36 @@ type ( binder struct { } - // Validator is the interface that wraps the Validate method. + // Validator is the interface that wraps the Validate function. Validator interface { Validate() error } - // Renderer is the interface that wraps the Render method. + // Renderer is the interface that wraps the Render function. Renderer interface { Render(io.Writer, string, interface{}, Context) error } ) +//-------------- +// HTTP methods +//-------------- const ( - // CONNECT HTTP method CONNECT = "CONNECT" - // DELETE HTTP method - DELETE = "DELETE" - // GET HTTP method - GET = "GET" - // HEAD HTTP method - HEAD = "HEAD" - // OPTIONS HTTP method + DELETE = "DELETE" + GET = "GET" + HEAD = "HEAD" OPTIONS = "OPTIONS" - // PATCH HTTP method - PATCH = "PATCH" - // POST HTTP method - POST = "POST" - // PUT HTTP method - PUT = "PUT" - // TRACE HTTP method - TRACE = "TRACE" - - //------------- - // Media types - //------------- + PATCH = "PATCH" + POST = "POST" + PUT = "PUT" + TRACE = "TRACE" +) +//------------- +// Media types +//------------- +const ( ApplicationJSON = "application/json" ApplicationJSONCharsetUTF8 = ApplicationJSON + "; " + CharsetUTF8 ApplicationJavaScript = "application/javascript" @@ -120,17 +165,19 @@ const ( TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8 MultipartForm = "multipart/form-data" OctetStream = "application/octet-stream" +) - //--------- - // Charset - //--------- - +//--------- +// Charset +//--------- +const ( CharsetUTF8 = "charset=utf-8" +) - //--------- - // Headers - //--------- - +//--------- +// Headers +//--------- +const ( AcceptEncoding = "Accept-Encoding" Authorization = "Authorization" ContentDisposition = "Content-Disposition" @@ -158,22 +205,24 @@ var ( PUT, TRACE, } +) - //-------- - // Errors - //-------- - +//-------- +// Errors +//-------- +var ( ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType) ErrNotFound = NewHTTPError(http.StatusNotFound) ErrUnauthorized = NewHTTPError(http.StatusUnauthorized) ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) ErrRendererNotRegistered = errors.New("renderer not registered") ErrInvalidRedirectCode = errors.New("invalid redirect status code") +) - //---------------- - // Error handlers - //---------------- - +//---------------- +// Error handlers +//---------------- +var ( notFoundHandler = HandlerFunc(func(c Context) error { return ErrNotFound }) @@ -206,12 +255,14 @@ func New() (e *Echo) { return } -func (m MiddlewareFunc) Handle(h Handler) Handler { - return m(h) +// Handle chains middleware. +func (f MiddlewareFunc) Handle(h Handler) Handler { + return f(h) } -func (h HandlerFunc) Handle(c Context) error { - return h(c) +// Handle serves HTTP request. +func (f HandlerFunc) Handle(c Context) error { + return f(c) } // Router returns router. @@ -261,12 +312,12 @@ func (e *Echo) SetHTTPErrorHandler(h HTTPErrorHandler) { e.httpErrorHandler = h } -// SetBinder registers a custom binder. It's invoked by Context.Bind(). +// SetBinder registers a custom binder. It's invoked by `Context#Bind()`. func (e *Echo) SetBinder(b Binder) { e.binder = b } -// SetRenderer registers an HTML template renderer. It's invoked by Context.Render(). +// SetRenderer registers an HTML template renderer. It's invoked by `Context#Render()`. func (e *Echo) SetRenderer(r Renderer) { e.renderer = r } @@ -301,59 +352,70 @@ func (e *Echo) chainMiddleware() { } } -// Connect adds a CONNECT route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(CONNECT, path, h, m...) } -// Delete adds a DELETE route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(DELETE, path, h, m...) } -// Get adds a GET route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(GET, path, h, m...) } -// Head adds a HEAD route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(HEAD, path, h, m...) } -// Options adds an OPTIONS route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(OPTIONS, path, h, m...) } -// Patch adds a PATCH route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(PATCH, path, h, m...) } -// Post adds a POST route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(POST, path, h, m...) } -// Put adds a PUT route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(PUT, path, h, m...) } -// Trace adds a TRACE route > handler to the router. +// 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 Handler, m ...Middleware) { e.add(TRACE, path, h, m...) } -// Any adds a route > handler to the router for all HTTP methods. +// 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 Handler, middleware ...Middleware) { for _, m := range methods { e.add(m, path, handler, middleware...) } } -// Match adds a route > handler to the router for multiple HTTP methods provided. +// 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 Handler, middleware ...Middleware) { for _, m := range methods { e.add(m, path, handler, middleware...) @@ -392,7 +454,7 @@ func (e *Echo) add(method, path string, handler Handler, middleware ...Middlewar e.router.routes = append(e.router.routes, r) } -// Group creates a new router group with prefix. +// Group creates a new router group with prefix and optional group-level middleware. func (e *Echo) Group(prefix string, m ...Middleware) (g *Group) { g = &Group{prefix: prefix, echo: e} g.Use(m...) @@ -448,7 +510,7 @@ func (e *Echo) PutContext(c Context) { func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) { c := e.pool.Get().(*context) - c.reset(req, res) + c.Reset(req, res) // Execute chain if err := e.head.Handle(c); err != nil { @@ -465,6 +527,7 @@ func (e *Echo) Run(s engine.Server) error { return s.Start() } +// NewHTTPError creates a new HTTPError instance. func NewHTTPError(code int, msg ...string) *HTTPError { he := &HTTPError{Code: code, Message: http.StatusText(code)} if len(msg) > 0 { diff --git a/engine/engine.go b/engine/engine.go index 35d5548c..a5526030 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -54,7 +54,7 @@ type ( // RemoteAddress returns the client's network address. RemoteAddress() string - // Method returns the request's HTTP method. + // Method returns the request's HTTP function. Method() string // SetMethod sets the HTTP method of the request. @@ -136,7 +136,7 @@ type ( QueryString() string } - // Config defines engine configuration. + // Config defines engine config. Config struct { Address string // TCP address to listen on. Listener net.Listener // Custom `net.Listener`. If set, server accepts connections on it. @@ -152,7 +152,8 @@ type ( ServeHTTP(Request, Response) } - // HandlerFunc is an adapter to allow the use of `func(Request, Response)` as HTTP handlers. + // HandlerFunc is an adapter to allow the use of `func(Request, Response)` as + // an HTTP handler. HandlerFunc func(Request, Response) ) diff --git a/engine/fasthttp/header.go b/engine/fasthttp/header.go index e3dfdbc1..b6a13280 100644 --- a/engine/fasthttp/header.go +++ b/engine/fasthttp/header.go @@ -16,27 +16,27 @@ type ( } ) -// Add implements `engine.Header#Add` method. +// Add implements `engine.Header#Add` function. func (h *RequestHeader) Add(key, val string) { // h.RequestHeader.Add(key, val) } -// Del implements `engine.Header#Del` method. +// Del implements `engine.Header#Del` function. func (h *RequestHeader) Del(key string) { h.RequestHeader.Del(key) } -// Set implements `engine.Header#Set` method. +// Set implements `engine.Header#Set` function. func (h *RequestHeader) Set(key, val string) { h.RequestHeader.Set(key, val) } -// Get implements `engine.Header#Get` method. +// Get implements `engine.Header#Get` function. func (h *RequestHeader) Get(key string) string { return string(h.Peek(key)) } -// Keys implements `engine.Header#Keys` method. +// Keys implements `engine.Header#Keys` function. func (h *RequestHeader) Keys() (keys []string) { keys = make([]string, h.Len()) i := 0 @@ -51,28 +51,28 @@ func (h *RequestHeader) reset(hdr *fasthttp.RequestHeader) { h.RequestHeader = hdr } -// Add implements `engine.Header#Add` method. +// Add implements `engine.Header#Add` function. func (h *ResponseHeader) Add(key, val string) { // TODO: https://github.com/valyala/fasthttp/issues/69 // h.header.Add(key, val) } -// Del implements `engine.Header#Del` method. +// Del implements `engine.Header#Del` function. func (h *ResponseHeader) Del(key string) { h.ResponseHeader.Del(key) } -// Get implements `engine.Header#Get` method. +// Get implements `engine.Header#Get` function. func (h *ResponseHeader) Get(key string) string { return string(h.Peek(key)) } -// Set implements `engine.Header#Set` method. +// Set implements `engine.Header#Set` function. func (h *ResponseHeader) Set(key, val string) { h.ResponseHeader.Set(key, val) } -// Keys implements `engine.Header#Keys` method. +// Keys implements `engine.Header#Keys` function. func (h *ResponseHeader) Keys() (keys []string) { keys = make([]string, h.Len()) i := 0 diff --git a/engine/fasthttp/request.go b/engine/fasthttp/request.go index cb4f6f40..0d95197b 100644 --- a/engine/fasthttp/request.go +++ b/engine/fasthttp/request.go @@ -22,72 +22,72 @@ type ( } ) -// TLS implements `engine.Request#TLS` method. +// TLS implements `engine.Request#TLS` function. func (r *Request) TLS() bool { return r.IsTLS() } -// Scheme implements `engine.Request#Scheme` method. +// Scheme implements `engine.Request#Scheme` function. func (r *Request) Scheme() string { return string(r.RequestCtx.URI().Scheme()) } -// Host implements `engine.Request#Host` method. +// Host implements `engine.Request#Host` function. func (r *Request) Host() string { return string(r.RequestCtx.Host()) } -// URL implements `engine.Request#URL` method. +// URL implements `engine.Request#URL` function. func (r *Request) URL() engine.URL { return r.url } -// Header implements `engine.Request#Header` method. +// Header implements `engine.Request#Header` function. func (r *Request) Header() engine.Header { return r.header } -// UserAgent implements `engine.Request#UserAgent` method. +// UserAgent implements `engine.Request#UserAgent` function. func (r *Request) UserAgent() string { return string(r.RequestCtx.UserAgent()) } -// RemoteAddress implements `engine.Request#RemoteAddress` method. +// RemoteAddress implements `engine.Request#RemoteAddress` function. func (r *Request) RemoteAddress() string { return r.RemoteAddr().String() } -// Method implements `engine.Request#Method` method. +// Method implements `engine.Request#Method` function. func (r *Request) Method() string { return string(r.RequestCtx.Method()) } -// SetMethod implements `engine.Request#SetMethod` method. +// SetMethod implements `engine.Request#SetMethod` function. func (r *Request) SetMethod(method string) { r.Request.Header.SetMethod(method) } -// URI implements `engine.Request#URI` method. +// URI implements `engine.Request#URI` function. func (r *Request) URI() string { return string(r.RequestURI()) } -// Body implements `engine.Request#Body` method. +// Body implements `engine.Request#Body` function. func (r *Request) Body() io.Reader { return bytes.NewBuffer(r.PostBody()) } -// FormValue implements `engine.Request#FormValue` method. +// FormValue implements `engine.Request#FormValue` function. func (r *Request) FormValue(name string) string { return string(r.RequestCtx.FormValue(name)) } -// FormFile implements `engine.Request#FormFile` method. +// FormFile implements `engine.Request#FormFile` function. func (r *Request) FormFile(name string) (*multipart.FileHeader, error) { return r.RequestCtx.FormFile(name) } -// MultipartForm implements `engine.Request#MultipartForm` method. +// MultipartForm implements `engine.Request#MultipartForm` function. func (r *Request) MultipartForm() (*multipart.Form, error) { return r.RequestCtx.MultipartForm() } diff --git a/engine/fasthttp/response.go b/engine/fasthttp/response.go index e56d0487..45643535 100644 --- a/engine/fasthttp/response.go +++ b/engine/fasthttp/response.go @@ -24,12 +24,12 @@ type ( } ) -// Header implements `engine.Response#Header` method. +// Header implements `engine.Response#Header` function. func (r *Response) Header() engine.Header { return r.header } -// WriteHeader implements `engine.Response#WriteHeader` method. +// WriteHeader implements `engine.Response#WriteHeader` function. func (r *Response) WriteHeader(code int) { if r.committed { r.logger.Warn("response already committed") @@ -40,34 +40,34 @@ func (r *Response) WriteHeader(code int) { r.committed = true } -// Write implements `engine.Response#Write` method. +// Write implements `engine.Response#Write` function. func (r *Response) Write(b []byte) (n int, err error) { n, err = r.writer.Write(b) r.size += int64(n) return } -// Status implements `engine.Response#Status` method. +// Status implements `engine.Response#Status` function. func (r *Response) Status() int { return r.status } -// Size implements `engine.Response#Size` method. +// Size implements `engine.Response#Size` function. func (r *Response) Size() int64 { return r.size } -// Committed implements `engine.Response#Committed` method. +// Committed implements `engine.Response#Committed` function. func (r *Response) Committed() bool { return r.committed } -// Writer implements `engine.Response#Writer` method. +// Writer implements `engine.Response#Writer` function. func (r *Response) Writer() io.Writer { return r.writer } -// SetWriter implements `engine.Response#SetWriter` method. +// SetWriter implements `engine.Response#SetWriter` function. func (r *Response) SetWriter(w io.Writer) { r.writer = w } diff --git a/engine/fasthttp/server.go b/engine/fasthttp/server.go index d6704e64..e6571404 100644 --- a/engine/fasthttp/server.go +++ b/engine/fasthttp/server.go @@ -87,17 +87,17 @@ func NewFromConfig(c engine.Config) (s *Server) { return } -// SetHandler implements `engine.Server#SetHandler` method. +// SetHandler implements `engine.Server#SetHandler` function. func (s *Server) SetHandler(h engine.Handler) { s.handler = h } -// SetLogger implements `engine.Server#SetLogger` method. +// SetLogger implements `engine.Server#SetLogger` function. func (s *Server) SetLogger(l *log.Logger) { s.logger = l } -// Start implements `engine.Server#Start` method. +// Start implements `engine.Server#Start` function. func (s *Server) Start() error { if s.config.Listener == nil { return s.startDefaultListener() diff --git a/engine/fasthttp/url.go b/engine/fasthttp/url.go index 0cb16e03..75dbedad 100644 --- a/engine/fasthttp/url.go +++ b/engine/fasthttp/url.go @@ -11,22 +11,22 @@ type ( } ) -// Path implements `engine.URL#Path` method. +// Path implements `engine.URL#Path` function. func (u *URL) Path() string { return string(u.URI.Path()) } -// SetPath implements `engine.URL#SetPath` method. +// SetPath implements `engine.URL#SetPath` function. func (u *URL) SetPath(path string) { u.URI.SetPath(path) } -// QueryValue implements `engine.URL#QueryValue` method. +// QueryValue implements `engine.URL#QueryValue` function. func (u *URL) QueryValue(name string) string { return string(u.QueryArgs().Peek(name)) } -// QueryString implements `engine.URL#QueryString` method. +// QueryString implements `engine.URL#QueryString` function. func (u *URL) QueryString() string { return string(u.URI.QueryString()) } diff --git a/engine/standard/header.go b/engine/standard/header.go index 962094dd..01f0e83c 100644 --- a/engine/standard/header.go +++ b/engine/standard/header.go @@ -9,27 +9,27 @@ type ( } ) -// Add implements `engine.Header#Add` method. +// Add implements `engine.Header#Add` function. func (h *Header) Add(key, val string) { h.Header.Add(key, val) } -// Del implements `engine.Header#Del` method. +// Del implements `engine.Header#Del` function. func (h *Header) Del(key string) { h.Header.Del(key) } -// Set implements `engine.Header#Set` method. +// Set implements `engine.Header#Set` function. func (h *Header) Set(key, val string) { h.Header.Set(key, val) } -// Get implements `engine.Header#Get` method. +// Get implements `engine.Header#Get` function. func (h *Header) Get(key string) string { return h.Header.Get(key) } -// Keys implements `engine.Header#Keys` method. +// Keys implements `engine.Header#Keys` function. func (h *Header) Keys() (keys []string) { keys = make([]string, len(h.Header)) i := 0 diff --git a/engine/standard/request.go b/engine/standard/request.go index 282ce063..bbab4b3f 100644 --- a/engine/standard/request.go +++ b/engine/standard/request.go @@ -17,12 +17,12 @@ type ( } ) -// TLS implements `engine.Request#TLS` method. +// TLS implements `engine.Request#TLS` function. func (r *Request) TLS() bool { return r.Request.TLS != nil } -// Scheme implements `engine.Request#Scheme` method. +// Scheme implements `engine.Request#Scheme` function. func (r *Request) Scheme() string { if r.TLS() { return "https" @@ -30,17 +30,17 @@ func (r *Request) Scheme() string { return "http" } -// Host implements `engine.Request#Host` method. +// Host implements `engine.Request#Host` function. func (r *Request) Host() string { return r.Request.Host } -// URL implements `engine.Request#URL` method. +// URL implements `engine.Request#URL` function. func (r *Request) URL() engine.URL { return r.url } -// Header implements `engine.Request#URL` method. +// Header implements `engine.Request#URL` function. func (r *Request) Header() engine.Header { return r.header } @@ -57,48 +57,48 @@ func (r *Request) Header() engine.Header { // return r.request.ProtoMinor() // } -// UserAgent implements `engine.Request#UserAgent` method. +// UserAgent implements `engine.Request#UserAgent` function. func (r *Request) UserAgent() string { return r.Request.UserAgent() } -// RemoteAddress implements `engine.Request#RemoteAddress` method. +// RemoteAddress implements `engine.Request#RemoteAddress` function. func (r *Request) RemoteAddress() string { return r.RemoteAddr } -// Method implements `engine.Request#Method` method. +// Method implements `engine.Request#Method` function. func (r *Request) Method() string { return r.Request.Method } -// SetMethod implements `engine.Request#SetMethod` method. +// SetMethod implements `engine.Request#SetMethod` function. func (r *Request) SetMethod(method string) { r.Request.Method = method } -// URI implements `engine.Request#URI` method. +// URI implements `engine.Request#URI` function. func (r *Request) URI() string { return r.RequestURI } -// Body implements `engine.Request#Body` method. +// Body implements `engine.Request#Body` function. func (r *Request) Body() io.Reader { return r.Request.Body } -// FormValue implements `engine.Request#FormValue` method. +// FormValue implements `engine.Request#FormValue` function. func (r *Request) FormValue(name string) string { return r.Request.FormValue(name) } -// FormFile implements `engine.Request#FormFile` method. +// FormFile implements `engine.Request#FormFile` function. func (r *Request) FormFile(name string) (*multipart.FileHeader, error) { _, fh, err := r.Request.FormFile(name) return fh, err } -// MultipartForm implements `engine.Request#MultipartForm` method. +// MultipartForm implements `engine.Request#MultipartForm` function. func (r *Request) MultipartForm() (*multipart.Form, error) { err := r.Request.ParseMultipartForm(32 << 20) // 32 MB return r.Request.MultipartForm, err diff --git a/engine/standard/response.go b/engine/standard/response.go index ad0810f3..0a179136 100644 --- a/engine/standard/response.go +++ b/engine/standard/response.go @@ -28,12 +28,12 @@ type ( } ) -// Header implements `engine.Response#Header` method. +// Header implements `engine.Response#Header` function. func (r *Response) Header() engine.Header { return r.header } -// WriteHeader implements `engine.Response#WriteHeader` method. +// WriteHeader implements `engine.Response#WriteHeader` function. func (r *Response) WriteHeader(code int) { if r.committed { r.logger.Warn("response already committed") @@ -44,48 +44,48 @@ func (r *Response) WriteHeader(code int) { r.committed = true } -// Write implements `engine.Response#Write` method. +// Write implements `engine.Response#Write` function. func (r *Response) Write(b []byte) (n int, err error) { n, err = r.writer.Write(b) r.size += int64(n) return } -// Status implements `engine.Response#Status` method. +// Status implements `engine.Response#Status` function. func (r *Response) Status() int { return r.status } -// Size implements `engine.Response#Size` method. +// Size implements `engine.Response#Size` function. func (r *Response) Size() int64 { return r.size } -// Committed implements `engine.Response#Committed` method. +// Committed implements `engine.Response#Committed` function. func (r *Response) Committed() bool { return r.committed } -// Writer implements `engine.Response#Writer` method. +// Writer implements `engine.Response#Writer` function. func (r *Response) Writer() io.Writer { return r.writer } -// SetWriter implements `engine.Response#SetWriter` method. +// SetWriter implements `engine.Response#SetWriter` function. func (r *Response) SetWriter(w io.Writer) { r.writer = w } // Flush implements the http.Flusher interface to allow an HTTP handler to flush // buffered data to the client. -// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher) +// See https://golang.org/pkg/net/http/#Flusher func (r *Response) Flush() { r.ResponseWriter.(http.Flusher).Flush() } // Hijack implements the http.Hijacker interface to allow an HTTP handler to // take over the connection. -// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker) +// See https://golang.org/pkg/net/http/#Hijacker func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { return r.ResponseWriter.(http.Hijacker).Hijack() } @@ -94,7 +94,7 @@ func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { // when the underlying connection has gone away. // This mechanism can be used to cancel long operations on the server if the // client has disconnected before the response is ready. -// See [http.CloseNotifier](https://golang.org/pkg/net/http/#CloseNotifier) +// See https://golang.org/pkg/net/http/#CloseNotifier func (r *Response) CloseNotify() <-chan bool { return r.ResponseWriter.(http.CloseNotifier).CloseNotify() } diff --git a/engine/standard/server.go b/engine/standard/server.go index b7a4f0a8..6fc5e10e 100644 --- a/engine/standard/server.go +++ b/engine/standard/server.go @@ -80,17 +80,17 @@ func NewFromConfig(c engine.Config) (s *Server) { return } -// SetHandler implements `engine.Server#SetHandler` method. +// SetHandler implements `engine.Server#SetHandler` function. func (s *Server) SetHandler(h engine.Handler) { s.handler = h } -// SetLogger implements `engine.Server#SetLogger` method. +// SetLogger implements `engine.Server#SetLogger` function. func (s *Server) SetLogger(l *log.Logger) { s.logger = l } -// Start implements `engine.Server#Start` method. +// Start implements `engine.Server#Start` function. func (s *Server) Start() error { if s.config.Listener == nil { return s.startDefaultListener() diff --git a/engine/standard/url.go b/engine/standard/url.go index a0a450ff..b382861e 100644 --- a/engine/standard/url.go +++ b/engine/standard/url.go @@ -10,17 +10,17 @@ type ( } ) -// Path implements `engine.URL#Path` method. +// Path implements `engine.URL#Path` function. func (u *URL) Path() string { return u.URL.Path } -// SetPath implements `engine.URL#SetPath` method. +// SetPath implements `engine.URL#SetPath` function. func (u *URL) SetPath(path string) { u.URL.Path = path } -// QueryValue implements `engine.URL#QueryValue` method. +// QueryValue implements `engine.URL#QueryValue` function. func (u *URL) QueryValue(name string) string { if u.query == nil { u.query = u.Query() @@ -28,7 +28,7 @@ func (u *URL) QueryValue(name string) string { return u.query.Get(name) } -// QueryString implements `engine.URL#QueryString` method. +// QueryString implements `engine.URL#QueryString` function. func (u *URL) QueryString() string { return u.URL.RawQuery } diff --git a/group.go b/group.go index 6a6f852f..8ee34255 100644 --- a/group.go +++ b/group.go @@ -1,6 +1,9 @@ package echo 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 + // from the parent echo instance while still inheriting from it. Group struct { prefix string middleware []Middleware @@ -8,59 +11,71 @@ type ( } ) +// Use implements `Echo#Use()` for sub-routes within the Group. func (g *Group) Use(m ...Middleware) { g.middleware = append(g.middleware, m...) } +// Connect implements `Echo#Connect()` for sub-routes within the Group. func (g *Group) Connect(path string, h Handler, m ...Middleware) { g.add(CONNECT, path, h, m...) } +// Delete implements `Echo#Delete()` for sub-routes within the Group. func (g *Group) Delete(path string, h Handler, m ...Middleware) { g.add(DELETE, path, h, m...) } +// Get implements `Echo#Get()` for sub-routes within the Group. func (g *Group) Get(path string, h Handler, m ...Middleware) { g.add(GET, path, h, m...) } +// Head implements `Echo#Head()` for sub-routes within the Group. func (g *Group) Head(path string, h Handler, m ...Middleware) { g.add(HEAD, path, h, m...) } +// Options implements `Echo#Options()` for sub-routes within the Group. func (g *Group) Options(path string, h Handler, m ...Middleware) { g.add(OPTIONS, path, h, m...) } +// Patch implements `Echo#Patch()` for sub-routes within the Group. func (g *Group) Patch(path string, h Handler, m ...Middleware) { g.add(PATCH, path, h, m...) } +// Post implements `Echo#Post()` for sub-routes within the Group. func (g *Group) Post(path string, h Handler, m ...Middleware) { g.add(POST, path, h, m...) } +// Put implements `Echo#Put()` for sub-routes within the Group. func (g *Group) Put(path string, h Handler, m ...Middleware) { g.add(PUT, path, h, m...) } +// Trace implements `Echo#Trace()` for sub-routes within the Group. func (g *Group) Trace(path string, h Handler, m ...Middleware) { g.add(TRACE, path, h, m...) } +// Any implements `Echo#Any()` for sub-routes within the Group. func (g *Group) Any(path string, handler Handler, middleware ...Middleware) { for _, m := range methods { g.add(m, path, handler, middleware...) } } +// Match implements `Echo#Match()` for sub-routes within the Group. func (g *Group) Match(methods []string, path string, handler Handler, middleware ...Middleware) { for _, m := range methods { g.add(m, path, handler, middleware...) } } -// Group creates a new sub-group with prefix. +// Group creates a new sub-group with prefix and optional sub-group-level middleware. func (g *Group) Group(prefix string, m ...Middleware) *Group { m = append(g.middleware, m...) return g.echo.Group(g.prefix+prefix, m...) diff --git a/middleware/auth.go b/middleware/auth.go index 670d782b..cbfb4c2d 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -7,10 +7,12 @@ import ( ) type ( + // BasicAuthConfig defines config for HTTP basic auth middleware. BasicAuthConfig struct { AuthFunc BasicAuthFunc } + // BasicAuthFunc defines a function to validate basic auth credentials. BasicAuthFunc func(string, string) bool ) @@ -19,10 +21,11 @@ const ( ) var ( + // DefaultBasicAuthConfig is the default basic auth middleware config. DefaultBasicAuthConfig = BasicAuthConfig{} ) -// BasicAuth returns an HTTP basic authentication middleware. +// BasicAuth returns an HTTP basic auth middleware. // // For valid credentials it calls the next handler. // For invalid credentials, it sends "401 - Unauthorized" response. @@ -32,6 +35,8 @@ func BasicAuth(f BasicAuthFunc) echo.MiddlewareFunc { return BasicAuthFromConfig(c) } +// BasicAuthFromConfig returns an HTTP basic auth middleware from config. +// See `BasicAuth()`. func BasicAuthFromConfig(config BasicAuthConfig) echo.MiddlewareFunc { return func(next echo.Handler) echo.Handler { return echo.HandlerFunc(func(c echo.Context) error { diff --git a/middleware/compress.go b/middleware/compress.go index 7f4aea6a..40a43577 100644 --- a/middleware/compress.go +++ b/middleware/compress.go @@ -13,7 +13,9 @@ import ( ) type ( + // GzipConfig defines config for gzip middleware. GzipConfig struct { + // Level is the gzip level. Level int } @@ -24,7 +26,8 @@ type ( ) var ( - defaultGzipConfig = GzipConfig{ + // DefaultGzipConfig is the default gzip middleware config. + DefaultGzipConfig = GzipConfig{ Level: gzip.DefaultCompression, } ) @@ -32,10 +35,11 @@ var ( // Gzip returns a middleware which compresses HTTP response using gzip compression // scheme. func Gzip() echo.MiddlewareFunc { - return GzipFromConfig(defaultGzipConfig) + return GzipFromConfig(DefaultGzipConfig) } -// GzipFromConfig return `Gzip` middleware from config. +// GzipFromConfig return gzip middleware from config. +// See `Gzip()`. func GzipFromConfig(config GzipConfig) echo.MiddlewareFunc { pool := gzipPool(config) scheme := "gzip" diff --git a/middleware/logger.go b/middleware/logger.go index d33a6977..cda593a8 100644 --- a/middleware/logger.go +++ b/middleware/logger.go @@ -15,26 +15,46 @@ import ( ) type ( + // LoggerConfig defines config for logger middleware. + // LoggerConfig struct { - Format string - Output io.Writer + // Format is the log format. + // + // Example "${remote_id} ${status}" + // Available tags: + // - time_rfc3339 + // - remote_ip + // - method + // - path + // - status + // - response_time + // - response_size + Format string + + // Output is the writer where logs are written. Default is `os.Stdout`. + Output io.Writer + template *fasttemplate.Template color *color.Color } ) var ( + // DefaultLoggerConfig is the default logger middleware config. DefaultLoggerConfig = LoggerConfig{ - Format: "time=${time_rfc3339}, remote_ip=${remote_ip}, method=${method}, path=${path}, status=${status}, response_time=${response_time}, size=${size}\n", + Format: "time=${time_rfc3339}, remote_ip=${remote_ip}, method=${method}, path=${path}, status=${status}, response_time=${response_time}, response_size=${response_size} bytes\n", color: color.New(), Output: os.Stdout, } ) +// Logger returns a middleware that logs HTTP requests. func Logger() echo.MiddlewareFunc { return LoggerFromConfig(DefaultLoggerConfig) } +// LoggerFromConfig returns a logger middleware from config. +// See `Logger()`. func LoggerFromConfig(config LoggerConfig) echo.MiddlewareFunc { config.template = fasttemplate.New(config.Format, "${", "}") config.color = color.New() @@ -94,7 +114,7 @@ func LoggerFromConfig(config LoggerConfig) echo.MiddlewareFunc { return w.Write([]byte(status)) case "response_time": return w.Write([]byte(took.String())) - case "size": + case "response_size": return w.Write([]byte(size)) default: return w.Write([]byte(fmt.Sprintf("[unknown tag %s]", tag))) diff --git a/middleware/recover.go b/middleware/recover.go index 3885fe5b..a1f03b2a 100644 --- a/middleware/recover.go +++ b/middleware/recover.go @@ -9,14 +9,22 @@ import ( ) type ( + // RecoverConfig defines config for recover middleware. RecoverConfig struct { - StackSize int - StackAll bool + // StackSize is the stack size to be printed. + StackSize int + + // StackAll is flag to format stack traces of all other goroutines into + // buffer after the trace for the current goroutine, or not. Default is true. + StackAll bool + + // PrintStack is the flag to print stack or not. Default is true. PrintStack bool } ) var ( + // DefaultRecoverConfig is the default recover middleware config. DefaultRecoverConfig = RecoverConfig{ StackSize: 4 << 10, // 4 KB StackAll: true, @@ -24,12 +32,14 @@ var ( } ) +// Recover returns a middleware which recovers from panics anywhere in the chain +// and handles the control to the centralized HTTPErrorHandler. func Recover() echo.MiddlewareFunc { return RecoverFromConfig(DefaultRecoverConfig) } -// Recover returns a middleware which recovers from panics anywhere in the chain -// and handles the control to the centralized HTTPErrorHandler. +// RecoverFromConfig returns a recover middleware from config. +// See `Recover()`. func RecoverFromConfig(config RecoverConfig) echo.MiddlewareFunc { return func(next echo.Handler) echo.Handler { return echo.HandlerFunc(func(c echo.Context) error { diff --git a/middleware/static.go b/middleware/static.go index efa9c311..b5446657 100644 --- a/middleware/static.go +++ b/middleware/static.go @@ -9,26 +9,38 @@ import ( ) type ( + // StaticConfig defines config for static middleware. StaticConfig struct { - Root string `json:"root"` - Index string `json:"index"` - Browse bool `json:"browse"` + // Root is the directory from where the static content is served. + Root string `json:"root"` + + // Index is the index file to be used while serving a directory. + // Default is `index.html`. + Index string `json:"index"` + + // Browse is the flag to list directory or not. Default is false. + Browse bool `json:"browse"` } ) var ( + // DefaultStaticConfig is the default static middleware config. DefaultStaticConfig = StaticConfig{ Index: "index.html", Browse: false, } ) +// Static returns a static middleware to deliever static content from the provided +// root directory. func Static(root string) echo.MiddlewareFunc { c := DefaultStaticConfig c.Root = root return StaticFromConfig(c) } +// StaticFromConfig returns a static middleware from config. +// See `Static()`. func StaticFromConfig(config StaticConfig) echo.MiddlewareFunc { return func(next echo.Handler) echo.Handler { return echo.HandlerFunc(func(c echo.Context) error { diff --git a/router.go b/router.go index e3f82227..86c97f20 100644 --- a/router.go +++ b/router.go @@ -1,6 +1,8 @@ package echo type ( + // Router is the registry of all registered routes for an `Echo` instance for + // request matching and URL path parameter parsing. Router struct { tree *node routes []Route @@ -37,6 +39,7 @@ const ( akind ) +// NewRouter returns a new Router instance. func NewRouter(e *Echo) *Router { return &Router{ tree: &node{ @@ -47,6 +50,7 @@ func NewRouter(e *Echo) *Router { } } +// Handle implements `echo.Middleware` which makes router a middleware. func (r *Router) Handle(next Handler) Handler { return HandlerFunc(func(c Context) error { method := c.Request().Method() @@ -56,10 +60,12 @@ func (r *Router) Handle(next Handler) Handler { }) } +// Priority is super secret. func (r *Router) Priority() int { return 0 } +// Add registers a new route for method and path with matching handler. func (r *Router) Add(method, path string, h Handler, e *Echo) { ppath := path // Pristine path pnames := []string{} // Param names @@ -280,6 +286,14 @@ func (n *node) check405() HandlerFunc { return notFoundHandler } +// Find lookup a handler registed for method and path. It also parses URL for path +// parameters and load them into context. +// +// For performance: +// +// - Get context from `Echo#GetContext()` +// - Reset it `Context#Reset()` +// - Return it `Echo#PutContext()`. func (r *Router) Find(method, path string, context Context) { ctx := context.Object() cn := r.tree // Current node as root