HTTP2 Server Push的商讨

作者: 前端应用  发布:2019-11-09

HTTP2 Server Push的研究

2017/01/05 · 幼功技巧 · HTTP/2

初稿出处: AlloyTeam   

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

1,HTTP2的新特色。

有关HTTP2的新特性,读着能够参谋小编后边的稿子,这里就不在多说了,本篇小说首要讲一下server push这一个个性。

HTTP,HTTP2.0,SPDY,HTTPS你应当理解的部分事

 


2,Server Push是什么。

简言之来说就是当客商的浏览器和服务器在创制链接后,服务器主动将豆蔻梢头部分能源推送给浏览器并缓存起来,这样当浏览器接下去央求这么些能源时就直接从缓存中读取,不会在从服务器上拉了,提高了速率。举三个例证便是:

设若一个页面有3个能源文件index.html,index.css,index.js,当浏览器央浼index.html的时候,服务器不仅仅再次回到index.html的剧情,同临时常候将index.css和index.js的原委push给浏览器,当浏览器后一次央求那2多个文件时就可以平素从缓存中读取了。

图片 1

3,Server Push原理是怎么。

要想了然server push原理,首先要明白一些概念。咱们清楚HTTP2传输的格式并不像HTTP1使用文本来传输,而是启用了二进制帧(Frames)格式来传输,和server push相关的帧首要分为那三种类型:

  1. HEADE纳瓦拉S 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。

Note:HTTP2.0连锁的帧其实包蕴10种帧,便是因为底部数据格式的改观,才为HTTP2.0带动好些个的风味,帧的引进不止有助于减量,也许有扶植数据的安全性和保障传输性。

刺探了相关的帧类型,上面正是具体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 3来推送index.css资源。
  4. server使用stream 1平常的发送HEADEXC90S frame和DATA frames将index.html的内容再次回到给client。
  5. client接收到PUSH_PROMISE frame获知stream 2和stream 3来接受推送能源。
  6. server获得index.css和index.js便会发送HEADE奥德赛S frame和DATA frames将财富发送给client。
  7. client获得push的财富后会缓存起来当呼吁这一个财富时会从第一手从从缓存中读取。

下图表示了一切工艺流程:

图片 2

HTTP/2

4,Server Push怎么用。

既然server push这么玄妙,那么我们怎样使用啊?怎么设置服务器push哪些文件呢?

先是并非兼具的服务器都扶助server push,nginx前段时间还不扶助那一个特点,能够在nginx的合法博客上赢得验证,可是Apache和nodejs都早已支撑了server push那多少个风味,供给证明一(Wissu卡塔 尔(阿拉伯语:قطر‎些的是server push那个特性是基于浏览器和服务器的,所以浏览器并未提供相应的js api来让顾客一贯操作和调节push的剧情,所以必须要是通过header新闻和server的布置来落到实处具体的push内容,本文主要以nodejs来证实实际怎么使用server push那风姿浪漫风味。

有备无患专业:下载nodejs http2扶持,本地运转nodejs服务。

1. 第一大家应用nodejs搭建基本的server:

JavaScript

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'); });

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,大家得以看来全体须求都走了http2,同一时间也足以作证多路复用的表征。

图片 3

此地要求小心几点:

  1. 成立http2的nodejs服务必得时依据https的,因为以往主流的浏览器都要扶助SSL/TLS的http2,证书和私钥能够和煦通过OPENSSL生成。
  2. node http2的相干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到我们的浏览器,大家能够在浏览器中查阅:

图片 4

能够见见,运维server push的财富timelime极快,大大加速了css的得到时间。

这里须求在意上边几点:

  1. 大家调用response.push(),正是风姿洒脱对黄金年代于server发起了PUSH_PROMISE frame来报告浏览器bootstrap.min.css将会由server push来赢得。
  2. response.push()再次回到的对象时二个平常的ServerResponse,end(),writeHeader()等措施都足以健康调用。
  3. 这里后生可畏旦针对某些能源调用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,其实整个http2都以以stream为单位,这里的stream其实能够领会成一个央浼,越来越多的api能够参见:node-http2。

  5. 最后给我们推荐二个老外写的特地服务http2的node server有兴趣的能够尝试一下。

HTTP/2 Server Push 是什么

当客户的浏览器和服务器在确立链接后,服务器主动将部分能源推送给浏览器并缓存起来,那样当浏览器接下去诉求这么些能源时就一直从缓存中读取,不会在从服务器上拉了,进步了速率。举叁个事例就是:

如果一个页面有3个能源文件index.html,index.css,index.js,当浏览器伏乞index.html的时候,服务器不仅仅再次回到index.html的始末,同期将index.css和index.js的源委push给浏览器,当浏览器下一次乞求这2多个文件时就足以平素从缓存中读取了。

平常来讲图所示:

图片 5

Apple-http2ServerPush

5,Server Push相关难题。

  1. 笔者们知晓今后大家web的财富通常都是投身CDN上的,那么CDN的优势和server push的优势有啥不同呢,到底是哪位很快吧?这一个难点我也直接在研商,本文的相关demo都只好算做叁个示范,具体的线上进行还在进展中。
  2. 出于HTTP2的有的新性情举例多路复用,server push等等都以基于同叁个域名的,所以那只怕会对我们事先对于HTTP1的风度翩翩部分优化措施举例(能源拆分域名,归拢等等)不肯定适用。
  3. server push既可以够看成拉取静态能源,大家的cgi须求即ajax要求相仿能够运用server push来发送数据。
  4. 最全面包车型地铁结果是CDN域名扶植HTTP2,web server域名也还要扶持HTTP2。

 

参谋资料:

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

图片 6

HTTP/2 Server Push 原理是什么

要想精晓server push原理,首先要清楚一些概念。我们掌握HTTP/2传输的格式并不像HTTP1使用文本来传输,而是启用了二进制帧(Frames)格式来传输,和server push相关的帧首要分为这几种档案的次序:

  1. HEADE宝马7系S 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/2相关的帧其实富含10种帧,便是因为尾巴部分数据格式的更动,才为HTTP/2带给超多的风味,帧的引进不止有帮衬压缩数量,也便于数据的安全性和可靠传输性。)

问询了有关的帧类型,上面正是现实server push的完结进程了:

  1. 由多路复用大家能够通晓HTTP/2中对于同三个域名的乞请会采取一条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 3来推送index.css财富。
  4. server使用stream 1平常的出殡和下葬HEADEEvoqueS frame和DATA frames将index.html的故事情节重回给client。
  5. client接收到PUSH_PROMISE frame得悉stream 2和stream 3来抽出推送财富。
  6. server获得index.css和index.js便会发送HEADE昂科雷S frame和DATA frames将财富发送给client。
  7. client获得push的财富后会缓存起来当号召那几个财富时会从直接从从缓存中读取。

Server Push 怎么用

使用 nghttp2 调试 HTTP/2 流量

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

  • 在 Chrome 地址栏输入 chrome://net-internals/#http2,使用 Chrome 自带的 HTTP/2 调节和测验工具;
    使用方便,但受限于 Chrome 浏览器,对于 Chrome 不帮忙的 h2c(HTTP/2 Cleartext,没有安排 TLS 的 HTTP/2卡塔尔合同不可能。同期,这么些工具突显的音讯透过了深入深入分析和筛选,非常不够康健。
  • 使用 Wireshark 调试 HTTP/2 流量;
    Wireshark 位于服务端和浏览器之间,充任的是高级中学级人剧中人物,用它查看 HTTP/2 over HTTPS 流量时,必需具备网站私钥也许借助浏览器分享对称密钥,技巧解密 TLS 流量,配置起来相比费心。

nghttp2,是四个用 C 完结的 HTTP/2 库,支持h2c。它能够做为其余软件的一片段,为其提供 HTTP/2 相关职能(例如 curl 的 HTTP/2 效率就是用的 nghttp2卡塔尔国。除此而外,它还提供了几个有效的 HTTP/2 工具:

  • nghttp:HTTP/2 客户端;
  • nghttpd:HTTP/2 服务端;
  • nghttpx:HTTP/2 代理,提供 HTTP/1、HTTP/2 等争辩时期的转换;
  • h2load:HTTP/2 质量测验工具;

nghttp2 安装

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

~ brew search nghttp
nghttp2

看来是有 nghttp2 的,再用 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

全部伏贴后,nghttp2 提供的多少个工具就足以一向用了。

nghttp

nghttp 做为二个效用生机勃勃体化的 HTTP/2 顾客端,特别符合用来查阅和调节和测验 HTTP/2 流量。它扶植的参数相当多,通过合法语档或然 nghttp -h 都能查看。最常用多少个参数如下:

  • -v, --verbose,输出完整的 debug 音讯;
  • -n, --null-out,丢掉下载的数额;
  • -a, --get-assets,下载 html 中的 css、js、image 等外链财富;
  • -H, --header = < HEADETiggo >,增加央浼底部字段,如 -H':method: PUT';
  • -u, --upgrade,使用 HTTP 的 Upgrade 机制来议和 HTTP/2 公约,用于 h2c,详见下边包车型地铁例子;

以下是选拔 nghttp 访问 https://h2o.examp1e.net 的结果。从调节和测量检验音信中得以清楚看出 h2c 协商以致 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>

使用 NodeJS 搭建 HTTP/2 服务器

在大前端的时代背景下,顾客端支出不会点 JavaScript 都快混不下去了,我方今在本人司前端换岗了两周,再增加在此以前也写过 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');
});

此间需求在意几点:

  • 创办http2的nodejs服务必得时依据https的,因为不久前主流的浏览器都要支持SSL/TLS的http2,证书和私钥能够团结通过OPENSSL生成。
  • node http2的有关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,如下图所示:

图片 7

chrome 查看 http2 server push

服务端介绍宗旨停止。下边大家来介绍一些 iOS 客商端对 Server Push 的使用。

iOS 使用 HTTP/2 Server Push

Apple 在此地点做的很好,基本完结了客商端无感调用http/2 server push。可是笔者查阅了不怎么素材,今后独有iOS 10 补助 http/2。

直接上代码吧:

#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卡塔尔国

正文相关德姆o

  • Github:lijianfeigeek

参照他事他说加以考查文献

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

本文由贝博体育app发布于前端应用,转载请注明出处:HTTP2 Server Push的商讨

关键词:

上一篇:高度自适应
下一篇:没有了