澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

golang中使用echo框架中的HTTP,一分钟预览

2019-05-17 作者:澳门新萄京赌场网址   |   浏览(91)

HTTP2 Server Push的研究

2017/01/05 · 基本功本事 · HTTP/2

原版的书文出处: AlloyTeam   

正文头阵地址为-iOS HTTP/二 Server Push 研究 | 李剑飞的博客

生命不仅仅,继续 go go go !!!

1分钟预览 HTTP贰 个性和抓包深入分析

2016/09/26 · JavaScript · HTTP/2

原稿出处: 段隆贤   

参谋cnodejs.org上面包车型地铁静态服务器例子,写了上边包车型地铁2个nodejs静态服务器例子,里面包涵cache,压缩,贴代码如下:

一,HTTP二的新性情。

关于HTTP2的新特点,读着能够参照我事先的篇章,这里就不在多说了,本篇文章首要讲一下server push那几个特点。

HTTP,HTTP贰.0,SPDY,HTTPS你应该知道的局部事

 


持续echo web框架,明日搞一下http二。

背景

近年来,http互联网请求量日益增加,以下是httparchive计算,从二零一三-11-01到201陆-0玖-01的呼吁数量和传导大小的大方向图:

图片 1

 

此时此刻大多数份客户端&服务端架构的应用程序,都以用http/一.一连连的,现代浏览器与单个域最明斯克接数,都在四-四个左右,由上海体育场所Total Requests数据,即便不用CDN分流,平均有21个左右的串行请求。
HTTP2是1997年颁发http一.1后的三遍首要的改正,在研讨层面改革了以上难题,减弱财富占用,来,直接感受一下差别:

HTTP/2 is the future of the Web, and it is here!
这是 Akamai 集团创立的3个官方的示范,用以申明 HTTP/贰 相比较于事先的 HTTP/壹.1 在品质上的庞然大物提高。 同时呼吁 37九 张图片,从Load time 的周旋统一能够见见 HTTP/2 在进度上的优势。

图片 2

 

正文全部源码和抓包文件在github

复制代码 代码如下:

2,Server Push是什么。

轻巧易行来说正是当用户的浏览器和服务器在制造链接后,服务器主动将一部分能源推送给浏览器并缓存起来,那样当浏览器接下去请求那几个能源时就直接从缓存中读取,不会在从服务器上拉了,提高了速率。举1个例证便是:

假设一个页面有一个能源文件index.html,index.css,index.js,当浏览器请求index.html的时候,服务器不止再次回到index.html的剧情,同时将index.css和index.js的剧情push给浏览器,当浏览器后一次呼吁那二七个公文时就可以直接从缓存中读取了。

图片 3

HTTP2

What is HTTP/2?
HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods, status codes and semantics are the same, and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.

The focus of the protocol is on performance; specifically, end-user perceived latency, network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.

新的二进制格式(Binary Format)
HTTP一.x的解析是依赖文本。基于文本协议的格式深入分析存在天然缺陷,文本的表现方式有多样性,要到位健壮性思考的场所必然很多,二进制则不一致,只认0和一的三结合。基于这种设想HTTP二.0的协商深入分析决定使用贰进制格式,实现便民且健壮。

多路复用(MultiPlexing)
即连续共享,即每一个request都是是当做连接共享机制的。二个request对应八个id,那样一个再三再四上能够有多少个request,每种连接的request能够随便的鱼龙混杂在联合,接收方能够依照request的 id将request再归属到各自分化的服务端请求里面。多路复用原理图:

header压缩
HTTP二.0使用encoder来减弱要求传输的header大小,通信双方各自cache一份header 田野(field)s表,既幸免了再一次header的传导,又减小了亟需传输的高低。

服务端推送(server push)
同SPDY一样,HTTP2.0也具有server push功能。

HTTP/2 源自 SPDY/2

SPDY 类别协议由谷歌费用,于 二零零六 年公然。它的统一筹算指标是降低 二分一的页面加载时间。当下广大响当当的互连网公司都在本人的网站或 APP 中使用了 SPDY 种类协议(当前最新版本是 SPDY/三.1),因为它对质量的升迁是备受瞩目标。主流的浏览器(谷歌(Google)、火狐、Opera)也都早已经协助SPDY,它已经成为了工业标准,HTTP Working-Group 最后决定以 SPDY/2为根基,开荒 HTTP/二。HTTP/2标准于20一伍年5月以BMWX三FC 7540规范刊出。

可是,HTTP/二 跟 SPDY 仍有例外的地点,首假诺以下两点:

HTTP/二 帮助明文 HTTP 传输,而 SPDY 强制行使 HTTPS
HTTP/贰 信息头的压缩算法接纳 HPACK ,而非 SPDY 接纳的 DEFLATE(感激网上朋友 逸风之狐指正)

共谋文书档案请见:rfc7540:HTTP2

/**
 * 静态文件服务器测试例子
 * User: xuwm
 * Date: 13-5-17
 * Time: 上午8:38
 * To change this template use File | Settings | File Templates.
 */
