Bug 1136361 - update node-http2 on ci. rs=mcmanus
authorNicholas Hurley <hurley@todesschaf.org>
Tue, 24 Feb 2015 14:01:10 -0800
changeset 249274 b56e18c9dbfc4ce4785c809e7eec5a835f4d325b
parent 249273 9f70f885338c0726de548d26b962e8e627875ea0
child 249275 dcb0abbfa4235ee531cddbed5a38a363cbcf1844
push id7860
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:46:02 +0000
treeherdermozilla-aurora@8ac636cd51f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1136361
milestone39.0a1
Bug 1136361 - update node-http2 on ci. rs=mcmanus
netwerk/test/unit/test_http2.js
testing/xpcshell/moz-http2/moz-http2.js
testing/xpcshell/node-http2/HISTORY.md
testing/xpcshell/node-http2/example/server.js
testing/xpcshell/node-http2/lib/protocol/framer.js
testing/xpcshell/node-http2/lib/protocol/index.js
testing/xpcshell/node-http2/package.json
testing/xpcshell/node-http2/test/framer.js
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -576,17 +576,17 @@ var altsvcClientListener2 = {
     }
   }
 };
 
 function altsvcHttp1Server(metadata, response) {
   response.setStatusLine(metadata.httpVersion, 200, "OK");
   response.setHeader("Content-Type", "text/plain", false);
   response.setHeader("Connection", "close", false);
-  response.setHeader("Alt-Svc", 'h2-16=":' + serverPort + '"', false);
+  response.setHeader("Alt-Svc", 'h2=":' + serverPort + '"', false);
   var body = "this is where a cool kid would write something neat.\n";
   response.bodyOutputStream.write(body, body.length);
 }
 
 function altsvcHttp1Server2(metadata, response) {
 // this server should never be used thanks to an alt svc frame from the
 // h2 server.. but in case of some async lag in setting the alt svc route
 // up we have it.
@@ -854,28 +854,30 @@ function addCertOverride(host, port, bit
     // This will fail since the server is not trusted yet
   }
 }
 
 var prefs;
 var spdypref;
 var spdy3pref;
 var spdypush;
+var http2draftpref;
 var http2pref;
 var tlspref;
 var altsvcpref1;
 var altsvcpref2;
 var loadGroup;
 var serverPort;
 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.v3-1", spdy3pref);
   prefs.setBoolPref("network.http.spdy.allow-push", spdypush);
