浅析negroni-gzip 过滤器的源码

  • 源码来源:negroni-gzip

  • gzip能对数据进行压缩,从而使服务端向客户端传输数据的速度加快。以下对源码的分析是基于源码注释加上自己在谷歌上搜索入header和Sec-WebSocket-Key等知识后的理解,由于时间原因没有深入使用过gzip,很可能存在错误请指正~

// 以下的压缩常量来自于compress/gzip 包
const (
    encodingGzip = "gzip" // 编码方式为gzip

    // request或response的header
    headerAcceptEncoding  = "Accept-Encoding" // 指定浏览器可以支持的web服务器返回内容压缩编码类型
    headerContentEncoding = "Content-Encoding" //web服务器支持的返回内容压缩编码类型
    headerContentLength   = "Content-Length" // 响应体的长度
    headerContentType     = "Content-Type" // 返回内容的MIME类型
    headerVary            = "Vary"
    headerSecWebSocketKey = "Sec-WebSocket-Key"

    // 压缩级别
    BestCompression    = gzip.BestCompression
    BestSpeed          = gzip.BestSpeed
    DefaultCompression = gzip.DefaultCompression
    NoCompression      = gzip.NoCompression
)

// gzipResponseWriter 包含 negroni.ResponseWriter
type gzipResponseWriter struct {
    w *gzip.Writer
    negroni.ResponseWriter
    wroteHeader bool
}

// 检查并设置response的header,并把wroteHeader 设为true,
func (grw *gzipResponseWriter) WriteHeader(code int) {
    headers := grw.ResponseWriter.Header()
    if headers.Get(headerContentEncoding) == "" {
        headers.Set(headerContentEncoding, encodingGzip)
        headers.Add(headerVary, headerAcceptEncoding)
    } else {
        grw.w.Reset(ioutil.Discard)
        grw.w = nil
    }
    grw.ResponseWriter.WriteHeader(code)// 设置状态码
    grw.wroteHeader = true
}


// 把byte数据写入 gzip.Writer并设置Content-Type
func (grw *gzipResponseWriter) Write(b []byte) (int, error) {
    if !grw.wroteHeader {// 判断是否已经设置过了response的header
        grw.WriteHeader(http.StatusOK) // 默认返回码200
    }
    if grw.w == nil {
        return grw.ResponseWriter.Write(b)
    }
    // 设置Content-Type
    if len(grw.Header().Get(headerContentType)) == 0 {
        grw.Header().Set(headerContentType, http.DetectContentType(b))
    }
    return grw.w.Write(b)
}

// handler结构体包含ServeHTTP方法,sync.Pool用来缓存对象
type handler struct {
    pool sync.Pool
}

// 根据压缩level来进行,返回一个handler去处理ServeHTTP中的gzip压缩
func Gzip(level int) *handler {
    h := &handler{}
    h.pool.New = func() interface{} {
        gz, err := gzip.NewWriterLevel(ioutil.Discard, level)
        if err != nil {
            panic(err)
        }
        return gz
    }
    return h
}

// ServeHTTP以包括http.ResponseWriter
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    // 判断客户端的AcceptEncoding,如果不接受gzip的编码格式则跳过压缩
    if !strings.Contains(r.Header.Get(headerAcceptEncoding), encodingGzip) {
        next(w, r)
        return
    }

    // 如果客户端是发送Sec-WebSocket-Key过来也无需压缩 if len(r.Header.Get(headerSecWebSocketKey)) > 0 {
        next(w, r)
        return
    }

    gz := h.pool.Get().(*gzip.Writer)// 从pool中获取Writer
    defer h.pool.Put(gz)// 使用完后放回pool池等待重新被利用
    gz.Reset(w)// 用ResponseWriter完成Reset

    // 新建一个包含negroni.ResponseWriter和Write的gzipResponseWriter对象,并把wroteHeader初始化为false
    nrw := negroni.NewResponseWriter(w)
    grw := gzipResponseWriter{gz, nrw, false}

    // gzipResponseWriter取代原来的中间件去应用下一个handlerFunc
    next(&grw, r)

    // 删除header的ContentLength
    grw.Header().Del(headerContentLength)

    gz.Close()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值