1package middleware 2 3import ( 4 "bufio" 5 "compress/gzip" 6 "fmt" 7 "net" 8 "net/http" 9 "strings" 10 11 "github.com/grafana/grafana/pkg/web" 12) 13 14type gzipResponseWriter struct { 15 w *gzip.Writer 16 web.ResponseWriter 17} 18 19func (grw *gzipResponseWriter) WriteHeader(c int) { 20 grw.Header().Del("Content-Length") 21 grw.ResponseWriter.WriteHeader(c) 22} 23 24func (grw gzipResponseWriter) Write(p []byte) (int, error) { 25 if grw.Header().Get("Content-Type") == "" { 26 grw.Header().Set("Content-Type", http.DetectContentType(p)) 27 } 28 grw.Header().Del("Content-Length") 29 return grw.w.Write(p) 30} 31 32func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 33 if hijacker, ok := grw.ResponseWriter.(http.Hijacker); ok { 34 return hijacker.Hijack() 35 } 36 return nil, nil, fmt.Errorf("GZIP ResponseWriter doesn't implement the Hijacker interface") 37} 38 39type matcher func(s string) bool 40 41func prefix(p string) matcher { return func(s string) bool { return strings.HasPrefix(s, p) } } 42func substr(p string) matcher { return func(s string) bool { return strings.Contains(s, p) } } 43 44var gzipIgnoredPaths = []matcher{ 45 prefix("/api/datasources"), 46 prefix("/api/plugins"), 47 prefix("/api/plugin-proxy/"), 48 prefix("/metrics"), 49 prefix("/api/live/ws"), // WebSocket does not support gzip compression. 50 prefix("/api/live/push"), // WebSocket does not support gzip compression. 51 substr("/resources"), 52} 53 54func Gziper() func(http.Handler) http.Handler { 55 return func(next http.Handler) http.Handler { 56 return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 57 requestPath := req.URL.RequestURI() 58 59 for _, pathMatcher := range gzipIgnoredPaths { 60 if pathMatcher(requestPath) { 61 next.ServeHTTP(rw, req) 62 return 63 } 64 } 65 66 if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { 67 next.ServeHTTP(rw, req) 68 return 69 } 70 71 grw := &gzipResponseWriter{gzip.NewWriter(rw), rw.(web.ResponseWriter)} 72 grw.Header().Set("Content-Encoding", "gzip") 73 grw.Header().Set("Vary", "Accept-Encoding") 74 75 next.ServeHTTP(grw, req) 76 // We can't really handle close errors at this point and we can't report them to the caller 77 _ = grw.w.Close() 78 }) 79 } 80} 81