1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-22 20:06:21 +02:00

Merge pull request #347 from syntaqx/quibbles

Quibbles and Linting
This commit is contained in:
Vishal Rana 2016-01-30 06:42:17 -08:00
commit 28b1b9d57b
13 changed files with 237 additions and 61 deletions

14
.gitattributes vendored
View File

@ -2,19 +2,21 @@
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
* 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
# order to prevent newline related issues)
.* text eol=lf
*.go text eol=lf
*.yml text eol=lf
*.html text eol=lf
*.css text eol=lf
*.go text eol=lf
*.html text eol=lf
*.js text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml 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
recipes/* linguist-documentation
_fixture/* linguist-documentation
website/* linguist-documentation

41
.gitignore vendored
View File

@ -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/make
website/Makefile
website/marathon*
.gitmodules
# Node.js
# node.js web dependencies
node_modules
# IntelliJ
.idea
*.iml
# code coverage output files
*.coverprofile

1
.godir Normal file
View File

@ -0,0 +1 @@
github.com/labstack/echo

View File

@ -1,13 +1,39 @@
language: go
sudo: false
go:
- 1.4
- tip
- 1.4
- 1.5
- 1.6rc1
- tip
env:
global:
- GO15VENDOREXPERIMENT=1
before_install:
- go get github.com/modocache/gover
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
- export PATH=$PATH:$GOPATH/bin
- go get golang.org/x/tools/cmd/vet
- 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:
- go test -coverprofile=echo.coverprofile
- go test -coverprofile=middleware.coverprofile ./middleware
- $HOME/gopath/bin/gover
- $HOME/gopath/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci
- go vet ./...
- go test -v -race ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- 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

View File

@ -1,6 +1,6 @@
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
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,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -120,7 +120,7 @@ func (c *Context) Bind(i interface{}) error {
// 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 RendererNotRegistered
return ErrRendererNotRegistered
}
buf := new(bytes.Buffer)
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.
func (c *Context) Redirect(code int, url string) error {
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
return InvalidRedirectCode
return ErrInvalidRedirectCode
}
http.Redirect(c.response, c.request, url, code)
return nil

View File

@ -234,6 +234,10 @@ func TestContext(t *testing.T) {
// reset
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) {
@ -285,6 +289,13 @@ func TestContextNetContext(t *testing.T) {
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) {
c.request.Header.Set(ContentType, ct)
u := new(user)
@ -307,7 +318,7 @@ func testBindError(t *testing.T, c *Context, ct string) {
}
default:
if assert.IsType(t, new(HTTPError), err) {
assert.Equal(t, UnsupportedMediaType, err)
assert.Equal(t, ErrUnsupportedMediaType, err)
}
}

59
echo.go
View File

@ -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
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
@ -14,14 +46,13 @@ import (
"strings"
"sync"
"encoding/xml"
"github.com/labstack/gommon/log"
"golang.org/x/net/http2"
"golang.org/x/net/websocket"
)
type (
// Echo is the top-level framework instance.
Echo struct {
prefix string
middleware []MiddlewareFunc
@ -39,21 +70,30 @@ type (
router *Router
}
// Route contains a handler and information for matching against requests.
Route struct {
Method string
Path string
Handler Handler
}
// HTTPError represents an error that occured while handling a request.
HTTPError struct {
code int
message string
}
Middleware interface{}
// Middleware ...
Middleware interface{}
// MiddlewareFunc ...
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 func(error, *Context)
@ -164,9 +204,9 @@ var (
// Errors
//--------
UnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
RendererNotRegistered = errors.New("renderer not registered")
InvalidRedirectCode = errors.New("invalid redirect status code")
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
ErrRendererNotRegistered = errors.New("renderer not registered")
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
//----------------
// 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 {
he := &HTTPError{code: code, message: http.StatusText(code)}
if len(msg) > 0 {
@ -691,7 +732,7 @@ func wrapHandler(h Handler) HandlerFunc {
func (binder) Bind(r *http.Request, i interface{}) (err error) {
ct := r.Header.Get(ContentType)
err = UnsupportedMediaType
err = ErrUnsupportedMediaType
if strings.HasPrefix(ct, ApplicationJSON) {
if err = json.NewDecoder(r.Body).Decode(i); err != nil {
err = NewHTTPError(http.StatusBadRequest, err.Error())

View File

@ -1,81 +1,102 @@
package echo
type (
Group struct {
echo Echo
}
)
// Group is a set of subroutes 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.
type Group struct {
echo Echo
}
// Use implements the echo.Use interface for subroutes within the Group.
func (g *Group) Use(m ...Middleware) {
for _, h := range m {
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) {
g.echo.Connect(path, h)
}
// Delete implements the echo.Delete interface for subroutes within the Group.
func (g *Group) Delete(path string, h Handler) {
g.echo.Delete(path, h)
}
// Get implements the echo.Get interface for subroutes within the Group.
func (g *Group) Get(path string, h Handler) {
g.echo.Get(path, h)
}
// Head implements the echo.Head interface for subroutes within the Group.
func (g *Group) Head(path string, h Handler) {
g.echo.Head(path, h)
}
// Options implements the echo.Options interface for subroutes within the Group.
func (g *Group) Options(path string, h Handler) {
g.echo.Options(path, h)
}
// Patch implements the echo.Patch interface for subroutes within the Group.
func (g *Group) Patch(path string, h Handler) {
g.echo.Patch(path, h)
}
// Post implements the echo.Post interface for subroutes within the Group.
func (g *Group) Post(path string, h Handler) {
g.echo.Post(path, h)
}
// Put implements the echo.Put interface for subroutes within the Group.
func (g *Group) Put(path string, h Handler) {
g.echo.Put(path, h)
}
// Trace implements the echo.Trace interface for subroutes within the Group.
func (g *Group) Trace(path string, h Handler) {
g.echo.Trace(path, h)
}
// Any implements the echo.Any interface for subroutes within the Group.
func (g *Group) Any(path string, h Handler) {
for _, m := range methods {
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) {
for _, m := range methods {
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) {
g.echo.WebSocket(path, h)
}
// Static implements the echo.Static interface for subroutes within the Group.
func (g *Group) Static(path, root string) {
g.echo.Static(path, root)
}
// ServeDir implements the echo.ServeDir interface for subroutes within the
// Group.
func (g *Group) ServeDir(path, root string) {
g.echo.ServeDir(path, root)
}
// ServeFile implements the echo.ServeFile interface for subroutes within the
// Group.
func (g *Group) ServeFile(path, file string) {
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 {
return g.echo.Group(prefix, m...)
}

View File

@ -8,17 +8,19 @@ import (
)
type (
// BasicValidateFunc is the expected format a BasicAuth fn argument is
// expected to implement.
BasicValidateFunc func(string, string) bool
)
const (
// Basic is the authentication scheme implemented by the middleware.
Basic = "Basic"
)
// BasicAuth returns an HTTP basic authentication middleware.
//
// For valid credentials it calls the next handler.
// For invalid credentials, it sends "401 - Unauthorized" response.
// BasicAuth returns a HTTP basic authentication middleware.
// For valid credentials, it calls the next handler.
// For invalid credentials, it returns a "401 Unauthorized" HTTP error.
func BasicAuth(fn BasicValidateFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
// Skip WebSocket

View File

@ -8,6 +8,9 @@ import (
"github.com/labstack/gommon/color"
)
const loggerFormat = "%s %s %s %s %s %d"
// Logger returns a Middleware that logs requests.
func Logger() echo.MiddlewareFunc {
return func(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
@ -47,7 +50,7 @@ func Logger() echo.MiddlewareFunc {
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
}
}

View File

@ -6,32 +6,46 @@ import (
"net/http"
)
type (
Response struct {
writer http.ResponseWriter
status int
size int64
committed bool
echo *Echo
}
)
// Response wraps an http.ResponseWriter and implements its interface to be used
// by an HTTP handler to construct an HTTP response.
// See [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter)
type Response struct {
writer http.ResponseWriter
status int
size int64
committed bool
echo *Echo
}
// NewResponse creates a new instance of Response.
func NewResponse(w http.ResponseWriter, e *Echo) *Response {
return &Response{writer: w, echo: e}
}
// SetWriter sets the http.ResponseWriter instance for this Response.
func (r *Response) SetWriter(w http.ResponseWriter) {
r.writer = w
}
func (r *Response) Header() http.Header {
return r.writer.Header()
}
// Writer returns the http.ResponseWriter instance for this Response.
func (r *Response) Writer() http.ResponseWriter {
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) {
if r.committed {
r.echo.Logger().Warn("response already committed")
@ -42,35 +56,49 @@ func (r *Response) WriteHeader(code int) {
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) {
n, err = r.writer.Write(b)
r.size += int64(n)
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() {
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) {
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 {
return r.writer.(http.CloseNotifier).CloseNotify()
}
// Status returns the HTTP status code of the response.
func (r *Response) Status() int {
return r.status
}
// Size returns the current size, in bytes, of the response.
func (r *Response) Size() int64 {
return r.size
}
// Committed asserts whether or not the response has been committed to.
func (r *Response) Committed() bool {
return r.committed
}

View File

@ -3,6 +3,12 @@ package echo
import "net/http"
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 {
tree *node
routes []Route
@ -40,6 +46,7 @@ const (
mkind
)
// NewRouter returns a new Router instance.
func NewRouter(e *Echo) *Router {
return &Router{
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) {
ppath := path // Pristine path
pnames := []string{} // Param names
@ -275,6 +283,8 @@ func (n *node) check405() HandlerFunc {
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) {
h = notFoundHandler
e = r.echo
@ -400,6 +410,9 @@ End:
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) {
c := r.echo.pool.Get().(*Context)
h, _ := r.Find(req.Method, req.URL.Path, c)