-
-
Save the42/1956518 to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"compress/gzip" | |
"io" | |
"net/http" | |
"strings" | |
) | |
type gzipResponseWriter struct { | |
io.Writer | |
http.ResponseWriter | |
} | |
func (w gzipResponseWriter) Write(b []byte) (int, error) { | |
return w.Writer.Write(b) | |
} | |
func makeGzipHandler(fn http.HandlerFunc) http.HandlerFunc { | |
return func(w http.ResponseWriter, r *http.Request) { | |
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { | |
fn(w, r) | |
return | |
} | |
w.Header().Set("Content-Encoding", "gzip") | |
gz := gzip.NewWriter(w) | |
defer gz.Close() | |
gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w} | |
fn(gzr, r) | |
} | |
} | |
func handler(w http.ResponseWriter, r *http.Request) { | |
w.Header().Set("Content-Type", "text/plain") | |
w.Write([]byte("This is a test.")) | |
} | |
func main() { | |
http.ListenAndServe(":1113", makeGzipHandler(handler)) | |
} |
In case you just want to encode a JSON (compressed or not, based on a flag):
func handler(w http.ResponseWriter, r *http.Request) {
response := (THIS IS MY RESPONSE STRUCT)
w.Header().Set("Content-Type", "application/json")
if settings.COMPRESS_RESPONSE { // this is a global flag I defined
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
json.NewEncoder(gz).Encode(response)
gz.Close()
} else {
json.NewEncoder(w).Encode(response)
}
}
I am very curious about these two parts:
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
gzipResponseWriter{Writer: gz, ResponseWriter: w}
So the two interfaces embedded in the struct (not so common?) make it satisfy all the http.ResponseWriter methods, but isn't the io.Writer method exactly the same as the http.ResponseWriter's since it IS an io.Writer?
Also would you mind sharing where I can get more details in the spec about referencing an embedded anonymous field by the type name?
A struct field never has no name at all. In the case of embedding, it re-uses the type name as the field name, which becomes really apparent when accessing embedded struct fields and reflection.
Since a stackoverflow article links here and likey gets some traffic, I figured I would warn people that the .Close on the gzip writer writes some kind of footer, and if the response from the handler needs to be a 304 or 204 etc then that will break.
As far as I can see in the spec http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7 its ok to leave the Content-Encoding header in the response but there should not be any body.
Also, the error from gz.Close() isn't being checked, which leads to mysterious failures like golang/go#14975 (comment). If you use this, make sure to check that error.
A added a new commit to my fork that fixes some issues: https://gist.github.com/erikdubbelboer/7df2b2b9f34f9f839a84
The issue from @optimality about not handling gz.Close
errors still stands as everyone should handle this in their own way since http.HandlerFunc
can't return errors.
License?
I had problems with this for responses that didn't explicitly set Content-Type. They were being auto-detected by the default ResponseWriter as 'Content-Type: application/x-gzip'. A simple fix is to define Write as: