Go的gzip包的简单解析和gbk包的实现

来源:互联网 发布:剑帝莱维 知乎 编辑:程序博客网 时间:2024/05/16 13:04

Go的gzip包的简单解析和gbk包的实现

  • gzip包的解析
  • gbk包的粗略实现(待测试)
a.首先看一下gzip包

这里gzip包有几个内容
首先是几个结构

type gzipResponseWriter struct {    w *gzip.Writer    negroni.ResponseWriter    wroteHeader bool}type handler struct {    pool sync.Pool}

然后是几个函数

func (grw *gzipResponseWriter) WriteHeader(code int)func (grw *gzipResponseWriter) Write(b []byte) (int, error)func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)func Gzip(level int) *handler

b.函数的解析

由上次我们解析Negroni库可知关键在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()}

这里我们看到,该函数首先根据request的情况做相应判断,然后从sync池取出Writer并且reset使用它,然后就把next中的responseWriter替换为本文件中的grw,相当于做了一层代理,这样当http请求如果用到Write方法或者WriteHead方法时使用的就是这里实现的对应方法。

c.接下来我们看看WriteHead和Write函数
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}

可以看到,根据条件,我们设置对应的grw,当header里的ContentEncoding为空时设置对应的参数,否则把grw的gzip.Writer置为nil,然后调用negrino的responseWriter的WriteHeader方法

接下来我们看看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)}

这个函数也很好理解,当gzip.Writer是nil的时候说明不用gzip就使用grw.ResponseWriter.Write(b),然后是否已经写了头部等等。。。
至于最后的Gzip它是返回一个handle这里也就不详述了

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}
小结

通过以上代码分析和根据之前我们另一篇博客对negroni的分析,当http调用ServeHttp的时候,该包会用grw代理原来的responseWrite于是到后面使用Write方法的时候就可以同gzip方式了,利用这种思维我模仿写了一个gbk(未测试)

gbk包

这是github地址https://github.com/caijh23/goWeb/tree/master/web/negroni-gbk
这是代码

package gbkimport (    "net/http"    "strings"    "io/ioutil"    "golang.org/x/text/transform"    "golang.org/x/text/encoding/simplifiedchinese"    "github.com/urfave/negroni")const (    headerContentType = "Content-type"    encodingGbk = "gbk"    encodingUTF8 = "UTF-8")type gbkResponseWriter struct {    w *transform.Writer    negroni.ResponseWriter    wroteHeader bool}func (grw *gbkResponseWriter) WriteHeader(code int) {    headers := grw.ResponseWriter.Header()    if headers.Get(headerContentType) == "" {        headers.Set(headerContentType,encodingGbk)    } else if strings.Contains(headers.Get(headerContentType),encodingUTF8) {        headers.Set(headerContentType,strings.Replace(headers.Get(headerContentType),encodingUTF8,encodingGbk,-1))    } else {        grw.w = nil    }    grw.ResponseWriter.WriteHeader(code)    grw.wroteHeader = true}func (grw *gbkResponseWriter) 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)}type handler struct {}func Gbk() *handler {    h := &handler{}    return h}func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {    if len(r.Header.Get(headerContentType)) == 0 {        next(w, r)        return    }    var nrw negroni.ResponseWriter    var gb *transform.Writer    if strings.Contains(r.Header.Get(headerContentType), encodingUTF8) {        nrw = negroni.NewResponseWriter(w)        gb = transform.NewWriter(nrw, nil)    }    if strings.Contains(r.Header.Get(headerContentType), encodingGbk) {        rd := transform.NewReader(r.Body, simplifiedchinese.GBK.NewDecoder())        r.Body = ioutil.NopCloser(rd)        nrw = negroni.NewResponseWriter(w)        gb = transform.NewWriter(nrw, simplifiedchinese.GBK.NewEncoder())    }    grw := gbkResponseWriter{gb, nrw, false}    next(&grw, r)}
原创粉丝点击