summaryrefslogtreecommitdiffstats
path: root/testing/xpcshell/node-http2/test/http.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testing/xpcshell/node-http2/test/http.js793
1 files changed, 793 insertions, 0 deletions
diff --git a/testing/xpcshell/node-http2/test/http.js b/testing/xpcshell/node-http2/test/http.js
new file mode 100644
index 0000000000..95a074e4a0
--- /dev/null
+++ b/testing/xpcshell/node-http2/test/http.js
@@ -0,0 +1,793 @@
+var expect = require('chai').expect;
+var util = require('./util');
+var fs = require('fs');
+var path = require('path');
+var url = require('url');
+var net = require('net');
+
+var http2 = require('../lib/http');
+var https = require('https');
+
+var serverOptions = {
+ key: fs.readFileSync(path.join(__dirname, '../example/localhost.key')),
+ cert: fs.readFileSync(path.join(__dirname, '../example/localhost.crt')),
+ rejectUnauthorized: true,
+ log: util.serverLog
+};
+
+var agentOptions = {
+ key: serverOptions.key,
+ ca: serverOptions.cert,
+ rejectUnauthorized: true,
+ log: util.clientLog
+};
+
+var globalAgent = new http2.Agent(agentOptions);
+
+describe('http.js', function() {
+ beforeEach(function() {
+ http2.globalAgent = globalAgent;
+ });
+ describe('Server', function() {
+ describe('new Server(options)', function() {
+ it('should throw if called without \'plain\' or TLS options', function() {
+ expect(function() {
+ new http2.Server();
+ }).to.throw(Error);
+ expect(function() {
+ http2.createServer(util.noop);
+ }).to.throw(Error);
+ });
+ });
+ describe('method `listen()`', function () {
+ it('should emit `listening` event', function (done) {
+ var server = http2.createServer(serverOptions);
+
+ server.on('listening', function () {
+ server.close();
+
+ done();
+ })
+
+ server.listen(0);
+ });
+ it('should emit `error` on failure', function (done) {
+ var server = http2.createServer(serverOptions);
+
+ // This TCP server is used to explicitly take a port to make
+ // server.listen() fails.
+ var net = require('net').createServer();
+
+ server.on('error', function () {
+ net.close()
+
+ done();
+ });
+
+ net.listen(0, function () {
+ server.listen(this.address().port);
+ });
+ });
+ });
+ describe('property `timeout`', function() {
+ it('should be a proxy for the backing HTTPS server\'s `timeout` property', function() {
+ var server = new http2.Server(serverOptions);
+ var backingServer = server._server;
+ var newTimeout = 10;
+ server.timeout = newTimeout;
+ expect(server.timeout).to.be.equal(newTimeout);
+ expect(backingServer.timeout).to.be.equal(newTimeout);
+ });
+ });
+ describe('method `setTimeout(timeout, [callback])`', function() {
+ it('should be a proxy for the backing HTTPS server\'s `setTimeout` method', function() {
+ var server = new http2.Server(serverOptions);
+ var backingServer = server._server;
+ var newTimeout = 10;
+ var newCallback = util.noop;
+ backingServer.setTimeout = function(timeout, callback) {
+ expect(timeout).to.be.equal(newTimeout);
+ expect(callback).to.be.equal(newCallback);
+ };
+ server.setTimeout(newTimeout, newCallback);
+ });
+ });
+ });
+ describe('Agent', function() {
+ describe('property `maxSockets`', function() {
+ it('should be a proxy for the backing HTTPS agent\'s `maxSockets` property', function() {
+ var agent = new http2.Agent({ log: util.clientLog });
+ var backingAgent = agent._httpsAgent;
+ var newMaxSockets = backingAgent.maxSockets + 1;
+ agent.maxSockets = newMaxSockets;
+ expect(agent.maxSockets).to.be.equal(newMaxSockets);
+ expect(backingAgent.maxSockets).to.be.equal(newMaxSockets);
+ });
+ });
+ describe('method `request(options, [callback])`', function() {
+ it('should use a new agent for request-specific TLS settings', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1234, function() {
+ var options = url.parse('https://localhost:1234' + path);
+ options.key = agentOptions.key;
+ options.ca = agentOptions.ca;
+ options.rejectUnauthorized = true;
+
+ http2.globalAgent = new http2.Agent({ log: util.clientLog });
+ http2.get(options, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ it('should throw when trying to use with \'http\' scheme', function() {
+ expect(function() {
+ var agent = new http2.Agent({ log: util.clientLog });
+ agent.request({ protocol: 'http:' });
+ }).to.throw(Error);
+ });
+ });
+ });
+ describe('OutgoingRequest', function() {
+ function testFallbackProxyMethod(name, originalArguments, done) {
+ var request = new http2.OutgoingRequest();
+
+ // When in HTTP/2 mode, this call should be ignored
+ request.stream = { reset: util.noop };
+ request[name].apply(request, originalArguments);
+ delete request.stream;
+
+ // When in fallback mode, this call should be forwarded
+ request[name].apply(request, originalArguments);
+ var mockFallbackRequest = { on: util.noop };
+ mockFallbackRequest[name] = function() {
+ expect(Array.prototype.slice.call(arguments)).to.deep.equal(originalArguments);
+ done();
+ };
+ request._fallback(mockFallbackRequest);
+ }
+ describe('method `setNoDelay(noDelay)`', function() {
+ it('should act as a proxy for the backing HTTPS agent\'s `setNoDelay` method', function(done) {
+ testFallbackProxyMethod('setNoDelay', [true], done);
+ });
+ });
+ describe('method `setSocketKeepAlive(enable, initialDelay)`', function() {
+ it('should act as a proxy for the backing HTTPS agent\'s `setSocketKeepAlive` method', function(done) {
+ testFallbackProxyMethod('setSocketKeepAlive', [true, util.random(10, 100)], done);
+ });
+ });
+ describe('method `setTimeout(timeout, [callback])`', function() {
+ it('should act as a proxy for the backing HTTPS agent\'s `setTimeout` method', function(done) {
+ testFallbackProxyMethod('setTimeout', [util.random(10, 100), util.noop], done);
+ });
+ });
+ describe('method `abort()`', function() {
+ it('should act as a proxy for the backing HTTPS agent\'s `abort` method', function(done) {
+ testFallbackProxyMethod('abort', [], done);
+ });
+ });
+ });
+ describe('OutgoingResponse', function() {
+ it('should throw error when writeHead is called multiple times on it', function() {
+ var called = false;
+ var stream = { _log: util.log, headers: function () {
+ if (called) {
+ throw new Error('Should not send headers twice');
+ } else {
+ called = true;
+ }
+ }, once: util.noop };
+ var response = new http2.OutgoingResponse(stream);
+
+ response.writeHead(200);
+ response.writeHead(404);
+ });
+ it('field finished should be Boolean', function(){
+ var stream = { _log: util.log, headers: function () {}, once: util.noop };
+ var response = new http2.OutgoingResponse(stream);
+ expect(response.finished).to.be.a('Boolean');
+ });
+ it('field finished should initially be false and then go to true when response completes',function(done){
+ var res;
+ var server = http2.createServer(serverOptions, function(request, response) {
+ res = response;
+ expect(res.finished).to.be.false;
+ response.end('HiThere');
+ });
+ server.listen(1236, function() {
+ http2.get('https://localhost:1236/finished-test', function(response) {
+ response.on('data', function(data){
+ var sink = data; //
+ });
+ response.on('end',function(){
+ expect(res.finished).to.be.true;
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('test scenario', function() {
+ describe('simple request', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1234, function() {
+ http2.get('https://localhost:1234' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('2 simple request in parallel', function() {
+ it('should work as expected', function(originalDone) {
+ var path = '/x';
+ var message = 'Hello world';
+ var done = util.callNTimes(2, function() {
+ server.close();
+ originalDone();
+ });
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1234, function() {
+ http2.get('https://localhost:1234' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ http2.get('https://localhost:1234' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('100 simple request in a series', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ var n = 100;
+ server.listen(1242, function() {
+ doRequest();
+ function doRequest() {
+ http2.get('https://localhost:1242' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ if (n) {
+ n -= 1;
+ doRequest();
+ } else {
+ server.close();
+ done();
+ }
+ });
+ });
+ }
+ });
+ });
+ });
+ describe('request with payload', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ request.once('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ response.end();
+ });
+ });
+
+ server.listen(1240, function() {
+ var request = http2.request({
+ host: 'localhost',
+ port: 1240,
+ path: path
+ });
+ request.write(message);
+ request.end();
+ request.on('response', function() {
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ describe('request with custom status code and headers', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+ var headerName = 'name';
+ var headerValue = 'value';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ // Request URL and headers
+ expect(request.url).to.equal(path);
+ expect(request.headers[headerName]).to.equal(headerValue);
+
+ // A header to be overwritten later
+ response.setHeader(headerName, 'to be overwritten');
+ expect(response.getHeader(headerName)).to.equal('to be overwritten');
+
+ // A header to be deleted
+ response.setHeader('nonexistent', 'x');
+ response.removeHeader('nonexistent');
+ expect(response.getHeader('nonexistent')).to.equal(undefined);
+
+ // A set-cookie header which should always be an array
+ response.setHeader('set-cookie', 'foo');
+
+ // Don't send date
+ response.sendDate = false;
+
+ // Specifying more headers, the status code and a reason phrase with `writeHead`
+ var moreHeaders = {};
+ moreHeaders[headerName] = headerValue;
+ response.writeHead(600, 'to be discarded', moreHeaders);
+ expect(response.getHeader(headerName)).to.equal(headerValue);
+
+ // Empty response body
+ response.end(message);
+ });
+
+ server.listen(1239, function() {
+ var headers = {};
+ headers[headerName] = headerValue;
+ var request = http2.request({
+ host: 'localhost',
+ port: 1239,
+ path: path,
+ headers: headers
+ });
+ request.end();
+ request.on('response', function(response) {
+ expect(response.headers[headerName]).to.equal(headerValue);
+ expect(response.headers['nonexistent']).to.equal(undefined);
+ expect(response.headers['set-cookie']).to.an.instanceof(Array)
+ expect(response.headers['set-cookie']).to.deep.equal(['foo'])
+ expect(response.headers['date']).to.equal(undefined);
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('request over plain TCP', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.raw.createServer({
+ log: util.serverLog
+ }, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1237, function() {
+ var request = http2.raw.request({
+ plain: true,
+ host: 'localhost',
+ port: 1237,
+ path: path
+ }, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ server.close();
+ done();
+ });
+ });
+ request.end();
+ });
+ });
+ });
+ describe('get over plain TCP', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.raw.createServer({
+ log: util.serverLog
+ }, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1237, function() {
+ var request = http2.raw.get('http://localhost:1237/x', function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ server.close();
+ done();
+ });
+ });
+ request.end();
+ });
+ });
+ });
+ describe('request to an HTTPS/1 server', function() {
+ it('should fall back to HTTPS/1 successfully', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = https.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(5678, function() {
+ http2.get('https://localhost:5678' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('2 parallel request to an HTTPS/1 server', function() {
+ it('should fall back to HTTPS/1 successfully', function(originalDone) {
+ var path = '/x';
+ var message = 'Hello world';
+ var done = util.callNTimes(2, function() {
+ server.close();
+ originalDone();
+ });
+
+ var server = https.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(6789, function() {
+ http2.get('https://localhost:6789' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ http2.get('https://localhost:6789' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('HTTPS/1 request to a HTTP/2 server', function() {
+ it('should fall back to HTTPS/1 successfully', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1236, function() {
+ var options = url.parse('https://localhost:1236' + path);
+ options.agent = new https.Agent(agentOptions);
+ https.get(options, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('two parallel request', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1237, function() {
+ done = util.callNTimes(2, done);
+ // 1. request
+ http2.get('https://localhost:1237' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ // 2. request
+ http2.get('https://localhost:1237' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('two subsequent request', function() {
+ it('should use the same HTTP/2 connection', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ response.end(message);
+ });
+
+ server.listen(1238, function() {
+ // 1. request
+ http2.get('https://localhost:1238' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+
+ // 2. request
+ http2.get('https://localhost:1238' + path, function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ describe('https server node module specification conformance', function() {
+ it('should provide API for remote HTTP 1.1 client address', function(done) {
+ var remoteAddress = null;
+ var remotePort = null;
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ // HTTPS 1.1 client with Node 0.10 server
+ if (!request.remoteAddress) {
+ if (request.socket.socket) {
+ remoteAddress = request.socket.socket.remoteAddress;
+ remotePort = request.socket.socket.remotePort;
+ } else {
+ remoteAddress = request.socket.remoteAddress;
+ remotePort = request.socket.remotePort;
+ }
+ } else {
+ // HTTPS 1.1/2.0 client with Node 0.12 server
+ remoteAddress = request.remoteAddress;
+ remotePort = request.remotePort;
+ }
+ response.write('Pong');
+ response.end();
+ });
+
+ server.listen(1259, 'localhost', function() {
+ var request = https.request({
+ host: 'localhost',
+ port: 1259,
+ path: '/',
+ ca: serverOptions.cert
+ });
+ request.write('Ping');
+ request.end();
+ request.on('response', function(response) {
+ response.on('data', function(data) {
+ var localAddress = response.socket.address();
+ expect(remoteAddress).to.equal(localAddress.address);
+ expect(remotePort).to.equal(localAddress.port);
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ it('should provide API for remote HTTP 2.0 client address', function(done) {
+ var remoteAddress = null;
+ var remotePort = null;
+ var localAddress = null;
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ remoteAddress = request.remoteAddress;
+ remotePort = request.remotePort;
+ response.write('Pong');
+ response.end();
+ });
+
+ server.listen(1258, 'localhost', function() {
+ var request = http2.request({
+ host: 'localhost',
+ port: 1258,
+ path: '/'
+ });
+ request.write('Ping');
+ globalAgent.on('false:localhost:1258', function(endpoint) {
+ localAddress = endpoint.socket.address();
+ });
+ request.end();
+ request.on('response', function(response) {
+ response.on('data', function(data) {
+ expect(remoteAddress).to.equal(localAddress.address);
+ expect(remotePort).to.equal(localAddress.port);
+ server.close();
+ done();
+ });
+ });
+ });
+ });
+ it('should expose net.Socket as .socket and .connection', function(done) {
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.socket).to.equal(request.connection);
+ expect(request.socket).to.be.instanceof(net.Socket);
+ response.write('Pong');
+ response.end();
+ done();
+ });
+
+ server.listen(1248, 'localhost', function() {
+ var request = https.request({
+ host: 'localhost',
+ port: 1248,
+ path: '/',
+ ca: serverOptions.cert
+ });
+ request.write('Ping');
+ request.end();
+ });
+ });
+ });
+ describe('request and response with trailers', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+ var requestTrailers = { 'content-md5': 'x' };
+ var responseTrailers = { 'content-md5': 'y' };
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ request.on('data', util.noop);
+ request.once('end', function() {
+ expect(request.trailers).to.deep.equal(requestTrailers);
+ response.write(message);
+ response.addTrailers(responseTrailers);
+ response.end();
+ });
+ });
+
+ server.listen(1241, function() {
+ var request = http2.request('https://localhost:1241' + path);
+ request.addTrailers(requestTrailers);
+ request.end();
+ request.on('response', function(response) {
+ response.on('data', util.noop);
+ response.once('end', function() {
+ expect(response.trailers).to.deep.equal(responseTrailers);
+ done();
+ });
+ });
+ });
+ });
+ });
+ describe('Handle socket error', function () {
+ it('HTTPS on Connection Refused error', function (done) {
+ var path = '/x';
+ var request = http2.request('https://127.0.0.1:6666' + path);
+
+ request.on('error', function (err) {
+ expect(err.errno).to.equal('ECONNREFUSED');
+ done();
+ });
+
+ request.on('response', function (response) {
+ server._server._handle.destroy();
+
+ response.on('data', util.noop);
+
+ response.once('end', function () {
+ done(new Error('Request should have failed'));
+ });
+ });
+
+ request.end();
+
+ });
+ it('HTTP on Connection Refused error', function (done) {
+ var path = '/x';
+
+ var request = http2.raw.request('http://127.0.0.1:6666' + path);
+
+ request.on('error', function (err) {
+ expect(err.errno).to.equal('ECONNREFUSED');
+ done();
+ });
+
+ request.on('response', function (response) {
+ server._server._handle.destroy();
+
+ response.on('data', util.noop);
+
+ response.once('end', function () {
+ done(new Error('Request should have failed'));
+ });
+ });
+
+ request.end();
+ });
+ });
+ describe('server push', function() {
+ it('should work as expected', function(done) {
+ var path = '/x';
+ var message = 'Hello world';
+ var pushedPath = '/y';
+ var pushedMessage = 'Hello world 2';
+
+ var server = http2.createServer(serverOptions, function(request, response) {
+ expect(request.url).to.equal(path);
+ var push1 = response.push('/y');
+ push1.end(pushedMessage);
+ var push2 = response.push({ path: '/y', protocol: 'https:' });
+ push2.end(pushedMessage);
+ response.end(message);
+ });
+
+ server.listen(1235, function() {
+ var request = http2.get('https://localhost:1235' + path);
+ done = util.callNTimes(5, done);
+
+ request.on('response', function(response) {
+ response.on('data', function(data) {
+ expect(data.toString()).to.equal(message);
+ done();
+ });
+ response.on('end', done);
+ });
+
+ request.on('push', function(promise) {
+ expect(promise.url).to.be.equal(pushedPath);
+ promise.on('response', function(pushStream) {
+ pushStream.on('data', function(data) {
+ expect(data.toString()).to.equal(pushedMessage);
+ done();
+ });
+ pushStream.on('end', done);
+ });
+ });
+ });
+ });
+ });
+ });
+});