-  prefs.setBoolPref("network.http.spdy.enabled.http2draft", http2pref);
+  prefs.setBoolPref("network.http.spdy.enabled.http2draft", http2draftpref);
+  prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlspref);
   prefs.setBoolPref("network.http.altsvc.enabled", altsvcpref1);
   prefs.setBoolPref("network.http.altsvc.oe", altsvcpref2);
 }
 
 function run_test() {
   var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   serverPort = env.get("MOZHTTP2-PORT");
@@ -894,25 +896,27 @@ function run_test() {
                   Ci.nsICertOverrideService.ERROR_TIME);
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
   // Enable all versions of spdy to see that we auto negotiate http/2
   spdypref = prefs.getBoolPref("network.http.spdy.enabled");
   spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3-1");
   spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
-  http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2draft");
+  http2draftpref = prefs.getBoolPref("network.http.spdy.enabled.http2draft");
+  http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
   tlspref = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
   altsvcpref1 = prefs.getBoolPref("network.http.altsvc.enabled");
   altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
 
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.v3-1", true);
   prefs.setBoolPref("network.http.spdy.allow-push", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2draft", true);
+  prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
   prefs.setBoolPref("network.http.altsvc.enabled", true);
   prefs.setBoolPref("network.http.altsvc.oe", true);
 
   loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
 
   httpserv = new HttpServer();
   httpserv.registerPathHandler("/altsvc1", altsvcHttp1Server);
--- a/testing/xpcshell/moz-http2/moz-http2.js
+++ b/testing/xpcshell/moz-http2/moz-http2.js
@@ -377,17 +377,17 @@ function handleRequest(req, res) {
       req.scheme != "http" ||
       req.headers['alt-used'] != ("localhost:" + serverPort)) {
       res.setHeader('Connection', 'close');
       res.writeHead(400);
       res.end("WHAT?");
       return;
    }
    // test the alt svc frame for use with altsvc2
-   res.altsvc("localhost", serverPort, "h2-16", 3600, req.headers['x-redirect-origin']);
+   res.altsvc("localhost", serverPort, "h2", 3600, req.headers['x-redirect-origin']);
   }
 
   else if (u.pathname === "/altsvc2") {
     if (req.httpVersionMajor != 2 ||
       req.scheme != "http" ||
       req.headers['alt-used'] != ("localhost:" + serverPort)) {
       res.setHeader('Connection', 'close');
       res.writeHead(400);
--- a/testing/xpcshell/node-http2/HISTORY.md
+++ b/testing/xpcshell/node-http2/HISTORY.md
@@ -1,11 +1,22 @@
 Version history
 ===============
 
+### 3.2.0 (2015-02-19) ###
+
+* Update ALPN token to final RFC version (h2).
+* Update altsvc implementation to draft 06: [draft-ietf-httpbis-alt-svc-06]
+
+[draft-ietf-httpbis-altsvc-06]: http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
+
+### 3.1.2 (2015-02-17) ###
+
+* Update the example server to have a safe push example.
+
 ### 3.1.1 (2015-01-29) ###
 
 * Bugfix release.
 * Fixes an issue sending a push promise that is large enough to fill the frame (#93).
 
 ### 3.1.0 (2014-12-11) ###
 
 * Upgrade to the latest draft: [draft-ietf-httpbis-http2-16]
--- a/testing/xpcshell/node-http2/example/server.js
+++ b/testing/xpcshell/node-http2/example/server.js
@@ -7,30 +7,30 @@ var cachedFile = fs.readFileSync(path.jo
 var cachedUrl = '/server.js';
 
 // The callback to handle requests
 function onRequest(request, response) {
   var filename = path.join(__dirname, request.url);
 
   // Serving server.js from cache. Useful for microbenchmarks.
   if (request.url === cachedUrl) {
+    if (response.push) {
+      // Also push down the client js, since it's possible if the requester wants
+      // one, they want both.
+      var push = response.push('/client.js');
+      push.writeHead(200);
+      fs.createReadStream(path.join(__dirname, '/client.js')).pipe(push);
+    }
     response.end(cachedFile);
   }
 
   // Reading file from disk if it exists and is safe.
   else if ((filename.indexOf(__dirname) === 0) && fs.existsSync(filename) && fs.statSync(filename).isFile()) {
     response.writeHead('200');
 
-    // If they download the certificate, push the private key too, they might need it.
-    if (response.push && request.url === '/localhost.crt') {
-      var push = response.push('/localhost.key');
-      push.writeHead(200);
-      fs.createReadStream(path.join(__dirname, '/localhost.key')).pipe(push);
-    }
-
     fs.createReadStream(filename).pipe(response);
   }
 
   // Otherwise responding with 404.
   else {
     response.writeHead('404');
     response.end();
   }
--- a/testing/xpcshell/node-http2/lib/protocol/framer.js
+++ b/testing/xpcshell/node-http2/lib/protocol/framer.js
@@ -795,49 +795,228 @@ Deserializer.CONTINUATION = function rea
 frameTypes[0xA] = 'ALTSVC';
 
 frameFlags.ALTSVC = [];
 
 //     0                   1                   2                   3
 //     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 //    |         Origin-Len (16)       | Origin? (*)                 ...
-//    +-------------------------------+-------------------------------+
+//    +-------------------------------+----------------+--------------+
 //    |                   Alt-Svc-Field-Value (*)                   ...
 //    +---------------------------------------------------------------+
 //
 // The ALTSVC frame contains the following fields:
 //
-// Origin-Len: An unsinged 16 bit integer indicating the length of the origin
-//    field. Must be empty for non zero stream id
+// Origin-Len: An unsigned, 16-bit integer indicating the length, in
+//    octets, of the Origin field.
 //
-// Origin: origin this directive applies to. If empty it applies to the
-//    same origin as the stream with the same id
+// Origin: An OPTIONAL sequence of characters containing ASCII
+//    serialisation of an origin ([RFC6454](http://tools.ietf.org/html/rfc6454),
+//    Section 6.2) that the alternate service is applicable to.
 //
-// Alt-Svc-Field-Value: an ascaii string corresponding to the HTTP response
-//    header field value for Alt-Svc
+// Alt-Svc-Field-Value: A sequence of octets (length determined by
+//    subtracting the length of all preceding fields from the frame
+//    length) containing a value identical to the Alt-Svc field value
+//    defined in (Section 3)[http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06#section-3]
+//    (ABNF production "Alt-Svc").
 
 typeSpecificAttributes.ALTSVC = ['maxAge', 'port', 'protocolID', 'host',
                                  'origin'];
 
+function istchar(c) {
+  return ('!#$&\'*+-.^_`|~1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.indexOf(c) > -1);
+}
+
+function hexencode(s) {
+  var t = '';
+  for (var i = 0; i < s.length; i++) {
+    if (!istchar(s[i])) {
+      t += '%';
+      t += new Buffer(s[i]).toString('hex');
+    } else {
+      t += s[i];
+    }
+  }
+  return t;
+}
+
 Serializer.ALTSVC = function writeAltSvc(frame, buffers) {
-  var hdr = frame.protocolID + "=\"" + frame.host + ":" + frame.port + "\"";
-  if (frame.maxAge) {
-   hdr += "; ma=" + frame.maxAge;
-  }
-  
-  buffer = new Buffer(2);
+  var buffer = new Buffer(2);
   buffer.writeUInt16BE(frame.origin.length, 0);
   buffers.push(buffer);
   buffers.push(new Buffer(frame.origin, 'ascii'));
-  buffers.push(new Buffer(hdr, 'ascii'));
+
+  var fieldValue = hexencode(frame.protocolID) + '="' + frame.host + ':' + frame.port + '"';
+  if (frame.maxAge !== 86400) { // 86400 is the default
+    fieldValue += "; ma=" + frame.maxAge;
+  }
+
+  buffers.push(new Buffer(fieldValue, 'ascii'));
 };
 
+function stripquotes(s) {
+  var start = 0;
+  var end = s.length;
+  while ((start < end) && (s[start] === '"')) {
+    start++;
+  }
+  while ((end > start) && (s[end - 1] === '"')) {
+    end--;
+  }
+  if (start >= end) {
+    return "";
+  }
+  return s.substring(start, end);
+}
+
+function splitNameValue(nvpair) {
+  var eq = -1;
+  var inQuotes = false;
+
+  for (var i = 0; i < nvpair.length; i++) {
+    if (nvpair[i] === '"') {
+      inQuotes = !inQuotes;
+      continue;
+    }
+    if (inQuotes) {
+      continue;
+    }
+    if (nvpair[i] === '=') {
+      eq = i;
+      break;
+    }
+  }
+
+  if (eq === -1) {
+    return {'name': nvpair, 'value': null};
+  }
+
+  var name = stripquotes(nvpair.substring(0, eq).trim());
+  var value = stripquotes(nvpair.substring(eq + 1).trim());
+  return {'name': name, 'value': value};
+}
+
+function splitHeaderParameters(hv) {
+  return parseHeaderValue(hv, ';', splitNameValue);
+}
+
+function parseHeaderValue(hv, separator, callback) {
+  var start = 0;
+  var inQuotes = false;
+  var values = [];
+
+  for (var i = 0; i < hv.length; i++) {
+    if (hv[i] === '"') {
+      inQuotes = !inQuotes;
+      continue;
+    }
+    if (inQuotes) {
+      // Just skip this
+      continue;
+    }
+    if (hv[i] === separator) {
+      var newValue = hv.substring(start, i).trim();
+      if (newValue.length > 0) {
+        newValue = callback(newValue);
+        values.push(newValue);
+      }
+      start = i + 1;
+    }
+  }
+
+  var newValue = hv.substring(start).trim();
+  if (newValue.length > 0) {
+    newValue = callback(newValue);
+    values.push(newValue);
+  }
+
+  return values;
+}
+
+function rsplit(s, delim, count) {
+  var nsplits = 0;
+  var end = s.length;
+  var rval = [];
+  for (var i = s.length - 1; i >= 0; i--) {
+    if (s[i] === delim) {
+      var t = s.substring(i + 1, end);
+      end = i;
+      rval.unshift(t);
+      nsplits++;
+      if (nsplits === count) {
+        break;
+      }
+    }
+  }
+  if (end !== 0) {
+    rval.unshift(s.substring(0, end));
+  }
+  return rval;
+}
+
+function ishex(c) {
+  return ('0123456789ABCDEFabcdef'.indexOf(c) > -1);
+}
+
+function unescape(s) {
+  var i = 0;
+  var t = '';
+  while (i < s.length) {
+    if (s[i] != '%' || !ishex(s[i + 1]) || !ishex(s[i + 2])) {
+      t += s[i];
+    } else {
+      ++i;
+      var hexvalue = '';
+      if (i < s.length) {
+        hexvalue += s[i];
+        ++i;
+      }
+      if (i < s.length) {
+        hexvalue += s[i];
+      }
+      if (hexvalue.length > 0) {
+        t += new Buffer(hexvalue, 'hex').toString();
+      } else {
+        t += '%';
+      }
+    }
+
+    ++i;
+  }
+  return t;
+}
+
 Deserializer.ALTSVC = function readAltSvc(buffer, frame) {
-  // todo
+  var originLength = buffer.readUInt16BE(0);
+  frame.origin = buffer.toString('ascii', 2, 2 + originLength);
+  var fieldValue = buffer.toString('ascii', 2 + originLength);
+  var values = parseHeaderValue(fieldValue, ',', splitHeaderParameters);
+  if (values.length > 1) {
+    // TODO - warn that we only use one here
+  }
+  if (values.length === 0) {
+    // Well that's a malformed frame. Just ignore it.
+    return;
+  }
+
+  var chosenAltSvc = values[0];
+  frame.maxAge = 86400; // Default
+  for (var i = 0; i < chosenAltSvc.length; i++) {
+    if (i === 0) {
+      // This corresponds to the protocolID="<host>:<port>" item
+      frame.protocolID = unescape(chosenAltSvc[i].name);
+      var hostport = rsplit(chosenAltSvc[i].value, ':', 1);
+      frame.host = hostport[0];
+      frame.port = parseInt(hostport[1], 10);
+    } else if (chosenAltSvc[i].name == 'ma') {
+      frame.maxAge = parseInt(chosenAltSvc[i].value, 10);
+    }
+    // Otherwise, we just ignore this
+  }
 };
 
 // BLOCKED
 // ------------------------------------------------------------
 //
 // The BLOCKED frame (type=0xB) indicates that the sender is unable to send data
 // due to a closed flow control window.
 //
--- a/testing/xpcshell/node-http2/lib/protocol/index.js
+++ b/testing/xpcshell/node-http2/lib/protocol/index.js
@@ -32,17 +32,17 @@
 // [http2-connheader]:    http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-3.5
 // [http2-stream]:        http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5
 // [http2-streamstate]:   http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
 // [node]:                http://nodejs.org/
 // [node-stream]:         http://nodejs.org/api/stream.html
 // [node-https]:          http://nodejs.org/api/https.html
 // [node-http]:           http://nodejs.org/api/http.html
 
-exports.VERSION = 'h2-16';
+exports.VERSION = 'h2';
 
 exports.Endpoint = require('./endpoint').Endpoint;
 
 /* Bunyan serializers exported by submodules that are worth adding when creating a logger. */
 exports.serializers = {};
 var modules = ['./framer', './compressor', './flow', './connection', './stream', './endpoint'];
 modules.map(require).forEach(function(module) {
   for (var name in module.serializers) {
--- a/testing/xpcshell/node-http2/package.json
+++ b/testing/xpcshell/node-http2/package.json
@@ -1,11 +1,11 @@
 {
   "name": "http2",
-  "version": "3.1.1",
+  "version": "3.2.0",
   "description": "An HTTP/2 client and server implementation",
   "main": "lib/index.js",
   "engines" : {
     "node" : ">=0.10.19"
   },
   "devDependencies": {
     "istanbul": "*",
     "chai": "*",
--- a/testing/xpcshell/node-http2/test/framer.js
+++ b/testing/xpcshell/node-http2/test/framer.js
@@ -10,17 +10,18 @@ var frame_types = {
   HEADERS:       ['priority_information', 'data'],
   PRIORITY:      ['priority_information'],
   RST_STREAM:    ['error'],
   SETTINGS:      ['settings'],
   PUSH_PROMISE:  ['promised_stream', 'data'],
   PING:          ['data'],
   GOAWAY:        ['last_stream', 'error'],
   WINDOW_UPDATE: ['window_size'],
-  CONTINUATION:  ['data']
+  CONTINUATION:  ['data'],
+  ALTSVC:        ['protocolID', 'host', 'port', 'origin', 'maxAge']
 };
 
 var test_frames = [{
   frame: {
     type: 'DATA',
     flags: { END_STREAM: false, RESERVED2: false, RESERVED4: false,
              PADDED: false },
     stream: 10,
@@ -171,47 +172,44 @@ var test_frames = [{
     type: 'CONTINUATION',
     flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true },
     stream: 10,
 
     data: new Buffer('12345678', 'hex')
   },
   // length + type + flags + stream +   content
   buffer: new Buffer('000004' + '09' + '04' + '0000000A' +   '12345678', 'hex')
-}, 
-// need to be updated for -06
-//{
-//  frame: {
-//    type: 'ALTSVC',
-//    flags: { },
-//    stream: 0,
+}, {
+  frame: {
+    type: 'ALTSVC',
+    flags: { },
+    stream: 0,
 
-//    maxAge: 31536000,
-//    port: 4443,
-//    protocolID: "h2",
-//    host: "altsvc.example.com",
-//    origin: ""
-//  },
-//  buffer: new Buffer('00001D' + '0A' + '00' + '00000000' + '01E13380' + '115B' + '00' + '02' + '6832' + '12' + '616C747376632E6578616D706C652E636F6D', 'hex')
-//}, {
-//  frame: {
-//    type: 'ALTSVC',
-//    flags: { },
-//    stream: 0,
-//
-//    maxAge: 31536000,
-//    port: 4443,
-//    protocolID: "h2",
-//    host: "altsvc.example.com",
-//    origin: "https://onlyme.example.com"
-//  },
-//  buffer: new Buffer('000037' + '0A' + '00' + '00000000' + '01E13380' + '115B' + '00' + '02' + '6832' + '12' + '616C747376632E6578616D706C652E636F6D' + '68747470733A2F2F6F6E6C796D652E6578616D706C652E636F6D', 'hex')
-//
-//},
- {
+    maxAge: 31536000,
+    port: 4443,
+    protocolID: "h2",
+    host: "altsvc.example.com",
+    origin: ""
+  },
+  buffer: new Buffer(new Buffer('00002B' + '0A' + '00' + '00000000' + '0000', 'hex') + new Buffer('h2="altsvc.example.com:4443"; ma=31536000', 'ascii'))
+}, {
+  frame: {
+    type: 'ALTSVC',
+    flags: { },
+    stream: 0,
+
+    maxAge: 31536000,
+    port: 4443,
+    protocolID: "h2",
+    host: "altsvc.example.com",
+    origin: "https://onlyme.example.com"
+  },
+  buffer: new Buffer(new Buffer('000045' + '0A' + '00' + '00000000' + '001A', 'hex') + new Buffer('https://onlyme.example.comh2="altsvc.example.com:4443"; ma=31536000', 'ascii'))
+
+}, {
   frame: {
     type: 'BLOCKED',
     flags: { },
     stream: 10
   },
   buffer: new Buffer('000000' + '0B' + '00' + '0000000A', 'hex')
 }];