mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 04:34:23 -06:00
51c94bccd7
This PR adds /api/gnet to the list of ignored paths in the gzip middleware. Without this, when gzip is enabled (`server.enable_gzip = true`), responses from the gnet proxy are double compressed: once by grafana.com and once by Grafana itself. With this change we only do one round of compression for these endpoints. To test this out, try a request like this with `server.enable_gzip = true` (after setting `GCOM_TOKEN` to a valid grafana.com token; you may need to change the 'bsull' slug, too): curl -v --user admin:admin \ -H "X-Api-Key: $GCOM_TOKEN" \ -H 'Accept-Encoding: gzip' \ localhost:3000/api/gnet/instances/bsull/provisioned-plugins/grafana-ml-app | gzip -d Note that there are two Content-Encoding: gzip headers before this PR, and the output is still compressed even after the `gzip -d`. After this PR things look as expected.
82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"bufio"
|
|
"compress/gzip"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
type gzipResponseWriter struct {
|
|
w *gzip.Writer
|
|
web.ResponseWriter
|
|
}
|
|
|
|
func (grw *gzipResponseWriter) WriteHeader(c int) {
|
|
grw.Header().Del("Content-Length")
|
|
grw.ResponseWriter.WriteHeader(c)
|
|
}
|
|
|
|
func (grw gzipResponseWriter) Write(p []byte) (int, error) {
|
|
if grw.Header().Get("Content-Type") == "" {
|
|
grw.Header().Set("Content-Type", http.DetectContentType(p))
|
|
}
|
|
grw.Header().Del("Content-Length")
|
|
return grw.w.Write(p)
|
|
}
|
|
|
|
func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
if hijacker, ok := grw.ResponseWriter.(http.Hijacker); ok {
|
|
return hijacker.Hijack()
|
|
}
|
|
return nil, nil, fmt.Errorf("GZIP ResponseWriter doesn't implement the Hijacker interface")
|
|
}
|
|
|
|
type matcher func(s string) bool
|
|
|
|
func prefix(p string) matcher { return func(s string) bool { return strings.HasPrefix(s, p) } }
|
|
func substr(p string) matcher { return func(s string) bool { return strings.Contains(s, p) } }
|
|
|
|
var gzipIgnoredPaths = []matcher{
|
|
prefix("/api/datasources"),
|
|
prefix("/api/plugins"),
|
|
prefix("/api/plugin-proxy/"),
|
|
prefix("/api/gnet/"), // Already gzipped by grafana.com.
|
|
prefix("/metrics"),
|
|
prefix("/api/live/ws"), // WebSocket does not support gzip compression.
|
|
prefix("/api/live/push"), // WebSocket does not support gzip compression.
|
|
substr("/resources"),
|
|
}
|
|
|
|
func Gziper() func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
requestPath := req.URL.RequestURI()
|
|
|
|
for _, pathMatcher := range gzipIgnoredPaths {
|
|
if pathMatcher(requestPath) {
|
|
next.ServeHTTP(rw, req)
|
|
return
|
|
}
|
|
}
|
|
|
|
if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
|
|
next.ServeHTTP(rw, req)
|
|
return
|
|
}
|
|
|
|
grw := &gzipResponseWriter{gzip.NewWriter(rw), rw.(web.ResponseWriter)}
|
|
grw.Header().Set("Content-Encoding", "gzip")
|
|
grw.Header().Set("Vary", "Accept-Encoding")
|
|
|
|
next.ServeHTTP(grw, req)
|
|
// We can't really handle close errors at this point and we can't report them to the caller
|
|
_ = grw.w.Close()
|
|
})
|
|
}
|
|
}
|