HTTP/2
slid.es/seanm/http2
About Me
Sean MacAvaney
macavaney.us • @SeanMacAvaney • sean.macavaney@gmail.com
- Software Engineering student at MSOE
- Software Developer at iit/SourceTech
- Interest in web technologies
Overview
- Background
- Technical Details
- Practical Details
Background
1991: HTTP/0.9
Demo
1996: HTTP/1.0
(RFC 1945)
Demo
1999: HTTP/1.1
(RFC 2616, replaced by RFCs 7230-7237)
2009: SPDY
2015: HTTP/2
(RFC 7540)
What's changed since 1999?
(Source: HTTP Archive, 2011-present, Alexa Top 1,000 sites)
Fast downloads, slow uploads.
Goal:
Improve perceived load time of modern web pages
Technical Details
Multiplexed Streams
Flow
Control
Server
Push
Header Compression
HTTP/1.1 Problem:
Only 1 request at a time per TCP socket*
(This makes loading many resources slow.)
Client -----------WAITING------------------------WAITING---------------- ...
\ /\ /
\ / \ /
Request 1 Response 1 Request 2 Response 2
\ / \ /
\ / \ /
Server ---------PROCESSING-----WAITING----------PROCESSING------------- ...
^ ^
t=0 t=100
Head-of-Line Blocking
HTTP/1.1 Solution:
More Connections!
~ 6±2 Per Host
"A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy... These guidelines are intended to improve HTTP response times and avoid congestion." RFC 2616 § 8.1.4
HTTP/1.1 Solution:
Domain Sharding!
HTTP/2 Solution:
Allow multiple concurrent requests on the same TCP connection.
Streams & Frames
HTTP/2 Streams
Over a single TCP socket, HTTP allows for many streams of data.
http://stackoverflow.com/questions/10480122
HTTP/2 Frames
Each stream is made up of many frames of data.
Each stream is initialized with a HEADERS or PUSH_PROMISE frame.
HTTP/2 Frames
-
0x00 - DATA
-
0x01 - HEADERS
-
0x02 - PRIORITY
-
0x03 - RST_STREAM
-
0x04 - SETTINGS
-
0x05 - PUSH_PROMISE
-
0x06 - PING
-
0x07 - GOAWAY
-
0x08 - WINDOW_UPDATE
-
0x09 - CONTINUATION
Stream: 1
Type: Headers
Stream: 1
Type: Headers
Stream: 1
Type: Data
Stream: 3
Type: Headers
Stream: 5
Type: Headers
Stream: 3
Type: Headers
Stream: 5
Type: Headers
Stream: 5
Type: Data
Stream: 3
Type: Data
Client
Server
GET /html
GET /img
GET /css
Stream: 1
Type: Headers
Stream: 1
Type: Headers
Stream: 1
Type: Data
Stream: 2
Type: Headers
Stream: 4
Type: Headers
Stream: 4
Type: Data
Stream: 2
Type: Data
Client
Server
GET /html
GET /img
GET /css
Stream: 2
Type: Promise
Stream: 4
Type: Promise
Stream: 1
Type: Headers
Stream: 1
Type: Headers
Stream: 1
Type: Data
Client
Server
GET /html
GET /img
GET /css
Stream: 2
Type: Promise
Stream: 4
Type: Promise
Stream: 2
Type: Reset
Stream: 4
Type: Reset
HTTP/1.1 Problem:
High Constant Request Overhead
HTTP/1.1 Request & Response
GET / HTTP/1.1
Host: www.example.com
Cookie: sessionId=mbi2pyrjqxp2rvxc4x4t52rl; _ga=GA1.2.4532145987.69853215412; mode=1
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,uk;q=0.6
----------------------------------------------------------------------------
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Wed, 18 Mar 2015 14:16:57 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Encoding: gzip
[compressed & chucked response data]
474 bytes
318 bytes
HTTP/1.1 Solution:
Make fewer requests.
Sacrifice resource granularity for file size.
this.A2A=this.A2A||{};(function(_){var window=this; try{ var m,p,aa,ba,ca,da,ea,r,ga,ha,ia,la,ja,ka,ma,na,oa,v;m=this;p=function(a){return void 0!==a};aa=function(a,b,c){a=a.split(".");c=c||m;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)!a.length&&p(b)?c[d]=b:c[d]?c=c[d]:c=c[d]={}};ba=function(a,b){for(var c=a.split("."),d=b||m,e;e=c.shift();)if(null!=d[e])d=d[e];else return null;return d};ca=function(){}; da=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; else if("function"==b&&"undefined"==typeof a.call)return"object";return b};ea=function(a){return"array"==da(a)};_.fa=function(a){var b=da(a);return"array"==b||"object"==b&&"number"==typeof a.length};r=function(a){return"string"==typeof a};ga=function(a){return"number"==typeof a};ha=function(a){return"function"==da(a)};ia=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b};la=function(a){return a[ja]||(a[ja]=++ka)};ja="closure_uid_"+(1E9*Math.random()>>>0);ka=0; ma=function(a,b,c){return a.call.apply(a.bind,arguments)};na=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}};_.u=function(a,b,c){_.u=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ma:na;return _.u.apply(null,arguments)}; oa=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}};v=Date.now||function(){return+new Date};_.x=function(a,b){function c(){}c.prototype=b.prototype;a.o=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.Ff=function(a,c,f){for(var g=Array(arguments.length-2),k=2;k<arguments.length;k++)g[k-2]=arguments[k];return b.prototype[c].apply(a,g)}}; var pa=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,pa);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};_.x(pa,Error);pa.prototype.name="CustomError";var qa;var ra=function(a){return/^[\s\xa0]*$/.test(a)},sa=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Aa=function(a){if(!ta.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ua,"&"));-1!=a.indexOf("<")&&(a=a.replace(va,"<"));-1!=a.indexOf(">")&&(a=a.replace(wa,">"));-1!=a.indexOf('"')&&(a=a.replace(xa,"""));-1!=a.indexOf("'")&&(a=a.replace(ya,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(za,"�"));return a},ua=/&/g,va=/</g, wa=/>/g,xa=/"/g,ya=/'/g,za=/\x00/g,ta=/[\x00&<>"']/,Ba=function(a){return null==a?"":String(a)},Ca=function(){return Math.floor(2147483648*Math.random()).toString(36)+Math.abs(Math.floor(2147483648*Math.random())^v()).toString(36)},Da=function(a,b){return a<b?-1:a>b?1:0},Ea=function(a){return String(a).replace(/\-([a-z])/g,function(a,c){return c.toUpperCase()})},Fa=function(a){var b=r(void 0)?"undefined".replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08"):"\\s";return a.replace(new RegExp("(^"+ (b?"|["+b+"]+":"")+")([a-z])","g"),function(a,b,e){return b+e.toUpperCase()})}; var Ga=function(a){Ga[" "](a);return a};Ga[" "]=ca;var Ha=function(a,b){try{return Ga(a[b]),!0}catch(c){}return!1};var Ia,La,Ma,Na,Qa,Ra,Ta;Ia=Array.prototype;_.Ja=Ia.indexOf?function(a,b,c){return Ia.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(r(a))return r(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1};_.Ka=Ia.forEach?function(a,b,c){Ia.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=r(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)}; La=Ia.filter?function(a,b,c){return Ia.filter.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=[],f=0,g=r(a)?a.split(""):a,k=0;k<d;k++)if(k in g){var l=g[k];b.call(c,l,k,a)&&(e[f++]=l)}return e};Ma=Ia.map?function(a,b,c){return Ia.map.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=Array(d),f=r(a)?a.split(""):a,g=0;g<d;g++)g in f&&(e[g]=b.call(c,f[g],g,a));return e}; Na=Ia.some?function(a,b,c){return Ia.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=r(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1};Qa=function(a,b){var c=(0,_.Ja)(a,b),d;(d=0<=c)&&_.Pa(a,c);return d};_.Pa=function(a,b){Ia.splice.call(a,b,1)};Ra=function(a){return Ia.concat.apply(Ia,arguments)};_.Sa=function(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}; Ta=function(a,b){for(var c=1;c<arguments.length;c++){var d=arguments[c];if(_.fa(d)){var e=a.length||0,f=d.length||0;a.length=e+f;for(var g=0;g<f;g++)a[e+g]=d[g]}else a.push(d)}}; var Ua=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},Va=function(a,b){for(var c in a)if(b.call(void 0,a[c],c,a))return!0;return!1},Wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},Xa="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Ya=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<Xa.length;f++)c=Xa[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}},Za=function(a){var b= arguments.length;if(1==b&&ea(arguments[0]))return Za.apply(null,arguments[0]);for(var c={},d=0;d<b;d++)c[arguments[d]]=!0;return c}; var $a;a:{var ab=m.navigator;if(ab){var bb=ab.userAgent;if(bb){$a=bb;break a}}$a=""}var cb=function(a){return-1!=$a.indexOf(a)};var db=function(){return cb("Edge")||cb("Trident")||cb("MSIE")};var eb=function(){return cb("Edge")};var fb=cb("Opera")||cb("OPR"),y=db(),gb=cb("Gecko")&&!(-1!=$a.toLowerCase().indexOf("webkit")&&!eb())&&!(cb("Trident")||cb("MSIE"))&&!eb(),z=-1!=$a.toLowerCase().indexOf("webkit")&&!eb(),hb=z&&cb("Mobile"),ib=m.navigator||null,jb=ib&&ib.platform||"",kb=cb("Macintosh"),lb=function(){var a=$a;if(gb)return/rv\:([^\);]+)(\)|;)/.exec(a);if(y&&eb())return/Edge\/([\d\.]+)/.exec(a);if(y)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(z)return/WebKit\/(\S+)/.exec(a)},mb=function(){var a=m.document;return a? a.documentMode:void 0},nb=function(){if(fb&&m.opera){var a=m.opera.version;return ha(a)?a():a}var a="",b=lb();b&&(a=b?b[1]:"");return y&&!eb()&&(b=mb(),b>(0,window.parseFloat)(a))?String(b):a}(),ob={},A=function(a){var b;if(!(b=ob[a])){b=0;for(var c=sa(String(nb)).split("."),d=sa(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",k=d[f]||"",l=RegExp("(\\d*)(\\D*)","g"),n=RegExp("(\\d*)(\\D*)","g");do{var t=l.exec(g)||["","",""],w=n.exec(k)||["","",""];if(0==t[0].length&& 0==w[0].length)break;b=Da(0==t[1].length?0:(0,window.parseInt)(t[1],10),0==w[1].length?0:(0,window.parseInt)(w[1],10))||Da(0==t[2].length,0==w[2].length)||Da(t[2],w[2])}while(0==b)}b=ob[a]=0<=b}return b},qb=function(a){return y&&(eb()||pb>=a)},rb=m.document,sb=mb(),pb=!rb||!y||!sb&&eb()?void 0:sb||("CSS1Compat"==rb.compatMode?(0,window.parseInt)(nb,10):5); var tb=!y||qb(9),ub=!y||qb(9),vb=y&&!A("9");!z||A("528");gb&&A("1.9b")||y&&A("8")||fb&&A("9.5")||z&&A("528");gb&&!A("8")||y&&A("9");var B=function()
Concatination of JS/CSS Files
Spriting Images
.icon {
background-image:url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAA);
}
Inlining Resources
HTTP/2 Solution:
Reduce Overhead by Compressing Headers
Why not use gzip?
(Like we do for HTTP response bodies)
Starting an HTTP/2 Connection
Negotiation over TLS
Application-Layer Protocol Negotiation (ALPN) with the "h2" identifier
Demo
Negotiation in Cleartext
-
Service discovery:
Alt-Svc: h2c Upgrade: h2c
-
Client connection preface: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
Security
SPDY Requires TLS
HTTP/2 Can Operate in Cleartext
But most major browsers so far have made statements that they will only support HTTP/2 over TLS.
Padding
HEADERS, DATA, and PUSH_PROMISE frames can include a padding segment to conceal the length of data to an observer.
Header Table Size
To help reduce the risk of Denial-of-Service attacks, memory requirements can be limited by the server, reducing the load.
Practical Details
- Browser Support
- Server Support
- Adoption
Browser Support
- Chrome 41+ (45+ for Android)
- Windows 10 versions of IE/Edge
- Firefox 36+
- Safari 9+ (El Capitan, iOS9)
- Opera 28+
50-60% of users (caniuse.com/#feat=http2)
Server Support
- Apache 2.4.12 with mod_h2
- IIS on Windows 10 / Server 2016
- nginx 1.9.5
- NodeJS Package
https://github.com/http2/http2-spec/wiki/Implementations
Microsfot
using System.Web;
public class ExamplePushHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Push GET /OtherResource to the client automatically
context.Response.PushPromise("/OtherResource");
. . .
}
. . .
}
NodeJS
npm install http2
var fs = require('fs');
var options = {
key: fs.readFileSync('./example/localhost.key'),
cert: fs.readFileSync('./example/localhost.crt')
};
require('http2').createServer(options, function(req, res) {
if (res.push) {
var push = res.push('/client.js');
push.writeHead(200);
fs.createReadStream(path.join(__dirname, '/client.js')).pipe(push);
}
. . .
}).listen(8080);
Adoption
HTTP/2
By seanm
HTTP/2
- 1,179