Skip to content

Instantly share code, notes, and snippets.

@bryfry
Last active July 25, 2024 03:38
Show Gist options
  • Save bryfry/09a650eb8aac0fb76c24 to your computer and use it in GitHub Desktop.
Save bryfry/09a650eb8aac0fb76c24 to your computer and use it in GitHub Desktop.
Idiomatic golang net/http gzip transparent compression (works with Alice)
package main
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
// Gzip Compression
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func Gzip(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
handler.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
handler.ServeHTTP(gzw, r)
})
}
@roustem
Copy link

roustem commented Sep 25, 2015

Missing return after line 23?

@janhalfar
Copy link

yes, that return seems to be missing ;)

@nahidakbar
Copy link

Content negotiation is overrated. Make way for CONTENT DICTATORSHIP!

@mappu
Copy link

mappu commented Jul 31, 2017

This gist is currently the second google result for "golang gzip http" and the first google result for some related terms.

About content-length

If your upstream writer sets Content-Length, this will result in the wrong (smaller) length being passed to the client. The middleware should remove any Content-Length header set by the upstream writer.

  • This cannot be done in the Gzip() function since the handler hasn't been called yet.
  • You could either overwrite WriteHeader, or, check if headers have been sent in the first call to Write and remove the Content-Length header then.
  • Tracking state of this process necessitates converting from value receiver to pointer receiver.

About sync.Pool

Performance of regularly creating gzip writers is non-optimal. The gzip writer should be kept in a sync.Pool for reuse to minimise GC pressure.

@ahmetb
Copy link

ahmetb commented Mar 27, 2018

I reached out to the owner of the gist to fix the update.

@novalagung
Copy link

Is there any further example of this gzip compression that also using the sync.Pool as per @mappu said ?

@CJEnright
Copy link

Here's an updated version if anyone's interested: https://gist.github.com/CJEnright/bc2d8b8dc0c1389a9feeddb110f822d7

@lyda
Copy link

lyda commented Jun 23, 2019

In the case of serving a file, would it be an option to look for an already compressed version of the file?

@jftuga
Copy link

jftuga commented Apr 16, 2020

For max compression:

gzip.NewWriterLevel(w, gzip.BestCompression)

@cli-ish
Copy link

cli-ish commented Apr 29, 2022

Content negotiation is overrated. Make way for CONTENT DICTATORSHIP!

Pure gold, was just seeking for a solution and found this 😄

@TelephoneTan
Copy link

TelephoneTan commented Sep 1, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment