go 53 lines · 8 steps

How gzip response compression works in Gin

A Gin middleware transparently gzip-compresses responses by wrapping the context's ResponseWriter when the client supports it.

Explained by highlit
1package middleware
2 
3import (
4 "compress/gzip"
5 "net/http"
6 "strings"
7 
8 "github.com/gin-gonic/gin"
9)
10 
11type gzipWriter struct {
12 gin.ResponseWriter
13 writer *gzip.Writer
14}
15 
16func (g *gzipWriter) Write(data []byte) (int, error) {
17 return g.writer.Write(data)
18}
19 
20func (g *gzipWriter) WriteString(s string) (int, error) {
21 return g.writer.Write([]byte(s))
22}
23 
24func (g *gzipWriter) WriteHeader(code int) {
25 g.Header().Del("Content-Length")
26 g.ResponseWriter.WriteHeader(code)
27}
28 
29func Gzip(level int) gin.HandlerFunc {
30 return func(c *gin.Context) {
31 if !strings.Contains(c.GetHeader("Accept-Encoding"), "gzip") {
32 c.Next()
33 return
34 }
35 
36 gz, err := gzip.NewWriterLevel(c.Writer, level)
37 if err != nil {
38 c.Next()
39 return
40 }
41 defer gz.Close()
42 
43 c.Header("Content-Encoding", "gzip")
44 c.Header("Vary", "Accept-Encoding")
45 c.Writer = &gzipWriter{ResponseWriter: c.Writer, writer: gz}
46 
47 c.Next()
48 
49 if err := gz.Close(); err != nil {
50 c.AbortWithStatus(http.StatusInternalServerError)
51 }
52 }
53}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Embedding an interface and overriding select methods lets you intercept writes while inheriting the rest of the behavior for free.
  2. 2Content negotiation should always check the client's Accept-Encoding before applying an encoding it may not understand.
  3. 3Compressed responses need Content-Length stripped and a Vary header so caches and clients handle the encoding correctly.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How gzip response compression works in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code