Go and HTTP Compression
This is a short article where I’d like to demonstrate a simple technique where an http.ResponseWriter is overloaded so that it may write a gzipped response instead of say, HTML.
Lo and behold.
package web
import (
“compress/gzip”
“io”
“net/http”
“strings”
)
type CompressWriter struct {
io.Writer
http.ResponseWriter
}func NewCompressWriter(w http.ResponseWriter) CompressWriter {
w.Header().Set(“Content-Encoding”, “gzip”)
return CompressWriter{gzip.NewWriter(w), w}
}func (w CompressWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}func CompressHandler(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get(“Accept-Encoding”), “gzip”) {
handler(w, r)
return
}cw := NewCompressWriter(w)
defer cw.Writer.(*gzip.Writer).Close()
handler(cw, r)
}
}
The trick is to create a struct that holds both the original http.ResponseWriter and a new gzip.Writer while redirecting the writes on the response to the gzip stream.
The only thing you must be aware of is that you should explicitly set the Content-Type of your handlers because if you don’t, the http package will detect the gzip stream and think you mean to send an actual compressed file.
Even though we can create middleware for Go, we cannot change the underlying writer of the response, instead we have to overload and redirect.
In effect, we now have a response that writes to our gzip stream, whose content is written to the underlying writer of the response.
Original idea found at https://gist.github.com/the42/1956518