summaryrefslogtreecommitdiffstats
path: root/testing/xpcshell/moz-http2
diff options
context:
space:
mode:
Diffstat (limited to 'testing/xpcshell/moz-http2')
-rw-r--r--testing/xpcshell/moz-http2/http2-cert.pem79
-rw-r--r--testing/xpcshell/moz-http2/http2-key.pem28
-rw-r--r--testing/xpcshell/moz-http2/moz-http2.js786
3 files changed, 893 insertions, 0 deletions
diff --git a/testing/xpcshell/moz-http2/http2-cert.pem b/testing/xpcshell/moz-http2/http2-cert.pem
new file mode 100644
index 000000000..d5944e531
--- /dev/null
+++ b/testing/xpcshell/moz-http2/http2-cert.pem
@@ -0,0 +1,79 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Maine, O=CA Example
+ Validity
+ Not Before: Apr 29 05:29:19 2015 GMT
+ Not After : Apr 26 05:29:19 2025 GMT
+ Subject: C=US, ST=Maine, O=Example Com, CN=foo.example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cf:ff:c0:27:3b:a3:11:b5:7f:5d:4f:22:f9:75:
+ 48:47:d9:3a:ce:9b:66:82:4e:e4:ae:ab:78:d3:4c:
+ 3a:9a:5c:37:97:b2:7b:4e:2a:54:77:16:2a:3e:6f:
+ 52:ee:4b:49:46:1d:6b:18:9a:ed:b1:ad:64:9f:8b:
+ e5:fa:e4:60:7b:39:0e:db:e8:b4:2d:4b:e8:ab:37:
+ e8:90:ec:eb:0f:3e:6b:40:7a:d1:da:e6:68:b3:f4:
+ f6:68:54:5b:27:90:6d:c2:c3:04:de:85:23:2b:3c:
+ 66:4e:06:79:58:93:a1:71:d7:ec:74:55:a4:84:9d:
+ 41:22:2a:7a:76:ae:56:b1:6f:15:2d:f2:f5:9c:64:
+ 3e:4f:0f:6e:8f:b6:28:66:e9:89:04:5d:1d:21:77:
+ f8:03:d3:89:ed:7c:f4:3b:42:02:c8:8d:de:47:74:
+ 1f:4a:5d:fe:8d:d1:57:37:08:54:bf:89:d8:f7:27:
+ 22:a7:2a:5d:aa:d5:b0:61:22:9b:96:75:ee:ab:09:
+ ca:a9:cb:2b:1e:88:7c:5a:53:7e:5f:88:c4:43:ea:
+ e8:a7:db:35:6c:b2:89:ad:98:e0:96:c9:83:c4:c1:
+ e7:2a:5c:f8:99:5c:9e:01:9c:e6:99:bd:18:5c:69:
+ d4:10:f1:46:88:37:0b:4e:76:5f:6a:1a:21:c2:a4:
+ 16:d1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 76:BC:13:90:F7:85:1B:1C:24:A1:CC:65:8A:4F:4C:0C:7F:10:D3:F5
+ X509v3 Authority Key Identifier:
+ keyid:F7:FC:76:AF:C5:1A:E9:C9:42:6C:38:DF:8B:07:9E:2B:2C:E5:8E:20
+
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 03:ab:2a:9e:e5:cd:5c:88:5a:6c:f7:4b:7a:7c:ef:85:2c:31:
+ df:03:79:31:a6:c5:c8:2b:c6:21:a5:33:2b:a0:4b:e2:7e:0a:
+ 86:9b:72:25:b6:75:43:41:7c:30:9f:15:b4:9f:34:50:57:eb:
+ 87:f9:1e:9f:b6:cd:81:36:92:61:66:d5:fe:e2:c5:ed:de:f1:
+ ce:85:0b:f9:6a:2b:32:4d:29:f1:a9:94:57:a3:0f:74:93:12:
+ c9:0a:28:5e:72:9f:4f:0f:78:f5:84:11:5a:9f:d7:1c:4c:fd:
+ 13:d8:3d:4c:f8:dd:4c:c6:1c:fd:63:ee:f5:d5:96:f5:00:2c:
+ e6:bb:c9:4c:d8:6a:19:59:58:2b:d4:05:ab:57:47:1c:49:d6:
+ c5:56:1a:e3:64:10:19:9b:44:3e:74:8b:19:73:28:86:96:b4:
+ d1:2a:49:23:07:25:97:64:8f:1b:1c:64:76:12:e0:df:e3:cf:
+ 55:d5:7c:e9:77:d4:69:2f:c7:9a:fd:ce:1a:29:ab:d7:88:68:
+ 93:de:75:e4:d6:85:29:e2:b6:b7:59:20:e3:b5:20:b7:e8:0b:
+ 23:9b:4c:b4:e8:d9:90:cf:e9:2f:9e:a8:22:a2:ef:6a:68:65:
+ f6:c4:81:ed:75:77:88:01:f2:47:03:1a:de:1f:44:38:47:fa:
+ aa:69:f2:98
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJVUzEO
+MAwGA1UECAwFTWFpbmUxEzARBgNVBAoMCkNBIEV4YW1wbGUwHhcNMTUwNDI5MDUy
+OTE5WhcNMjUwNDI2MDUyOTE5WjBNMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFTWFp
+bmUxFDASBgNVBAoMC0V4YW1wbGUgQ29tMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5j
+b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/8AnO6MRtX9dTyL5
+dUhH2TrOm2aCTuSuq3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSfi+X6
+5GB7OQ7b6LQtS+irN+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZOBnlY
+k6Fx1+x0VaSEnUEiKnp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04ntfPQ7
+QgLIjd5HdB9KXf6N0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxaU35f
+iMRD6uin2zVssomtmOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9qGiHC
+pBbRAgMBAAGjWjBYMB0GA1UdDgQWBBR2vBOQ94UbHCShzGWKT0wMfxDT9TAfBgNV
+HSMEGDAWgBT3/HavxRrpyUJsON+LB54rLOWOIDAJBgNVHRMEAjAAMAsGA1UdDwQE
+AwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAA6sqnuXNXIhabPdLenzvhSwx3wN5MabF
+yCvGIaUzK6BL4n4KhptyJbZ1Q0F8MJ8VtJ80UFfrh/ken7bNgTaSYWbV/uLF7d7x
+zoUL+WorMk0p8amUV6MPdJMSyQooXnKfTw949YQRWp/XHEz9E9g9TPjdTMYc/WPu
+9dWW9QAs5rvJTNhqGVlYK9QFq1dHHEnWxVYa42QQGZtEPnSLGXMohpa00SpJIwcl
+l2SPGxxkdhLg3+PPVdV86XfUaS/Hmv3OGimr14hok9515NaFKeK2t1kg47Ugt+gL
+I5tMtOjZkM/pL56oIqLvamhl9sSB7XV3iAHyRwMa3h9EOEf6qmnymA==
+-----END CERTIFICATE-----
diff --git a/testing/xpcshell/moz-http2/http2-key.pem b/testing/xpcshell/moz-http2/http2-key.pem
new file mode 100644
index 000000000..387449ddc
--- /dev/null
+++ b/testing/xpcshell/moz-http2/http2-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDP/8AnO6MRtX9d
+TyL5dUhH2TrOm2aCTuSuq3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSf
+i+X65GB7OQ7b6LQtS+irN+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZO
+BnlYk6Fx1+x0VaSEnUEiKnp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04nt
+fPQ7QgLIjd5HdB9KXf6N0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxa
+U35fiMRD6uin2zVssomtmOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9q
+GiHCpBbRAgMBAAECggEBAKqcsQQ9cdQr2S4zpI+UuVZeBFPGun32srPn6TMA2y0U
+qXEgMO574E7SepI+BHt8e70sklVbd89/WANa4Kk8vTs2IU8XAPwKwO347SY7f9BA
+Nf9s/0gcKRQ7wgbv8tlwKehQyWSxNpjXcV9dBho29n2ITOdb/Jfe2bdpuowxEuF0
+rugkKh7P7LJTG1SAw01UTIszoOGIqHU2XlmYQOws4EvRov/BRTn9axBHH33top+m
+dX+96ntgWxdHOJjTcoXLGhTu1c0ZlJgtgEaH03jjy0f+3Qc+jIgbaZ4WLZkF/oZh
+hscL56XhsT3hR2Sdtxccw2zZ0exLO+qV1RykIAlUXkECgYEA7U+ljowyPxbREHnf
+SRTauIZfJNP6IHT60MkslltlYn7jABvx+u2xCC/QhZxCJi/iAs6iNvkbXR6uK/MH
+NrXwdk67SDUXaDZ9LM3rXPqjuwmvkc+e7P5an6KRtyzQD8K8mjbze1NfxbcGgKti
+A+8GL8H3V29EQ6xp2+UxIF/3UNkCgYEA4GEm9NLbu4neP+A+1NpUS4tUgMCdTkPm
+fiOECd4jjTizPZjjrk+zTin9aP+eBRYHharIGrDP2Uj98uv4kQ8u0rQbcjPwitog
+8DgccMQ92E6DYGDGECh5Hg2Zu71+zQQNzOEJTyrFLx4Gf5SkBzLlbDZDpNhbuQc9
+zvRYBc11urkCgYBOu2Dy9SJqefhsnfJtfaS/GZ2RS16tzAG2qTfIvpPZZL2NOLhE
+hv13+N0WpuvvXW1/fuykjmr8rwQcAqo/BYe8yIwr/alBYuqOpdbTZzhRAnqkRpy0
+hgKs+bOccRqqT/Jgu6B2JwgcQYe/wpxnL7L+vzx/XqPoS9hnIxf0ZMJZqQKBgQDa
+KJuf3oQWS23z3Sw5+C2NZeK7bIuF1S795bozffBDFqXvdf+pM4S6ssjYlfAmMc0O
+gYYdrVvpf7apwhTjtUdpRgSJfUabOopcBbJhUexvq6bAxlbMzw0z0zVt/EiVPSPN
+198dQhCGR0M6OGNjPHEkTX5ngJVtyUSnO5t5yNJ2wQKBgQDheEUJYgo2UjLNsdTs
+b4og5gHkyoKS3paWV64itJQbVBuri4HWeIExM9ayBB6nSJ2VvpZPyE6XfiYYGNhR
+jOc394qlnrx+oi2KdSmIWfQU0I+rW3bMqpoyWPYxP/hN6w4LAwjnJOSOIMCACm5J
+d8IebWjY2B3Zc6FFVzbmhXtlig==
+-----END PRIVATE KEY-----
diff --git a/testing/xpcshell/moz-http2/moz-http2.js b/testing/xpcshell/moz-http2/moz-http2.js
new file mode 100644
index 000000000..760fef1ef
--- /dev/null
+++ b/testing/xpcshell/moz-http2/moz-http2.js
@@ -0,0 +1,786 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This module is the stateful server side of test_http2.js and is meant
+// to have node be restarted in between each invocation
+
+var node_http2_root = '../node-http2';
+if (process.env.NODE_HTTP2_ROOT) {
+ node_http2_root = process.env.NODE_HTTP2_ROOT;
+}
+var http2 = require(node_http2_root);
+var fs = require('fs');
+var url = require('url');
+var crypto = require('crypto');
+
+// Hook into the decompression code to log the decompressed name-value pairs
+var compression_module = node_http2_root + "/lib/protocol/compressor";
+var http2_compression = require(compression_module);
+var HeaderSetDecompressor = http2_compression.HeaderSetDecompressor;
+var originalRead = HeaderSetDecompressor.prototype.read;
+var lastDecompressor;
+var decompressedPairs;
+HeaderSetDecompressor.prototype.read = function() {
+ if (this != lastDecompressor) {
+ lastDecompressor = this;
+ decompressedPairs = [];
+ }
+ var pair = originalRead.apply(this, arguments);
+ if (pair) {
+ decompressedPairs.push(pair);
+ }
+ return pair;
+}
+
+var connection_module = node_http2_root + "/lib/protocol/connection";
+var http2_connection = require(connection_module);
+var Connection = http2_connection.Connection;
+var originalClose = Connection.prototype.close;
+Connection.prototype.close = function (error, lastId) {
+ if (lastId !== undefined) {
+ this._lastIncomingStream = lastId;
+ }
+
+ originalClose.apply(this, arguments);
+}
+
+var framer_module = node_http2_root + "/lib/protocol/framer";
+var http2_framer = require(framer_module);
+var Serializer = http2_framer.Serializer;
+var originalTransform = Serializer.prototype._transform;
+var newTransform = function (frame, encoding, done) {
+ if (frame.type == 'DATA') {
+ // Insert our empty DATA frame
+ emptyFrame = {};
+ emptyFrame.type = 'DATA';
+ emptyFrame.data = new Buffer(0);
+ emptyFrame.flags = [];
+ emptyFrame.stream = frame.stream;
+ var buffers = [];
+ Serializer['DATA'](emptyFrame, buffers);
+ Serializer.commonHeader(emptyFrame, buffers);
+ for (var i = 0; i < buffers.length; i++) {
+ this.push(buffers[i]);
+ }
+
+ // Reset to the original version for later uses
+ Serializer.prototype._transform = originalTransform;
+ }
+ originalTransform.apply(this, arguments);
+};
+
+function getHttpContent(path) {
+ var content = '<!doctype html>' +
+ '<html>' +
+ '<head><title>HOORAY!</title></head>' +
+ '<body>You Win! (by requesting' + path + ')</body>' +
+ '</html>';
+ return content;
+}
+
+function generateContent(size) {
+ var content = '';
+ for (var i = 0; i < size; i++) {
+ content += '0';
+ }
+ return content;
+}
+
+/* This takes care of responding to the multiplexed request for us */
+var m = {
+ mp1res: null,
+ mp2res: null,
+ buf: null,
+ mp1start: 0,
+ mp2start: 0,
+
+ checkReady: function() {
+ if (this.mp1res != null && this.mp2res != null) {
+ this.buf = generateContent(30*1024);
+ this.mp1start = 0;
+ this.mp2start = 0;
+ this.send(this.mp1res, 0);
+ setTimeout(this.send.bind(this, this.mp2res, 0), 5);
+ }
+ },
+
+ send: function(res, start) {
+ var end = Math.min(start + 1024, this.buf.length);
+ var content = this.buf.substring(start, end);
+ res.write(content);
+ if (end < this.buf.length) {
+ setTimeout(this.send.bind(this, res, end), 10);
+ } else {
+ res.end();
+ }
+ }
+};
+
+var runlater = function() {};
+runlater.prototype = {
+ req : null,
+ resp : null,
+
+ onTimeout : function onTimeout() {
+ this.resp.writeHead(200);
+ this.resp.end("It's all good 750ms.");
+ }
+};
+
+var moreData = function() {};
+moreData.prototype = {
+ req : null,
+ resp : null,
+ iter: 3,
+
+ onTimeout : function onTimeout() {
+ // 1mb of data
+ content = generateContent(1024*1024);
+ this.resp.write(content); // 1mb chunk
+ this.iter--;
+ if (!this.iter) {
+ this.resp.end();
+ } else {
+ setTimeout(executeRunLater, 1, this);
+ }
+ }
+};
+
+function executeRunLater(arg) {
+ arg.onTimeout();
+}
+
+var Compressor = http2_compression.Compressor;
+var HeaderSetCompressor = http2_compression.HeaderSetCompressor;
+var originalCompressHeaders = Compressor.prototype.compress;
+
+function insertSoftIllegalHpack(headers) {
+ var originalCompressed = originalCompressHeaders.apply(this, headers);
+ var illegalLiteral = new Buffer([
+ 0x00, // Literal, no index
+ 0x08, // Name: not huffman encoded, 8 bytes long
+ 0x3a, 0x69, 0x6c, 0x6c, 0x65, 0x67, 0x61, 0x6c, // :illegal
+ 0x10, // Value: not huffman encoded, 16 bytes long
+ // REALLY NOT LEGAL
+ 0x52, 0x45, 0x41, 0x4c, 0x4c, 0x59, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x4c, 0x45, 0x47, 0x41, 0x4c
+ ]);
+ var newBufferLength = originalCompressed.length + illegalLiteral.length;
+ var concatenated = new Buffer(newBufferLength);
+ originalCompressed.copy(concatenated, 0);
+ illegalLiteral.copy(concatenated, originalCompressed.length);
+ return concatenated;
+}
+
+function insertHardIllegalHpack(headers) {
+ var originalCompressed = originalCompressHeaders.apply(this, headers);
+ // Now we have to add an invalid header
+ var illegalIndexed = HeaderSetCompressor.integer(5000, 7);
+ // The above returns an array of buffers, but there's only one buffer, so
+ // get rid of the array.
+ illegalIndexed = illegalIndexed[0];
+ // Set the first bit to 1 to signal this is an indexed representation
+ illegalIndexed[0] |= 0x80;
+ var newBufferLength = originalCompressed.length + illegalIndexed.length;
+ var concatenated = new Buffer(newBufferLength);
+ originalCompressed.copy(concatenated, 0);
+ illegalIndexed.copy(concatenated, originalCompressed.length);
+ return concatenated;
+}
+
+var h11required_conn = null;
+var h11required_header = "yes";
+var didRst = false;
+var rstConnection = null;
+var illegalheader_conn = null;
+
+function handleRequest(req, res) {
+ // We do this first to ensure nothing goes wonky in our tests that don't want
+ // the headers to have something illegal in them
+ Compressor.prototype.compress = originalCompressHeaders;
+
+ var u = url.parse(req.url);
+ var content = getHttpContent(u.pathname);
+ var push, push1, push1a, push2, push3;
+
+ // PushService tests.
+ var pushPushServer1, pushPushServer2, pushPushServer3, pushPushServer4;
+
+ if (req.httpVersionMajor === 2) {
+ res.setHeader('X-Connection-Http2', 'yes');
+ res.setHeader('X-Http2-StreamId', '' + req.stream.id);
+ } else {
+ res.setHeader('X-Connection-Http2', 'no');
+ }
+
+ if (u.pathname === '/exit') {
+ res.setHeader('Content-Type', 'text/plain');
+ res.setHeader('Connection', 'close');
+ res.writeHead(200);
+ res.end('ok');
+ process.exit();
+ }
+
+ if (u.pathname === '/750ms') {
+ var rl = new runlater();
+ rl.req = req;
+ rl.resp = res;
+ setTimeout(executeRunLater, 750, rl);
+ return;
+ }
+
+ else if ((u.pathname === '/multiplex1') && (req.httpVersionMajor === 2)) {
+ res.setHeader('Content-Type', 'text/plain');
+ res.writeHead(200);
+ m.mp1res = res;
+ m.checkReady();
+ return;
+ }
+
+ else if ((u.pathname === '/multiplex2') && (req.httpVersionMajor === 2)) {
+ res.setHeader('Content-Type', 'text/plain');
+ res.writeHead(200);
+ m.mp2res = res;
+ m.checkReady();
+ return;
+ }
+
+ else if (u.pathname === "/header") {
+ var val = req.headers["x-test-header"];
+ if (val) {
+ res.setHeader("X-Received-Test-Header", val);
+ }
+ }
+
+ else if (u.pathname === "/doubleheader") {
+ res.setHeader('Content-Type', 'text/html');
+ res.writeHead(200);
+ res.write(content);
+ res.writeHead(200);
+ res.end();
+ return;
+ }
+
+ else if (u.pathname === "/cookie_crumbling") {
+ res.setHeader("X-Received-Header-Pairs", JSON.stringify(decompressedPairs));
+ }
+
+ else if (u.pathname === "/push") {
+ push = res.push('/push.js');
+ push.writeHead(200, {
+ 'content-type': 'application/javascript',
+ 'pushed' : 'yes',
+ 'content-length' : 11,
+ 'X-Connection-Http2': 'yes'
+ });
+ push.end('// comments');
+ content = '<head> <script src="push.js"/></head>body text';
+ }
+
+ else if (u.pathname === "/push2") {
+ push = res.push('/push2.js');
+ push.writeHead(200, {
+ 'content-type': 'application/javascript',
+ 'pushed' : 'yes',
+ // no content-length
+ 'X-Connection-Http2': 'yes'
+ });
+ push.end('// comments');
+ content = '<head> <script src="push2.js"/></head>body text';
+ }
+
+ else if (u.pathname === "/push5") {
+ push = res.push('/push5.js');
+ push.writeHead(200, {
+ 'content-type': 'application/javascript',
+ 'pushed' : 'yes',
+ // no content-length
+ 'X-Connection-Http2': 'yes'
+ });
+ content = generateContent(1024 * 150);
+ push.write(content);
+ push.end();
+ content = '<head> <script src="push5.js"/></head>body text';
+ }
+
+ else if (u.pathname === "/pushapi1") {
+ push1 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/1', method : 'GET',
+ headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
+ push1.writeHead(200, {
+ 'pushed' : 'yes',
+ 'content-length' : 1,
+ 'subresource' : '1',
+ 'X-Connection-Http2': 'yes'
+ });
+ push1.end('1');
+
+ push1a = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/1', method : 'GET',
+ headers: {'x-foo' : 'bar', 'x-pushed-request': 'true'}});
+ push1a.writeHead(200, {
+ 'pushed' : 'yes',
+ 'content-length' : 1,
+ 'subresource' : '1a',
+ 'X-Connection-Http2': 'yes'
+ });
+ push1a.end('1');
+
+ push2 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/2', method : 'GET',
+ headers: {'x-pushed-request': 'true'}});
+ push2.writeHead(200, {
+ 'pushed' : 'yes',
+ 'subresource' : '2',
+ 'content-length' : 1,
+ 'X-Connection-Http2': 'yes'
+ });
+ push2.end('2');
+
+ push3 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/3', method : 'GET',
+ headers: {'x-pushed-request': 'true'}});
+ push3.writeHead(200, {
+ 'pushed' : 'yes',
+ 'content-length' : 1,
+ 'subresource' : '3',
+ 'X-Connection-Http2': 'yes'
+ });
+ push3.end('3');
+
+ content = '0';
+ }
+
+ else if (u.pathname === "/big") {
+ content = generateContent(128 * 1024);
+ var hash = crypto.createHash('md5');
+ hash.update(content);
+ var md5 = hash.digest('hex');
+ res.setHeader("X-Expected-MD5", md5);
+ }
+
+ else if (u.pathname === "/huge") {
+ content = generateContent(1024);
+ res.setHeader('Content-Type', 'text/plain');
+ res.writeHead(200);
+ // 1mb of data
+ for (var i = 0; i < (1024 * 1); i++) {
+ res.write(content); // 1kb chunk
+ }
+ res.end();
+ return;
+ }
+
+ else if (u.pathname === "/post" || u.pathname === "/patch") {
+ if (req.method != "POST" && req.method != "PATCH") {
+ res.writeHead(405);
+ res.end('Unexpected method: ' + req.method);
+ return;
+ }
+
+ var post_hash = crypto.createHash('md5');
+ req.on('data', function receivePostData(chunk) {
+ post_hash.update(chunk.toString());
+ });
+ req.on('end', function finishPost() {
+ var md5 = post_hash.digest('hex');
+ res.setHeader('X-Calculated-MD5', md5);
+ res.writeHead(200);
+ res.end(content);
+ });
+
+ return;
+ }
+
+ else if (u.pathname === "/750msPost") {
+ if (req.method != "POST") {
+ res.writeHead(405);
+ res.end('Unexpected method: ' + req.method);
+ return;
+ }
+
+ var accum = 0;
+ req.on('data', function receivePostData(chunk) {
+ accum += chunk.length;
+ });
+ req.on('end', function finishPost() {
+ res.setHeader('X-Recvd', accum);
+ var rl = new runlater();
+ rl.req = req;
+ rl.resp = res;
+ setTimeout(executeRunLater, 750, rl);
+ return;
+ });
+
+ return;
+ }
+
+ else if (u.pathname === "/h11required_stream") {
+ if (req.httpVersionMajor === 2) {
+ h11required_conn = req.stream.connection;
+ res.stream.reset('HTTP_1_1_REQUIRED');
+ return;
+ }
+ }
+
+ else if (u.pathname === "/bigdownload") {
+
+ res.setHeader('Content-Type', 'text/html');
+ res.writeHead(200);
+
+ var rl = new moreData();
+ rl.req = req;
+ rl.resp = res;
+ setTimeout(executeRunLater, 1, rl);
+ return;
+ }
+
+ else if (u.pathname === "/h11required_session") {
+ if (req.httpVersionMajor === 2) {
+ if (h11required_conn !== req.stream.connection) {
+ h11required_header = "no";
+ }
+ res.stream.connection.close('HTTP_1_1_REQUIRED', res.stream.id - 2);
+ return;
+ } else {
+ res.setHeader('X-H11Required-Stream-Ok', h11required_header);
+ }
+ }
+
+ else if (u.pathname === "/rstonce") {
+ if (!didRst && req.httpVersionMajor === 2) {
+ didRst = true;
+ rstConnection = req.stream.connection;
+ req.stream.reset('REFUSED_STREAM');
+ return;
+ }
+
+ if (rstConnection === null ||
+ rstConnection !== req.stream.connection) {
+ res.setHeader('Connection', 'close');
+ res.writeHead(400);
+ res.end("WRONG CONNECTION, HOMIE!");
+ return;
+ }
+
+ if (req.httpVersionMajor != 2) {
+ res.setHeader('Connection', 'close');
+ }
+ res.writeHead(200);
+ res.end("It's all good.");
+ return;
+ }
+
+ else if (u.pathname === "/continuedheaders") {
+ var pushRequestHeaders = {'x-pushed-request': 'true'};
+ var pushResponseHeaders = {'content-type': 'text/plain',
+ 'content-length': '2',
+ 'X-Connection-Http2': 'yes'};
+ var pushHdrTxt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+ var pullHdrTxt = pushHdrTxt.split('').reverse().join('');
+ for (var i = 0; i < 265; i++) {
+ pushRequestHeaders['X-Push-Test-Header-' + i] = pushHdrTxt;
+ res.setHeader('X-Pull-Test-Header-' + i, pullHdrTxt);
+ }
+ push = res.push({
+ hostname: 'localhost:' + serverPort,
+ port: serverPort,
+ path: '/continuedheaders/push',
+ method: 'GET',
+ headers: pushRequestHeaders
+ });
+ push.writeHead(200, pushResponseHeaders);
+ push.end("ok");
+ }
+
+ else if (u.pathname === "/altsvc1") {
+ if (req.httpVersionMajor != 2 ||
+ req.scheme != "http" ||
+ req.headers['alt-used'] != ("foo.example.com:" + serverPort)) {
+ res.writeHead(400);
+ res.end("WHAT?");
+ return;
+ }
+ // test the alt svc frame for use with altsvc2
+ res.altsvc("foo.example.com", serverPort, "h2", 3600, req.headers['x-redirect-origin']);
+ }
+
+ else if (u.pathname === "/altsvc2") {
+ if (req.httpVersionMajor != 2 ||
+ req.scheme != "http" ||
+ req.headers['alt-used'] != ("foo.example.com:" + serverPort)) {
+ res.writeHead(400);
+ res.end("WHAT?");
+ return;
+ }
+ }
+
+ // for use with test_altsvc.js
+ else if (u.pathname === "/altsvc-test") {
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Alt-Svc', 'h2=' + req.headers['x-altsvc']);
+ }
+
+ else if (u.pathname === "/.well-known/http-opportunistic") {
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Content-Type', 'application/json');
+ res.writeHead(200, "OK");
+ res.end('{"http://' + req.headers['host'] + '": { "tls-ports": [' + serverPort + '] }}');
+ return;
+ }
+
+ // for PushService tests.
+ else if (u.pathname === "/pushSubscriptionSuccess/subscribe") {
+ res.setHeader("Location",
+ 'https://localhost:' + serverPort + '/pushSubscriptionSuccesss');
+ res.setHeader("Link",
+ '</pushEndpointSuccess>; rel="urn:ietf:params:push", ' +
+ '</receiptPushEndpointSuccess>; rel="urn:ietf:params:push:receipt"');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionSuccesss") {
+ // do nothing.
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionMissingLocation/subscribe") {
+ res.setHeader("Link",
+ '</pushEndpointMissingLocation>; rel="urn:ietf:params:push", ' +
+ '</receiptPushEndpointMissingLocation>; rel="urn:ietf:params:push:receipt"');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionMissingLink/subscribe") {
+ res.setHeader("Location",
+ 'https://localhost:' + serverPort + '/subscriptionMissingLink');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionLocationBogus/subscribe") {
+ res.setHeader("Location", '1234');
+ res.setHeader("Link",
+ '</pushEndpointLocationBogus; rel="urn:ietf:params:push", ' +
+ '</receiptPushEndpointLocationBogus>; rel="urn:ietf:params:push:receipt"');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionMissingLink1/subscribe") {
+ res.setHeader("Location",
+ 'https://localhost:' + serverPort + '/subscriptionMissingLink1');
+ res.setHeader("Link",
+ '</receiptPushEndpointMissingLink1>; rel="urn:ietf:params:push:receipt"');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionMissingLink2/subscribe") {
+ res.setHeader("Location",
+ 'https://localhost:' + serverPort + '/subscriptionMissingLink2');
+ res.setHeader("Link",
+ '</pushEndpointMissingLink2>; rel="urn:ietf:params:push"');
+ res.writeHead(201, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/subscriptionMissingLink2") {
+ // do nothing.
+ return;
+ }
+
+ else if (u.pathname === "/pushSubscriptionNot201Code/subscribe") {
+ res.setHeader("Location",
+ 'https://localhost:' + serverPort + '/subscriptionNot2xxCode');
+ res.setHeader("Link",
+ '</pushEndpointNot201Code>; rel="urn:ietf:params:push", ' +
+ '</receiptPushEndpointNot201Code>; rel="urn:ietf:params:push:receipt"');
+ res.writeHead(200, "OK");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname ==="/pushNotifications/subscription1") {
+ pushPushServer1 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort,
+ path : '/pushNotificationsDeliver1', method : 'GET',
+ headers: { 'Encryption-Key': 'keyid="notification1"; dh="BO_tgGm-yvYAGLeRe16AvhzaUcpYRiqgsGOlXpt0DRWDRGGdzVLGlEVJMygqAUECarLnxCiAOHTP_znkedrlWoU"',
+ 'Encryption': 'keyid="notification1";salt="uAZaiXpOSfOLJxtOCZ09dA"',
+ 'Content-Encoding': 'aesgcm128',
+ }
+ });
+ pushPushServer1.writeHead(200, {
+ 'subresource' : '1'
+ });
+
+ pushPushServer1.end('370aeb3963f12c4f12bf946bd0a7a9ee7d3eaff8f7aec62b530fc25cfa', 'hex');
+ return;
+ }
+
+ else if (u.pathname ==="/pushNotifications/subscription2") {
+ pushPushServer2 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort,
+ path : '/pushNotificationsDeliver3', method : 'GET',
+ headers: { 'Encryption-Key': 'keyid="notification2"; dh="BKVdQcgfncpNyNWsGrbecX0zq3eHIlHu5XbCGmVcxPnRSbhjrA6GyBIeGdqsUL69j5Z2CvbZd-9z1UBH0akUnGQ"',
+ 'Encryption': 'keyid="notification2";salt="vFn3t3M_k42zHBdpch3VRw"',
+ 'Content-Encoding': 'aesgcm128',
+ }
+ });
+ pushPushServer2.writeHead(200, {
+ 'subresource' : '1'
+ });
+
+ pushPushServer2.end('66df5d11daa01e5c802ff97cdf7f39684b5bf7c6418a5cf9b609c6826c04b25e403823607ac514278a7da945', 'hex');
+ return;
+ }
+
+ else if (u.pathname ==="/pushNotifications/subscription3") {
+ pushPushServer3 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort,
+ path : '/pushNotificationsDeliver3', method : 'GET',
+ headers: { 'Encryption-Key': 'keyid="notification3";dh="BD3xV_ACT8r6hdIYES3BJj1qhz9wyv7MBrG9vM2UCnjPzwE_YFVpkD-SGqE-BR2--0M-Yf31wctwNsO1qjBUeMg"',
+ 'Encryption': 'keyid="notification3"; salt="DFq188piWU7osPBgqn4Nlg"; rs=24',
+ 'Content-Encoding': 'aesgcm128',
+ }
+ });
+ pushPushServer3.writeHead(200, {
+ 'subresource' : '1'
+ });
+
+ pushPushServer3.end('2caaeedd9cf1059b80c58b6c6827da8ff7de864ac8bea6d5775892c27c005209cbf9c4de0c3fbcddb9711d74eaeebd33f7275374cb42dd48c07168bc2cc9df63e045ce2d2a2408c66088a40c', 'hex');
+ return;
+ }
+
+ else if (u.pathname == "/pushNotifications/subscription4") {
+ pushPushServer4 = res.push(
+ { hostname: 'localhost:' + serverPort, port: serverPort,
+ path : '/pushNotificationsDeliver4', method : 'GET',
+ headers: { 'Crypto-Key': 'keyid="notification4";dh="BJScXUUTcs7D8jJWI1AOxSgAKkF7e56ay4Lek52TqDlWo1yGd5czaxFWfsuP4j7XNWgGYm60-LKpSUMlptxPFVQ"',
+ 'Encryption': 'keyid="notification4"; salt="sn9p2QqF3V6KBclda8vx7w"',
+ 'Content-Encoding': 'aesgcm',
+ }
+ });
+ pushPushServer4.writeHead(200, {
+ 'subresource' : '1'
+ });
+
+ pushPushServer4.end('9eba7ba6192544a39bd9e9b58e702d0748f1776b27f6616cdc55d29ed5a015a6db8f2dd82cd5751a14315546194ff1c18458ab91eb36c9760ccb042670001fd9964557a079553c3591ee131ceb259389cfffab3ab873f873caa6a72e87d262b8684c3260e5940b992234deebf57a9ff3a8775742f3cbcb152d249725a28326717e19cce8506813a155eff5df9bdba9e3ae8801d3cc2b7e7f2f1b6896e63d1fdda6f85df704b1a34db7b2dd63eba11ede154300a318c6f83c41a3d32356a196e36bc905b99195fd91ae4ff3f545c42d17f1fdc1d5bd2bf7516d0765e3a859fffac84f46160b79cedda589f74c25357cf6988cd8ba83867ebd86e4579c9d3b00a712c77fcea3b663007076e21f9819423faa830c2176ff1001c1690f34be26229a191a938517', 'hex');
+ return;
+ }
+
+ else if ((u.pathname === "/pushNotificationsDeliver1") ||
+ (u.pathname === "/pushNotificationsDeliver2") ||
+ (u.pathname === "/pushNotificationsDeliver3")) {
+ res.writeHead(410, "GONE");
+ res.end("");
+ return;
+ }
+
+ else if (u.pathname === "/illegalhpacksoft") {
+ // This will cause the compressor to compress a header that is not legal,
+ // but only affects the stream, not the session.
+ illegalheader_conn = req.stream.connection;
+ Compressor.prototype.compress = insertSoftIllegalHpack;
+ // Fall through to the default response behavior
+ }
+
+ else if (u.pathname === "/illegalhpackhard") {
+ // This will cause the compressor to insert an HPACK instruction that will
+ // cause a session failure.
+ Compressor.prototype.compress = insertHardIllegalHpack;
+ // Fall through to default response behavior
+ }
+
+ else if (u.pathname === "/illegalhpack_validate") {
+ if (req.stream.connection === illegalheader_conn) {
+ res.setHeader('X-Did-Goaway', 'no');
+ } else {
+ res.setHeader('X-Did-Goaway', 'yes');
+ }
+ // Fall through to the default response behavior
+ }
+
+ else if (u.pathname === "/foldedheader") {
+ res.setHeader('X-Folded-Header', 'this is\n folded');
+ // Fall through to the default response behavior
+ }
+
+ else if (u.pathname === "/emptydata") {
+ // Overwrite the original transform with our version that will insert an
+ // empty DATA frame at the beginning of the stream response, then fall
+ // through to the default response behavior.
+ Serializer.prototype._transform = newTransform;
+ }
+
+ // for use with test_immutable.js
+ else if (u.pathname === "/immutable-test-without-attribute") {
+ res.setHeader('Cache-Control', 'max-age=100000');
+ res.setHeader('Etag', '1');
+ if (req.headers["if-none-match"]) {
+ res.setHeader("x-conditional", "true");
+ }
+ // default response from here
+ }
+ else if (u.pathname === "/immutable-test-with-attribute") {
+ res.setHeader('Cache-Control', 'max-age=100000, immutable');
+ res.setHeader('Etag', '2');
+ if (req.headers["if-none-match"]) {
+ res.setHeader("x-conditional", "true");
+ }
+ // default response from here
+ }
+
+ res.setHeader('Content-Type', 'text/html');
+ if (req.httpVersionMajor != 2) {
+ res.setHeader('Connection', 'close');
+ }
+ res.writeHead(200);
+ res.end(content);
+}
+
+// Set up the SSL certs for our server - this server has a cert for foo.example.com
+// signed by netwerk/tests/unit/CA.cert.der
+var options = {
+ key: fs.readFileSync(__dirname + '/http2-key.pem'),
+ cert: fs.readFileSync(__dirname + '/http2-cert.pem'),
+};
+
+if (process.env.HTTP2_LOG !== undefined) {
+ var log_module = node_http2_root + "/test/util";
+ options.log = require(log_module).createLogger('server')
+}
+
+var server = http2.createServer(options, handleRequest);
+
+server.on('connection', function(socket) {
+ socket.on('error', function() {
+ // Ignoring SSL socket errors, since they usually represent a connection that was tore down
+ // by the browser because of an untrusted certificate. And this happens at least once, when
+ // the first test case if done.
+ });
+});
+
+var serverPort;
+function listenok() {
+ serverPort = server._server.address().port;
+ console.log('HTTP2 server listening on port ' + serverPort);
+}
+var portSelection = 0;
+var envport = process.env.MOZHTTP2_PORT;
+if (envport !== undefined) {
+ try {
+ portSelection = parseInt(envport, 10);
+ } catch (e) {
+ portSelection = -1;
+ }
+}
+server.listen(portSelection, "0.0.0.0", 200, listenok);