var port=3333;
var http = require("http");
var url = require("url");
var fs = require("fs");
var path = require("path");
var mime = require("./mime").types;
var config = require("./config");
var zlib = require("zlib");
//创建http服务端
var server=http.createServer(function(request,response){
    var obj= url.parse(request.url);
    response.setHeader("Server","Node/V8");
    console.log(obj);
    var pathname=obj.pathname;
    if(pathname.slice(-1)==="/"){
        pathname=pathname config.Welcome.file;   //暗中认可取当前暗中同意下的index.html
    }
    var realPath = path.join("assets", path.normalize(pathname.replace(/../g, "")));
    console.log(realPath) ;
    var pathHandle=function(realPath){
    //用fs.stat方法获得文件
        fs.stat(realPath,function(err,stats){
            if(err){
                response.writeHead(404,"not found",{'Content-Type':'text/plain'});
                response.write("the request " realPath " is not found");
                response.end();
            }else{
                if(stats.isDirectory()){
                }else{
                    var ext = path.extname(realPath);
                    ext = ext ? ext.slice(1) : 'unknown';
                    var contentType = mime[ext] || "text/plain";
                    response.setHeader("Content-Type", contentType);

三,Server Push原理是怎么样。

要想领会server push原理,首先要知道一些概念。大家领略HTTP二传输的格式并不像HTTP一使用文本来传输,而是启用了贰进制帧(Frames)格式来传输,和server push相关的帧首要分为这两种档案的次序:

  1. HEADECR-VS frame(请求再次来到头帧):这种帧首要指导的http请求头信息,和HTTP壹的header类似。
  2. DATA frames(数据帧) :这种帧存放真正的数额content,用来传输。
  3. PUSH_PROMISE frame(推送帧):这种帧是由server端发送给client的帧,用来表示server push的帧,这种帧是落实server push的根本帧类型。
  4. RST_STREAM(撤废推送帧):这种帧表示请求关闭帧,轻便讲就是当client不想接受一些财富依然接受timeout时会向发送方发送此帧,和PUSH_PROMISE frame一同使用时表示拒绝可能关闭server push。

Note:HTTP二.0相关的帧其实包蕴10种帧,正是因为尾部数据格式的更动,才为HTTP二.0拉动众多的风味,帧的引进不仅仅有利于缩短数量,也便宜数据的安全性和保证传输性。

询问了有关的帧类型,上面正是具体server push的落到实处进度了:

  1. 由多路复用大家能够领会HTTP第22中学对此同叁个域名的请求会利用一条tcp链接而用差异的stream ID来分化各自的央求。
  2. 当client使用stream 壹请求index.html时,server日常管理index.html的央浼,并得以摸清index.html页面还将要会呈请index.css和index.js。
  3. server使用stream 1发送PUSH_PROMISE frame给client告诉client作者那边能够利用stream 二来推送index.js和stream 三来推送index.css财富。
  4. server使用stream 一符合规律的发送HEADE途乐S frame和DATA frames将index.html的内容再次回到给client。
  5. client接收到PUSH_PROMISE frame得知stream 二和stream 3来接受推送能源。
  6. server获得index.css和index.js便会发送HEADE揽胜极光S frame和DATA frames将财富发送给client。
  7. client得到push的能源后会缓存起来当呼吁那个能源时会从一向从从缓存中读取。

下图表示了全套工艺流程:

图片 4

HTTP/2

变迁证书

go run C:gosrccryptotlsgenerate_cert.go --host localhost
2017/11/22 10:06:58 written cert.pem
2017/11/22 10 :06:58 written key.pem

HTTP二性情概览

                    var lastModified = stats.mtime.toUTCString();
                    var ifModifiedSince = "If-Modified-Since".toLowerCase();
                    response.setHeader("Last-Modified", lastModified);

4,Server Push怎么用。

既然server push这么神奇,那么大家怎么选拔啊?怎么设置服务器push哪些文件呢?

率先并不是具有的服务器都帮助server push,nginx近些日子还不援救那本性情,能够在nginx的法定博客上获得证实,然则Apache和nodejs都早已扶助了server push那贰个本性,要求表明某个的是server push那些天性是依照浏览器和服务器的,所以浏览器并不曾提供相应的js api来让用户平昔操作和垄断push的内容,所以只能是透过header音信和server的安插来贯彻具体的push内容,本文首要以nodejs来表明实际怎样行使server push这壹特性。

积谷防饥职业:下载nodejs http2支撑,本地运维nodejs服务。

一. 首先大家运用nodejs搭建基本的server:

JavaScript

var http二 = require('http二');   var url=require('url'); var fs=require('fs'); var mine=require('./mine').types; var path=require('path');   var server = http二.createServer({   key: fs.readFileSync('./zs/localhost.key'),   cert: fs.readFileSync('./zs/localhost.crt') }, function(request, response) {     var pathname = url.parse(request.url).pathname;     var realPath = path.join("my", pathname);    //这里安装自身的文件名称;       var pushArray = [];     var ext = path.extname(realPath);     ext = ext ? ext.slice(1) : 'unknown';     var contentType = mine[ext] || "text/plain";       if (fs.existsSync(realPath)) {           response.writeHead(200, {             'Content-Type': contentType         });           response.write(fs.readFileSync(realPath,'binary'));       } else {       response.writeHead(404, {           'Content-Type': 'text/plain'       });         response.write("This request URL " pathname " was not found on this server.");       response.end();     }   });   server.listen(443, function() {   console.log('listen on 443'); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var http2 = require('http2');
 
var url=require('url');
var fs=require('fs');
var mine=require('./mine').types;
var path=require('path');
 
var server = http2.createServer({
  key: fs.readFileSync('./zs/localhost.key'),
  cert: fs.readFileSync('./zs/localhost.crt')
}, function(request, response) {
    var pathname = url.parse(request.url).pathname;
    var realPath = path.join("my", pathname);    //这里设置自己的文件名称;
 
    var pushArray = [];
    var ext = path.extname(realPath);
    ext = ext ? ext.slice(1) : 'unknown';
    var contentType = mine[ext] || "text/plain";
 
    if (fs.existsSync(realPath)) {
 
        response.writeHead(200, {
            'Content-Type': contentType
        });
 
        response.write(fs.readFileSync(realPath,'binary'));
 
    } else {
      response.writeHead(404, {
          'Content-Type': 'text/plain'
      });
 
      response.write("This request URL " pathname " was not found on this server.");
      response.end();
    }
 
});
 
server.listen(443, function() {
  console.log('listen on 443');
});

这几行代码便是简单搭建贰个nodejs http2服务,展开chrome,大家可以看出全体请求都走了http贰,同时也足以注脚多路复用的特色。

图片 5

此间要求小心几点:

  1. 创造http二的nodejs服务必须时依照https的,因为未来主流的浏览器都要辅助SSL/TLS的http2,证书和私钥能够协和通过OPENSSL生成。
  2. node http二的有关api和正规的node httpserver一样,可以一贯动用。

  3. 设置大家的server push:

JavaScript

var pushItem = response.push('/css/bootstrap.min.css', {        request: {             accept: '*/*'        },       response: {             'content-type': 'text/css'      } }); pushItem.end(fs.readFileSync('/css/bootstrap.min.css','binary'));

1
2
3
4
5
6
7
8
9
var pushItem = response.push('/css/bootstrap.min.css', {
       request: {
            accept: '*/*'
       },
      response: {
            'content-type': 'text/css'
     }
});
pushItem.end(fs.readFileSync('/css/bootstrap.min.css','binary'));

大家设置了bootstrap.min.css来经过server push到大家的浏览器,我们得以在浏览器中查看:

图片 6

能够看到,运营server push的财富timelime十分的快,大大加快了css的收获时间。

这里必要注意上面几点:

  1. 作者们调用response.push(),便是相当于server发起了PUSH_PROMISE frame来告诉浏览器bootstrap.min.css将会由server push来收获。
  2. response.push()重返的对象时叁个常规的ServerResponse,end(),writeHeader()等方法都足以健康调用。
  3. 此间1旦针对有些财富调用response.push()即发起PUSH_PROMISE frame后,要盘活容错机制,因为浏览器在下一次央浼那些财富时会且只会等待这几个server push回来的能源,这里要办好超时和容错即下边包车型大巴代码:
  4. JavaScript

    try {     pushItem.end(fs.readFileSync('my/css/bootstrap.min.css','binary'));     } catch(e) {        response.writeHead(404, {            'Content-Type': 'text/plain'        });        response.end('request error'); }   pushItem.stream.on('error', function(err){     response.end(err.message); });   pushItem.stream.on('finish', function(err){    console.log('finish'); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    try {
        pushItem.end(fs.readFileSync('my/css/bootstrap.min.css','binary'));
        } catch(e) {
           response.writeHead(404, {
               'Content-Type': 'text/plain'
           });
           response.end('request error');
    }
     
    pushItem.stream.on('error', function(err){
        response.end(err.message);
    });
     
    pushItem.stream.on('finish', function(err){
       console.log('finish');
    });

    地点的代码你可能会发掘众多和健康nodejs的httpserver分化样的事物,那正是stream,其实全数http贰都以以stream为单位,这里的stream其实能够领会成一个呼吁,越来越多的api能够参见:node-http2。

  5. 说起底给咱们推荐贰个鬼子写的专门服务http二的node server有兴趣的能够品尝一下。

HTTP/2 Server Push 是什么

当用户的浏览器和服务器在创制链接后,服务器主动将一些能源推送给浏览器并缓存起来,那样当浏览器接下去请求这么些能源时就直接从缓存中读取,不会在从服务器上拉了,升高了速率。举1个例证正是:

要是一个页面有3个资源文件index.html,index.css,index.js,当浏览器请求index.html的时候,服务器不只有重回index.html的内容,同时将index.css和index.js的内容push给浏览器,当浏览器后一次恳请那贰八个公文时就能够直接从缓存中读取了。

正如图所示:

图片 7

Apple-http2ServerPush

echo中的HTTP/2

代码main.go:

package main

import (
    "fmt"
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/request", func(c echo.Context) error {
        req := c.Request()
        format := `
            <code>
                Protocol: %s<br>
                Host: %s<br>
                Remote Address: %s<br>
                Method: %s<br>
                Path: %s<br>
            </code>
        `
        return c.HTML(http.StatusOK, fmt.Sprintf(format, req.Proto, req.Host, req.RemoteAddr, req.Method, req.URL.Path))
    })
    e.Logger.Fatal(e.StartTLS(":1323", "cert.pem", "key.pem"))
}

浏览器输入:

结果:

Protocol: HTTP/2.0
Host: localhost:1323
Remote Address: [::1]:1905
Method: GET
Path: /request

只要出现错误:
http: TLS handshake error from [::1]:1735: tls: first record does not look like a TLS handshake.

请检查是否输入的是https

一. 二进制协议

HTTP/二 接纳二进制格式传输数据,而非 HTTP/一.x 的文本格式

图片 8

 

由上图能够看出HTTP2在本来的应用层和HTTP层增加了1层二进制传输。

2进制协议的3个好处是,能够定义额外的帧。

HTTP/二定义了近10种帧(实际情况可剖析抓包文件),为以后的高端应用打好了基础。假使运用文本完成这种意义,深入分析数据将会变得特别麻烦,二进制分析则有利于得多。
RFC7540:Frame Definitions

图片 9

探讨中定义的帧

                    if (ext.match(config.Expires.fileMatch)) {
                        var expires = new Date();
                        expires.setTime(expires.getTime() config.Expires.maxAge * 1000);
                        response.setHeader("Expires", expires.toUTCString());
                        response.setHeader("Cache-Control", "max-age=" config.Expires.maxAge);
                    }

伍,Server Push相关难题。

  1. 大家了解今后大家web的财富一般都以位于CDN上的,那么CDN的优势和server push的优势有什么差距吗,到底是哪个不慢呢?那些标题小编也一直在商量,本文的连带demo都不得不算做3个演示,具体的线上推行还在拓展中。
  2. 由于HTTP二的部分新特点比如多路复用,server push等等都是依据同一个域名的,所以那也许会对我们事先对于HTTP一的局地优化措施举例(能源拆分域名,合并等等)不必然适用。
  3. server push不仅仅能够当做拉取静态财富,大家的cgi请求即ajax请求一样能够应用server push来发送数据。
  4. 最全面包车型客车结果是CDN域名帮衬HTTP2,web server域名也还要支持HTTP2。

 

参照他事他说加以考察资料:

  1. HTTP二官方正式:
  2. 维基百科:
  3. 1 赞 1 收藏 评论

图片 10

HTTP/二 Server Push 原理是哪些

要想精通server push原理,首先要理解一些定义。大家掌握HTTP/二传输的格式并不像HTTP壹使用文本来传输,而是启用了2进制帧(Frames)格式来传输,和server push相关的帧主要分为那两种等级次序:

  1. HEADELANDS frame(请求重回头帧):这种帧首要指点的http请求头新闻,和HTTP1的header类似。
  2. DATA frames(数据帧) :这种帧存放真正的数据content,用来传输。
  3. PUSH_PROMISE frame(推送帧):这种帧是由server端发送给client的帧,用来代表server push的帧,这种帧是促成server push的最首要帧类型。
  4. RST_STREAM(撤销推送帧):这种帧表示请求关闭帧,简单讲正是当client不想接受有个别财富照旧接受timeout时会向发送方发送此帧,和PUSH_PROMISE frame一同使用时表示拒绝可能关闭server push。

(PS:HTTP/二相关的帧其实包罗10种帧,便是因为尾部数据格式的改造,才为HTTP/贰带来很多的性状,帧的引进不唯有有利压缩数量,也可以有益数据的安全性和可信赖传输性。)

摸底了相关的帧类型,上面正是现实server push的贯彻进程了:

  1. 由多路复用大家得以通晓HTTP/第22中学对于同三个域名的请求会使用一条tcp链接而用分化的stream ID来区分各自的央浼。
  2. 当client使用stream 1请求index.html时,server符合规律管理index.html的央求,并能够得知index.html页面还将在会呈请index.css和index.js。
  3. server使用stream 1发送PUSH_PROMISE frame给client告诉client我那边能够运用stream 2来推送index.js和stream 三来推送index.css财富。
  4. server使用stream 1平常的殡葬HEADE奥迪Q5S frame和DATA frames将index.html的开始和结果重回给client。
  5. client接收到PUSH_PROMISE frame得知stream 二和stream 三来收纳推送财富。
  6. server获得index.css和index.js便会发送HEADEXC60S frame和DATA frames将能源发送给client。
  7. client得到push的能源后会缓存起来当呼吁那几个财富时会从直接从从缓存中读取。

golang.org/x/net/http2

文书档案地址:

获取:
get golang.org/x/net/http2

代码main.go:

package main

import (
    "fmt"
    "html"
    "log"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    var srv http.Server
    http2.VerboseLogs = true
    srv.Addr = ":8080"
    // This enables http2 support
    http2.ConfigureServer(&srv, nil)

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hi tester %qn", html.EscapeString(r.URL.Path))
        ShowRequestInfoHandler(w, r)
    })
    // Listen as https ssl server
    // NOTE: WITHOUT SSL IT WONT WORK!!
    log.Fatal(srv.ListenAndServeTLS("cert.pem", "key.pem"))
}
func ShowRequestInfoHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprintf(w, "Method: %sn", r.Method)
    fmt.Fprintf(w, "Protocol: %sn", r.Proto)
    fmt.Fprintf(w, "Host: %sn", r.Host)
    fmt.Fprintf(w, "RemoteAddr: %sn", r.RemoteAddr)
    fmt.Fprintf(w, "RequestURI: %qn", r.RequestURI)
    fmt.Fprintf(w, "URL: %#vn", r.URL)
    fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)n", r.ContentLength)
    fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)n", r.Close)
    fmt.Fprintf(w, "TLS: %#vn", r.TLS)
    fmt.Fprintf(w, "nHeaders:n")
    r.Header.Write(w)
}

浏览器输入:

结果:

Hi tester "/"
Method: GET
Protocol: HTTP/2.0
Host: localhost:8080
RemoteAddr: [::1]:2750
RequestURI: "/"
URL: &url.URL{Scheme:"", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/", RawPath:"", ForceQuery:false, RawQuery:"", Fragment:""}
Body.ContentLength: 0 (-1 means unknown)
Close: false (relevant for HTTP/1 only)
TLS: &tls.ConnectionState{Version:0x303, HandshakeComplete:true, DidResume:false, CipherSuite:0xc02f, NegotiatedProtocol:"h2", NegotiatedProtocolIsMutual:true, ServerName:"localhost", PeerCertificates:[]*x509.Certificate(nil), VerifiedChains:[][]*x509.Certificate(nil), SignedCertificateTimestamps:[][]uint8(nil), OCSPResponse:[]uint8(nil), TLSUnique:[]uint8{0xa6, 0x3c, 0xfe, 0x93, 0x3c, 0x15, 0x4f, 0x74, 0xfc, 0x97, 0xca, 0x73}}

Headers:
Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Alexatoolbar-Alx_ns_ph: AlexaToolbar/alx-4.0
Cookie: _ga=GA1.1.981224509.1509938615
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36

二. 多路复用

HTTP/二复用TCP连接,在二个接连里,客户端和浏览器都得以同时发送两个请求或应对,而且不要遵照顺序依次对应,那样就防止了”队头堵塞”(见TCP/IP详解卷一)。
golang中使用echo框架中的HTTP,一分钟预览。各类 Frame Header 都有贰个 Stream ID 便是被用于落到实处该特性。每一次请求/响应使用差别的 Stream ID。仿佛同3个 TCP 链接上的多少包通过 IP: PORT 来分别出多少包去往哪里同样。

图片 11

rfc7540: HTTP2 Multiplexing中对Multiplexing的说明

Streams and Multiplexing A "stream" is an independent, bidirectional sequence of frames exchanged between the client and server within an HTTP/2 connection. Streams have several important characteristics: o A single HTTP/2 connection can contain multiple concurrently open streams, with either endpoint interleaving frames from multiple streams. o Streams can be established and used unilaterally or shared by either the client or server. o Streams can be closed by either endpoint. o The order in which frames are sent on a stream is significant. Recipients process frames in the order they are received. In particular, the order of HEADERS and DATA frames is semantically significant. o Streams are identified by an integer. Stream identifiers are assigned to streams by the endpoint initiating the stream.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Streams and Multiplexing
 
   A "stream" is an independent, bidirectional sequence of frames
   exchanged between the client and server within an HTTP/2 connection.
   Streams have several important characteristics:
 
   o  A single HTTP/2 connection can contain multiple concurrently open
      streams, with either endpoint interleaving frames from multiple
      streams.
 
   o  Streams can be established and used unilaterally or shared by
      either the client or server.
 
   o  Streams can be closed by either endpoint.
 
   o  The order in which frames are sent on a stream is significant.
      Recipients process frames in the order they are received.  In
      particular, the order of HEADERS and DATA frames is semantically
      significant.
 
   o  Streams are identified by an integer.  Stream identifiers are
      assigned to streams by the endpoint initiating the stream.

                    if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {
                        console.log("从浏览器cache里取")
                        response.writeHead(304, "Not Modified");
                        response.end();
                    } else {
                        var raw = fs.createReadStream(realPath);
                        var acceptEncoding = request.headers['accept-encoding'] || "";
                        var matched = ext.match(config.Compress.match);

Server Push 怎么用

Server Push

Server Push是什么

归纳来说正是当用户的浏览器和服务器在建设构造链接后,服务器主动将有个别财富推送给浏览器并缓存起来,那样当浏览器接下去请求这个能源时就平昔从缓存中读取,不会在从服务器上拉了,进步了速率。举三个例子就是:
就算三个页面有三个能源文件index.html,index.css,index.js,当浏览器请求index.html的时候,服务器不仅仅重回index.html的从头到尾的经过,同时将index.css和index.js的原委push给浏览器,当浏览器后一次央求那二四个文本时就能够直接从缓存中读取了。

Server Push原理是哪些

要想明白server push原理,首先要清楚一些定义。大家知道HTTP2传输的格式并不像HTTP1使用文本来传输,而是启用了二进制帧(Frames)格式来传输,和server push相关的帧首要分为那三种档期的顺序:

HEADE奥迪Q5S frame(请求再次来到头帧):这种帧首要引导的http请求头新闻,和HTTP壹的header类似。

DATA frames(数据帧) :这种帧存放真正的多寡content,用来传输。
PUSH_PROMISE frame(推送帧):这种帧是由server端发送给client的帧,用来代表server push的帧,这种帧是兑现server push的主要性帧类型。

RST_STREAM(撤废推送帧):这种帧表示请求关闭帧,轻松讲正是当client不想接受一些财富依然收受timeout时会向发送方发送此帧,和PUSH_PROMISE frame一同使用时表示拒绝可能关闭server push。

打探了相关的帧类型,上边正是现实server push的贯彻进程了:
由多路复用我们得以清楚HTTP2中对于同二个域名的呼吁会选择一条tcp链接而用不一样的stream ID来区分各自的请求。
当client使用stream 壹请求index.html时,server平常管理index.html的伸手,并能够摸清index.html页面还就要会呈请index.css和index.js。
server使用stream 1发送PUSH_PROMISE frame给client告诉client作者那边能够应用stream 二来推送index.js和stream 叁来推送index.css财富。
server使用stream 一符合规律的出殡和埋葬HEADEXC90S frame和DATA frames将index.html的原委重回给client。
client接收到PUSH_PROMISE frame得知stream 2和stream 三来收纳推送能源。
server获得index.css和index.js便会发送HEADEXC60S frame和DATA frames将能源发送给client。
client获得push的财富后会缓存起来当呼吁这么些能源时会从直接从从缓存中读取。

3. 数据流

多少流发送到二分一的时候,客户端和服务器都得以发送功率信号(本田UR-VST_STREAM帧),打消以此数据流。一.一版打消数据流的唯壹方法,便是倒闭TCP连接。这便是说,HTTP/贰能够撤销某二回呼吁,同时保险TCP连接还开采着,可以被其它请求使用。

                        if (matched && acceptEncoding.match(/bgzipb/)) {
                            response.writeHead(200, "Ok", {'Content-Encoding': 'gzip'});
                            raw.pipe(zlib.createGzip()).pipe(response);
                        } else if (matched && acceptEncoding.match(/bdeflateb/)) {
                            response.writeHead(200, "Ok", {'Content-Encoding': 'deflate'});
                            raw.pipe(zlib.createDeflate()).pipe(response);
                        } else {
                            response.writeHead(200, "Ok");
                            raw.pipe(response);
                        }
                    }
                }
golang中使用echo框架中的HTTP,一分钟预览。            }
        });

使用 nghttp2 调试 HTTP/2 流量

翻开 HTTP/贰 流量的两种艺术

  • 在 Chrome 地址栏输入 chrome://net-internals/#http2,使用 Chrome 自带的 HTTP/二 调节和测试工具;
    使用方便,但受限于 Chrome 浏览器,对于 Chrome 不帮衬的 h二c(HTTP/2 Cleartext,未有安排 TLS 的 HTTP/二)协议不可能。同时,这么些工具呈现的新闻经过了剖判和筛选,不够健全。
  • 使用 Wireshark 调试 HTTP/2 流量;
    Wireshark 位于服务端和浏览器之间,充当的是高级中学级人剧中人物,用它查看 HTTP/2 over HTTPS 流量时,必须有所网址私钥只怕借助浏览器共享对称密钥,才具解密 TLS 流量,配置起来相比较劳苦。

nghttp2,是多个用 C 实现的 HTTP/2 库,辅助h贰c。它能够做为别的软件的壹局部,为其提供 HTTP/二 相关成效(比方 curl 的 HTTP/2 作用正是用的 nghttp2)。除了那一个之外,它还提供了多个有效的 HTTP/2工具:

  • nghttp:HTTP/2 客户端;
  • nghttpd:HTTP/2 服务端;
  • nghttpx:HTTP/贰 代理,提供 HTTP/壹、HTTP/二 等争执时期的调换;
  • h二load:HTTP/2 品质测试工具;

Golang1.8中的Server Push

代码main.go:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

var image []byte

// preparing image
func init() {
    var err error
    image, err = ioutil.ReadFile("./image.png")
    if err != nil {
        panic(err)
    }
}

// Send HTML and push image
func handlerHtml(w http.ResponseWriter, r *http.Request) {
    pusher, ok := w.(http.Pusher)
    if ok {
        fmt.Println("Push /image")
        pusher.Push("/image", nil)
    }
    w.Header().Add("Content-Type", "text/html")
    fmt.Fprintf(w, `<html><body><img src="/image"></body></html>`)
}

// Send image as usual HTTP request
func handlerImage(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "image/png")
    w.Write(image)
}
func main() {
    http.HandleFunc("/", handlerHtml)
    http.HandleFunc("/image", handlerImage)
    fmt.Println("start http listening :18443")
    err := http.ListenAndServeTLS(":18443", "server.crt", "server.key", nil)
    fmt.Println(err)
}

