gzip.go 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. package gzip
  2. import (
  3. "compress/gzip"
  4. "io/ioutil"
  5. "net/http"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "github.com/gin-gonic/gin"
  10. )
  11. const (
  12. BestCompression = gzip.BestCompression
  13. BestSpeed = gzip.BestSpeed
  14. DefaultCompression = gzip.DefaultCompression
  15. NoCompression = gzip.NoCompression
  16. )
  17. func Gzip(level int) gin.HandlerFunc {
  18. var gzPool sync.Pool
  19. gzPool.New = func() interface{} {
  20. gz, err := gzip.NewWriterLevel(ioutil.Discard, level)
  21. if err != nil {
  22. panic(err)
  23. }
  24. return gz
  25. }
  26. return func(c *gin.Context) {
  27. if !shouldCompress(c.Request) {
  28. return
  29. }
  30. gz := gzPool.Get().(*gzip.Writer)
  31. defer gzPool.Put(gz)
  32. gz.Reset(c.Writer)
  33. c.Header("Content-Encoding", "gzip")
  34. c.Header("Vary", "Accept-Encoding")
  35. c.Writer = &gzipWriter{c.Writer, gz}
  36. defer func() {
  37. c.Header("Content-Length", "0")
  38. gz.Close()
  39. }()
  40. c.Next()
  41. }
  42. }
  43. type gzipWriter struct {
  44. gin.ResponseWriter
  45. writer *gzip.Writer
  46. }
  47. func (g *gzipWriter) WriteString(s string) (int, error) {
  48. return g.writer.Write([]byte(s))
  49. }
  50. func (g *gzipWriter) Write(data []byte) (int, error) {
  51. return g.writer.Write(data)
  52. }
  53. func shouldCompress(req *http.Request) bool {
  54. if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
  55. return false
  56. }
  57. extension := filepath.Ext(req.URL.Path)
  58. if len(extension) < 4 { // fast path
  59. return true
  60. }
  61. switch extension {
  62. case ".png", ".gif", ".jpeg", ".jpg":
  63. return false
  64. default:
  65. return true
  66. }
  67. }