Gin源码分析 – 中间件 – Gzip
•
Gin框架
1 介绍
本文介绍中间件Gzip的使用以及源码实现。
2 使用
首先下载该中间件
go get github.com/gin-contrib/gzip
编写如下的代码
package main import ( "net/http" "github.com/gin-contrib/gzip" "github.com/gin-gonic/gin" ) func main() { r := gin.New() r.Use(gzip.Gzip(gzip.DefaultCompression)) r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix())) }) r.Run(":8080") }
使用HTTPie进行测试结果如下,能够看到在响应的头中,Content-Encoding:gzip
(python_study) D:\Python\Envs\python_study>http -v 127.0.0.1:8080/ping GET /ping HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: 127.0.0.1:8080 User-Agent: HTTPie/3.1.0 HTTP/1.1 200 OK Content-Encoding: gzip Content-Length: 39 Content-Type: text/plain; charset=utf-8 Date: Tue, 12 Apr 2022 12:39:01 GMT Vary: Accept-Encoding pong 1649767141
3 代码分析
3.1 入口函数
func Gzip(level int, options ...Option) gin.HandlerFunc { return newGzipHandler(level, options...).Handle }
这个函数传入两个参数
(1)level,表示压缩等级,有如下几种形式
const ( BestCompression = gzip.BestCompression BestSpeed = gzip.BestSpeed DefaultCompression = gzip.DefaultCompression NoCompression = gzip.NoCompression )
(2)options,表示一些选项,有如下几种设置选项,每一种选项实际都是一个选项设置函数
type Options struct { ExcludedExtensions ExcludedExtensions ExcludedPaths ExcludedPaths ExcludedPathesRegexs ExcludedPathesRegexs DecompressFn func(c *gin.Context) }
3.2 选项函数
相关数据结构如下:
var ( DefaultExcludedExtentions = NewExcludedExtensions([]string{ ".png", ".gif", ".jpeg", ".jpg", }) DefaultOptions = &Options{ ExcludedExtensions: DefaultExcludedExtentions, } ) type Options struct { ExcludedExtensions ExcludedExtensions ExcludedPaths ExcludedPaths ExcludedPathesRegexs ExcludedPathesRegexs DecompressFn func(c *gin.Context) } // Using map for better lookup performance type ExcludedExtensions map[string]bool type ExcludedPaths []string type ExcludedPathesRegexs []*regexp.Regexp type Option func(*Options)
- ExcludedExtensions ,管理不需要进行压缩的文件类型的后缀;
- ExcludedPaths,管理不需要进行压缩的URL路径;
- ExcludedPathesRegecs,管理不需要进行压缩的URL路径的正则表达式;
- Option是一个用于设置选项的函数。
(1)WithExcludedExtension,定义不需要进行压缩的文件类型,下面的代码表示当服务器返回pdf, mp4两种格式的文件时,不会进行gzip压缩。
gzip.WithExcludedExtensions([]string{".pdf",".mp4"}))
代码实现如下所示。
func WithExcludedExtensions(args []string) Option { return func(o *Options) { o.ExcludedExtensions = NewExcludedExtensions(args) } } // Using map for better lookup performance type ExcludedExtensions map[string]bool func NewExcludedExtensions(extensions []string) ExcludedExtensions { res := make(ExcludedExtensions) for _, e := range extensions { res[e] = true } return res } func (e ExcludedExtensions) Contains(target string) bool { _, ok := e[target] return ok }
- 这个函数实际上返回一个Option的函数NewExcludedExtensions,该函数完成对Options的具体设置;
- NewExcludedExtensions,创建一个管理文件后缀的map;
- 提供一个Contains函数,判读是否包含某一个文件类型。
(2)WithExcludedPaths,定义不需要进行压缩的请求路径,下面的代码表示针对/api/的HTTP请求,不会进行gzip压缩。
gzip.WithExcludedPaths([]string{"/api/"})
代码实现如下所示。
func WithExcludedPaths(args []string) Option { return func(o *Options) { o.ExcludedPaths = NewExcludedPaths(args) } } type ExcludedPaths []string func NewExcludedPaths(paths []string) ExcludedPaths { return ExcludedPaths(paths) } func (e ExcludedPaths) Contains(requestURI string) bool { for _, path := range e { if strings.HasPrefix(requestURI, path) { return true } } return false }
- WithExcludedPaths返回一个Option函数NewExcludedPaths,
- NewExcludedPaths返回一个ExcludedPaths类型的字符串数组;
- ExcludedPaths提供了一个Contains函数,判读是否包含某一个文件类型。
(3)WithExcludedPathsRegexs,定义不需要进行压缩的请求路径,采用的是正则表达式的方式,下面给出了一个例子
gzip.WithExcludedPathsRegexs([]string{".*"})
代码实现如下所示。
func WithExcludedPathsRegexs(args []string) Option { return func(o *Options) { o.ExcludedPathesRegexs = NewExcludedPathesRegexs(args) } } type ExcludedPathesRegexs []*regexp.Regexp func NewExcludedPathesRegexs(regexs []string) ExcludedPathesRegexs { result := make([]*regexp.Regexp, len(regexs)) for i, reg := range regexs { result[i] = regexp.MustCompile(reg) } return result } func (e ExcludedPathesRegexs) Contains(requestURI string) bool { for _, reg := range e { if reg.MatchString(requestURI) { return true } } return false }
- WithExcludedPathsRegexs返回一个Option函数NewExcludedPathesRegexs,
- NewExcludedPathesRegexs,调用regexp.MustCompile进行编译,然后返回一个ExcludedPathesRegexs 类型的数组;
- ExcludedPathesRegexs 提供了一个Contains函数,判读是否包含该URL,调用reg.MatchString进行判断。
(4)WithDecompressFn,自定义压缩函数
func WithDecompressFn(decompressFn func(c *gin.Context)) Option { return func(o *Options) { o.DecompressFn = decompressFn } }
下面给出了默认的压缩函数的代码
func DefaultDecompressHandle(c *gin.Context) { if c.Request.Body == nil { return } r, err := gzip.NewReader(c.Request.Body) if err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) return } c.Request.Header.Del("Content-Encoding") c.Request.Header.Del("Content-Length") c.Request.Body = r }