1
0
mirror of https://github.com/labstack/echo.git synced 2024-11-24 08:22:21 +02:00

Added gzip middleware

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-05-15 12:29:14 -07:00
parent 4f45cd1517
commit 1ee3bc23e3
15 changed files with 138 additions and 42 deletions

View File

@ -91,7 +91,7 @@ func main() {
e := echo.New() e := echo.New()
// Middleware // Middleware
e.Use(mw.Logger) e.Use(mw.Logger())
// Routes // Routes
e.Get("/", hello) e.Get("/", hello)

View File

@ -53,8 +53,8 @@ func (c *Context) Param(name string) (value string) {
// Bind binds the request body into specified type v. Default binder does it // Bind binds the request body into specified type v. Default binder does it
// based on Content-Type header. // based on Content-Type header.
func (c *Context) Bind(v interface{}) *HTTPError { func (c *Context) Bind(i interface{}) *HTTPError {
return c.echo.binder(c.Request, v) return c.echo.binder(c.Request, i)
} }
// Render invokes the registered HTML template renderer and sends a text/html // Render invokes the registered HTML template renderer and sends a text/html
@ -69,10 +69,10 @@ func (c *Context) Render(code int, name string, data interface{}) *HTTPError {
} }
// JSON sends an application/json response with status code. // JSON sends an application/json response with status code.
func (c *Context) JSON(code int, v interface{}) *HTTPError { func (c *Context) JSON(code int, i interface{}) *HTTPError {
c.Response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8") c.Response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8")
c.Response.WriteHeader(code) c.Response.WriteHeader(code)
if err := json.NewEncoder(c.Response).Encode(v); err != nil { if err := json.NewEncoder(c.Response).Encode(i); err != nil {
return &HTTPError{Error: err} return &HTTPError{Error: err}
} }
return nil return nil

View File

@ -90,7 +90,9 @@ const (
//--------- //---------
Accept = "Accept" Accept = "Accept"
AcceptEncoding = "Accept-Encoding"
ContentDisposition = "Content-Disposition" ContentDisposition = "Content-Disposition"
ContentEncoding = "Content-Encoding"
ContentLength = "Content-Length" ContentLength = "Content-Length"
ContentType = "Content-Type" ContentType = "Content-Type"
Authorization = "Authorization" Authorization = "Authorization"

View File

@ -61,7 +61,7 @@ func main() {
e := echo.New() e := echo.New()
// Middleware // Middleware
e.Use(mw.Logger) e.Use(mw.Logger())
// Routes // Routes
e.Post("/users", createUser) e.Post("/users", createUser)

View File

@ -17,7 +17,7 @@ func main() {
e := echo.New() e := echo.New()
// Middleware // Middleware
e.Use(mw.Logger) e.Use(mw.Logger())
// Routes // Routes
e.Get("/", hello) e.Get("/", hello)

View File

@ -21,7 +21,7 @@ func main() {
//------------ //------------
// Logger // Logger
e.Use(mw.Logger) e.Use(mw.Logger())
// Basic auth // Basic auth
e.Use(mw.BasicAuth(func(u, p string) bool { e.Use(mw.BasicAuth(func(u, p string) bool {
@ -41,6 +41,9 @@ func main() {
// e.Use(mw.RedirectToSlash()) // e.Use(mw.RedirectToSlash())
// Gzip
e.Use(mw.Gzip())
// Routes // Routes
e.Get("/", hello) e.Get("/", hello)

View File

@ -65,7 +65,7 @@ func main() {
e := echo.New() e := echo.New()
// Middleware // Middleware
e.Use(mw.Logger) e.Use(mw.Logger())
//------------------------ //------------------------
// Third-party middleware // Third-party middleware

View File

@ -14,7 +14,7 @@ const (
Basic = "Basic" Basic = "Basic"
) )
// BasicAuth provides HTTP basic authentication middleware. // BasicAuth provides HTTP basic authentication.
func BasicAuth(fn AuthFunc) echo.HandlerFunc { func BasicAuth(fn AuthFunc) echo.HandlerFunc {
return func(c *echo.Context) (he *echo.HTTPError) { return func(c *echo.Context) (he *echo.HTTPError) {
auth := c.Request.Header.Get(echo.Authorization) auth := c.Request.Header.Get(echo.Authorization)

43
middleware/compress.go Normal file
View File

@ -0,0 +1,43 @@
package middleware
import (
"compress/gzip"
"io"
"strings"
"github.com/labstack/echo"
)
type (
gzipResponseWriter struct {
io.Writer
*echo.Response
}
)
func (g gzipResponseWriter) Write(b []byte) (int, error) {
return g.Writer.Write(b)
}
// Gzip 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) *echo.HTTPError {
if !strings.Contains(c.Request.Header.Get(echo.AcceptEncoding), scheme) {
return nil
}
w := gzip.NewWriter(c.Response.Writer)
defer w.Close()
gw := gzipResponseWriter{Writer: w, Response: c.Response}
c.Response.Header().Set(echo.ContentEncoding, scheme)
c.Response = &echo.Response{Writer: gw}
if he := h(c); he != nil {
c.Error(he)
}
return nil
}
}
}

View File

@ -0,0 +1,42 @@
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"compress/gzip"
"github.com/labstack/echo"
"io/ioutil"
)
func TestGzip(t *testing.T) {
req, _ := http.NewRequest(echo.GET, "/", nil)
req.Header.Set(echo.AcceptEncoding, "gzip")
w := httptest.NewRecorder()
res := &echo.Response{Writer: w}
c := echo.NewContext(req, res, echo.New())
Gzip()(func(c *echo.Context) *echo.HTTPError {
return c.String(http.StatusOK, "test")
})(c)
if w.Header().Get(echo.ContentEncoding) != "gzip" {
t.Errorf("expected Content-Encoding: gzip, got %d", w.Header().Get(echo.ContentEncoding))
}
r, err := gzip.NewReader(w.Body)
defer r.Close()
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadAll(r)
if err != nil {
t.Error(err)
}
s := string(b)
if s != "test" {
t.Errorf(`expected "test", got "%s"`, s)
}
}

View File

@ -8,28 +8,34 @@ import (
"github.com/labstack/gommon/color" "github.com/labstack/gommon/color"
) )
func Logger(h echo.HandlerFunc) echo.HandlerFunc { func Logger() echo.MiddlewareFunc {
return func(c *echo.Context) *echo.HTTPError { return func(h echo.HandlerFunc) echo.HandlerFunc {
start := time.Now() return func(c *echo.Context) *echo.HTTPError {
if he := h(c); he != nil { start := time.Now()
c.Error(he) if he := h(c); he != nil {
} c.Error(he)
end := time.Now() }
m := c.Request.Method end := time.Now()
p := c.Request.URL.Path method := c.Request.Method
n := c.Response.Status() path := c.Request.URL.Path
col := color.Green if path == "" {
path = "/"
}
size := c.Response.Size()
switch { n := c.Response.Status()
case n >= 500: code := color.Green(n)
col = color.Red switch {
case n >= 400: case n >= 500:
col = color.Yellow code = color.Red(n)
case n >= 300: case n >= 400:
col = color.Cyan code = color.Yellow(n)
} case n >= 300:
code = color.Cyan(n)
}
log.Printf("%s %s %s %s", m, p, col(n), end.Sub(start)) log.Printf("%s %s %s %s %d", method, path, code, end.Sub(start), size)
return nil return nil
}
} }
} }

View File

@ -10,8 +10,8 @@ import (
type ( type (
Response struct { Response struct {
Writer http.ResponseWriter Writer http.ResponseWriter
status int status int
size int size uint64
committed bool committed bool
} }
) )
@ -20,20 +20,20 @@ func (r *Response) Header() http.Header {
return r.Writer.Header() return r.Writer.Header()
} }
func (r *Response) WriteHeader(n int) { func (r *Response) WriteHeader(code int) {
if r.committed { if r.committed {
// TODO: Warning // TODO: Warning
log.Printf("echo: %s", color.Yellow("response already committed")) log.Printf("echo: %s", color.Yellow("response already committed"))
return return
} }
r.status = n r.status = code
r.Writer.WriteHeader(n) r.Writer.WriteHeader(code)
r.committed = true r.committed = true
} }
func (r *Response) Write(b []byte) (n int, err error) { func (r *Response) Write(b []byte) (n int, err error) {
n, err = r.Writer.Write(b) n, err = r.Writer.Write(b)
r.size += n r.size += uint64(n)
return n, err return n, err
} }
@ -41,7 +41,7 @@ func (r *Response) Status() int {
return r.status return r.status
} }
func (r *Response) Size() int { func (r *Response) Size() uint64 {
return r.size return r.size
} }

View File

@ -163,9 +163,9 @@ h := func(*echo.Context) *HTTPError {
e.Get("/users/:id", h) e.Get("/users/:id", h)
``` ```
## (Middleware)[https://github.com/labstack/echo/tree/master/examples/middleware] ## Middleware
*WIP* [*WIP*](https://github.com/labstack/echo/tree/master/examples/middleware)
## Response ## Response

View File

@ -66,7 +66,7 @@ func main() {
e := echo.New() e := echo.New()
// Middleware // Middleware
e.Use(mw.Logger) e.Use(mw.Logger())
// Routes // Routes
e.Get("/", hello) e.Get("/", hello)
@ -78,7 +78,7 @@ func main() {
`echo.New()` returns a new instance of Echo. `echo.New()` returns a new instance of Echo.
`e.Use(mw.Logger)` adds logging middleware to the chain. It logs every HTTP request `e.Use(mw.Logger())` adds logging middleware to the chain. It logs every HTTP request
made to the server, producing output made to the server, producing output
```sh ```sh