feat: add compression middleware

This commit is contained in:
2026-07-03 02:59:28 +02:00
parent 40eb3a146e
commit 37717fec69

View File

@@ -148,19 +148,16 @@ func StaticCacheMiddleware() gin.HandlerFunc {
} }
} }
//nolint:unused
type compressionMode uint8 type compressionMode uint8
const ( const (
compressionUndecided compressionMode = iota //nolint:unused compressionUndecided compressionMode = iota
compressionPlain //nolint:unused compressionPlain
compressionGzip //nolint:unused compressionGzip
) )
//nolint:unused
const compressionMinLength = 1024 const compressionMinLength = 1024
//nolint:unused
func acceptsGzip(value string) bool { func acceptsGzip(value string) bool {
wildcard := false wildcard := false
for _, part := range strings.Split(value, ",") { for _, part := range strings.Split(value, ",") {
@@ -189,7 +186,6 @@ func acceptsGzip(value string) bool {
return wildcard return wildcard
} }
//nolint:unused
func canNegotiateCompression(r *http.Request) bool { func canNegotiateCompression(r *http.Request) bool {
if r.Method == http.MethodHead || r.Header.Get("Range") != "" { if r.Method == http.MethodHead || r.Header.Get("Range") != "" {
return false return false
@@ -203,7 +199,6 @@ func canNegotiateCompression(r *http.Request) bool {
return !isCompressedPath(r.URL.Path) return !isCompressedPath(r.URL.Path)
} }
//nolint:unused
type compressionWriter struct { type compressionWriter struct {
gin.ResponseWriter gin.ResponseWriter
buffer bytes.Buffer buffer bytes.Buffer
@@ -214,14 +209,12 @@ type compressionWriter struct {
acceptsGzip bool acceptsGzip bool
} }
//nolint:unused
func (w *compressionWriter) WriteHeader(status int) { func (w *compressionWriter) WriteHeader(status int) {
if status > 0 && w.mode == compressionUndecided && !w.wroteBody { if status > 0 && w.mode == compressionUndecided && !w.wroteBody {
w.status = status w.status = status
} }
} }
//nolint:unused
func (w *compressionWriter) WriteHeaderNow() { func (w *compressionWriter) WriteHeaderNow() {
if w.mode == compressionUndecided { if w.mode == compressionUndecided {
w.startPlain() w.startPlain()
@@ -229,12 +222,10 @@ func (w *compressionWriter) WriteHeaderNow() {
} }
} }
//nolint:unused
func (w *compressionWriter) WriteString(data string) (int, error) { func (w *compressionWriter) WriteString(data string) (int, error) {
return w.Write([]byte(data)) return w.Write([]byte(data))
} }
//nolint:unused
func (w *compressionWriter) Status() int { func (w *compressionWriter) Status() int {
if w.status != 0 { if w.status != 0 {
return w.status return w.status
@@ -242,7 +233,6 @@ func (w *compressionWriter) Status() int {
return w.ResponseWriter.Status() return w.ResponseWriter.Status()
} }
//nolint:unused
func (w *compressionWriter) Size() int { func (w *compressionWriter) Size() int {
if size := w.ResponseWriter.Size(); size >= 0 { if size := w.ResponseWriter.Size(); size >= 0 {
return size return size
@@ -253,12 +243,10 @@ func (w *compressionWriter) Size() int {
return -1 return -1
} }
//nolint:unused
func (w *compressionWriter) Written() bool { func (w *compressionWriter) Written() bool {
return w.wroteBody || w.ResponseWriter.Written() return w.wroteBody || w.ResponseWriter.Written()
} }
//nolint:unused
func (w *compressionWriter) startPlain() { func (w *compressionWriter) startPlain() {
if w.mode != compressionUndecided { if w.mode != compressionUndecided {
return return
@@ -267,7 +255,6 @@ func (w *compressionWriter) startPlain() {
w.ResponseWriter.WriteHeader(w.Status()) w.ResponseWriter.WriteHeader(w.Status())
} }
//nolint:unused
func (w *compressionWriter) flushBuffer() error { func (w *compressionWriter) flushBuffer() error {
if w.buffer.Len() == 0 { if w.buffer.Len() == 0 {
return nil return nil
@@ -284,7 +271,6 @@ func (w *compressionWriter) flushBuffer() error {
return err return err
} }
//nolint:unused
func (w *compressionWriter) writePlain(data []byte) (int, error) { func (w *compressionWriter) writePlain(data []byte) (int, error) {
w.startPlain() w.startPlain()
if err := w.flushBuffer(); err != nil { if err := w.flushBuffer(); err != nil {
@@ -294,7 +280,6 @@ func (w *compressionWriter) writePlain(data []byte) (int, error) {
return w.ResponseWriter.Write(data) return w.ResponseWriter.Write(data)
} }
//nolint:unused
func (w *compressionWriter) startGzip() { func (w *compressionWriter) startGzip() {
if w.mode != compressionUndecided { if w.mode != compressionUndecided {
return return
@@ -310,7 +295,6 @@ func (w *compressionWriter) startGzip() {
w.gzip = gzip.NewWriter(w.ResponseWriter) w.gzip = gzip.NewWriter(w.ResponseWriter)
} }
//nolint:unused
func (w *compressionWriter) knownContentLength() (compress, known bool) { func (w *compressionWriter) knownContentLength() (compress, known bool) {
contentLength := w.Header().Get("Content-Length") contentLength := w.Header().Get("Content-Length")
if contentLength == "" { if contentLength == "" {
@@ -323,7 +307,6 @@ func (w *compressionWriter) knownContentLength() (compress, known bool) {
return length >= compressionMinLength, true return length >= compressionMinLength, true
} }
//nolint:unused
func (w *compressionWriter) canCompress(data []byte) bool { func (w *compressionWriter) canCompress(data []byte) bool {
status := w.Status() status := w.Status()
if status < http.StatusOK || status == http.StatusNoContent || status == http.StatusPartialContent || status == http.StatusNotModified { if status < http.StatusOK || status == http.StatusNoContent || status == http.StatusPartialContent || status == http.StatusNotModified {
@@ -345,7 +328,6 @@ func (w *compressionWriter) canCompress(data []byte) bool {
return w.acceptsGzip return w.acceptsGzip
} }
//nolint:unused
func (w *compressionWriter) Write(data []byte) (int, error) { func (w *compressionWriter) Write(data []byte) (int, error) {
w.wroteBody = true w.wroteBody = true
@@ -380,7 +362,6 @@ func (w *compressionWriter) Write(data []byte) (int, error) {
return w.gzip.Write(data) return w.gzip.Write(data)
} }
//nolint:unused
func (w *compressionWriter) Flush() { func (w *compressionWriter) Flush() {
if w.mode == compressionUndecided { if w.mode == compressionUndecided {
if w.canCompress(w.buffer.Bytes()) { if w.canCompress(w.buffer.Bytes()) {
@@ -397,7 +378,6 @@ func (w *compressionWriter) Flush() {
w.ResponseWriter.Flush() w.ResponseWriter.Flush()
} }
//nolint:unused
func (w *compressionWriter) finish() error { func (w *compressionWriter) finish() error {
if w.mode == compressionUndecided { if w.mode == compressionUndecided {
if w.Status() == http.StatusNotModified { if w.Status() == http.StatusNotModified {
@@ -420,3 +400,21 @@ func (w *compressionWriter) finish() error {
} }
return w.gzip.Close() return w.gzip.Close()
} }
func CompressionMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if !canNegotiateCompression(c.Request) {
return
}
writer := &compressionWriter{
ResponseWriter: c.Writer,
acceptsGzip: acceptsGzip(c.Request.Header.Get("Accept-Encoding")),
}
c.Writer = writer
c.Next()
if err := writer.finish(); err != nil {
_ = c.Error(err)
}
}
}