1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-12 01:22:21 +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.
func (c *context) Query(name string) string {
if c.query == nil {
// TODO: v2
// c.query = c.request.URL.Query()
}
return c.query.Get(name)
return c.request.URL().QueryValue(name)
}
// Form returns form parameter by name.
func (c *context) Form(name string) string {
// TODO: v2
// return c.request.FormValue(name)
return ""
return c.request.FormValue(name)
}
// Get retrieves data from the context.

View File

@ -192,7 +192,7 @@ func TestContext(t *testing.T) {
// File
rec = test.NewResponseRecorder()
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) {
assert.Equal(t, http.StatusOK, rec.Status())
assert.Equal(t, 219885, rec.Body.Len())
@ -201,7 +201,7 @@ func TestContext(t *testing.T) {
// File as attachment
rec = test.NewResponseRecorder()
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) {
assert.Equal(t, http.StatusOK, rec.Status())
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/standard"
"github.com/labstack/gommon/log"
"golang.org/x/net/http2"
)
type (
@ -237,7 +236,7 @@ func (e *Echo) SetLogPrefix(prefix string) {
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) {
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) {
// fs := http.Dir(dir)
// f, err := fs.Open(file)
// if err != nil {
// return NewHTTPError(http.StatusNotFound)
// }
// defer f.Close()
fs := http.Dir(dir)
f, err := fs.Open(file)
if err != nil {
return NewHTTPError(http.StatusNotFound)
}
defer f.Close()
// fi, _ := f.Stat()
// if fi.IsDir() {
// /* NOTE:
// Not checking the Last-Modified header as it caches the response `304` when
// changing differnt directories for the same path.
// */
// d := f
fi, _ := f.Stat()
if fi.IsDir() {
/* NOTE:
Not checking the Last-Modified header as it caches the response `304` when
changing differnt directories for the same path.
*/
d := f
// // Index file
// file = filepath.Join(file, indexPage)
// f, err = fs.Open(file)
// if err != nil {
// if e.autoIndex {
// // Auto index
// return listDir(d, c)
// }
// return NewHTTPError(http.StatusForbidden)
// }
// fi, _ = f.Stat() // Index file stat
// }
// Index file
file = filepath.Join(file, indexPage)
f, err = fs.Open(file)
if err != nil {
if e.autoIndex {
// Auto index
return listDir(d, c)
}
return NewHTTPError(http.StatusForbidden)
}
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)
return
}
@ -513,39 +515,38 @@ func (e *Echo) Routes() []Route {
return e.router.routes
}
// ServeHTTP implements `http.Handler` interface, which serves HTTP requests.
func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO: v2
// if e.hook != nil {
// e.hook(w, r)
// }
//
// c := e.pool.Get().(*context)
// h, e := e.router.Find(r.Method, r.URL.Path, c)
// c.reset(r, w, e)
//
// // Chain middleware with handler in the end
// for i := len(e.middleware) - 1; i >= 0; i-- {
// h = e.middleware[i](h)
// }
//
// // Execute chain
// if err := h(c); err != nil {
// e.httpErrorHandler(err, c)
// }
//
// e.pool.Put(c)
// ServeHTTP serves HTTP requests.
func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) {
if e.hook != nil {
e.hook(req, res)
}
c := e.pool.Get().(*context)
h, e := e.router.Find(req.Method(), req.URL().Path(), c)
c.reset(req, res, e)
// Chain middleware with handler in the end
for i := len(e.middleware) - 1; i >= 0; i-- {
h = e.middleware[i](h)
}
// Execute chain
if err := h(c); err != nil {
e.httpErrorHandler(err, c)
}
e.pool.Put(c)
}
// Server returns the internal *http.Server.
func (e *Echo) Server(addr string) *http.Server {
s := &http.Server{Addr: addr, Handler: e}
// TODO: Remove in Go 1.6+
if e.http2 {
http2.ConfigureServer(s, nil)
}
return s
}
// func (e *Echo) Server(addr string) *http.Server {
// s := &http.Server{Addr: addr, Handler: e}
// // TODO: Remove in Go 1.6+
// if e.http2 {
// http2.ConfigureServer(s, nil)
// }
// return s
// }
func (e *Echo) SetEngine(t engine.Type) {
e.engineType = t
@ -589,32 +590,32 @@ func (e *Echo) Run(address string) {
// RunTLS runs a server with TLS configuration.
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.
func (e *Echo) RunServer(s *http.Server) {
e.run(s)
// e.run(s)
}
// RunTLSServer runs a custom server with TLS configuration.
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) {
s.Handler = e
// TODO: Remove in Go 1.6+
if e.http2 {
http2.ConfigureServer(s, nil)
}
if len(files) == 0 {
e.logger.Fatal(s.ListenAndServe())
} else if len(files) == 2 {
e.logger.Fatal(s.ListenAndServeTLS(files[0], files[1]))
} else {
e.logger.Fatal("invalid TLS configuration")
}
// s.Handler = e
// // TODO: Remove in Go 1.6+
// if e.http2 {
// http2.ConfigureServer(s, nil)
// }
// if len(files) == 0 {
// e.logger.Fatal(s.ListenAndServe())
// } else if len(files) == 2 {
// e.logger.Fatal(s.ListenAndServeTLS(files[0], files[1]))
// } else {
// e.logger.Fatal("invalid TLS configuration")
// }
}
func NewHTTPError(code int, msg ...string) *HTTPError {

View File

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

View File

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

View File

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

View File

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

View File

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