123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- package middleware
- import (
- "bufio"
- "compress/gzip"
- "io"
- "io/ioutil"
- "net"
- "net/http"
- "strings"
- "github.com/labstack/echo"
- )
- type (
- // GzipConfig defines the config for Gzip middleware.
- GzipConfig struct {
- // Skipper defines a function to skip middleware.
- Skipper Skipper
- // Gzip compression level.
- // Optional. Default value -1.
- Level int `yaml:"level"`
- }
- gzipResponseWriter struct {
- io.Writer
- http.ResponseWriter
- }
- )
- const (
- gzipScheme = "gzip"
- )
- var (
- // DefaultGzipConfig is the default Gzip middleware config.
- DefaultGzipConfig = GzipConfig{
- Skipper: DefaultSkipper,
- Level: -1,
- }
- )
- // Gzip returns a middleware which compresses HTTP response using gzip compression
- // scheme.
- func Gzip() echo.MiddlewareFunc {
- return GzipWithConfig(DefaultGzipConfig)
- }
- // GzipWithConfig return Gzip middleware with config.
- // See: `Gzip()`.
- func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
- // Defaults
- if config.Skipper == nil {
- config.Skipper = DefaultGzipConfig.Skipper
- }
- if config.Level == 0 {
- config.Level = DefaultGzipConfig.Level
- }
- return func(next echo.HandlerFunc) echo.HandlerFunc {
- return func(c echo.Context) error {
- if config.Skipper(c) {
- return next(c)
- }
- res := c.Response()
- res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
- if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
- res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
- rw := res.Writer
- w, err := gzip.NewWriterLevel(rw, config.Level)
- if err != nil {
- return err
- }
- defer func() {
- if res.Size == 0 {
- if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
- res.Header().Del(echo.HeaderContentEncoding)
- }
- // We have to reset response to it's pristine state when
- // nothing is written to body or error is returned.
- // See issue #424, #407.
- res.Writer = rw
- w.Reset(ioutil.Discard)
- }
- w.Close()
- }()
- grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
- res.Writer = grw
- }
- return next(c)
- }
- }
- }
- func (w *gzipResponseWriter) WriteHeader(code int) {
- if code == http.StatusNoContent { // Issue #489
- w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
- }
- w.Header().Del(echo.HeaderContentLength) // Issue #444
- w.ResponseWriter.WriteHeader(code)
- }
- func (w *gzipResponseWriter) Write(b []byte) (int, error) {
- if w.Header().Get(echo.HeaderContentType) == "" {
- w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
- }
- return w.Writer.Write(b)
- }
- func (w *gzipResponseWriter) Flush() {
- w.Writer.(*gzip.Writer).Flush()
- }
- func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- return w.ResponseWriter.(http.Hijacker).Hijack()
- }
- func (w *gzipResponseWriter) CloseNotify() <-chan bool {
- return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
- }
|