Skip to content

Commit

Permalink
Check for and handle listen errors (resolves #4029) (hyperledger-arch…
Browse files Browse the repository at this point in the history
…ives#4035)

Signed-off-by: Simon Stone <[email protected]>
  • Loading branch information
Simon Stone authored and nklincoln committed May 31, 2018
1 parent e444146 commit b57a538
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 40 deletions.
9 changes: 9 additions & 0 deletions packages/composer-connector-server/lib/connectorserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefi
const Logger = require('composer-common').Logger;
const realSerializerr = require('serializerr');
const uuid = require('uuid');
const version = require('../package.json').version;

const LOG = Logger.getLog('ConnectorServer');

Expand Down Expand Up @@ -80,6 +81,14 @@ class ConnectorServer {
LOG.exit(method);
}

/**
* Test the connection to the connector server.
* @param {function} callback The callback to call when complete.
*/
async ping(callback) {
callback(null, { version });
}

/**
* Handle a request from the client to get a busines network card.
* @param {string} cardName The name of the card.
Expand Down
15 changes: 14 additions & 1 deletion packages/composer-connector-server/test/connectorserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ConnectorServer = require('..');
const SecurityContext = require('composer-common').SecurityContext;
const Logger= require('composer-common').Logger;
const uuid = require('uuid');
const version = require('../package.json').version;

const should = require('chai').should();
const sinon = require('sinon');
Expand Down Expand Up @@ -152,7 +153,8 @@ describe('ConnectorServer', () => {
'/api/connectionUpgrade',
'/api/connectionManagerExportIdentity',
'/api/connectionManagerRemoveIdentity',
'/api/connectionCreateTransactionId'
'/api/connectionCreateTransactionId',
'/api/ping'
].sort());
mockSocket.on.args.forEach((args) => {
isFunction(args[1]).should.be.true;
Expand All @@ -173,6 +175,17 @@ describe('ConnectorServer', () => {

});

describe('#ping', () => {

it('should ping', async () => {
const cb = sinon.stub();
await connectorServer.ping(cb);
sinon.assert.calledOnce(cb);
sinon.assert.calledWith(cb, null, { version });
});

});

describe('#businessNetworkCardStoreGet', () => {
it('should get a business network card', () => {
mockBusinessNetworkCardStore.get.withArgs(cardName).resolves(card);
Expand Down
26 changes: 13 additions & 13 deletions packages/composer-playground-api/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ const Logger = require('composer-common').Logger;
Logger.setCLIDefaults();
const LOG = Logger.getLog('PlaygroundAPI');



const method = 'main';
LOG.entry(method);

const app = require('.')(argv.port, argv.test);

if (process.env.COMPOSER_CONFIG) {
const config = JSON.parse(process.env.COMPOSER_CONFIG);
app.get('/config.json', (req, res, next) => {
res.json(config);
});
}
(async function main() {
const method = 'main';
LOG.entry(method);

const app = await require('.')(argv.port, argv.test);

if (process.env.COMPOSER_CONFIG) {
const config = JSON.parse(process.env.COMPOSER_CONFIG);
app.get('/config.json', (req, res, next) => {
res.json(config);
});
}
})();
23 changes: 15 additions & 8 deletions packages/composer-playground-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
const ConnectionProfileManager = require('composer-common').ConnectionProfileManager;
const ConnectorServer = require('composer-connector-server');
const http = require('http');
const socketIO = require('socket.io');
const Logger = require('composer-common').Logger;
const Util = require('./lib/util');
const NetworkCardStoreManager = require('composer-common').NetworkCardStoreManager;
const npmRoute = require('./routes/npm');
const socketIO = require('socket.io');
const Util = require('./lib/util');

const LOG = Logger.getLog('PlaygroundAPI');
const NetworkCardStoreManager = require('composer-common').NetworkCardStoreManager;
/**
* Create an Express.js application that hosts both the REST API for Composer Playground
* and the Connector Server for supporting the proxy connector.
* @param {number} port The port for the Express.js application.
* @param {testMode} testMode Is the api started in test mode
* @return {Object} The Express.js application.
*/
function createServer (port, testMode) {
async function createServer (port, testMode) {
const method = 'createServer';
LOG.entry(method, port, testMode);

Expand All @@ -49,16 +49,23 @@ function createServer (port, testMode) {
const io = socketIO(server);
io.on('connect', (socket) => {
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' connected`);
socket.on('disconnect', (reason) => {
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' disconnected (${reason})`);
});
new ConnectorServer(businessNetworkCardStore, connectionProfileManager, socket);
});
io.on('disconnect', (socket) => {
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' disconnected`);

port = await new Promise((resolve, reject) => {
server.listen(port, (error) => {
if (error) {
return reject(error);
}
resolve(server.address().port);
});
});

server.listen(port);
// Save the port back into the app. If the port was 0, it will now have
// been set to a dynamically assigned port.
port = server.address().port;
app.set('port', port);
LOG.info(method, `Playground API started on port ${port}`);
if(testMode) {
Expand Down
4 changes: 2 additions & 2 deletions packages/composer-playground-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"mocha": "3.4.2",
"nyc": "11.1.0",
"proxyquire": "1.7.11",
"sinon": "2.3.8"
"sinon": "2.3.8",
"socket.io-client": "1.7.3"
},
"dependencies": {
"async": "2.5.0",
Expand All @@ -98,7 +99,6 @@
"systest/**",
"test/**",
"config/environment/**",
"index.js",
"lib/util.js",
"cli.js"
],
Expand Down
9 changes: 1 addition & 8 deletions packages/composer-playground-api/routes/npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,10 @@ const LOG = Logger.getLog('NPM');
const sampleList = [{name : 'basic-sample-network'}];
const fs = require('fs');

let router = null;

module.exports = (app, testMode) => {

// Did we already create a router?
if (router !== null) {
return router;
}

// Create a new router.
router = express.Router();
const router = express.Router();

app.use('/', router);

Expand Down
95 changes: 95 additions & 0 deletions packages/composer-playground-api/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

const createServer = require('..');
const http = require('http');
const io = require('socket.io-client');

const chai = require('chai');
chai.should();
chai.use(require('chai-as-promised'));
chai.use(require('chai-http'));
const sinon = require('sinon');

describe('#createServer', () => {

let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();
});

it('should start a server and return the port', async () => {
const app = await createServer(0, false);
app.get('port').should.be.greaterThan(0);
});

it('should start a socket.io server', async () => {
const app = await createServer(0, false);
const port = app.get('port');
const socket = io(`http://localhost:${port}`, {
autoConnect: false
});
await new Promise((resolve, reject) => {
socket.once('connect', () => {
resolve();
});
socket.open();
});
const result = await new Promise((resolve, reject) => {
socket.emit('/api/ping', (error, result) => {
if (error) {
return reject(error);
}
resolve(result);
});
});
result.version.should.be.a('string');
await new Promise((resolve, reject) => {
socket.once('disconnect', () => {
resolve();
});
socket.close();
});
});

it('should start a server in test mode', async () => {
const app = await createServer(0, true);
const result = await chai.request(app).get('/api/getSampleList');
result.body.should.deep.equal([{ name: 'basic-sample-network' }]);
});

it('should throw an error if listen throws an error', async () => {
const server = http.createServer();
sandbox.stub(http, 'createServer').returns(server);
sinon.stub(server, 'listen').throws(new Error('such throw error'));
await createServer(0, false)
.should.be.rejectedWith(/such throw error/);
});

it('should throw an error if listen calls the callback with an error', async () => {
const server = http.createServer();
sandbox.stub(http, 'createServer').returns(server);
sinon.stub(server, 'listen').yields(new Error('such callback error'));
await createServer(0, false)
.should.be.rejectedWith(/such callback error/);
});

});
14 changes: 8 additions & 6 deletions packages/composer-playground/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ if (process.env.COMPOSER_CONFIG) {
config = JSON.parse(process.env.COMPOSER_CONFIG);
}

const method = 'main';
LOG.entry(method);
(async function main() {
const method = 'main';
LOG.entry(method);

require('.')(argv.port, argv.test, config);
await require('.')(argv.port, argv.test, config);

if (!isDocker()) {
opener(`http://localhost:${argv.port}`);
}
if (!isDocker()) {
opener(`http://localhost:${argv.port}`);
}
})();
4 changes: 2 additions & 2 deletions packages/composer-playground/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ const playgroundAPI = require('composer-playground-api');
* @param {Object} [config] The configuration.
* @return {Object} The Express.js application.
*/
function createServer (port, testMode, config) {
async function createServer (port, testMode, config) {

// Create the playground API server.
const app = playgroundAPI(port, testMode);
const app = await playgroundAPI(port, testMode);

const dist = path.resolve(__dirname, 'dist');

Expand Down

0 comments on commit b57a538

Please sign in to comment.