diff options
Diffstat (limited to 'testing/xpcshell/moz-http2')
-rw-r--r-- | testing/xpcshell/moz-http2/http2-cert.pem | 79 | ||||
-rw-r--r-- | testing/xpcshell/moz-http2/http2-key.pem | 28 | ||||
-rw-r--r-- | testing/xpcshell/moz-http2/moz-http2.js | 786 |
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); |