浏览器输入:

能够选用插件HTTP/2 and SPDY indicator
chrome://net-internals/#http2

四. 头新闻压缩:

HTTP/二 对音讯头选拔 HPACK 进行压缩传输,能够节省音讯头占用的网络的流量。而 HTTP/一.x 每一次请求,都会指点大批量冗余头音信,浪费了繁多带宽资源。
HTTP2对http头建立索引表,一样的头只发送hash table 的index, 同时还用了霍夫曼编码和古板的gzip压缩。

    }
    pathHandle(realPath);
});
server.listen(port);
console.log("http server run in port:" port);

nghttp2 安装

先来用 brew 看一下有未有 nghttp 相关的库:

~ brew search nghttp
nghttp2

总的来讲是有 nghttp二 的,再用 brew 看下须求设置哪些条件:

~ brew info nghttp2
nghttp2: stable 1.21.0 (bottled), HEAD
HTTP/2 C Library
https://nghttp2.org/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/nghttp2.rb
==> Dependencies
Build: sphinx-doc ✘, pkg-config ✔, cunit ✘
Required: c-ares ✘, libev ✘, openssl ✔, libevent ✘, jansson ✘, boost ✘, spdylay ✘
Recommended: jemalloc ✘
==> Requirements
Optional: python3 ✔
==> Options
--with-examples
    Compile and install example programs
