1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-26 03:20:08 +02:00

v2 is compiling now

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2016-01-28 23:46:11 -08:00
parent dbd1e8e230
commit 688293b5ed
37 changed files with 832 additions and 1212 deletions

View File

@ -135,18 +135,12 @@ func (c *context) Param(name string) (value string) {
// Query returns query parameter by name. // Query returns query parameter by name.
func (c *context) Query(name string) string { func (c *context) Query(name string) string {
if c.query == nil { return c.request.URL().QueryValue(name)
// TODO: v2
// c.query = c.request.URL.Query()
}
return c.query.Get(name)
} }
// Form returns form parameter by name. // Form returns form parameter by name.
func (c *context) Form(name string) string { func (c *context) Form(name string) string {
// TODO: v2 return c.request.FormValue(name)
// return c.request.FormValue(name)
return ""
} }
// Get retrieves data from the context. // Get retrieves data from the context.

View File

@ -192,7 +192,7 @@ func TestContext(t *testing.T) {
// File // File
rec = test.NewResponseRecorder() rec = test.NewResponseRecorder()
c = NewContext(req, rec, e) c = NewContext(req, rec, e)
err = c.File("testing/fixture/walle.png", "", false) err = c.File("_fixture/images/walle.png", "", false)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, http.StatusOK, rec.Status()) assert.Equal(t, http.StatusOK, rec.Status())
assert.Equal(t, 219885, rec.Body.Len()) assert.Equal(t, 219885, rec.Body.Len())
@ -201,7 +201,7 @@ func TestContext(t *testing.T) {
// File as attachment // File as attachment
rec = test.NewResponseRecorder() rec = test.NewResponseRecorder()
c = NewContext(req, rec, e) c = NewContext(req, rec, e)
err = c.File("testing/fixture/walle.png", "WALLE.PNG", true) err = c.File("_fixture/images/walle.png", "WALLE.PNG", true)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.Equal(t, http.StatusOK, rec.Status()) assert.Equal(t, http.StatusOK, rec.Status())
assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=WALLE.PNG") assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=WALLE.PNG")

145
echo.go
View File

@ -20,7 +20,6 @@ import (
"github.com/labstack/echo/engine/fasthttp" "github.com/labstack/echo/engine/fasthttp"
"github.com/labstack/echo/engine/standard" "github.com/labstack/echo/engine/standard"
"github.com/labstack/gommon/log" "github.com/labstack/gommon/log"
"golang.org/x/net/http2"
) )
type ( type (
@ -237,7 +236,7 @@ func (e *Echo) SetLogPrefix(prefix string) {
e.logger.SetPrefix(prefix) e.logger.SetPrefix(prefix)
} }
// SetLogOutput sets the output destination for the logger. Default value is `os.Std*` // SetLogOutput sets the output destination for the logger. Default value is `os.Stdout`
func (e *Echo) SetLogOutput(w io.Writer) { func (e *Echo) SetLogOutput(w io.Writer) {
e.logger.SetOutput(w) e.logger.SetOutput(w)
} }
@ -408,33 +407,36 @@ func (e *Echo) ServeFile(path, file string) {
} }
func (e *Echo) serveFile(dir, file string, c Context) (err error) { func (e *Echo) serveFile(dir, file string, c Context) (err error) {
// fs := http.Dir(dir) fs := http.Dir(dir)
// f, err := fs.Open(file) f, err := fs.Open(file)
// if err != nil { if err != nil {
// return NewHTTPError(http.StatusNotFound) return NewHTTPError(http.StatusNotFound)
// } }
// defer f.Close() defer f.Close()
// fi, _ := f.Stat() fi, _ := f.Stat()
// if fi.IsDir() { if fi.IsDir() {
// /* NOTE: /* NOTE:
// Not checking the Last-Modified header as it caches the response `304` when Not checking the Last-Modified header as it caches the response `304` when
// changing differnt directories for the same path. changing differnt directories for the same path.
// */ */
// d := f d := f
// // Index file // Index file
// file = filepath.Join(file, indexPage) file = filepath.Join(file, indexPage)
// f, err = fs.Open(file) f, err = fs.Open(file)
// if err != nil { if err != nil {
// if e.autoIndex { if e.autoIndex {
// // Auto index // Auto index
// return listDir(d, c) return listDir(d, c)
// } }
// return NewHTTPError(http.StatusForbidden) return NewHTTPError(http.StatusForbidden)
// } }
// fi, _ = f.Stat() // Index file stat fi, _ = f.Stat() // Index file stat
// } }
c.Response().WriteHeader(http.StatusOK)
io.Copy(c.Response(), f)
// TODO:
// http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f) // http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
return return
} }
@ -513,39 +515,38 @@ func (e *Echo) Routes() []Route {
return e.router.routes return e.router.routes
} }
// ServeHTTP implements `http.Handler` interface, which serves HTTP requests. // ServeHTTP serves HTTP requests.
func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) {
// TODO: v2 if e.hook != nil {
// if e.hook != nil { e.hook(req, res)
// e.hook(w, r) }
// }
// c := e.pool.Get().(*context)
// c := e.pool.Get().(*context) h, e := e.router.Find(req.Method(), req.URL().Path(), c)
// h, e := e.router.Find(r.Method, r.URL.Path, c) c.reset(req, res, e)
// c.reset(r, w, e)
// // Chain middleware with handler in the end
// // Chain middleware with handler in the end for i := len(e.middleware) - 1; i >= 0; i-- {
// for i := len(e.middleware) - 1; i >= 0; i-- { h = e.middleware[i](h)
// h = e.middleware[i](h) }
// }
// // Execute chain
// // Execute chain if err := h(c); err != nil {
// if err := h(c); err != nil { e.httpErrorHandler(err, c)
// e.httpErrorHandler(err, c) }
// }
// e.pool.Put(c)
// e.pool.Put(c)
} }
// Server returns the internal *http.Server. // Server returns the internal *http.Server.
func (e *Echo) Server(addr string) *http.Server { // func (e *Echo) Server(addr string) *http.Server {
s := &http.Server{Addr: addr, Handler: e} // s := &http.Server{Addr: addr, Handler: e}
// TODO: Remove in Go 1.6+ // // TODO: Remove in Go 1.6+
if e.http2 { // if e.http2 {
http2.ConfigureServer(s, nil) // http2.ConfigureServer(s, nil)
} // }
return s // return s
} // }
func (e *Echo) SetEngine(t engine.Type) { func (e *Echo) SetEngine(t engine.Type) {
e.engineType = t e.engineType = t
@ -589,32 +590,32 @@ func (e *Echo) Run(address string) {
// RunTLS runs a server with TLS configuration. // RunTLS runs a server with TLS configuration.
func (e *Echo) RunTLS(addr, crtFile, keyFile string) { func (e *Echo) RunTLS(addr, crtFile, keyFile string) {
e.run(e.Server(addr), crtFile, keyFile) // e.run(e.Server(addr), crtFile, keyFile)
} }
// RunServer runs a custom server. // RunServer runs a custom server.
func (e *Echo) RunServer(s *http.Server) { func (e *Echo) RunServer(s *http.Server) {
e.run(s) // e.run(s)
} }
// RunTLSServer runs a custom server with TLS configuration. // RunTLSServer runs a custom server with TLS configuration.
func (e *Echo) RunTLSServer(s *http.Server, crtFile, keyFile string) { func (e *Echo) RunTLSServer(s *http.Server, crtFile, keyFile string) {
e.run(s, crtFile, keyFile) // e.run(s, crtFile, keyFile)
} }
func (e *Echo) run(s *http.Server, files ...string) { func (e *Echo) run(s *http.Server, files ...string) {
s.Handler = e // s.Handler = e
// TODO: Remove in Go 1.6+ // // TODO: Remove in Go 1.6+
if e.http2 { // if e.http2 {
http2.ConfigureServer(s, nil) // http2.ConfigureServer(s, nil)
} // }
if len(files) == 0 { // if len(files) == 0 {
e.logger.Fatal(s.ListenAndServe()) // e.logger.Fatal(s.ListenAndServe())
} else if len(files) == 2 { // } else if len(files) == 2 {
e.logger.Fatal(s.ListenAndServeTLS(files[0], files[1])) // e.logger.Fatal(s.ListenAndServeTLS(files[0], files[1]))
} else { // } else {
e.logger.Fatal("invalid TLS configuration") // e.logger.Fatal("invalid TLS configuration")
} // }
} }
func NewHTTPError(code int, msg ...string) *HTTPError { func NewHTTPError(code int, msg ...string) *HTTPError {

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"
"reflect" "reflect"
@ -44,7 +43,7 @@ func TestEcho(t *testing.T) {
func TestEchoIndex(t *testing.T) { func TestEchoIndex(t *testing.T) {
e := New() e := New()
e.Index("recipes/website/public/index.html") e.Index("_fixture/index.html")
c, b := request(GET, "/", e) c, b := request(GET, "/", e)
assert.Equal(t, http.StatusOK, c) assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b) assert.NotEmpty(t, b)
@ -52,7 +51,7 @@ func TestEchoIndex(t *testing.T) {
func TestEchoFavicon(t *testing.T) { func TestEchoFavicon(t *testing.T) {
e := New() e := New()
e.Favicon("recipes/website/public/favicon.ico") e.Favicon("_fixture/favicon.ico")
c, b := request(GET, "/favicon.ico", e) c, b := request(GET, "/favicon.ico", e)
assert.Equal(t, http.StatusOK, c) assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b) assert.NotEmpty(t, b)
@ -62,23 +61,23 @@ func TestEchoStatic(t *testing.T) {
e := New() e := New()
// OK // OK
e.Static("/scripts", "recipes/website/public/scripts") e.Static("/images", "_fixture/images")
c, b := request(GET, "/scripts/main.js", e) c, b := request(GET, "/images/walle.png", e)
assert.Equal(t, http.StatusOK, c) assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b) assert.NotEmpty(t, b)
// No file // No file
e.Static("/scripts", "recipes/website/public/scripts") e.Static("/images", "_fixture/scripts")
c, _ = request(GET, "/scripts/index.js", e) c, _ = request(GET, "/images/bolt.png", e)
assert.Equal(t, http.StatusNotFound, c) assert.Equal(t, http.StatusNotFound, c)
// Directory // Directory
e.Static("/scripts", "recipes/website/public/scripts") e.Static("/images", "_fixture/images")
c, _ = request(GET, "/scripts", e) c, _ = request(GET, "/images", e)
assert.Equal(t, http.StatusForbidden, c) assert.Equal(t, http.StatusForbidden, c)
// Directory with index.html // Directory with index.html
e.Static("/", "recipes/website/public") e.Static("/", "_fixture")
c, r := request(GET, "/", e) c, r := request(GET, "/", e)
assert.Equal(t, http.StatusOK, c) assert.Equal(t, http.StatusOK, c)
assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>")) assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>"))
@ -86,7 +85,8 @@ func TestEchoStatic(t *testing.T) {
// Sub-directory with index.html // Sub-directory with index.html
c, r = request(GET, "/folder", e) c, r = request(GET, "/folder", e)
assert.Equal(t, http.StatusOK, c) assert.Equal(t, http.StatusOK, c)
assert.Equal(t, "sub directory", r) assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>"))
// assert.Equal(t, "sub directory", r)
} }
func TestEchoMiddleware(t *testing.T) { func TestEchoMiddleware(t *testing.T) {
@ -250,11 +250,13 @@ func TestEchoGroup(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
e.Use(func(h HandlerFunc) HandlerFunc { e.Use(func(h HandlerFunc) HandlerFunc {
return func(c Context) error { return func(c Context) error {
buf.WriteString("a") buf.WriteString("0")
return h(c) return h(c)
} }
}) })
h := func(Context) error { return nil } h := func(c Context) error {
return c.NoContent(http.StatusOK)
}
//-------- //--------
// Routes // Routes
@ -284,18 +286,13 @@ func TestEchoGroup(t *testing.T) {
// Nested groups // Nested groups
g3 := e.Group("/group3") g3 := e.Group("/group3")
g4 := g3.Group("/group4") g4 := g3.Group("/group4")
g4.Use(func(h HandlerFunc) HandlerFunc { g4.Get("/", h)
return func(c Context) error {
return c.NoContent(http.StatusOK)
}
})
request(GET, "/users", e) request(GET, "/users", e)
assert.Equal(t, "0", buf.String()) assert.Equal(t, "0", buf.String())
buf.Reset() buf.Reset()
request(GET, "/group1/", e) request(GET, "/group1/", e)
// println(len(g1.echo.middleware))
assert.Equal(t, "01", buf.String()) assert.Equal(t, "01", buf.String())
buf.Reset() buf.Reset()
@ -309,10 +306,10 @@ func TestEchoGroup(t *testing.T) {
func TestEchoNotFound(t *testing.T) { func TestEchoNotFound(t *testing.T) {
e := New() e := New()
r, _ := http.NewRequest(GET, "/files", nil) req := test.NewRequest(GET, "/files", nil)
w := httptest.NewRecorder() res := test.NewResponseRecorder()
e.ServeHTTP(w, r) e.ServeHTTP(req, res)
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, res.Status())
} }
func TestEchoMethodNotAllowed(t *testing.T) { func TestEchoMethodNotAllowed(t *testing.T) {
@ -320,10 +317,10 @@ func TestEchoMethodNotAllowed(t *testing.T) {
e.Get("/", func(c Context) error { e.Get("/", func(c Context) error {
return c.String(http.StatusOK, "Echo!") return c.String(http.StatusOK, "Echo!")
}) })
r, _ := http.NewRequest(POST, "/", nil) req := test.NewRequest(POST, "/", nil)
w := httptest.NewRecorder() res := test.NewResponseRecorder()
e.ServeHTTP(w, r) e.ServeHTTP(req, res)
assert.Equal(t, http.StatusMethodNotAllowed, w.Code) assert.Equal(t, http.StatusMethodNotAllowed, res.Status())
} }
func TestEchoHTTPError(t *testing.T) { func TestEchoHTTPError(t *testing.T) {
@ -334,9 +331,9 @@ func TestEchoHTTPError(t *testing.T) {
} }
func TestEchoServer(t *testing.T) { func TestEchoServer(t *testing.T) {
e := New() // e := New()
s := e.Server(":1323") // s := e.Server(":1323")
assert.IsType(t, &http.Server{}, s) // assert.IsType(t, &http.Server{}, s)
} }
func TestEchoHook(t *testing.T) { func TestEchoHook(t *testing.T) {
@ -348,13 +345,13 @@ func TestEchoHook(t *testing.T) {
path := req.URL().Path() path := req.URL().Path()
l := len(path) - 1 l := len(path) - 1
if path != "/" && path[l] == '/' { if path != "/" && path[l] == '/' {
// req.URL().Path() = path[:l] req.URL().SetPath(path[:l])
} }
}) })
r, _ := http.NewRequest(GET, "/test/", nil) req := test.NewRequest(GET, "/test/", nil)
w := httptest.NewRecorder() res := test.NewResponseRecorder()
e.ServeHTTP(w, r) e.ServeHTTP(req, res)
assert.Equal(t, r.URL.Path, "/test") assert.Equal(t, req.URL().Path(), "/test")
} }
func testMethod(t *testing.T, method, path string, e *Echo) { func testMethod(t *testing.T, method, path string, e *Echo) {
@ -372,8 +369,8 @@ func testMethod(t *testing.T, method, path string, e *Echo) {
} }
func request(method, path string, e *Echo) (int, string) { func request(method, path string, e *Echo) (int, string) {
r, _ := http.NewRequest(method, path, nil) req := test.NewRequest(method, path, nil)
w := httptest.NewRecorder() res := test.NewResponseRecorder()
e.ServeHTTP(w, r) e.ServeHTTP(req, res)
return w.Code, w.Body.String() return res.Status(), res.Body.String()
} }

59
engine/engine.go Normal file
View File

@ -0,0 +1,59 @@
package engine
import "io"
type (
Type uint8
HandlerFunc func(Request, Response)
Engine interface {
Start()
}
Request interface {
Header() Header
// Proto() string
// ProtoMajor() int
// ProtoMinor() int
RemoteAddress() string
Method() string
URI() string
URL() URL
Body() io.ReadCloser
FormValue(string) string
}
Response interface {
Header() Header
WriteHeader(int)
Write(b []byte) (int, error)
Status() int
Size() int64
Committed() bool
}
Header interface {
Add(string, string)
Del(string)
Get(string) string
Set(string, string)
}
URL interface {
Scheme() string
SetPath(string)
Path() string
Host() string
QueryValue(string) string
}
Config struct {
Address string
}
)
const (
Standard Type = iota
FastHTTP
)

46
engine/fasthttp/header.go Normal file
View File

@ -0,0 +1,46 @@
package fasthttp
import "github.com/valyala/fasthttp"
type (
RequestHeader struct {
fasthttp.RequestHeader
}
ResponseHeader struct {
fasthttp.ResponseHeader
}
)
func (h *RequestHeader) Add(key, val string) {
// h.RequestHeader.Add(key, val)
}
func (h *RequestHeader) Del(key string) {
h.RequestHeader.Del(key)
}
func (h *RequestHeader) Get(key string) string {
return string(h.RequestHeader.Peek(key))
}
func (h *RequestHeader) Set(key, val string) {
h.RequestHeader.Set(key, val)
}
func (h *ResponseHeader) Add(key, val string) {
// h.ResponseHeader.Add(key, val)
}
func (h *ResponseHeader) Del(key string) {
h.ResponseHeader.Del(key)
}
func (h *ResponseHeader) Get(key string) string {
// return h.ResponseHeader.Get(key)
return ""
}
func (h *ResponseHeader) Set(key, val string) {
h.ResponseHeader.Set(key, val)
}

View File

@ -0,0 +1,45 @@
package fasthttp
import "io"
import (
"github.com/labstack/echo/engine"
"github.com/valyala/fasthttp"
)
type (
Request struct {
context *fasthttp.RequestCtx
url engine.URL
header engine.Header
}
)
func (r *Request) Header() engine.Header {
return r.header
}
func (r *Request) RemoteAddress() string {
return r.context.RemoteAddr().String()
}
func (r *Request) Method() string {
return string(r.context.Method())
}
func (r *Request) URI() string {
return string(r.context.RequestURI())
}
func (r *Request) URL() engine.URL {
return r.url
}
func (r *Request) Body() io.ReadCloser {
// return r.context.PostBody()
return nil
}
func (r *Request) FormValue(name string) string {
return ""
}

View File

@ -0,0 +1,40 @@
package fasthttp
import (
"github.com/labstack/echo/engine"
"github.com/valyala/fasthttp"
)
type (
Response struct {
context *fasthttp.RequestCtx
header engine.Header
status int
size int64
committed bool
}
)
func (r *Response) Header() engine.Header {
return r.header
}
func (r *Response) WriteHeader(code int) {
r.context.SetStatusCode(code)
}
func (r *Response) Write(b []byte) (int, error) {
return r.context.Write(b)
}
func (r *Response) Status() int {
return r.status
}
func (r *Response) Size() int64 {
return r.size
}
func (r *Response) Committed() bool {
return r.committed
}

43
engine/fasthttp/server.go Normal file
View File

@ -0,0 +1,43 @@
package fasthttp
import (
"log"
"net/http"
)
import (
"github.com/labstack/echo/engine"
"github.com/valyala/fasthttp"
)
type (
Server struct {
*http.Server
config *engine.Config
handler engine.HandlerFunc
}
)
func NewServer(config *engine.Config, handler engine.HandlerFunc) *Server {
return &Server{
Server: new(http.Server),
config: config,
handler: handler,
}
}
func (s *Server) Start() {
fasthttp.ListenAndServe(s.config.Address, func(ctx *fasthttp.RequestCtx) {
println("FastHTTP")
req := &Request{
context: ctx,
url: &URL{ctx.URI()},
header: &RequestHeader{ctx.Request.Header},
}
res := &Response{
context: ctx,
header: &ResponseHeader{ctx.Response.Header},
}
s.handler(req, res)
})
log.Fatal(s.ListenAndServe())
}

29
engine/fasthttp/url.go Normal file
View File

@ -0,0 +1,29 @@
package fasthttp
import "github.com/valyala/fasthttp"
type (
URL struct {
*fasthttp.URI
}
)
func (u *URL) Scheme() string {
return string(u.URI.Scheme())
}
func (u *URL) Host() string {
return string(u.URI.Host())
}
func (u *URL) SetPath(path string) {
// return string(u.URI.Path())
}
func (u *URL) Path() string {
return string(u.URI.Path())
}
func (u *URL) QueryValue(name string) string {
return ""
}

25
engine/standard/header.go Normal file
View File

@ -0,0 +1,25 @@
package standard
import "net/http"
type (
Header struct {
http.Header
}
)
func (h *Header) Add(key, val string) {
h.Header.Add(key, val)
}
func (h *Header) Del(key string) {
h.Header.Del(key)
}
func (h *Header) Get(key string) string {
return h.Header.Get(key)
}
func (h *Header) Set(key, val string) {
h.Header.Set(key, val)
}

View File

@ -0,0 +1,56 @@
package standard
import (
"io"
"net/http"
"github.com/labstack/echo/engine"
)
type (
Request struct {
request *http.Request
url engine.URL
header engine.Header
}
)
func NewRequest(r *http.Request) *Request {
return &Request{
request: r,
url: NewURL(r.URL),
header: &Header{r.Header},
}
}
func (r *Request) Request() *http.Request {
return r.request
}
func (r *Request) Header() engine.Header {
return r.header
}
func (r *Request) URL() engine.URL {
return r.url
}
func (r *Request) RemoteAddress() string {
return r.request.RemoteAddr
}
func (r *Request) Method() string {
return r.request.Method
}
func (r *Request) URI() string {
return r.request.RequestURI
}
func (r *Request) Body() io.ReadCloser {
return r.request.Body
}
func (r *Request) FormValue(name string) string {
return r.request.FormValue(name)
}

View File

@ -0,0 +1,53 @@
package standard
import "net/http"
import "github.com/labstack/echo/engine"
type (
Response struct {
response http.ResponseWriter
header engine.Header
status int
size int64
committed bool
}
)
func NewResponse(w http.ResponseWriter) *Response {
return &Response{
response: w,
header: &Header{w.Header()},
}
}
func (r *Response) Header() engine.Header {
return r.header
}
func (r *Response) WriteHeader(code int) {
if r.committed {
// r.echo.Logger().Warn("response already committed")
return
}
r.status = code
r.response.WriteHeader(code)
r.committed = true
}
func (r *Response) Write(b []byte) (n int, err error) {
n, err = r.response.Write(b)
r.size += int64(n)
return
}
func (r *Response) Status() int {
return r.status
}
func (r *Response) Size() int64 {
return r.size
}
func (r *Response) Committed() bool {
return r.committed
}

32
engine/standard/server.go Normal file
View File

@ -0,0 +1,32 @@
package standard
import (
"log"
"net/http"
"github.com/labstack/echo/engine"
)
type (
Server struct {
*http.Server
config *engine.Config
handler engine.HandlerFunc
}
)
func NewServer(config *engine.Config, handler engine.HandlerFunc) *Server {
return &Server{
Server: new(http.Server),
config: config,
handler: handler,
}
}
func (s *Server) Start() {
s.Addr = s.config.Address
s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s.handler(NewRequest(r), NewResponse(w))
})
log.Fatal(s.ListenAndServe())
}

41
engine/standard/url.go Normal file
View File

@ -0,0 +1,41 @@
package standard
import "net/url"
type (
URL struct {
url *url.URL
query url.Values
}
)
func NewURL(u *url.URL) *URL {
return &URL{url: u}
}
func (u *URL) URL() *url.URL {
return u.url
}
func (u *URL) Scheme() string {
return u.url.Scheme
}
func (u *URL) Host() string {
return u.url.Host
}
func (u *URL) SetPath(path string) {
u.url.Path = path
}
func (u *URL) Path() string {
return u.url.Path
}
func (u *URL) QueryValue(name string) string {
if u.query == nil {
u.query = u.url.Query()
}
return u.query.Get(name)
}

View File

@ -3,18 +3,18 @@ package middleware
import ( import (
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/echo/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestBasicAuth(t *testing.T) { func TestBasicAuth(t *testing.T) {
e := echo.New() e := echo.New()
req, _ := http.NewRequest(echo.GET, "/", nil) req := test.NewRequest(echo.GET, "/", nil)
rec := httptest.NewRecorder() res := test.NewResponseRecorder()
c := echo.NewContext(req, echo.NewResponse(rec, e), e) c := echo.NewContext(req, res, e)
fn := func(u, p string) bool { fn := func(u, p string) bool {
if u == "joe" && p == "secret" { if u == "joe" && p == "secret" {
return true return true
@ -37,22 +37,22 @@ func TestBasicAuth(t *testing.T) {
req.Header().Set(echo.Authorization, auth) req.Header().Set(echo.Authorization, auth)
he := ba(c).(*echo.HTTPError) he := ba(c).(*echo.HTTPError)
assert.Equal(t, http.StatusUnauthorized, he.Code()) assert.Equal(t, http.StatusUnauthorized, he.Code())
assert.Equal(t, Basic+" realm=Restricted", rec.Header().Get(echo.WWWAuthenticate)) assert.Equal(t, Basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
// Empty Authorization header // Empty Authorization header
req.Header.Set(echo.Authorization, "") req.Header().Set(echo.Authorization, "")
he = ba(c).(*echo.HTTPError) he = ba(c).(*echo.HTTPError)
assert.Equal(t, http.StatusUnauthorized, he.Code()) assert.Equal(t, http.StatusUnauthorized, he.Code())
assert.Equal(t, Basic+" realm=Restricted", rec.Header().Get(echo.WWWAuthenticate)) assert.Equal(t, Basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
// Invalid Authorization header // Invalid Authorization header
auth = base64.StdEncoding.EncodeToString([]byte("invalid")) auth = base64.StdEncoding.EncodeToString([]byte("invalid"))
req.Header.Set(echo.Authorization, auth) req.Header().Set(echo.Authorization, auth)
he = ba(c).(*echo.HTTPError) he = ba(c).(*echo.HTTPError)
assert.Equal(t, http.StatusUnauthorized, he.Code()) assert.Equal(t, http.StatusUnauthorized, he.Code())
assert.Equal(t, Basic+" realm=Restricted", rec.Header().Get(echo.WWWAuthenticate)) assert.Equal(t, Basic+" realm=Restricted", res.Header().Get(echo.WWWAuthenticate))
// WebSocket // WebSocket
c.Request().Header.Set(echo.Upgrade, echo.WebSocket) c.Request().Header().Set(echo.Upgrade, echo.WebSocket)
assert.NoError(t, ba(c)) assert.NoError(t, ba(c))
} }

View File

@ -1,73 +1,74 @@
package middleware package middleware
import ( //
"bufio" // import (
"compress/gzip" // "bufio"
"io" // "compress/gzip"
"io/ioutil" // "io"
"net" // "io/ioutil"
"net/http" // "net"
"strings" // "net/http"
"sync" // "strings"
// "sync"
"github.com/labstack/echo" //
) // "github.com/labstack/echo"
// )
type ( //
gzipWriter struct { // type (
io.Writer // gzipWriter struct {
http.ResponseWriter // io.Writer
} // http.ResponseWriter
) // }
// )
func (w gzipWriter) Write(b []byte) (int, error) { //
if w.Header().Get(echo.ContentType) == "" { // func (w gzipWriter) Write(b []byte) (int, error) {
w.Header().Set(echo.ContentType, http.DetectContentType(b)) // if w.Header().Get(echo.ContentType) == "" {
} // w.Header().Set(echo.ContentType, http.DetectContentType(b))
return w.Writer.Write(b) // }
} // return w.Writer.Write(b)
// }
func (w gzipWriter) Flush() error { //
return w.Writer.(*gzip.Writer).Flush() // func (w gzipWriter) Flush() error {
} // return w.Writer.(*gzip.Writer).Flush()
// }
func (w gzipWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { //
return w.ResponseWriter.(http.Hijacker).Hijack() // func (w gzipWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
} // return w.ResponseWriter.(http.Hijacker).Hijack()
// }
func (w *gzipWriter) CloseNotify() <-chan bool { //
return w.ResponseWriter.(http.CloseNotifier).CloseNotify() // func (w *gzipWriter) CloseNotify() <-chan bool {
} // return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
// }
var writerPool = sync.Pool{ //
New: func() interface{} { // var writerPool = sync.Pool{
return gzip.NewWriter(ioutil.Discard) // New: func() interface{} {
}, // return gzip.NewWriter(ioutil.Discard)
} // },
// }
// Gzip returns a middleware which compresses HTTP response using gzip compression //
// scheme. // // Gzip returns a middleware which compresses HTTP response using gzip compression
func Gzip() echo.MiddlewareFunc { // // scheme.
scheme := "gzip" // func Gzip() echo.MiddlewareFunc {
// scheme := "gzip"
return func(h echo.HandlerFunc) echo.HandlerFunc { //
return func(c echo.Context) error { // return func(h echo.HandlerFunc) echo.HandlerFunc {
c.Response().Header().Add(echo.Vary, echo.AcceptEncoding) // return func(c echo.Context) error {
if strings.Contains(c.Request().Header().Get(echo.AcceptEncoding), scheme) { // c.Response().Header().Add(echo.Vary, echo.AcceptEncoding)
w := writerPool.Get().(*gzip.Writer) // if strings.Contains(c.Request().Header().Get(echo.AcceptEncoding), scheme) {
w.Reset(c.Response().Writer()) // w := writerPool.Get().(*gzip.Writer)
defer func() { // w.Reset(c.Response().Writer())
w.Close() // defer func() {
writerPool.Put(w) // w.Close()
}() // writerPool.Put(w)
gw := gzipWriter{Writer: w, ResponseWriter: c.Response().Writer()} // }()
c.Response().Header().Set(echo.ContentEncoding, scheme) // gw := gzipWriter{Writer: w, ResponseWriter: c.Response().Writer()}
c.Response().SetWriter(gw) // c.Response().Header().Set(echo.ContentEncoding, scheme)
} // c.Response().SetWriter(gw)
if err := h(c); err != nil { // }
c.Error(err) // if err := h(c); err != nil {
} // c.Error(err)
return nil // }
} // return nil
} // }
} // }
// }

View File

@ -1,144 +1,146 @@
package middleware package middleware
import ( //
"bytes" // import (
"compress/gzip" // "bytes"
"net/http" // "compress/gzip"
"net/http/httptest" // "net/http"
"testing" // "net/http/httptest"
"time" // "testing"
// "time"
"github.com/labstack/echo" //
"github.com/stretchr/testify/assert" // "github.com/labstack/echo"
) // "github.com/labstack/echo/test"
// "github.com/stretchr/testify/assert"
type closeNotifyingRecorder struct { // )
*httptest.ResponseRecorder //
closed chan bool // type closeNotifyingRecorder struct {
} // *httptest.ResponseRecorder
// closed chan bool
func newCloseNotifyingRecorder() *closeNotifyingRecorder { // }
return &closeNotifyingRecorder{ //
httptest.NewRecorder(), // func newCloseNotifyingRecorder() *closeNotifyingRecorder {
make(chan bool, 1), // return &closeNotifyingRecorder{
} // test.NewResponseRecorder(),
} // make(chan bool, 1),
// }
func (c *closeNotifyingRecorder) close() { // }
c.closed <- true //
} // func (c *closeNotifyingRecorder) close() {
// c.closed <- true
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool { // }
return c.closed //
} // func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
// return c.closed
func TestGzip(t *testing.T) { // }
e := echo.New() //
req, _ := http.NewRequest(echo.GET, "/", nil) // func TestGzip(t *testing.T) {
rec := httptest.NewRecorder() // e := echo.New()
c := echo.NewContext(req, echo.NewResponse(rec, e), e) // req := test.NewRequest(echo.GET, "/", nil)
h := func(c echo.Context) error { // res := test.NewResponseRecorder()
c.Response().Write([]byte("test")) // For Content-Type sniffing // c := echo.NewContext(req, res, e)
return nil // h := func(c echo.Context) error {
} // c.Response().Write([]byte("test")) // For Content-Type sniffing
// return nil
// Skip if no Accept-Encoding header // }
Gzip()(h)(c) //
assert.Equal(t, http.StatusOK, rec.Code) // // Skip if no Accept-Encoding header
assert.Equal(t, "test", rec.Body.String()) // Gzip()(h)(c)
// assert.Equal(t, http.StatusOK, res.Status())
req, _ = http.NewRequest(echo.GET, "/", nil) // assert.Equal(t, "test", res.Body().String())
req.Header.Set(echo.AcceptEncoding, "gzip") //
rec = httptest.NewRecorder() // req = test.NewRequest(echo.GET, "/", nil)
c = echo.NewContext(req, echo.NewResponse(rec, e), e) // req.Header.Set(echo.AcceptEncoding, "gzip")
// res = test.NewResponseRecorder()
// Gzip // c = echo.NewContext(req, res, e)
Gzip()(h)(c) //
assert.Equal(t, http.StatusOK, rec.Code) // // Gzip
assert.Equal(t, "gzip", rec.Header().Get(echo.ContentEncoding)) // Gzip()(h)(c)
assert.Contains(t, rec.Header().Get(echo.ContentType), echo.TextPlain) // assert.Equal(t, http.StatusOK, res.Status())
r, err := gzip.NewReader(rec.Body) // assert.Equal(t, "gzip", res.Header().Get(echo.ContentEncoding))
defer r.Close() // assert.Contains(t, res.Header().Get(echo.ContentType), echo.TextPlain)
if assert.NoError(t, err) { // r, err := gzip.NewReader(res.Body())
buf := new(bytes.Buffer) // defer r.Close()
buf.ReadFrom(r) // if assert.NoError(t, err) {
assert.Equal(t, "test", buf.String()) // buf := new(bytes.Buffer)
} // buf.ReadFrom(r)
} // assert.Equal(t, "test", buf.String())
// }
func TestGzipFlush(t *testing.T) { // }
rec := httptest.NewRecorder() //
buf := new(bytes.Buffer) // func TestGzipFlush(t *testing.T) {
w := gzip.NewWriter(buf) // res := test.NewResponseRecorder()
gw := gzipWriter{Writer: w, ResponseWriter: rec} // buf := new(bytes.Buffer)
// w := gzip.NewWriter(buf)
n0 := buf.Len() // gw := gzipWriter{Writer: w, ResponseWriter: res}
if n0 != 0 { //
t.Fatalf("buffer size = %d before writes; want 0", n0) // n0 := buf.Len()
} // if n0 != 0 {
// t.Fatalf("buffer size = %d before writes; want 0", n0)
if err := gw.Flush(); err != nil { // }
t.Fatal(err) //
} // if err := gw.Flush(); err != nil {
// t.Fatal(err)
n1 := buf.Len() // }
if n1 == 0 { //
t.Fatal("no data after first flush") // n1 := buf.Len()
} // if n1 == 0 {
// t.Fatal("no data after first flush")
gw.Write([]byte("x")) // }
//
n2 := buf.Len() // gw.Write([]byte("x"))
if n1 != n2 { //
t.Fatalf("after writing a single byte, size changed from %d to %d; want no change", n1, n2) // n2 := buf.Len()
} // if n1 != n2 {
// t.Fatalf("after writing a single byte, size changed from %d to %d; want no change", n1, n2)
if err := gw.Flush(); err != nil { // }
t.Fatal(err) //
} // if err := gw.Flush(); err != nil {
// t.Fatal(err)
n3 := buf.Len() // }
if n2 == n3 { //
t.Fatal("Flush didn't flush any data") // n3 := buf.Len()
} // if n2 == n3 {
} // t.Fatal("Flush didn't flush any data")
// }
func TestGzipCloseNotify(t *testing.T) { // }
rec := newCloseNotifyingRecorder() //
buf := new(bytes.Buffer) // func TestGzipCloseNotify(t *testing.T) {
w := gzip.NewWriter(buf) // rec := newCloseNotifyingRecorder()
gw := gzipWriter{Writer: w, ResponseWriter: rec} // buf := new(bytes.Buffer)
closed := false // w := gzip.NewWriter(buf)
notifier := gw.CloseNotify() // gw := gzipWriter{Writer: w, ResponseWriter: rec}
rec.close() // closed := false
// notifier := gw.CloseNotify()
select { // rec.close()
case <-notifier: //
closed = true // select {
case <-time.After(time.Second): // case <-notifier:
} // closed = true
// case <-time.After(time.Second):
assert.Equal(t, closed, true) // }
} //
// assert.Equal(t, closed, true)
func BenchmarkGzip(b *testing.B) { // }
b.StopTimer() //
b.ReportAllocs() // func BenchmarkGzip(b *testing.B) {
// b.StopTimer()
h := func(c echo.Context) error { // b.ReportAllocs()
c.Response().Write([]byte("test")) // For Content-Type sniffing //
return nil // h := func(c echo.Context) error {
} // c.Response().Write([]byte("test")) // For Content-Type sniffing
req, _ := http.NewRequest(echo.GET, "/", nil) // return nil
req.Header.Set(echo.AcceptEncoding, "gzip") // }
// req, _ := http.NewRequest(echo.GET, "/", nil)
b.StartTimer() // req.Header().Set(echo.AcceptEncoding, "gzip")
//
for i := 0; i < b.N; i++ { // b.StartTimer()
e := echo.New() //
rec := httptest.NewRecorder() // for i := 0; i < b.N; i++ {
c := echo.NewContext(req, echo.NewResponse(rec, e), e) // e := echo.New()
Gzip()(h)(c) // res := test.NewResponseRecorder()
} // c := echo.NewContext(req, res, e)
// Gzip()(h)(c)
} // }
//
// }

View File

@ -4,19 +4,19 @@ import (
"bytes" "bytes"
"errors" "errors"
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/echo/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestLogger(t *testing.T) { func TestLogger(t *testing.T) {
// Note: Just for the test coverage, not a real test. // Note: Just for the test coverage, not a real test.
e := echo.New() e := echo.New()
req, _ := http.NewRequest(echo.GET, "/", nil) req := test.NewRequest(echo.GET, "/", nil)
rec := httptest.NewRecorder() res := test.NewResponseRecorder()
c := echo.NewContext(req, echo.NewResponse(rec, e), e) c := echo.NewContext(req, res, e)
// Status 2xx // Status 2xx
h := func(c echo.Context) error { h := func(c echo.Context) error {
@ -25,25 +25,25 @@ func TestLogger(t *testing.T) {
Logger()(h)(c) Logger()(h)(c)
// Status 3xx // Status 3xx
rec = httptest.NewRecorder() res = test.NewResponseRecorder()
c = echo.NewContext(req, echo.NewResponse(rec, e), e) c = echo.NewContext(req, res, e)
h = func(c echo.Context) error { h = func(c echo.Context) error {
return c.String(http.StatusTemporaryRedirect, "test") return c.String(http.StatusTemporaryRedirect, "test")
} }
Logger()(h)(c) Logger()(h)(c)
// Status 4xx // Status 4xx
rec = httptest.NewRecorder() res = test.NewResponseRecorder()
c = echo.NewContext(req, echo.NewResponse(rec, e), e) c = echo.NewContext(req, res, e)
h = func(c echo.Context) error { h = func(c echo.Context) error {
return c.String(http.StatusNotFound, "test") return c.String(http.StatusNotFound, "test")
} }
Logger()(h)(c) Logger()(h)(c)
// Status 5xx with empty path // Status 5xx with empty path
req, _ = http.NewRequest(echo.GET, "", nil) req = test.NewRequest(echo.GET, "", nil)
rec = httptest.NewRecorder() res = test.NewResponseRecorder()
c = echo.NewContext(req, echo.NewResponse(rec, e), e) c = echo.NewContext(req, res, e)
h = func(c echo.Context) error { h = func(c echo.Context) error {
return errors.New("error") return errors.New("error")
} }
@ -52,9 +52,9 @@ func TestLogger(t *testing.T) {
func TestLoggerIPAddress(t *testing.T) { func TestLoggerIPAddress(t *testing.T) {
e := echo.New() e := echo.New()
req, _ := http.NewRequest(echo.GET, "/", nil) req := test.NewRequest(echo.GET, "/", nil)
rec := httptest.NewRecorder() res := test.NewResponseRecorder()
c := echo.NewContext(req, echo.NewResponse(rec, e), e) c := echo.NewContext(req, res, e)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
e.Logger().SetOutput(buf) e.Logger().SetOutput(buf)
ip := "127.0.0.1" ip := "127.0.0.1"
@ -65,14 +65,14 @@ func TestLoggerIPAddress(t *testing.T) {
mw := Logger() mw := Logger()
// With X-Real-IP // With X-Real-IP
req.Header.Add(echo.XRealIP, ip) req.Header().Add(echo.XRealIP, ip)
mw(h)(c) mw(h)(c)
assert.Contains(t, buf.String(), ip) assert.Contains(t, buf.String(), ip)
// With X-Forwarded-For // With X-Forwarded-For
buf.Reset() buf.Reset()
req.Header.Del(echo.XRealIP) req.Header().Del(echo.XRealIP)
req.Header.Add(echo.XForwardedFor, ip) req.Header().Add(echo.XForwardedFor, ip)
mw(h)(c) mw(h)(c)
assert.Contains(t, buf.String(), ip) assert.Contains(t, buf.String(), ip)

View File

@ -2,23 +2,23 @@ package middleware
import ( import (
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/echo/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestRecover(t *testing.T) { func TestRecover(t *testing.T) {
e := echo.New() e := echo.New()
e.SetDebug(true) e.SetDebug(true)
req, _ := http.NewRequest(echo.GET, "/", nil) req := test.NewRequest(echo.GET, "/", nil)
rec := httptest.NewRecorder() res := test.NewResponseRecorder()
c := echo.NewContext(req, echo.NewResponse(rec, e), e) c := echo.NewContext(req, res, e)
h := func(c echo.Context) error { h := func(c echo.Context) error {
panic("test") panic("test")
} }
Recover()(h)(c) Recover()(h)(c)
assert.Equal(t, http.StatusInternalServerError, rec.Code) assert.Equal(t, http.StatusInternalServerError, res.Status())
assert.Contains(t, rec.Body.String(), "panic recover") assert.Contains(t, res.Body.String(), "panic recover")
} }

View File

@ -1,75 +0,0 @@
package main
import (
"net/http"
"strconv"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
type (
user struct {
ID int
Name string
}
)
var (
users = map[int]*user{}
seq = 1
)
//----------
// Handlers
//----------
func createUser(c echo.Context) error {
u := &user{
ID: seq,
}
if err := c.Bind(u); err != nil {
return err
}
users[u.ID] = u
seq++
return c.JSON(http.StatusCreated, u)
}
func getUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
return c.JSON(http.StatusOK, users[id])
}
func updateUser(c echo.Context) error {
u := new(user)
if err := c.Bind(u); err != nil {
return err
}
id, _ := strconv.Atoi(c.Param("id"))
users[id].Name = u.Name
return c.JSON(http.StatusOK, users[id])
}
func deleteUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
delete(users, id)
return c.NoContent(http.StatusNoContent)
}
func main() {
e := echo.New()
// Middleware
e.Use(mw.Logger())
e.Use(mw.Recover())
// Routes
e.Post("/users", createUser)
e.Get("/users/:id", getUser)
e.Patch("/users/:id", updateUser)
e.Delete("/users/:id", deleteUser)
// Start server
e.Run(":1323")
}

View File

@ -1,26 +0,0 @@
package main
import (
"net/http"
"github.com/GeertJohan/go.rice"
"github.com/labstack/echo"
)
func main() {
e := echo.New()
// the file server for rice. "app" is the folder where the files come from.
assetHandler := http.FileServer(rice.MustFindBox("app").HTTPBox())
// serves the index.html from rice
e.Get("/", func(c echo.Context) error {
assetHandler.ServeHTTP(c.Response().Writer(), c.Request())
return nil
})
// servers other static files
e.Get("/static/*", func(c echo.Context) error {
http.StripPrefix("/static/", assetHandler).
ServeHTTP(c.Response().Writer(), c.Request())
return nil
})
e.Run(":3000")
}

View File

@ -1,56 +0,0 @@
package main
import (
"fmt"
"io"
"os"
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
func upload(c echo.Context) error {
req := c.Request()
req.ParseMultipartForm(16 << 20) // Max memory 16 MiB
// Read form fields
name := c.Form("name")
email := c.Form("email")
// Read files
files := req.MultipartForm.File["files"]
for _, f := range files {
// Source file
src, err := f.Open()
if err != nil {
return err
}
defer src.Close()
// Destination file
dst, err := os.Create(f.Filename)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
}
return c.String(http.StatusOK, fmt.Sprintf("Thank You! %s <%s>, %d files uploaded successfully.",
name, email, len(files)))
}
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.Post("/upload", upload)
e.Run(":1323")
}

View File

@ -1,54 +0,0 @@
package main
import (
"net/http"
"github.com/labstack/echo"
"github.com/rs/cors"
)
type (
user struct {
ID string `json:"id"`
Name string `json:"name"`
}
)
var (
users map[string]user
)
func init() {
users = map[string]user{
"1": user{
ID: "1",
Name: "Wreck-It Ralph",
},
}
// hook into the echo instance to create an endpoint group
// and add specific middleware to it plus handlers
g := e.Group("/users")
g.Use(cors.Default().Handler)
g.Post("", createUser)
g.Get("", getUsers)
g.Get("/:id", getUser)
}
func createUser(c echo.Context) error {
u := new(user)
if err := c.Bind(u); err != nil {
return err
}
users[u.ID] = *u
return c.JSON(http.StatusCreated, u)
}
func getUsers(c echo.Context) error {
return c.JSON(http.StatusOK, users)
}
func getUser(c echo.Context) error {
return c.JSON(http.StatusOK, users[c.P(0)])
}

View File

@ -1,31 +0,0 @@
package main
import (
"html/template"
"io"
"net/http"
"github.com/labstack/echo"
)
type (
Template struct {
templates *template.Template
}
)
func init() {
t := &Template{
templates: template.Must(template.ParseFiles("templates/welcome.html")),
}
e.SetRenderer(t)
e.Get("/welcome", welcome)
}
func (t *Template) Render(w io.Writer, name string, data interface{}) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func welcome(c echo.Context) error {
return c.Render(http.StatusOK, "welcome", "Joe")
}

View File

@ -1,27 +0,0 @@
package main
import (
"net/http"
"github.com/facebookgo/grace/gracehttp"
"github.com/labstack/echo"
)
func main() {
// Setup
e := echo.New()
e.Get("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Six sick bricks tick")
})
// Get the http.Server
s := e.Server(":1323")
// HTTP2 is currently enabled by default in echo.New(). To override TLS handshake errors
// you will need to override the TLSConfig for the server so it does not attempt to validate
// the connection using TLS as required by HTTP2
s.TLSConfig = nil
// Serve it like a boss
gracehttp.Serve(s)
}

View File

@ -1,19 +0,0 @@
package main
import (
"net/http"
"time"
"github.com/labstack/echo"
"github.com/tylerb/graceful"
)
func main() {
// Setup
e := echo.New()
e.Get("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Sue sews rose on slow joe crows nose")
})
graceful.ListenAndServe(e.Server(":1323"), 5*time.Second)
}

View File

@ -1,28 +0,0 @@
package main
import (
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
// Handler
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
}
func main() {
// Echo instance
e := echo.New()
// Middleware
e.Use(mw.Logger())
e.Use(mw.Recover())
// Routes
e.Get("/", hello)
// Start server
e.Run(":1323")
}

View File

@ -1,31 +0,0 @@
package main
import (
"math/rand"
"net/http"
"time"
"github.com/labstack/echo"
)
func main() {
// Setup
e := echo.New()
e.ServeDir("/", "public")
e.Get("/jsonp", func(c echo.Context) error {
callback := c.Query("callback")
var content struct {
Response string `json:"response"`
Timestamp time.Time `json:"timestamp"`
Random int `json:"random"`
}
content.Response = "Sent via JSONP"
content.Timestamp = time.Now().UTC()
content.Random = rand.Intn(1000)
return c.JSONP(http.StatusOK, callback, &content)
})
// Start server
e.Run(":3999")
}

View File

@ -1,76 +0,0 @@
package main
import (
"fmt"
"net/http"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
const (
Bearer = "Bearer"
SigningKey = "somethingsupersecret"
)
// A JSON Web Token middleware
func JWTAuth(key string) echo.HandlerFunc {
return func(c echo.Context) error {
// Skip WebSocket
if (c.Request().Header.Get(echo.Upgrade)) == echo.WebSocket {
return nil
}
auth := c.Request().Header.Get("Authorization")
l := len(Bearer)
he := echo.NewHTTPError(http.StatusUnauthorized)
if len(auth) > l+1 && auth[:l] == Bearer {
t, err := jwt.Parse(auth[l+1:], func(token *jwt.Token) (interface{}, error) {
// Always check the signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// Return the key for validation
return []byte(key), nil
})
if err == nil && t.Valid {
// Store token claims in echo.Context
c.Set("claims", t.Claims)
return nil
}
}
return he
}
}
func accessible(c echo.Context) error {
return c.String(http.StatusOK, "No auth required for this route.\n")
}
func restricted(c echo.Context) error {
return c.String(http.StatusOK, "Access granted with JWT.\n")
}
func main() {
// Echo instance
e := echo.New()
// Logger
e.Use(mw.Logger())
// Unauthenticated route
e.Get("/", accessible)
// Restricted group
r := e.Group("/restricted")
r.Use(JWTAuth(SigningKey))
r.Get("", restricted)
// Start server
e.Run(":1323")
}

View File

@ -1,48 +0,0 @@
package main
import (
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
// Handler
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
}
func main() {
// Echo instance
e := echo.New()
// Debug mode
e.Debug()
//------------
// Middleware
//------------
// Logger
e.Use(mw.Logger())
// Recover
e.Use(mw.Recover())
// Basic auth
e.Use(mw.BasicAuth(func(usr, pwd string) bool {
if usr == "joe" && pwd == "secret" {
return true
}
return false
}))
// Gzip
e.Use(mw.Gzip())
// Routes
e.Get("/", hello)
// Start server
e.Run(":1323")
}

View File

@ -1,81 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"io"
"net/http"
"os"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
func upload(c echo.Context) error {
mr, err := c.Request().MultipartReader()
if err != nil {
return err
}
// Read form field `name`
part, err := mr.NextPart()
if err != nil {
return err
}
defer part.Close()
b, err := ioutil.ReadAll(part)
if err != nil {
return err
}
name := string(b)
// Read form field `email`
part, err = mr.NextPart()
if err != nil {
return err
}
defer part.Close()
b, err = ioutil.ReadAll(part)
if err != nil {
return err
}
email := string(b)
// Read files
i := 0
for {
part, err := mr.NextPart()
if err != nil {
if err == io.EOF {
break
}
return err
}
defer part.Close()
file, err := os.Create(part.FileName())
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(file, part); err != nil {
return err
}
i++
}
return c.String(http.StatusOK, fmt.Sprintf("Thank You! %s <%s>, %d files uploaded successfully.",
name, email, i))
}
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.Post("/upload", upload)
e.Run(":1323")
}

View File

@ -1,45 +0,0 @@
package main
import (
"net/http"
"time"
"encoding/json"
"github.com/labstack/echo"
)
type (
Geolocation struct {
Altitude float64
Latitude float64
Longitude float64
}
)
var (
locations = []Geolocation{
{-97, 37.819929, -122.478255},
{1899, 39.096849, -120.032351},
{2619, 37.865101, -119.538329},
{42, 33.812092, -117.918974},
{15, 37.77493, -122.419416},
}
)
func main() {
e := echo.New()
e.Get("/", func(c echo.Context) error {
c.Response().Header().Set(echo.ContentType, echo.ApplicationJSON)
c.Response().WriteHeader(http.StatusOK)
for _, l := range locations {
if err := json.NewEncoder(c.Response()).Encode(l); err != nil {
return err
}
c.Response().Flush()
time.Sleep(1 * time.Second)
}
return nil
})
e.Run(":1323")
}

View File

@ -1,67 +0,0 @@
package main
import (
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
type Hosts map[string]http.Handler
func (h Hosts) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if handler := h[r.Host]; handler != nil {
handler.ServeHTTP(w, r)
} else {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
}
func main() {
// Host map
hosts := make(Hosts)
//-----
// API
//-----
api := echo.New()
api.Use(mw.Logger())
api.Use(mw.Recover())
hosts["api.localhost:1323"] = api
api.Get("/", func(c echo.Context) error {
return c.String(http.StatusOK, "API")
})
//------
// Blog
//------
blog := echo.New()
blog.Use(mw.Logger())
blog.Use(mw.Recover())
hosts["blog.localhost:1323"] = blog
blog.Get("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Blog")
})
//---------
// Website
//---------
site := echo.New()
site.Use(mw.Logger())
site.Use(mw.Recover())
hosts["localhost:1323"] = site
site.Get("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Welcome!")
})
http.ListenAndServe(":1323", hosts)
}

View File

@ -1,146 +0,0 @@
package main
import (
"io"
"net/http"
"html/template"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
"github.com/rs/cors"
"github.com/thoas/stats"
)
type (
// Template provides HTML template rendering
Template struct {
templates *template.Template
}
user struct {
ID string `json:"id"`
Name string `json:"name"`
}
)
var (
users map[string]user
)
// Render HTML
func (t *Template) Render(w io.Writer, name string, data interface{}) error {
return t.templates.ExecuteTemplate(w, name, data)
}
//----------
// Handlers
//----------
func welcome(c echo.Context) error {
return c.Render(http.StatusOK, "welcome", "Joe")
}
func createUser(c echo.Context) error {
u := new(user)
if err := c.Bind(u); err != nil {
return err
}
users[u.ID] = *u
return c.JSON(http.StatusCreated, u)
}
func getUsers(c echo.Context) error {
return c.JSON(http.StatusOK, users)
}
func getUser(c echo.Context) error {
return c.JSON(http.StatusOK, users[c.P(0)])
}
func main() {
e := echo.New()
// Middleware
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Use(mw.Gzip())
//------------------------
// Third-party middleware
//------------------------
// https://github.com/rs/cors
e.Use(cors.Default().Handler)
// https://github.com/thoas/stats
s := stats.New()
e.Use(s.Handler)
// Route
e.Get("/stats", func(c echo.Context) error {
return c.JSON(http.StatusOK, s.Data())
})
// Serve index file
e.Index("public/index.html")
// Serve favicon
e.Favicon("public/favicon.ico")
// Serve static files
e.Static("/scripts", "public/scripts")
//--------
// Routes
//--------
e.Post("/users", createUser)
e.Get("/users", getUsers)
e.Get("/users/:id", getUser)
//-----------
// Templates
//-----------
t := &Template{
// Cached templates
templates: template.Must(template.ParseFiles("public/views/welcome.html")),
}
e.SetRenderer(t)
e.Get("/welcome", welcome)
//-------
// Group
//-------
// Group with parent middleware
a := e.Group("/admin")
a.Use(func(c echo.Context) error {
// Security middleware
return nil
})
a.Get("", func(c echo.Context) error {
return c.String(http.StatusOK, "Welcome admin!")
})
// Group with no parent middleware
g := e.Group("/files", func(c echo.Context) error {
// Security middleware
return nil
})
g.Get("", func(c echo.Context) error {
return c.String(http.StatusOK, "Your files!")
})
// Start server
e.Run(":1323")
}
func init() {
users = map[string]user{
"1": user{
ID: "1",
Name: "Wreck-It Ralph",
},
}
}

View File

@ -1,34 +0,0 @@
package main
import (
"fmt"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
"golang.org/x/net/websocket"
)
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.WebSocket("/ws", func(c echo.Context) (err error) {
ws := c.Socket()
msg := ""
for {
if err = websocket.Message.Send(ws, "Hello, Client!"); err != nil {
return
}
if err = websocket.Message.Receive(ws, &msg); err != nil {
return
}
fmt.Println(msg)
}
})
e.Run(":1323")
}

View File

@ -35,7 +35,7 @@ SetLogPrefix sets the prefix for the logger. Default value is `echo`.
`echo#SetLogOutput(w io.Writer)` `echo#SetLogOutput(w io.Writer)`
SetLogOutput sets the output destination for the logger. Default value is `os.Std*` SetLogOutput sets the output destination for the logger. Default value is `os.Stdout`
### Log level ### Log level