this repo has no description
1// httpencoding does on-the-fly zstd or gzip compression of the responses of the wrapped handlers.
2// The handlers it wraps should not do their own compression.
3package httpencoding
4
5import (
6 "compress/gzip"
7 "io"
8 "net/http"
9 "strconv"
10 "strings"
11
12 "github.com/klauspost/compress/zstd"
13)
14
15// Handler wraps the given handler to return write responses in gzip or zstd content-encoding
16// if the incoming request has the corresponding accept-encoding header.
17func Handler(h http.Handler) http.Handler {
18 return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
19 var acceptGz, acceptZstd float64
20 for _, encQ := range strings.Split(r.Header.Get("accept-encoding"), ",") {
21 enc, qkv, ok := strings.Cut(encQ, ";")
22 var q float64 = 1
23 if ok {
24 litq, qs, ok := strings.Cut(qkv, "=")
25 if litq == "q" && ok {
26 q, _ = strconv.ParseFloat(qs, 64)
27 }
28 }
29 switch strings.TrimSpace(enc) {
30 case "gzip":
31 acceptGz = q
32 case "zstd":
33 acceptZstd = q
34 }
35 }
36
37 if acceptGz == 0 && acceptZstd == 0 {
38 h.ServeHTTP(rw, r)
39 } else if acceptGz > acceptZstd {
40 nrw := newGzip(rw)
41 defer nrw.Flush()
42 rw.Header().Set("content-encoding", "gzip")
43 h.ServeHTTP(nrw, r)
44 } else {
45 nrw := newZstd(rw)
46 defer nrw.Flush()
47 rw.Header().Set("content-encoding", "zstd")
48 h.ServeHTTP(nrw, r)
49 }
50 })
51}
52
53type encoder interface {
54 io.Writer
55 Flush() error
56}
57
58type responseWriter struct {
59 http.ResponseWriter
60 e encoder
61}
62
63func newGzip(rw http.ResponseWriter) *responseWriter {
64 return &responseWriter{
65 rw,
66 gzip.NewWriter(rw),
67 }
68}
69
70func newZstd(rw http.ResponseWriter) *responseWriter {
71 e, _ := zstd.NewWriter(rw)
72 return &responseWriter{
73 rw,
74 e,
75 }
76}
77
78func (r *responseWriter) Flush() {
79 r.e.Flush()
80 if flusher, ok := r.ResponseWriter.(http.Flusher); ok {
81 flusher.Flush()
82 }
83}
84
85func (r *responseWriter) Write(b []byte) (int, error) {
86 return r.e.Write(b)
87}
88
89func (r *responseWriter) Unwrap() http.ResponseWriter {
90 return r.ResponseWriter
91}