--with-python3
    Build python3 bindings
--without-docs
    Don't build man pages
--without-jemalloc
    Build without jemalloc support
--HEAD
    Install HEAD version

看来要求的重视性还挺多。

使用 brew 安装 nghttp2 :

brew install nghttp2

整个妥善后,nghttp二 提供的多少个工具就足以一向用了。

echo框架中的Server Push

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>HTTP/2 Server Push</title>
  <link rel="stylesheet" href="/app.css">
  <script src="/app.js"></script>
</head>
<body>
  <img class="echo" src="/echo.png">
  <h2>The following static files are served via HTTP/2 server push</h2>
  <ul>
    <li><code>/app.css</code></li>
    <li><code>/app.js</code></li>
    <li><code>/echo.png</code></li>
  </ul>
</body>
</html>

main.go

package main

import (
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.Static("/", "static")
    e.GET("/", func(c echo.Context) (err error) {
        pusher, ok := c.Response().Writer.(http.Pusher)
        if ok {
            if err = pusher.Push("/app.css", nil); err != nil {
                return
            }
            if err = pusher.Push("/app.js", nil); err != nil {
                return
            }
            if err = pusher.Push("/echo.png", nil); err != nil {
                return
            }
        }
        return c.File("index.html")
    })
    e.Logger.Fatal(e.StartTLS(":1323", "cert.pem", "key.pem"))
}

