46

I'm starting to write server-side applications in Go. I'd like to use the Accept-Encoding request header to determine whether to compress the response entity using GZIP. I had hoped to find a way to do this directly using the http.Serve or http.ServeFile methods.

This is quite a general requirement; did I miss something or do I need to roll my own solution?

Rick-777
  • 9,714
  • 5
  • 34
  • 50
  • 5
    Example of a homebrew solution: https://gist.github.com/982674#file-webserver-go-L191 – miku Dec 28 '12 at 17:36

4 Answers4

29

The New York Times have released their gzip middleware package for Go.

You just pass your http.HandlerFunc through their GzipHandler and you're done. It looks like this:

package main

import (
    "io"
    "net/http"
    "github.com/nytimes/gziphandler"
)

func main() {
    withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/plain")
        io.WriteString(w, "Hello, World")
    })

    withGz := gziphandler.GzipHandler(withoutGz)

    http.Handle("/", withGz)
    http.ListenAndServe("0.0.0.0:8000", nil)
}
Zippo
  • 15,850
  • 10
  • 60
  • 58
28

There is no “out of the box” support for gzip-compressed HTTP responses yet. But adding it is pretty trivial. Have a look at

https://gist.github.com/the42/1956518

also

https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/cgUp8_ATNtc

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
rputikar
  • 1,523
  • 13
  • 14
  • 7
    It would also be 'nice to have' if the http.ServeFile method would check for the presence of xxx.gz and serve it if present and the accept header includes gzip. This is a trick that nginx does and it greatly accelerates the serving of static css, js etc files because they can be compressed just once during the build process. There's the added benefit too that they are smaller files to read, as well as needing fewer bytes on the wire. – Rick-777 Dec 29 '12 at 00:06
  • 1
    I believe the Gist, as well as the httpgzip package, might break on partial responses ([GitHub issue](https://github.com/daaku/go.httpgzip/issues/2)) and would need a Vary header ([GitHub issue](https://github.com/daaku/go.httpgzip/issues/3)). – Jo Liss Sep 29 '13 at 16:04
2

For the sake of completeness, I eventually answered my own question with a handler that is simple and specialises in solving this issue.

This serves static files from a Go http server, including the asked-for performance-enhancing features. It is based on the standard net/http ServeFiles, with gzip/brotli and cache performance enhancements.

Rick-777
  • 9,714
  • 5
  • 34
  • 50
0

There is yet another "out of the box" middleware now, supporting net/http and Gin:

https://github.com/nanmu42/gzip

net/http example:

import github.com/nanmu42/gzip

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        writeString(w, fmt.Sprintf("This content is compressed: l%sng!", strings.Repeat("o", 1000)))
    })

    // wrap http.Handler using default settings
    log.Println(http.ListenAndServe(fmt.Sprintf(":%d", 3001), gzip.DefaultHandler().WrapHandler(mux)))
}

func writeString(w http.ResponseWriter, payload string) {
    w.Header().Set("Content-Type", "text/plain; charset=utf8")
    _, _ = io.WriteString(w, payload+"\n")
}

Gin example:

import github.com/nanmu42/gzip

func main() {
    g := gin.Default()

    // use default settings
    g.Use(gzip.DefaultHandler().Gin)

    g.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, map[string]interface{}{
            "code": 0,
            "msg":  "hello",
            "data": fmt.Sprintf("l%sng!", strings.Repeat("o", 1000)),
        })
    })

    log.Println(g.Run(fmt.Sprintf(":%d", 3000)))
}
nanmu42
  • 1
  • 1