mirror of
https://github.com/labstack/echo.git
synced 2024-12-22 20:06:21 +02:00
commit
28b1b9d57b
14
.gitattributes
vendored
14
.gitattributes
vendored
@ -2,19 +2,21 @@
|
|||||||
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
|
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# For the following file types, normalize line endings to LF on checking and
|
# For the following file types, normalize line endings to LF on checkin and
|
||||||
# prevent conversion to CRLF when they are checked out (this is required in
|
# prevent conversion to CRLF when they are checked out (this is required in
|
||||||
# order to prevent newline related issues)
|
# order to prevent newline related issues)
|
||||||
.* text eol=lf
|
.* text eol=lf
|
||||||
*.go text eol=lf
|
|
||||||
*.yml text eol=lf
|
|
||||||
*.html text eol=lf
|
|
||||||
*.css text eol=lf
|
*.css text eol=lf
|
||||||
|
*.go text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
*.js text eol=lf
|
*.js text eol=lf
|
||||||
*.json text eol=lf
|
*.json text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
LICENSE text eol=lf
|
LICENSE text eol=lf
|
||||||
|
|
||||||
# Exclude `website` and `recipes` from Github's language statistics
|
# Exclude documentation-related directories from Github's language statistics
|
||||||
# https://github.com/github/linguist#using-gitattributes
|
# https://github.com/github/linguist#using-gitattributes
|
||||||
recipes/* linguist-documentation
|
_fixture/* linguist-documentation
|
||||||
website/* linguist-documentation
|
website/* linguist-documentation
|
||||||
|
41
.gitignore
vendored
41
.gitignore
vendored
@ -1,13 +1,42 @@
|
|||||||
# Website
|
# gitignore - Specifies intentionally untracked files to ignore
|
||||||
|
# http://git-scm.com/docs/gitignore
|
||||||
|
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
# submodule properties
|
||||||
|
.gitmodules
|
||||||
|
|
||||||
|
# compiled web files
|
||||||
website/public
|
website/public
|
||||||
website/make
|
website/make
|
||||||
website/Makefile
|
website/Makefile
|
||||||
website/marathon*
|
website/marathon*
|
||||||
.gitmodules
|
|
||||||
|
|
||||||
# Node.js
|
# node.js web dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# IntelliJ
|
# code coverage output files
|
||||||
.idea
|
*.coverprofile
|
||||||
*.iml
|
|
||||||
|
44
.travis.yml
44
.travis.yml
@ -1,13 +1,39 @@
|
|||||||
language: go
|
language: go
|
||||||
|
sudo: false
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.4
|
- 1.4
|
||||||
- tip
|
- 1.5
|
||||||
|
- 1.6rc1
|
||||||
|
- tip
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- GO15VENDOREXPERIMENT=1
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/modocache/gover
|
- export PATH=$PATH:$GOPATH/bin
|
||||||
- go get github.com/mattn/goveralls
|
- go get golang.org/x/tools/cmd/vet
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/modocache/gover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -t -v ./...
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test -coverprofile=echo.coverprofile
|
- go vet ./...
|
||||||
- go test -coverprofile=middleware.coverprofile ./middleware
|
- go test -v -race ./...
|
||||||
- $HOME/gopath/bin/gover
|
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||||
- $HOME/gopath/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci
|
- go test -v -coverprofile=echo.coverprofile
|
||||||
|
- go test -v -coverprofile=middleware.coverprofile ./middleware
|
||||||
|
- gover
|
||||||
|
- goveralls -coverprofile=gover.coverprofile -service=travis-ci
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
3
LICENSE
3
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2015 LabStack
|
Copyright (c) 2016 LabStack
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ func (c *Context) Bind(i interface{}) error {
|
|||||||
// code. Templates can be registered using `Echo.SetRenderer()`.
|
// code. Templates can be registered using `Echo.SetRenderer()`.
|
||||||
func (c *Context) Render(code int, name string, data interface{}) (err error) {
|
func (c *Context) Render(code int, name string, data interface{}) (err error) {
|
||||||
if c.echo.renderer == nil {
|
if c.echo.renderer == nil {
|
||||||
return RendererNotRegistered
|
return ErrRendererNotRegistered
|
||||||
}
|
}
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err = c.echo.renderer.Render(buf, name, data); err != nil {
|
if err = c.echo.renderer.Render(buf, name, data); err != nil {
|
||||||
@ -244,7 +244,7 @@ func (c *Context) NoContent(code int) error {
|
|||||||
// Redirect redirects the request using http.Redirect with status code.
|
// Redirect redirects the request using http.Redirect with status code.
|
||||||
func (c *Context) Redirect(code int, url string) error {
|
func (c *Context) Redirect(code int, url string) error {
|
||||||
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
|
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
|
||||||
return InvalidRedirectCode
|
return ErrInvalidRedirectCode
|
||||||
}
|
}
|
||||||
http.Redirect(c.response, c.request, url, code)
|
http.Redirect(c.response, c.request, url, code)
|
||||||
return nil
|
return nil
|
||||||
|
@ -234,6 +234,10 @@ func TestContext(t *testing.T) {
|
|||||||
|
|
||||||
// reset
|
// reset
|
||||||
c.reset(req, NewResponse(httptest.NewRecorder(), e), e)
|
c.reset(req, NewResponse(httptest.NewRecorder(), e), e)
|
||||||
|
|
||||||
|
// after reset (nil store) set test
|
||||||
|
c.Set("user", "Joe")
|
||||||
|
assert.Equal(t, "Joe", c.Get("user"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextPath(t *testing.T) {
|
func TestContextPath(t *testing.T) {
|
||||||
@ -285,6 +289,13 @@ func TestContextNetContext(t *testing.T) {
|
|||||||
assert.Equal(t, "val", c.Value("key"))
|
assert.Equal(t, "val", c.Value("key"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextEcho(t *testing.T) {
|
||||||
|
c := new(Context)
|
||||||
|
|
||||||
|
// Should be null when initialized without one
|
||||||
|
assert.Nil(t, c.Echo())
|
||||||
|
}
|
||||||
|
|
||||||
func testBindOk(t *testing.T, c *Context, ct string) {
|
func testBindOk(t *testing.T, c *Context, ct string) {
|
||||||
c.request.Header.Set(ContentType, ct)
|
c.request.Header.Set(ContentType, ct)
|
||||||
u := new(user)
|
u := new(user)
|
||||||
@ -307,7 +318,7 @@ func testBindError(t *testing.T, c *Context, ct string) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if assert.IsType(t, new(HTTPError), err) {
|
if assert.IsType(t, new(HTTPError), err) {
|
||||||
assert.Equal(t, UnsupportedMediaType, err)
|
assert.Equal(t, ErrUnsupportedMediaType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
59
echo.go
59
echo.go
@ -1,8 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Package echo implements a fast and unfancy micro web framework for Go.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
mw "github.com/labstack/echo/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hello(c *echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "Hello, World!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
e.Use(mw.Logger())
|
||||||
|
e.Use(mw.Recover())
|
||||||
|
|
||||||
|
e.Get("/", hello)
|
||||||
|
|
||||||
|
e.Run(":1323")
|
||||||
|
}
|
||||||
|
|
||||||
|
Learn more at https://labstack.com/echo
|
||||||
|
*/
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -14,14 +46,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"encoding/xml"
|
|
||||||
|
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// Echo is the top-level framework instance.
|
||||||
Echo struct {
|
Echo struct {
|
||||||
prefix string
|
prefix string
|
||||||
middleware []MiddlewareFunc
|
middleware []MiddlewareFunc
|
||||||
@ -39,21 +70,30 @@ type (
|
|||||||
router *Router
|
router *Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Route contains a handler and information for matching against requests.
|
||||||
Route struct {
|
Route struct {
|
||||||
Method string
|
Method string
|
||||||
Path string
|
Path string
|
||||||
Handler Handler
|
Handler Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPError represents an error that occured while handling a request.
|
||||||
HTTPError struct {
|
HTTPError struct {
|
||||||
code int
|
code int
|
||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
Middleware interface{}
|
// Middleware ...
|
||||||
|
Middleware interface{}
|
||||||
|
|
||||||
|
// MiddlewareFunc ...
|
||||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
||||||
Handler interface{}
|
|
||||||
HandlerFunc func(*Context) error
|
// Handler ...
|
||||||
|
Handler interface{}
|
||||||
|
|
||||||
|
// HandlerFunc ...
|
||||||
|
HandlerFunc func(*Context) error
|
||||||
|
|
||||||
// HTTPErrorHandler is a centralized HTTP error handler.
|
// HTTPErrorHandler is a centralized HTTP error handler.
|
||||||
HTTPErrorHandler func(error, *Context)
|
HTTPErrorHandler func(error, *Context)
|
||||||
@ -164,9 +204,9 @@ var (
|
|||||||
// Errors
|
// Errors
|
||||||
//--------
|
//--------
|
||||||
|
|
||||||
UnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
||||||
RendererNotRegistered = errors.New("renderer not registered")
|
ErrRendererNotRegistered = errors.New("renderer not registered")
|
||||||
InvalidRedirectCode = errors.New("invalid redirect status code")
|
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
// Error handlers
|
// Error handlers
|
||||||
@ -588,6 +628,7 @@ func (e *Echo) run(s *http.Server, files ...string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHTTPError creates a new HTTPError instance.
|
||||||
func NewHTTPError(code int, msg ...string) *HTTPError {
|
func NewHTTPError(code int, msg ...string) *HTTPError {
|
||||||
he := &HTTPError{code: code, message: http.StatusText(code)}
|
he := &HTTPError{code: code, message: http.StatusText(code)}
|
||||||
if len(msg) > 0 {
|
if len(msg) > 0 {
|
||||||
@ -691,7 +732,7 @@ func wrapHandler(h Handler) HandlerFunc {
|
|||||||
|
|
||||||
func (binder) Bind(r *http.Request, i interface{}) (err error) {
|
func (binder) Bind(r *http.Request, i interface{}) (err error) {
|
||||||
ct := r.Header.Get(ContentType)
|
ct := r.Header.Get(ContentType)
|
||||||
err = UnsupportedMediaType
|
err = ErrUnsupportedMediaType
|
||||||
if strings.HasPrefix(ct, ApplicationJSON) {
|
if strings.HasPrefix(ct, ApplicationJSON) {
|
||||||
if err = json.NewDecoder(r.Body).Decode(i); err != nil {
|
if err = json.NewDecoder(r.Body).Decode(i); err != nil {
|
||||||
err = NewHTTPError(http.StatusBadRequest, err.Error())
|
err = NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
|
31
group.go
31
group.go
@ -1,81 +1,102 @@
|
|||||||
package echo
|
package echo
|
||||||
|
|
||||||
type (
|
// Group is a set of subroutes for a specified route. It can be used for inner
|
||||||
Group struct {
|
// routes that share a common middlware or functionality that should be separate
|
||||||
echo Echo
|
// from the parent echo instance while still inheriting from it.
|
||||||
}
|
type Group struct {
|
||||||
)
|
echo Echo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use implements the echo.Use interface for subroutes within the Group.
|
||||||
func (g *Group) Use(m ...Middleware) {
|
func (g *Group) Use(m ...Middleware) {
|
||||||
for _, h := range m {
|
for _, h := range m {
|
||||||
g.echo.middleware = append(g.echo.middleware, wrapMiddleware(h))
|
g.echo.middleware = append(g.echo.middleware, wrapMiddleware(h))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect implements the echo.Connect interface for subroutes within the Group.
|
||||||
func (g *Group) Connect(path string, h Handler) {
|
func (g *Group) Connect(path string, h Handler) {
|
||||||
g.echo.Connect(path, h)
|
g.echo.Connect(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete implements the echo.Delete interface for subroutes within the Group.
|
||||||
func (g *Group) Delete(path string, h Handler) {
|
func (g *Group) Delete(path string, h Handler) {
|
||||||
g.echo.Delete(path, h)
|
g.echo.Delete(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get implements the echo.Get interface for subroutes within the Group.
|
||||||
func (g *Group) Get(path string, h Handler) {
|
func (g *Group) Get(path string, h Handler) {
|
||||||
g.echo.Get(path, h)
|
g.echo.Get(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Head implements the echo.Head interface for subroutes within the Group.
|
||||||
func (g *Group) Head(path string, h Handler) {
|
func (g *Group) Head(path string, h Handler) {
|
||||||
g.echo.Head(path, h)
|
g.echo.Head(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options implements the echo.Options interface for subroutes within the Group.
|
||||||
func (g *Group) Options(path string, h Handler) {
|
func (g *Group) Options(path string, h Handler) {
|
||||||
g.echo.Options(path, h)
|
g.echo.Options(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Patch implements the echo.Patch interface for subroutes within the Group.
|
||||||
func (g *Group) Patch(path string, h Handler) {
|
func (g *Group) Patch(path string, h Handler) {
|
||||||
g.echo.Patch(path, h)
|
g.echo.Patch(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post implements the echo.Post interface for subroutes within the Group.
|
||||||
func (g *Group) Post(path string, h Handler) {
|
func (g *Group) Post(path string, h Handler) {
|
||||||
g.echo.Post(path, h)
|
g.echo.Post(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put implements the echo.Put interface for subroutes within the Group.
|
||||||
func (g *Group) Put(path string, h Handler) {
|
func (g *Group) Put(path string, h Handler) {
|
||||||
g.echo.Put(path, h)
|
g.echo.Put(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trace implements the echo.Trace interface for subroutes within the Group.
|
||||||
func (g *Group) Trace(path string, h Handler) {
|
func (g *Group) Trace(path string, h Handler) {
|
||||||
g.echo.Trace(path, h)
|
g.echo.Trace(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Any implements the echo.Any interface for subroutes within the Group.
|
||||||
func (g *Group) Any(path string, h Handler) {
|
func (g *Group) Any(path string, h Handler) {
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
g.echo.add(m, path, h)
|
g.echo.add(m, path, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match implements the echo.Match interface for subroutes within the Group.
|
||||||
func (g *Group) Match(methods []string, path string, h Handler) {
|
func (g *Group) Match(methods []string, path string, h Handler) {
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
g.echo.add(m, path, h)
|
g.echo.add(m, path, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebSocket implements the echo.WebSocket interface for subroutes within the
|
||||||
|
// Group.
|
||||||
func (g *Group) WebSocket(path string, h HandlerFunc) {
|
func (g *Group) WebSocket(path string, h HandlerFunc) {
|
||||||
g.echo.WebSocket(path, h)
|
g.echo.WebSocket(path, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Static implements the echo.Static interface for subroutes within the Group.
|
||||||
func (g *Group) Static(path, root string) {
|
func (g *Group) Static(path, root string) {
|
||||||
g.echo.Static(path, root)
|
g.echo.Static(path, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeDir implements the echo.ServeDir interface for subroutes within the
|
||||||
|
// Group.
|
||||||
func (g *Group) ServeDir(path, root string) {
|
func (g *Group) ServeDir(path, root string) {
|
||||||
g.echo.ServeDir(path, root)
|
g.echo.ServeDir(path, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeFile implements the echo.ServeFile interface for subroutes within the
|
||||||
|
// Group.
|
||||||
func (g *Group) ServeFile(path, file string) {
|
func (g *Group) ServeFile(path, file string) {
|
||||||
g.echo.ServeFile(path, file)
|
g.echo.ServeFile(path, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group implements the echo.Group interface for subroutes within the Group.
|
||||||
func (g *Group) Group(prefix string, m ...Middleware) *Group {
|
func (g *Group) Group(prefix string, m ...Middleware) *Group {
|
||||||
return g.echo.Group(prefix, m...)
|
return g.echo.Group(prefix, m...)
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// BasicValidateFunc is the expected format a BasicAuth fn argument is
|
||||||
|
// expected to implement.
|
||||||
BasicValidateFunc func(string, string) bool
|
BasicValidateFunc func(string, string) bool
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Basic is the authentication scheme implemented by the middleware.
|
||||||
Basic = "Basic"
|
Basic = "Basic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicAuth returns an HTTP basic authentication middleware.
|
// BasicAuth returns a HTTP basic authentication middleware.
|
||||||
//
|
// For valid credentials, it calls the next handler.
|
||||||
// For valid credentials it calls the next handler.
|
// For invalid credentials, it returns a "401 Unauthorized" HTTP error.
|
||||||
// For invalid credentials, it sends "401 - Unauthorized" response.
|
|
||||||
func BasicAuth(fn BasicValidateFunc) echo.HandlerFunc {
|
func BasicAuth(fn BasicValidateFunc) echo.HandlerFunc {
|
||||||
return func(c *echo.Context) error {
|
return func(c *echo.Context) error {
|
||||||
// Skip WebSocket
|
// Skip WebSocket
|
||||||
|
@ -8,6 +8,9 @@ import (
|
|||||||
"github.com/labstack/gommon/color"
|
"github.com/labstack/gommon/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const loggerFormat = "%s %s %s %s %s %d"
|
||||||
|
|
||||||
|
// Logger returns a Middleware that logs requests.
|
||||||
func Logger() echo.MiddlewareFunc {
|
func Logger() echo.MiddlewareFunc {
|
||||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c *echo.Context) error {
|
return func(c *echo.Context) error {
|
||||||
@ -47,7 +50,7 @@ func Logger() echo.MiddlewareFunc {
|
|||||||
code = color.Cyan(n)
|
code = color.Cyan(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("%s %s %s %s %s %d", remoteAddr, method, path, code, stop.Sub(start), size)
|
logger.Info(loggerFormat, remoteAddr, method, path, code, stop.Sub(start), size)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
response.go
60
response.go
@ -6,32 +6,46 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Response wraps an http.ResponseWriter and implements its interface to be used
|
||||||
Response struct {
|
// by an HTTP handler to construct an HTTP response.
|
||||||
writer http.ResponseWriter
|
// See [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter)
|
||||||
status int
|
type Response struct {
|
||||||
size int64
|
writer http.ResponseWriter
|
||||||
committed bool
|
status int
|
||||||
echo *Echo
|
size int64
|
||||||
}
|
committed bool
|
||||||
)
|
echo *Echo
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResponse creates a new instance of Response.
|
||||||
func NewResponse(w http.ResponseWriter, e *Echo) *Response {
|
func NewResponse(w http.ResponseWriter, e *Echo) *Response {
|
||||||
return &Response{writer: w, echo: e}
|
return &Response{writer: w, echo: e}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetWriter sets the http.ResponseWriter instance for this Response.
|
||||||
func (r *Response) SetWriter(w http.ResponseWriter) {
|
func (r *Response) SetWriter(w http.ResponseWriter) {
|
||||||
r.writer = w
|
r.writer = w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Response) Header() http.Header {
|
// Writer returns the http.ResponseWriter instance for this Response.
|
||||||
return r.writer.Header()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Response) Writer() http.ResponseWriter {
|
func (r *Response) Writer() http.ResponseWriter {
|
||||||
return r.writer
|
return r.writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header returns the header map for the writer that will be sent by
|
||||||
|
// WriteHeader. Changing the header after a call to WriteHeader (or Write) has
|
||||||
|
// no effect unless the modified headers were declared as trailers by setting
|
||||||
|
// the "Trailer" header before the call to WriteHeader (see example)
|
||||||
|
// To suppress implicit response headers, set their value to nil.
|
||||||
|
// Example [ResponseWriter.Trailers](https://golang.org/pkg/net/http/#example_ResponseWriter_trailers)
|
||||||
|
func (r *Response) Header() http.Header {
|
||||||
|
return r.writer.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader sends an HTTP response header with status code. If WriteHeader is
|
||||||
|
// not called explicitly, the first call to Write will trigger an implicit
|
||||||
|
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
|
||||||
|
// used to send error codes.
|
||||||
func (r *Response) WriteHeader(code int) {
|
func (r *Response) WriteHeader(code int) {
|
||||||
if r.committed {
|
if r.committed {
|
||||||
r.echo.Logger().Warn("response already committed")
|
r.echo.Logger().Warn("response already committed")
|
||||||
@ -42,35 +56,49 @@ func (r *Response) WriteHeader(code int) {
|
|||||||
r.committed = true
|
r.committed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write wraps and implements the http.Response.Write specification.
|
||||||
|
// Additionally, Write will increment the size of the current response.
|
||||||
|
// See [http.Response.Write](https://golang.org/pkg/net/http/#Response.Write)
|
||||||
func (r *Response) Write(b []byte) (n int, err error) {
|
func (r *Response) Write(b []byte) (n int, err error) {
|
||||||
n, err = r.writer.Write(b)
|
n, err = r.writer.Write(b)
|
||||||
r.size += int64(n)
|
r.size += int64(n)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush wraps response writer's Flush function.
|
// 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)
|
||||||
func (r *Response) Flush() {
|
func (r *Response) Flush() {
|
||||||
r.writer.(http.Flusher).Flush()
|
r.writer.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hijack wraps response writer's Hijack function.
|
// 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)
|
||||||
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return r.writer.(http.Hijacker).Hijack()
|
return r.writer.(http.Hijacker).Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseNotify wraps response writer's CloseNotify function.
|
// CloseNotify implements the http.CloseNotifier interface to allow detecting
|
||||||
|
// 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)
|
||||||
func (r *Response) CloseNotify() <-chan bool {
|
func (r *Response) CloseNotify() <-chan bool {
|
||||||
return r.writer.(http.CloseNotifier).CloseNotify()
|
return r.writer.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status returns the HTTP status code of the response.
|
||||||
func (r *Response) Status() int {
|
func (r *Response) Status() int {
|
||||||
return r.status
|
return r.status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the current size, in bytes, of the response.
|
||||||
func (r *Response) Size() int64 {
|
func (r *Response) Size() int64 {
|
||||||
return r.size
|
return r.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Committed asserts whether or not the response has been committed to.
|
||||||
func (r *Response) Committed() bool {
|
func (r *Response) Committed() bool {
|
||||||
return r.committed
|
return r.committed
|
||||||
}
|
}
|
||||||
|
13
router.go
13
router.go
@ -3,6 +3,12 @@ package echo
|
|||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
||||||
|
// Router is the registry of all registered routes for an Echo instance for
|
||||||
|
// request matching and handler dispatching.
|
||||||
|
//
|
||||||
|
// Router implements the http.Handler specification and can be registered
|
||||||
|
// to serve requests.
|
||||||
Router struct {
|
Router struct {
|
||||||
tree *node
|
tree *node
|
||||||
routes []Route
|
routes []Route
|
||||||
@ -40,6 +46,7 @@ const (
|
|||||||
mkind
|
mkind
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewRouter returns a new Router instance.
|
||||||
func NewRouter(e *Echo) *Router {
|
func NewRouter(e *Echo) *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
tree: &node{
|
tree: &node{
|
||||||
@ -50,6 +57,7 @@ func NewRouter(e *Echo) *Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add registers a new route with a matcher for the URL path.
|
||||||
func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
|
func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {
|
||||||
ppath := path // Pristine path
|
ppath := path // Pristine path
|
||||||
pnames := []string{} // Param names
|
pnames := []string{} // Param names
|
||||||
@ -275,6 +283,8 @@ func (n *node) check405() HandlerFunc {
|
|||||||
return notFoundHandler
|
return notFoundHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find dispatches the request to the handler whos route is matched with the
|
||||||
|
// specified request path.
|
||||||
func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo) {
|
func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo) {
|
||||||
h = notFoundHandler
|
h = notFoundHandler
|
||||||
e = r.echo
|
e = r.echo
|
||||||
@ -400,6 +410,9 @@ End:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the Handler interface and can be registered to serve a
|
||||||
|
// particular path or subtree in an HTTP server.
|
||||||
|
// See Router.Find()
|
||||||
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
c := r.echo.pool.Get().(*Context)
|
c := r.echo.pool.Get().(*Context)
|
||||||
h, _ := r.Find(req.Method, req.URL.Path, c)
|
h, _ := r.Find(req.Method, req.URL.Path, c)
|
||||||
|
Loading…
Reference in New Issue
Block a user