浏览器输入:

参考:

图片 12

伍. 服务器推送

服务端能够更快的把能源推送给客户端。比如服务端能够积极把 JS 和 CSS 文件推送给客户端,而无需客户端深入分析 HTML 再发送这几个请求。当客户端须求的时候,它早已在客户端了。

那么存在贰个难点,借使客户端设置了缓存如何做。有三种方法(来自社区)

  • 客户端能够透过安装SETTINGS_ENABLE_PUSH为0值文告服务器端禁止使用推送
  • 意识缓存后,客户端和服务器都足以发送实信号(科雷傲ST_STREAM帧),裁撤这一个数据流。
  • cache-digest(提案)

    rfc7540: HTTP2 Server Push

    #### 6. 流优先级

    HTTP贰允许浏览器内定财富的优先级。

    rfc7540: Stream Priority

率先需求在JS文件里创制三个assets的文本夹,里面放入你要浏览的静态文件,例如,index.html,demo.js等。

nghttp

nghttp 做为八个功效一体化的 HTTP/二 客户端,非常适合用来查阅和调治 HTTP/贰流量。它帮助的参数大多,通过官方文书档案只怕 nghttp -h 都能查看。最常用多少个参数如下:

  • -v, --verbose,输出完整的 debug 音讯;
  • -n, --null-out,放弃下载的多寡;
  • -a, --get-assets,下载 html 中的 css、js、image 等外链财富;
  • -H, --header = < HEADE宝马X三 >,增多请求底部字段,如 -H':method: PUT';
  • -u, --upgrade,使用 HTTP 的 Upgrade 机制来研究 HTTP/贰 协议,用于 h二c,详见上面包车型地铁例证;

以下是行使 nghttp 访问 https://h2o.examp1e.net 的结果。从调节和测试新闻中得以清楚看到 h二c 协商以及 Server Push 的百分百进程:

