123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- 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);
- });
- });
- });
- });
- });
- });
- });
|