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:
parent
dbd1e8e230
commit
688293b5ed
10
context.go
10
context.go
@ -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.
|
||||
|
@ -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
145
echo.go
@ -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 {
|
||||
|
75
echo_test.go
75
echo_test.go
@ -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
59
engine/engine.go
Normal 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
46
engine/fasthttp/header.go
Normal 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)
|
||||
}
|
45
engine/fasthttp/request.go
Normal file
45
engine/fasthttp/request.go
Normal 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 ""
|
||||
}
|
40
engine/fasthttp/response.go
Normal file
40
engine/fasthttp/response.go
Normal 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
43
engine/fasthttp/server.go
Normal 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
29
engine/fasthttp/url.go
Normal 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
25
engine/standard/header.go
Normal 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)
|
||||
}
|
56
engine/standard/request.go
Normal file
56
engine/standard/request.go
Normal 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)
|
||||
}
|
53
engine/standard/response.go
Normal file
53
engine/standard/response.go
Normal 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
32
engine/standard/server.go
Normal 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
41
engine/standard/url.go
Normal 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)
|
||||
}
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)])
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
@ -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",
|
||||
},
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user