nghttp -nv 'https://h2o.examp1e.net'
[  0.201] Connected
The negotiated protocol: h2
[  1.180] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  1.180] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  1.180] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  1.180] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  1.180] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  1.180] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  1.180] send HEADERS frame <length=39, flags=0x25, stream_id=13>
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: https
          :authority: h2o.examp1e.net
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.21.1
[  1.373] recv SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):16777216]
[  1.373] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  1.373] recv (stream_id=13) :method: GET
[  1.373] recv (stream_id=13) :scheme: https
[  1.373] recv (stream_id=13) :authority: h2o.examp1e.net
[  1.373] recv (stream_id=13) :path: /search/jquery-1.9.1.min.js
[  1.373] recv (stream_id=13) accept: */*
[  1.373] recv (stream_id=13) accept-encoding: gzip, deflate
[  1.373] recv (stream_id=13) user-agent: nghttp2/1.21.1
[  1.373] recv PUSH_PROMISE frame <length=59, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=2)
[  1.373] recv (stream_id=2) :status: 200
[  1.373] recv (stream_id=2) server: h2o/2.2.0-beta2
[  1.373] recv (stream_id=2) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.373] recv (stream_id=2) content-type: application/javascript
[  1.373] recv (stream_id=2) last-modified: Thu, 14 May 2015 04:10:14 GMT
[  1.373] recv (stream_id=2) etag: "55542026-169d5"
[  1.373] recv (stream_id=2) accept-ranges: bytes
[  1.373] recv (stream_id=2) x-http2-push: pushed
[  1.373] recv (stream_id=2) content-length: 92629
[  1.373] recv HEADERS frame <length=126, flags=0x04, stream_id=2>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  1.373] recv (stream_id=13) :method: GET
[  1.373] recv (stream_id=13) :scheme: https
[  1.373] recv (stream_id=13) :authority: h2o.examp1e.net
[  1.373] recv (stream_id=13) :path: /search/oktavia-jquery-ui.js
[  1.373] recv (stream_id=13) accept: */*
[  1.373] recv (stream_id=13) accept-encoding: gzip, deflate
[  1.373] recv (stream_id=13) user-agent: nghttp2/1.21.1
[  1.373] recv PUSH_PROMISE frame <length=33, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=4)
[  1.373] recv (stream_id=4) :status: 200
[  1.373] recv (stream_id=4) server: h2o/2.2.0-beta2
[  1.373] recv (stream_id=4) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.373] recv (stream_id=4) content-type: application/javascript
[  1.373] recv (stream_id=4) last-modified: Thu, 14 May 2015 04:10:14 GMT
[  1.373] recv (stream_id=4) etag: "55542026-1388"
[  1.373] recv (stream_id=4) accept-ranges: bytes
[  1.374] recv (stream_id=4) x-http2-push: pushed
[  1.374] recv (stream_id=4) content-length: 5000
[  1.374] recv HEADERS frame <length=28, flags=0x04, stream_id=4>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  1.374] recv (stream_id=13) :method: GET
[  1.374] recv (stream_id=13) :scheme: https
[  1.374] recv (stream_id=13) :authority: h2o.examp1e.net
[  1.374] recv (stream_id=13) :path: /search/oktavia-english-search.js
[  1.374] recv (stream_id=13) accept: */*
[  1.374] recv (stream_id=13) accept-encoding: gzip, deflate
[  1.374] recv (stream_id=13) user-agent: nghttp2/1.21.1
[  1.374] recv PUSH_PROMISE frame <length=35, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=6)
[  1.374] recv (stream_id=6) :status: 200
[  1.374] recv (stream_id=6) server: h2o/2.2.0-beta2
[  1.374] recv (stream_id=6) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.374] recv (stream_id=6) content-type: application/javascript
[  1.374] recv (stream_id=6) last-modified: Thu, 14 May 2015 04:10:14 GMT
[  1.374] recv (stream_id=6) etag: "55542026-34dd6"
[  1.374] recv (stream_id=6) accept-ranges: bytes
[  1.374] recv (stream_id=6) x-http2-push: pushed
[  1.374] recv (stream_id=6) content-length: 216534
[  1.374] recv HEADERS frame <length=31, flags=0x04, stream_id=6>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  1.374] recv (stream_id=13) :method: GET
[  1.374] recv (stream_id=13) :scheme: https
[  1.374] recv (stream_id=13) :authority: h2o.examp1e.net
[  1.374] recv (stream_id=13) :path: /assets/style.css
[  1.374] recv (stream_id=13) accept: */*
[  1.374] recv (stream_id=13) accept-encoding: gzip, deflate
[  1.374] recv (stream_id=13) user-agent: nghttp2/1.21.1
[  1.374] recv PUSH_PROMISE frame <length=24, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=8)
[  1.374] recv (stream_id=8) :status: 200
[  1.374] recv (stream_id=8) server: h2o/2.2.0-beta2
[  1.374] recv (stream_id=8) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.374] recv (stream_id=8) content-type: text/css
[  1.374] recv (stream_id=8) last-modified: Tue, 20 Sep 2016 05:27:06 GMT
[  1.374] recv (stream_id=8) etag: "57e0c8aa-1586"
[  1.374] recv (stream_id=8) accept-ranges: bytes
[  1.374] recv (stream_id=8) x-http2-push: pushed
[  1.374] recv (stream_id=8) content-length: 5510
[  1.374] recv HEADERS frame <length=58, flags=0x04, stream_id=8>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  1.374] recv (stream_id=13) :method: GET
[  1.374] recv (stream_id=13) :scheme: https
[  1.374] recv (stream_id=13) :authority: h2o.examp1e.net
[  1.374] recv (stream_id=13) :path: /assets/searchstyle.css
[  1.374] recv (stream_id=13) accept: */*
[  1.374] recv (stream_id=13) accept-encoding: gzip, deflate
[  1.374] recv (stream_id=13) user-agent: nghttp2/1.21.1
[  1.374] recv PUSH_PROMISE frame <length=28, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=10)
[  1.374] recv (stream_id=10) :status: 200
[  1.374] recv (stream_id=10) server: h2o/2.2.0-beta2
[  1.374] recv (stream_id=10) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.374] recv (stream_id=10) content-type: text/css
[  1.374] recv (stream_id=10) last-modified: Tue, 20 Sep 2016 05:27:06 GMT
[  1.374] recv (stream_id=10) etag: "57e0c8aa-8dd"
[  1.374] recv (stream_id=10) accept-ranges: bytes
[  1.374] recv (stream_id=10) x-http2-push: pushed
[  1.374] recv (stream_id=10) content-length: 2269
[  1.374] recv HEADERS frame <length=27, flags=0x04, stream_id=10>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  1.374] recv (stream_id=13) :status: 200
[  1.374] recv (stream_id=13) server: h2o/2.2.0-beta2
[  1.374] recv (stream_id=13) date: Mon, 10 Apr 2017 06:30:29 GMT
[  1.374] recv (stream_id=13) link: </search/jquery-1.9.1.min.js>; rel=preload
[  1.374] recv (stream_id=13) link: </search/oktavia-jquery-ui.js>; rel=preload
[  1.374] recv (stream_id=13) link: </search/oktavia-english-search.js>; rel=preload
[  1.374] recv (stream_id=13) link: </assets/style.css>; rel=preload
[  1.374] recv (stream_id=13) link: </assets/searchstyle.css>; rel=preload
[  1.374] recv (stream_id=13) cache-control: no-cache
[  1.374] recv (stream_id=13) content-type: text/html
[  1.374] recv (stream_id=13) last-modified: Wed, 05 Apr 2017 06:55:14 GMT
[  1.374] recv (stream_id=13) etag: "58e494d2-1665"
[  1.374] recv (stream_id=13) accept-ranges: bytes
[  1.374] recv (stream_id=13) set-cookie: h2o_casper=AmgAAAAAAAAAAAAYxfEYAAABSA; Path=/; Expires=Tue, 01 Jan 2030 00:00:00 GMT; Secure
[  1.374] recv (stream_id=13) content-length: 5733
[  1.374] recv HEADERS frame <length=304, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  1.375] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  1.566] recv DATA frame <length=16137, flags=0x00, stream_id=2>
[  1.567] recv DATA frame <length=5000, flags=0x01, stream_id=4>
          ; END_STREAM
[  1.567] recv DATA frame <length=4915, flags=0x00, stream_id=6>
[  1.766] recv DATA frame <length=2829, flags=0x00, stream_id=8>
[  1.766] recv DATA frame <length=2269, flags=0x01, stream_id=10>
          ; END_STREAM
[  1.766] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=33120)
[  1.767] recv DATA frame <length=9065, flags=0x00, stream_id=2>
[  1.970] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  1.970] recv DATA frame <length=2681, flags=0x01, stream_id=8>
          ; END_STREAM
[  1.971] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=2>
          (window_size_increment=33855)
[  1.971] recv DATA frame <length=10072, flags=0x00, stream_id=2>
[  2.172] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  2.172] recv DATA frame <length=4248, flags=0x00, stream_id=2>
[  2.173] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  2.173] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34002)
[  2.173] recv DATA frame <length=4248, flags=0x00, stream_id=2>
[  2.577] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  2.578] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  2.579] recv DATA frame <length=12762, flags=0x00, stream_id=6>
[  2.777] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  2.777] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=33241)
[  2.778] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  3.177] recv DATA frame <length=8505, flags=0x00, stream_id=2>
[  3.177] recv DATA frame <length=5667, flags=0x00, stream_id=6>
[  3.177] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=33993)
[  3.177] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  3.177] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  3.378] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  3.579] recv DATA frame <length=11343, flags=0x00, stream_id=6>
[  3.580] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34002)
[  3.580] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=2>
          (window_size_increment=33984)
[  3.583] recv DATA frame <length=7086, flags=0x00, stream_id=2>
[  3.779] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  4.186] recv DATA frame <length=7086, flags=0x00, stream_id=2>
[  4.186] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  4.186] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  4.395] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  4.396] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  4.602] recv DATA frame <length=5667, flags=0x00, stream_id=6>
[  4.602] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=33993)
[  4.602] recv DATA frame <length=2829, flags=0x00, stream_id=2>
[  4.602] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=33975)
[  4.808] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  4.809] recv DATA frame <length=6379, flags=0x01, stream_id=2>
          ; END_STREAM
[  5.010] recv DATA frame <length=3536, flags=0x00, stream_id=6>
[  5.420] recv DATA frame <length=8505, flags=0x00, stream_id=6>
[  5.420] recv DATA frame <length=5667, flags=0x00, stream_id=6>
[  5.628] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  5.842] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  5.842] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  5.842] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34002)
[  5.842] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=33281)
[  6.057] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  6.273] recv DATA frame <length=8505, flags=0x00, stream_id=6>
[  6.490] recv DATA frame <length=9924, flags=0x00, stream_id=6>
[  6.490] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  6.706] recv DATA frame <length=4248, flags=0x00, stream_id=6>
[  6.706] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34002)
[  6.706] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=34002)
[  6.924] recv DATA frame <length=8505, flags=0x00, stream_id=6>
[  7.141] recv DATA frame <length=8505, flags=0x00, stream_id=6>
[  7.361] recv DATA frame <length=8505, flags=0x00, stream_id=6>
[  7.361] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34020)
[  7.574] recv DATA frame <length=9924, flags=0x00, stream_id=6>
[  7.574] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=34029)
[  7.787] recv DATA frame <length=9924, flags=0x00, stream_id=6>
[  7.787] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  7.998] recv DATA frame <length=7086, flags=0x00, stream_id=6>
[  8.210] recv DATA frame <length=9924, flags=0x00, stream_id=6>
[  8.210] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=34011)
[  8.210] send WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=6>
          (window_size_increment=34011)
[  8.425] recv DATA frame <length=11343, flags=0x00, stream_id=6>
[  8.426] recv DATA frame <length=2829, flags=0x00, stream_id=6>
[  8.426] recv DATA frame <length=4053, flags=0x01, stream_id=6>
          ; END_STREAM
[  8.631] recv DATA frame <length=4443, flags=0x00, stream_id=13>
[  8.633] recv DATA frame <length=1290, flags=0x01, stream_id=13>
          ; END_STREAM
[  8.633] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=10, error_code=NO_ERROR(0x00), opaque_data(0)=[])

当然,大家也足以运用 grep 搜索出来 server push 的相关 stream:

nghttp -nv 'https://h2o.examp1e.net' | grep 'PUSH_PROMISE'
[  1.582] recv PUSH_PROMISE frame <length=59, flags=0x04, stream_id=13>
[  1.582] recv PUSH_PROMISE frame <length=33, flags=0x04, stream_id=13>
[  1.582] recv PUSH_PROMISE frame <length=35, flags=0x04, stream_id=13>
[  1.582] recv PUSH_PROMISE frame <length=24, flags=0x04, stream_id=13>
[  1.582] recv PUSH_PROMISE frame <length=28, flags=0x04, stream_id=13>

浏览器帮衬

主流浏览器都只帮忙 HTTP/二 Over TLS

运作方式为:在指令行里切换成地点的JS的文件目录,然后输入 node JS文件名

使用 NodeJS 搭建 HTTP/2 服务器

在大前端的时期背景下,客户端支出不会点 JavaScript 都快混不下去了,小编前2个月在自个儿司前端轮岗了两周,再增进在此之前也写过 ReactNative,但要么以为到前端变化之快领人惊讶,革命尚未终了,同志仍需努力啊。

大家直接上代码:

var http2 = require('http2');// http2
var url=require('url'); // https://www.npmjs.com/package/url
var fs=require('fs'); // https://www.npmjs.com/package/fs
var mine=require('mine');
var path=require('path'); // 路径

var server = http2.createServer({
  key: fs.readFileSync('./localhost.key'),
  cert: fs.readFileSync('./localhost.crt')
}, function(request, response) {

    // var pathname = url.parse(request.url).pathname;
    var realPath = './push.json' ;//path.join(pathname,"push.json");    //这里设置自己的文件路径,这是该次response返回的内容;

    var pushArray = [];
    var ext = path.extname(realPath);
    ext = ext ? ext.slice(1) : 'unknown';
    var contentType = mine[ext] || "text/plain";

    if (fs.existsSync(realPath)) {

        console.log('success')
        response.writeHead(200, {
            'Content-Type': contentType
        });

        response.write(fs.readFileSync(realPath,'binary'));

        // 注意 push 路径必须是绝对路径,这是该次 server push 返回的内容
        var pushItem = response.push('/Users/f.li/Desktop/http2-nodeServer/newpush.json', {
                response: {
                  'content-type': contentType
                }    
        });
        pushItem.end(fs.readFileSync('/Users/f.li/Desktop/http2-nodeServer/newpush.json','binary'),()=>{
          console.log('newpush end')
        });

        response.end();

    } else {
      response.writeHead(404, {
          'Content-Type': 'text/plain'
      });

      response.write("This request URL "   realPath   " was not found on this server.");
      response.end();
    }

});

server.listen(3000, function() {
  console.log('listen on 3000');
});

这里要求注意几点:

  • 创办http二的nodejs服务必须时依照https的,因为现在主流的浏览器都要支持SSL/TLS的http贰,证书和私钥能够友善通过OPENSSL生成。
  • node http二的连锁api和例行的node httpserver同样,可以一向动用。

接纳 nghttp 测试一下大家的代码有未有拓展 server push:

~ nghttp -nv 'https://localhost:3000/'
[  0.007] Connected
The negotiated protocol: h2
[  0.029] recv SETTINGS frame <length=0, flags=0x00, stream_id=0>
          (niv=0)
[  0.029] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.029] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.029] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.029] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.029] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.029] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.029] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.029] send HEADERS frame <length=38, flags=0x25, stream_id=13>
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: https
          :authority: localhost:3000
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.21.1
[  0.043] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.049] recv (stream_id=13) :status: 200
[  0.049] recv (stream_id=13) content-type: text/plain
[  0.049] recv (stream_id=13) date: Tue, 11 Apr 2017 08:34:46 GMT
[  0.049] recv HEADERS frame <length=34, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  0.049] recv DATA frame <length=35, flags=0x00, stream_id=13>
[  0.049] recv (stream_id=13) :method: GET
[  0.049] recv (stream_id=13) :scheme: https
[  0.050] recv (stream_id=13) :authority: localhost:3000
[  0.050] recv (stream_id=13) :path: /Users/f.li/Desktop/http2-nodeServer/newpush.json
[  0.050] recv PUSH_PROMISE frame <length=56, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=2)
[  0.050] recv DATA frame <length=0, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.050] recv (stream_id=2) :status: 200
[  0.050] recv (stream_id=2) date: Tue, 11 Apr 2017 08:34:46 GMT
[  0.050] recv HEADERS frame <length=2, flags=0x04, stream_id=2>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  0.050] recv DATA frame <length=21, flags=0x00, stream_id=2>
[  0.050] recv DATA frame <length=0, flags=0x01, stream_id=2>
          ; END_STREAM
[  0.050] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[])

看到了 PUSH_PROMISE 的帧,表明实行了 server push。

一律也得以行使chrome查看 server push,如下图所示:

图片 13

chrome 查看 http2 server push

服务端介绍主题竣事。上面我们来介绍一些 iOS 客户端对 Server Push 的利用。

node中启用http2

node中得以用spdy模块来运转应用,spdy的api,与https是如出1辙的且主流浏览器只帮助HTTP/2Over TLS,供给布署 私钥和证书,本地自签署服务器配置可参看引用6,7

JavaScript

const express = require('express'); const fs = require('fs'); const http2 = require('spdy'); const path = require('path'); const options = { key: fs.readFileSync('./keys/privatekey.pem'), cert: fs.readFileSync('./keys/certificate.pem') }; const app = new express(); http2 .createServer(options, app) .listen(8080, ()=>{ console.log(`Server is listening on . You can open the URL in the browser.`) } ) app.use("/",(req,res)=>{ res.send("hello http2!"); })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const express = require('express');
const fs =  require('fs');
const http2 = require('spdy');
const path = require('path');
const options = {
    key: fs.readFileSync('./keys/privatekey.pem'),
    cert: fs.readFileSync('./keys/certificate.pem')
};
const app = new express();
http2
  .createServer(options, app)
  .listen(8080, ()=>{
    console.log(`Server is listening on https://localhost:8080.
     You can open the URL in the browser.`)
  }
)
app.use("/",(req,res)=>{
    
  res.send("hello http2!");
})

如上,对于已存在的门类只要修改几行代码就足以行使http贰.0了。

请求头和响应头:

证实:新版的Chrome,对不安全的证件(如本地的自签署服务)会降级到http一.一,firefox不汇合世此难题。

启动server push

JavaScript

app.get("/",(req,res)=>{ var stream = res.push('/app.js', { //服务器推送 status: 200, // optional method: 'GET', // optional request: { accept: '*/*' }, response: { 'content-type': 'application/javascript' } }) stream.on('error', function() { }) stream.end('console.log("http2 push stream, by Lucien ");') res.send(`hello http2! <script src="/app.js"></script>`);//express 并没有host static ,这个app.js 来自push })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.get("/",(req,res)=>{
    var stream = res.push('/app.js', {   //服务器推送
    status: 200, // optional
    method: 'GET', // optional
    request: {
      accept: '*/*'
    },
    response: {
      'content-type': 'application/javascript'
    }
  })
  stream.on('error', function() {
  })
  stream.end('console.log("http2 push stream, by Lucien ");')
 
  res.send(`hello http2!
    <script src="/app.js"></script>`);//express 并没有host static ,这个app.js 来自push
})

