服务计算课程拓展
gzip包分析
写在前面
以上仅仅为个人总结,如果有错误,希望大家可以指出来,以避免舞蹈初学者,谢谢
正文
github链接
这是一个negroni的中间件,所以接下来从它的调用开始,慢慢来追踪具体流程,代码很短,只有126行而已
1. gzip.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
}
其实这段代码就只是构造了一个handler,这个handler可以之后将进行相应的操作,正如negroni的Use
传入的参数一样,它需要实现ServeHTTP
函数,从而实现相应的接口,具体的gzip内容我们不关注,这里我们就看看handler的ServeHttp
函数的实现
2. ServeHTTP
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// Skip compression if the client doesn't accept gzip encoding.
if !strings.Contains(r.Header.Get(headerAcceptEncoding), encodingGzip) {
next(w, r)
return
}
// Skip compression if client attempt WebSocket connection
if len(r.Header.Get(headerSecWebSocketKey)) > 0 {
next(w, r)
return
}
// Retrieve gzip writer from the pool. Reset it to use the ResponseWriter.
// This allows us to re-use an already allocated buffer rather than
// allocating a new buffer for every request.
// We defer g.pool.Put here so that the gz writer is returned to the
// pool if any thing after here fails for some reason (functions in
// next could potentially panic, etc)
gz := h.pool.Get().(*gzip.Writer)
defer h.pool.Put(gz)
gz.Reset(w)
// Wrap the original http.ResponseWriter with negroni.ResponseWriter
// and create the gzipResponseWriter.
nrw := negroni.NewResponseWriter(w)
grw := gzipResponseWriter{gz, nrw, false}
// Call the next handler supplying the gzipResponseWriter instead of
// the original.
next(&grw, r)
// Delete the content length after we know we have been written to.
grw.Header().Del(headerContentLength)
gz.Close()
}
前面两个就是简单地跳过不能使用的情况,而后面就是对内容进行压缩,为了复用压缩器,也就是避免重新申请空间去使用压缩器,这里使用了Reset
,这里我有个疑问,那就是为什么要把ResponseWriter给包裹起来再传给下一个中间件,这里就要涉及到golang的继承问题了,感兴趣的可以自己查查了解了解,还是挺好玩的,具体就体现在这里的倒数第二段代码,你可能会想,grw并没有Header啊,为什么可以直接调用,其实,是因为grw有nrw,而nrw有w,w具有Header函数,因此grw可以直接调用Header(),很神奇是不是?我也觉得,因为我第一次接触也迷迷糊糊,看到了别人的代码和测试了自己的代码才相信这是真的。我们注意到,这里的gzipResponseWriter实现了http.ResponseWriter接口,那我们接下来就对具体的这三个函数进行分析一下
3. Write()
func (grw *gzipResponseWriter) Write(b []byte) (int, error) {
if !grw.wroteHeader {
grw.WriteHeader(http.StatusOK)
}
if grw.w == nil {
return grw.ResponseWriter.Write(b)
}
if len(grw.Header().Get(headerContentType)) == 0 {
grw.Header().Set(headerContentType, http.DetectContentType(b))
}
return grw.w.Write(b)
}
这里就是实现了Writer接口的Write函数,首先就是对于Header的处理,之后判断有没有定义好的Writer,如果没有的话,就用原本的Writer来进行处理;如果存在,则用我们定义好的压缩的Writer来进行数据的写入。
至于另外两个函数,一个是直接使用了传入的w的Header函数,另外一个看名字就知道了,所以这里不再提及
4. 总结
以上的内容分析结束后我们就差不多解决了这个中间件的处理逻辑,即判断并构建writer->包装成新的HttpWriter->判断并处理数据