gibhub地址:针对negroni的gzip

作业内容:支持了gzip的服务器小程序

这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip。源码共有一百多行并不多,下面记录一下对gzip源码的分析与理解。

首先gzip是干啥用的?gzip是一种压缩方式。浏览器和web服务器之间为了减少传输链路上的文件的大小,浏览器和服务器在传送数据的时候会将数据以某种方式进行压缩,使用什么压缩方式则记录在报文头里面。这里的gzip便是一种压缩方式。并不是所有的浏览器和服务器都能支持所有的压缩方式。比如说浏览器支持gzip压缩方式,即浏览器能够解析gzip压缩后的内容,那么他会在请求中发送Accept-Encoding请求报头值为"gzip"  表明浏览器支持gzip这种压缩方式,web服务器根据读取Accept-Encoding请求报头的值来判断浏览器是否接受压缩的内容,服务器发现浏览器能够解析gzip压缩后的内容之后就会对要发送的数据进行gzip压缩再发送到客户端,同时设置Content-Encoding实体报头值为gzip以告知浏览器实体正文采用了gzip的压缩编码。

那么这里的negroni-gzip就是一个中间件,用来使regroni搭建的服务器能够支持gzip压缩。

源码分析:

  • const数据内容

    // These compression constants are copied from the compress/gzip package.
    const (encodingGzip = "gzip"headerAcceptEncoding  = "Accept-Encoding"headerContentEncoding = "Content-Encoding"headerContentLength   = "Content-Length"headerContentType     = "Content-Type"headerVary            = "Vary"headerSecWebSocketKey = "Sec-WebSocket-Key"BestCompression    = gzip.BestCompressionBestSpeed          = gzip.BestSpeedDefaultCompression = gzip.DefaultCompressionNoCompression      = gzip.NoCompression
    )
    

    上面的数据代表了接下来的代码中各种const常量代表的值。里面使用了compress/gzip里面的值,其中NoCompression = 0,BestSpeed = 1,BestCompression = 9,DefaultCompression = -1。这些值代表压缩的level,不能超BestCompression。

  • // gzipResponseWriter is the ResponseWriter that negroni.ResponseWriter is
    // wrapped in.
    type gzipResponseWriter struct {w *gzip.Writernegroni.ResponseWriterwroteHeader bool
    }

    结构体gzipResponseWriter,wroteHeader代表response(即响应内容)是否已经编码 。

  • // Check whether underlying response is already pre-encoded and disable
    // gzipWriter before the body gets written, otherwise encoding headers
    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}// Avoid sending Content-Length header before compression. The length would// be invalid, and some browsers like Safari will report// "The network connection was lost." errorsgrw.Header().Del(headerContentLength)grw.ResponseWriter.WriteHeader(code)grw.wroteHeader = true
    }
    

    WriteHeader函数,如果要发送给客户端的响应内容未预编码,则采用gzip压缩方式压缩后再发送到客户端,同时设置Content-Encoding实体报头值为gzip。否则在写之前令gzipWriter失效,使得它对任何写调用无条件成功。

  • // Write writes bytes to the gzip.Writer. It will also set the Content-Type
    // header using the net/http library content type detection if the Content-Type
    // header was not set yet.
    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中写入字节流。如果报头没写的话就写报头,如果不是使用的gzip压缩的话就用ResponseWriter写。如果头的Content-Type还没有被设置,则用net/http库中的类型检测来完成设置。

  • type gzipResponseWriterCloseNotifier struct {*gzipResponseWriter
    }

    一个简单的数据类型定义。

  • func (rw *gzipResponseWriterCloseNotifier) CloseNotify() <-chan bool {return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
    }func newGzipResponseWriter(rw negroni.ResponseWriter, w *gzip.Writer) negroni.ResponseWriter {wr := &gzipResponseWriter{w: w, ResponseWriter: rw}if _, ok := rw.(http.CloseNotifier); ok {return &gzipResponseWriterCloseNotifier{gzipResponseWriter: wr}}return wr
    }

    gzipResponseWriter,用于写入gzip编码后的数据。

  • // handler struct contains the ServeHTTP method
    type handler struct {pool sync.Pool
    }

    一个sync.Pool对象就是一组临时对象的集合,Pool用于存储那些被分配了但是没有被使用,而未来可能会使用的值,以减小垃圾回收的压力。

  • // Gzip returns a handler which will handle the Gzip compression in ServeHTTP.
    // Valid values for level are identical to those in the compress/gzip package.
    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
    }

    Gzip返回一个handler来处理在ServeHTTP中的压缩,需要调用gzip库的NewWriterLevel方法。

  • // ServeHTTP wraps the http.ResponseWriter with a gzip.Writer.
    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 connectionif 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 := newGzipResponseWriter(nrw, gz)// Call the next handler supplying the gzipResponseWriter instead of// the original.next(grw, r)gz.Close()
    }

    处理handler中压缩请求的函数:如果客户端不支持gzip编码则跳过并不压缩。如果客户端在尝试WebSocket连接时,也会不压缩。接下来从pool中遍历writer,如果之后遇到的错误,就通过defer的方法,返回pool,用ResponseWriter重置,这让我们可以再利用已经被分配的buffer,而不是为每一个单独的请求开辟新的buffer。最后用negroni.ResponseWriter打包原来的ResponseWriter,并创建一个新的gzipResponseWriter,并且调用下一个handler。最后关闭gz。

