84fc6bf873 | ||
---|---|---|
.. | ||
.gitignore | ||
LICENSE | ||
README.Chinese.md | ||
README.md | ||
_config.yml | ||
doc.go | ||
handler.go | ||
requestfilters.go | ||
responsefilters.go | ||
writerwrapper.go |
README.md
English | 中文
Gzip Middleware for Go
An out-of-the-box, also customizable gzip middleware for Gin and net/http.
Examples
Use DefaultHandler()
to create a ready-to-go gzip middleware.
Gin
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)))
}
net/http
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")
}
Customize Handler
Handler can be customized using NewHandler
and Config
:
import github.com/nanmu42/gzip
handler := gzip.NewHandler(gzip.Config{
// gzip compression level to use
CompressionLevel: 6,
// minimum content length to trigger gzip, the unit is in byte.
MinContentLength: 1024,
// RequestFilter decide whether or not to compress response judging by request.
// Filters are applied in the sequence here.
RequestFilter: []RequestFilter{
NewCommonRequestFilter(),
DefaultExtensionFilter(),
},
// ResponseHeaderFilter decide whether or not to compress response
// judging by response header
ResponseHeaderFilter: []ResponseHeaderFilter{
NewSkipCompressedFilter(),
DefaultContentTypeFilter(),
},
})
RequestFilter
and ResponseHeaderFilter
are interfaces.
You may define one that specially suits your need.
Performance
- When response payload is small, the handler is smart enough to skip compression automatically, which takes neglectable overhead.
- At the time when the payload is big enough, gzip kicks in and there is a reasonable price.
$ go test -benchmem -bench .
goos: linux
goarch: amd64
pkg: github.com/nanmu42/gzip
BenchmarkSoleGin_SmallPayload-4 4104684 276 ns/op 64 B/op 2 allocs/op
BenchmarkGinWithDefaultHandler_SmallPayload-4 1683307 707 ns/op 96 B/op 3 allocs/op
BenchmarkSoleGin_BigPayload-4 4198786 274 ns/op 64 B/op 2 allocs/op
BenchmarkGinWithDefaultHandler_BigPayload-4 44780 27636 ns/op 190 B/op 5 allocs/op
PASS
ok github.com/nanmu42/gzip 6.373s
Note: due to an awkward man-mistake, benchmark of and before v1.0.0
are not accurate.
Limitation
- You should always provide a
Content-Type
in http response's header, though handler guesses byhttp.DetectContentType()
as makeshift; - When
Content-Length
is not available, handler may buffer your writes to decide if its big enough to do a meaningful compression. A highMinContentLength
may bring memory overhead, although the handler tries to be smart by reusing buffers and testing iflen(data)
of the firsthttp.ResponseWriter.Write(data []byte)
calling suffices or not.
Status: Stable
All APIs are finalized, and no breaking changes will be made in the 1.x series of releases.
Acknowledgement
During the development of this work, the author took following works/materials as reference:
- https://github.com/caddyserver/caddy/tree/master/caddyhttp/gzip (Apache License 2.0)
- https://github.com/gin-contrib/gzip (MIT License)
- https://blog.cloudflare.com/results-experimenting-brotli/
- https://support.cloudflare.com/hc/en-us/articles/200168396-What-will-Cloudflare-compress-
This package uses klauspost's compress package to handle gzip compression.
Logo generated at Gopherize.me.
License
MIT License
Copyright (c) 2019 LI Zhennan
Caddy is licensed under the Apache License
Copyright 2015 Light Code Labs, LLC