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

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 gbk

import (
    "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)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值