feat: add compression middleware
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user