jhoss
jhoss

Reputation: 487

How enable gzip compression middleware in go-chi

How can I enable gzip compression using the gzip middleware of the go-chi framework?

Try using the example shown here:

https://github.com/go-chi/chi/issues/204

but when I check with curl, I get this:

$ curl -H "Accept-Encoding: gzip" -I http://127.0.0.1:3333
HTTP/1.1 405 Method Not Allowed
Date: Sat, 31 Aug 2019 19:06:39 GMT

I tried the code "hello world":

package main

import (
    "net/http"

    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
)

func main() {
    r := chi.NewRouter()
    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    //r.Use(middleware.DefaultCompress) //using this produces the same result
    r.Use(middleware.Compress(5, "gzip"))
    r.Get("/", Hello)
    http.ListenAndServe(":3333", r)
}

func Hello(w http.ResponseWriter, r *http.Request){
    w.Header().Set("Content-Type", "text/html") //according to the documentation this must be here to enable gzip
    w.Write([]byte("hello world\n"))
}

but when I try to verify with curl, the result is the same

$ curl -H "Accept-Encoding: gzip" -I http://127.0.0.1:3333
HTTP/1.1 405 Method Not Allowed
Date: Sat, 31 Aug 2019 19:06:39 GMT

what's going on?

Upvotes: 0

Views: 6479

Answers (4)

codewarrior
codewarrior

Reputation: 981

The problem is the second parameter is the type of content in response body that you want to be compressed, leaving it empty falls back to the defaultCompressibleContentTypes. Also, the implementation of this Compress middleware only accepts deflate and gzip in Accept-Encoding, usually there are br and zstd, those 2 are not supported but can be easily extended like this:

func newCompressMiddleware(level int) func(next http.Handler) http.Handler {
    compressor := middleware.NewCompressor(level)

    // brotli encoder
    compressor.SetEncoder("br", func(w io.Writer, level int) io.Writer {
        brWriter, err := brotli.NewWriterLevel(w, level)
        if err != nil {
            return nil
        }
        return brWriter
    })

    // zstd encoder
    compressor.SetEncoder("zstd", func(w io.Writer, level int) io.Writer {
        zstdWriter, err := zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedDefault))
        if err != nil {
            return nil
        }
        return zstdWriter
    })

    return compressor.Handler
}

Upvotes: 0

Mikel Pascual
Mikel Pascual

Reputation: 2211

The other answers are outdated now. I had to solve this myself, so here what I found out.

Your error is here:

r.Use(middleware.Compress(5, "gzip"))

The second argument ("types") refers to the content types that the compression will be applied to. For example: "text/html", "application/json", etc

Just add a list of the content-types you want to compress, or remove the argument altogether:

func main() {
    r := chi.NewRouter()
    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    r.Use(middleware.Compress(5))
    r.Get("/", Hello)
    http.ListenAndServe(":3333", r)
}

This will compress all the content-types defined in the default list from middleware.Compress:

var defaultCompressibleContentTypes = []string{
    "text/html",
    "text/css",
    "text/plain",
    "text/javascript",
    "application/javascript",
    "application/x-javascript",
    "application/json",
    "application/atom+xml",
    "application/rss+xml",
    "image/svg+xml",
}

Good luck!

Upvotes: 14

Marco Borromeo
Marco Borromeo

Reputation: 428

r.Use(middleware.DefaultCompress) has now been marked as DEPRECATED.

To enable compression you need to create a compressor, and use its handler.

r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.Logger)

compressor := middleware.NewCompressor(flate.DefaultCompression)
r.Use(compressor.Handler())

r.Get("/", Hello)
http.ListenAndServe(":3333", r)

The flate package must be imported as compress/flate.

Upvotes: 8

Pablo Lalloni
Pablo Lalloni

Reputation: 2755

Use the commented middleware.DefaultCompress and a normal GET request.

package main

import (
    "net/http"

    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
)

func main() {
    r := chi.NewRouter()
    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    r.Use(middleware.DefaultCompress)
    r.Get("/", Hello)
    http.ListenAndServe(":3333", r)
}

func Hello(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/html")
    w.Write([]byte("hello world\n"))
}

Try with curl:

$ curl -v http://localhost:3333 --compressed
* Rebuilt URL to: http://localhost:3333/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3333 (#0)
> GET / HTTP/1.1
> Host: localhost:3333
> User-Agent: curl/7.58.0
> Accept: */*
> Accept-Encoding: deflate, gzip
>
< HTTP/1.1 200 OK
< Content-Encoding: gzip
< Content-Type: text/html
< Date: Sat, 31 Aug 2019 23:37:52 GMT
< Content-Length: 36
<
hello world
* Connection #0 to host localhost left intact

Or HTTPie:

$ http :3333
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 36
Content-Type: text/html
Date: Sat, 31 Aug 2019 23:38:31 GMT

hello world

Upvotes: 0

Related Questions