源码在github

响应

浏览器内输入

iOS 使用 HTTP/2 Server Push

Apple 在那方面做的很好,基本落到实处了客户端无感调用http/二 server push。可是我查阅了有点素材,现在唯有iOS 拾 支持 http/二。

直白上代码吧:

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDelegate>

@property(nonatomic,strong)NSURLSession *session;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}
#pragma mark - Touch
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self urlSession];
}
#pragma mark - 发送请求
- (void)urlSession
{
    NSURL *url = [NSURL URLWithString:@"https://localhost:3000"];

    //发送HTTPS请求是需要对网络会话设置代理的
    _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSURLSessionDataTask *dataTask = [_session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        // 收到该次请求后,立即请求下次的内容
        [self urlSessionPush];

    }];

    [dataTask resume];
}

- (void)urlSessionPush
{
    NSURL *url = [NSURL URLWithString:@"https://localhost:3000/Users/f.li/Desktop/http2-nodeServer/newpush.json"];
    NSURLSessionDataTask *dataTask = [_session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    [dataTask resume];
}

#pragma mark - URLSession Delegate
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    // 这里还要设置下 plist 中设置 ATS
    if (![challenge.protectionSpace.authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"])
    {
        return;
    }
    NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
{
    NSArray *fetchTypes = @[ @"Unknown", @"Network Load", @"Server Push", @"Local Cache"];

    for(NSURLSessionTaskTransactionMetrics *transactionMetrics in [metrics transactionMetrics])
    {

        NSLog(@"protocol[%@] reuse[%d] fetch:%@ - %@", [transactionMetrics networkProtocolName], [transactionMetrics isReusedConnection], fetchTypes[[transactionMetrics resourceFetchType]], [[transactionMetrics request] URL]);

        if([transactionMetrics resourceFetchType] == NSURLSessionTaskMetricsResourceFetchTypeServerPush)
        {
            NSLog(@"Asset was server pushed");
        }
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

分别看下服务端和客户端的Log:
客户端:

Http2ServerPush[2525:274943] protocol[h2] reuse[0] fetch:Network Load - https://localhost:3000/
Http2ServerPush[2525:274943] {"message":" http2.0 server is ok"}
Http2ServerPush[2525:274943] protocol[h2] reuse[1] fetch:Server Push - https://localhost:3000/Users/f.li/Desktop/http2-nodeServer/newpush.json
Http2ServerPush[2525:274943] Asset was server pushed
Http2ServerPush[2525:274943] {"message":"newPush"}

服务端:

http2-nodeServer npm start

> http2-nodeServer@1.0.0 start /Users/f.li/Desktop/http2-nodeServer
> node index.js

listen on 3000
success
newpush end

总的看确实是客户端发出了五回呼吁,可是服务端只响应了叁回(该次响应 server push)

抓包分析

能够用chrome 内部自带的工具(chrome://net-internals/)查看http二流量,但以此包音讯量相比较少,结构比不上大家熟谙的Fiddler查看http2流量,但这个包信息量比较少,结构不如我们熟悉的Fiddler) or Wireshark清晰。

Fiddler是向来作为中间代理,能够视作客户端直接与服务端通信,能够像浏览器那样直接解密https,直接看出https报文,
可是出于受限于.NET Framework暂不帮助Http贰.

用wireshark直接抓包 https:4四三端口的流量是那样的:

多少被加密了,协议细节完全看不到。
这里介绍了一种办法拿到私钥解包。
抓包https包时要把代理关了,不然私钥不是同3个,wireshark不可能解包(被这些坑了两时辰T T)。

3个包内有八个分歧的Steam ID

跟踪解密后TCP流能够看到,由于多路复用,各类分化的呼吁交替传输不一样的帧,所以流数据是乱的。但在同等帧内数据如故好端端的。

--补上地点代码里贫乏的三个模块

本文相关德姆o

  • Github:lijianfeigeek

最后

末段,HTTP2有越来越高的传输速度,越来越少的能源占用,能够去除各个质量优化tricks(如css sprite,inline-image.)
倒车WEB开垦的美好以后T.T

mime.js

参谋文献

  • HTTP2 Server Push的研究 | AlloyTeam
  • 使用 nghttp2 调试 HTTP/2 流量 | JerryQu 的小站
  • objective c - HTTP/2 Server Push in iOS 10 - Stack Overflow
  • 使用NSURLSession或者AFN发送HTTPS请求 - 简书

参照他事他说加以考察资料

  1. Turn-on HTTP/2 today!
  2. Hypertext Transfer Protocol Version 2 (HTTP/2)
  3. npm spdy
  4. npm spdy push
  5. How to create a self-signed SSL Certificate
  6. HPACK: Header Compression for HTTP/2
  7. 用Node.js创立自签订契约的HTTPS服务器

    1 赞 收藏 评论

图片 14

复制代码 代码如下:

exports.types = {

  "css": "text/css",

  "gif": "image/gif",

  "html": "text/html",

  "ico": "image/x-icon",

  "jpeg": "image/jpeg",

  "jpg": "image/jpeg",

  "js": "text/javascript",

  "json": "application/json",

  "pdf": "application/pdf",

  "png": "image/png",

  "svg": "image/svg xml",

  "swf": "application/x-shockwave-flash",

  "tiff": "image/tiff",

  "txt": "text/plain",

  "wav": "audio/x-wav",

  "wma": "audio/x-ms-wma",

  "wmv": "video/x-ms-wmv",

  "xml": "text/xml"
};

config.js

复制代码 代码如下:

exports.Expires = {
    fileMatch: /^(gif|png|jpg|js|css)$/ig,
    maxAge: 60 * 60 * 24 * 365
};

exports.Compress = {
    match: /css|js|html/ig
};

exports.Welcome = {
    file: "index.html"
};

你只怕感兴趣的稿子:

  • 实战node静态文件服务器的示范代码
  • Node.js静态服务器的贯彻方式
  • nodejs创设地面web测试服务器 怎么着化解访问静态财富难点
  • 用Nodejs搭建服务器访问html、css、JS等静态财富文件
  • 在windows上用nodejs搭建静态文件服务器的粗略方法
  • Node.js静态文件服务器创新版
  • 接纳nodejs、Python写的二个轻便HTTP静态文件服务器
  • 手记Node静态财富服务器的贯彻格局

本文由澳门新萄京官方网站发布于澳门新萄京赌场网址,转载请注明出处:golang中使用echo框架中的HTTP,一分钟预览

关键词: