In this paper, the starting address to discover – iOS HTTP Server Push / 2 | jian-fei li’s blog
What is HTTP/2 Server Push
When the user’s browser and server establish a link, the server will actively push some resources to the browser and cache them, so that when the browser requests these resources, it will directly read from the cache, instead of pulling from the server, increasing the speed. Here’s an example:
If a page has three resource file index. The HTML, but CSS, index, js, when a browser requests index. The HTML, not only to return to the index server. The HTML content, at the same time will index. The CSS and the index of js content push to the browser, The next time the browser requests these two files, they can be read directly from the cache.
As shown below:
How does HTTP/2 Server Push work
To understand how Server Push works, you need to understand a few concepts. We know that HTTP/2 does not use text as HTTP1 does. Instead, HTTP/2 enables the transmission of Frames. Server push related Frames are divided into the following types:
- HEADERS Frame: This frame mainly carries HTTP request HEADERS, similar to HTTP1 HEADERS.
- DATA Frames: This type of frame holds the actual DATA content for transmission.
- PUSH_PROMISE frame: This frame is sent by the server to the client to represent the server push frame. This frame is the main frame type used to implement the server push.
- RST_STREAM(unpush frame): This frame represents a request to close a frame, which is simply sent to the sender when the client does not want to accept some resource or accepts a timeout. Used with PUSH_PROMISE frame, this frame means to reject or close a server push.
(PS:HTTP/2 related frames actually include 10 types of frames. It is because of the changes in the underlying data format that HTTP/2 brings many features. The introduction of frames is not only conducive to data compression, but also conducive to data security and reliable transmission.)
Knowing the frame types, here’s how to implement server push:
- From multiplexing we know that HTTP/2 requests for the same domain name will use a TCP link and different stream ids to distinguish each request.
- When a client requests index.html using Stream 1, the server processes the request normally and knows that the index.html page will also request index.css and index.js.
- The server sends a PUSH_PROMISE frame to the client using stream 1 to tell the client that I can use Stream 2 to push index.js and stream 3 to push index. CSS.
- The server sends the HEADERS frame and DATA frames to the client using Stream 1.
- The client receives a PUSH_PROMISE frame that tells stream 2 and Stream 3 to receive the push resource.
- CSS and index.js, the server sends the HEADERS Frame and DATA frames to the client.
- The client takes the push resource and caches it when it requests it and reads it directly from the cache.
How to use Server Push
Debug HTTP/2 traffic using NGHTTp2
Several ways to view HTTP/2 traffic
- Enter it in the Chrome address bar
chrome://net-internals/#http2
Use the HTTP/2 debugging tool that comes with Chrome. Easy to use, but limited by Chrome, the H2C (HTTP/2 Cleartext, HTTP/2 without TLS deployment) protocol is not supported by Chrome. At the same time, the information displayed by this tool is parsed and filtered and not comprehensive enough. - Use Wireshark to debug HTTP/2 traffic. The Wireshark serves as a middleman between the server and the browser. When using the Wireshark to view HTTP/2 over HTTPS traffic, you must have the website private key or use the browser to share the symmetric key to decrypt TLS traffic. Therefore, the Wireshark is difficult to configure.
Nghttp2, an HTTP/2 library implemented in C, supports H2C. It can be used as part of other software to provide HTTP/2 functionality (for example, curl uses NGHTTP2 for HTTP/2 functionality). In addition, it provides four useful HTTP/2 tools:
- NGHTTP: HTTP/2 client;
- NGHTTPD: HTTP/2 server;
- NGHTTPX: HTTP/2 proxy, providing HTTP/1, HTTP/2 and other protocols between the conversion;
- H2load: HTTP/2 performance testing tool;
Nghttp2 installation
Brew to see if there are any libraries associated with NGHTTP
~ brew search nghttp
nghttp2
Copy the code
Nghttp2 is available, and brew is available to see what environment you need to install:
~ brew info nGHTTP2 NGHTTP2: stable 1.21.0 (bottler), 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 - Disposition, PKG-config stocking, cunit C-ares -, Libev -, OpenSSL configuration, 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 versionCopy the code
Looks like there’s a lot to rely on.
Install NGHTTp2 using BREW:
brew install nghttp2
Copy the code
Once everything is in place, several tools provided by NGHTTP2 can be used directly.
nghttp
As a full-featured HTTP/2 client, NGHTTP is ideal for viewing and debugging HTTP/2 traffic. It supports a wide range of parameters, which can be viewed through official documentation or nGHTTP-H. The most commonly used parameters are as follows:
- -v, –verbose displays complete debugging information.
- -n, –null-out, discards downloaded data;
- -a, –get-assets, download HTML CSS, JS, image and other external chain resources;
- -h, –header = < header >, add the request header field, such as -h ‘:method: PUT’;
- -u, –upgrade, uses the upgrade mechanism of HTTP to negotiate the HTTP/2 protocol for H2C, as shown in the following example;
Here are the results of accessing https://h2o.examp1e.net using NGHTTP. From the debugging information, you can clearly see the entire process of H2C negotiation and 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] stream_id=13 accept-encoding: */* [1.373] 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.0-beta2 [1.373] recv (stream_id=2) date: Mon, 10 Apr 2017 06:30:29 GMT [1.373] Stream_id =2 Application /javascript [1.373] stream_id=2 Thu, 14 May 2015 04:10:14 GMT [1.373] recv (stream_id=2) etag:"55542026-169d5"[1.373] Accept-ranges: bytes [1.373] accept-ranges: bytes [1.373] accept-ranges: bytes [1.373] accept-ranges (stream_id=2) x-http2-push: Pushed [1.373] recV (stream_id=2) Content-Length: 92629 [1.373] recv frame <length=126, flags=0x04, stream_id=2>; END_HEADERS (padlen=0) ; First push response header [1.373] recv (stream_id=13) : 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: 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.0-beta2 [1.373] recv (stream_id=4) date: Mon, 10 Apr 2017 06:30:29 GMT [1.373] Stream_id =4 Application /javascript [1.373] stream_id=4 Thu, 14 May 2015 04:10:14 GMT [1.373] recv (stream_id=4) etag:"55542026-1388"[1.373] Accept-ranges: bytes [1.374] Accept-ranges: bytes [1.374] accept-ranges (stream_id=4) x-http2-push: Pushed [1.374] recV (stream_id=4) Content-Length: 5000 [1.374] recv 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) 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] stream_id=13 stream_id accept: */* [1.374] recv (stream_id=13) Accept-encoding: gzip, deflate (stream_id=13) 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.0-beta2 [1.374] recv (stream_id=6) date: Mon, 10 Apr 2017 06:30:29 GMT [1.374] Stream_id =6 Application /javascript [1.374] stream_id=6 last-Modified: Thu, 14 May 2015 04:10:14 GMT [1.374] recv (stream_id=6) eTAG:"55542026-34dd6"[1.374] Accept-ranges: bytes [1.374] accept-ranges: bytes [1.374] accept-ranges (stream_id=6) x-http2-push: Pushed [1.374] recV (stream_id=6) Content-Length: 216534 [1.374] recv 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) 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.0-beta2 [1.374] recv (stream_id=8) date: Mon, 10 Apr 2017 06:30:29 GMT [1.374] 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] Accept-ranges: bytes [1.374] accept-ranges: bytes [1.374] accept-ranges (stream_id=8) x-http2-push: Pushed [1.374] recV (stream_id=8) Content-Length: [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) 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] :status: 200 [1.374] recv (stream_id=10) server: H2O / 2.0-beta2 [1.374] recv (stream_id=10) date: Mon, 10 Apr 2017 06:30:29 GMT [1.374] 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] Accept-ranges: bytes [1.374] accept-ranges: bytes [1.374] accept-ranges (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] stream_id=13 :status: 200 [1.374] recv (stream_id=13) server: Date: Mon, 10 Apr 2017 06:30:29 GMT [1.374] Recv (stream_id=13) Link: stream_id=13 < / search/jquery - 1.9.1. Min. Js >; Rel = stream_id=13 link: </search/oktavia-jquery-ui.js>; Rel =preload [1.374] stream_id=13 link: </search/oktavia-english-search.js>; Rel =preload [1.374] recv (stream_id=13) link: </assets/ styl.css >; Rel =preload [1.374] recv (stream_id=13) link: </assets/searchstyle.css>; Rel = stream_id=13 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] accept-ranges: bytes [1.374] accept-ranges: bytes [1.374] accept-ranges (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)=[])
Copy the code
Of course, we can also use grep to search for the relevant stream of server push:
nghttp -nv 'https://h2o.examp1e.net' | grep 'PUSH_PROMISE'[1.582] recV PUSH_PROMISE frame <length=59, flags=0x04, stream_id=13> Stream_id =13> [1.582] recV PUSH_PROMISE frame <length=35, flags=0x04, stream_id=13> [1.582] 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>Copy the code
Build HTTP/2 server with NodeJS
In the era of big front end, it is almost impossible to get along with the development of client side without JavaScript. Recently, I have been working in front of our company for two weeks, and I also wrote about ReactNative before, but I still feel the rapid change of front end is amazing. The revolution is not over yet, and we still need to work hard.
Let’s go straight to the code:
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"); // Set your own file path here, which is what this response returns; 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')); // Note that the push path must be an absolute path. This is what the server push returns.'/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');
});
Copy the code
A few things to note here:
- The nodeJS service that creates HTTP2 must be based on HTTPS, because today’s mainstream browsers are required to support SSL/TLS http2. Certificates and private keys can be generated by OPENSSL themselves.
- The node http2 API is the same as the normal Node HttpServer API and can be used directly.
Use NGHTTP to test if our code does 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: 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) 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: [0.050] recv (stream_id=13) :authority: localhost:3000 [0.050] recv (stream_id=13) :path: I/Desktop/http2 / Users/f.l - nodeServer/newpush. Json [0.050] recv PUSH_PROMISE frame < length = 56, flags = 0 x04, 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] stream_id=2 :status: 200 [0.050] stream_id=2 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)=[])Copy the code
You see a PUSH_PROMISE frame, indicating a server push.
You can also view Server push using Chrome, as shown below:
The introduction of the server is almost complete. Let’s take a look at some iOS client uses of Server Push.
IOS uses HTTP/2 Server Push
Apple has done a good job in this area, basically realizing the client to call HTTP /2 server push without feeling. However, I checked a few sources and only iOS 10 currently supports HTTP /2.
Go straight to the code:
#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 - Send requests
- (void)urlSession
{
NSURL *url = [NSURL URLWithString:@"https://localhost:3000"]; / / send HTTPS requests is need to network session setup agent _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]); // After receiving this request, immediately request the next content [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 {// Set the ATS in the plistif(! [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"The @"Network Load"The @"Server Push"The @"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
Copy the code
Log on server and client:
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"}
Copy the code
Server:
Http2 - nodeServer NPM start > [email protected] start/Users/f.l I/Desktop/http2 - nodeServer > node index. The js listen on 3000 success newpush endCopy the code
It does appear that the client made two requests, but the server only responded once (this response + server push)
Relevant Demo of this article
- Github:lijianfeigeek
reference
- HTTP2 Server Push research | AlloyTeam
- Using flow nghttp2 debug HTTP / 2 | JerryQu station
- objective c – HTTP/2 Server Push in iOS 10 – Stack Overflow
- Use NSURLSession or AFN to send HTTPS requests – simple book