negroni-gzip源码分析相关推荐

  1. golang学习之negroni/gizp源码分析

    在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它是一个具备微型.非嵌入式.鼓励使用原生 net/http 库特征的中间件.利用它地Use功能,我们可以很简单地自定义中间件并使用.其 ...

  2. Negroni中间件源码分析

    概述 我们可以先看一下HTTP Server的处理逻辑: 在这个逻辑处理的环节中,Negroni充当一个HTTP Handler的角色,并对于所有的HTTP Request的处理都会通过Negroni ...

  3. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  4. tornado源码分析

    tornado源码分析 本源码为tornado1.0版本 源码附带例子helloworld import tornado.httpserver import tornado.ioloop import ...

  5. 微服务发现与注册之Eureka源码分析

    作者:陌北有棵树,Java人,架构师社区合伙人! [一]微服务之服务发现概述 关于微服务,近年来可谓是大火,业界也吹刮着一种实践微服务的风潮.本人有幸在去年参与到一个向微服务过渡的产品,再结合自己所学 ...

  6. 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  7. Tomcat 处理 HTTP 请求源码分析(上)【转】

    原文地址:https://www.infoq.cn/article/zh-tomcat-http-request-1 很多开源应用服务器都是集成 tomcat 作为 web container 的,而 ...

  8. Eureka源码分析

    Eureka源码分析 Eureka server 入口: Spring.factories PS: 意味着如果加载EurekaServerAutoConfiguration成功,需要 @Conditi ...

  9. Scrapy-redis 源码分析 及 框架使用

    From:https://blog.csdn.net/weixin_37947156/article/details/75044971 From:https://cuiqingcai.com/6058 ...

最新文章

  1. 转 plsql dev中Dynamic Performance Tables not accessible分析解决
  2. matlab 文件路径问题
  3. c++向mysql通信_C++连接MySQL
  4. WAP自助建站 我编程之路的启蒙
  5. mysql自带加密解密字符集问题
  6. 理解允许定位,音频,网络电话..
  7. CentOS x86_64系统手动释放内存
  8. Leetcode每日一题:66.plus-one(加一)
  9. 【计算机网络】Internet原理与技术2(因特网的路由协议RIP、OSPF、BGP,网络地址转换NAT,网络协议IPv6)
  10. 事件处理介绍(简要学习笔记十七)
  11. mz04那智不二越机器人编程_买买买!这些炫酷的机器人已加入进博会“购物车”!...
  12. PHP调用类函数定义位置,OOP PHP – 如何有选择地调用类的构造函数中定义的特定方法?...
  13. 手把手教,使用VMware虚拟机安装Windows XP系统,爷青回
  14. 系统分析师——论文篇(三)
  15. Java基本数据类型取值范围
  16. 向工程腐化开炮|动态链接库so治理
  17. oracle 天转换成月函数_oracle日期函数,转换函数
  18. ANO匿名飞控分析(2)— 任务调度
  19. vue 超出三行隐藏_文字超出三行省略...显示全文
  20. Component(组件)的创建

热门文章

  1. 基于STM32的门禁系统源码分享
  2. wps怎么关闭视图保护_怎么在PPT2013中清除编辑受保护视图功能?
  3. android5.1.1版本怎么升级,升级Android 5.1
  4. 从零开始学数据分析之——《笨办法学Python》(习题0-10)
  5. ECCV2022论文汇总:检测/分割/跟踪/3D/深度估计/姿态解算等多个方向!
  6. python求级数的值_python中的级数和
  7. 【XSY3657】因数分解(容斥,DP)
  8. 感恩中国30年,速速来看甲骨文数据库云大会
  9. val_loss先下降后上升或不下降只上升
  10. JQuery拖拽通过八个点改变div大小