Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit b57a538

Browse files
Simon Stonenklincoln
authored andcommitted
Check for and handle listen errors (resolves #4029) (#4035)
Signed-off-by: Simon Stone <[email protected]>
1 parent e444146 commit b57a538

File tree

9 files changed

+159
-40
lines changed

9 files changed

+159
-40
lines changed

packages/composer-connector-server/lib/connectorserver.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefi
1818
const Logger = require('composer-common').Logger;
1919
const realSerializerr = require('serializerr');
2020
const uuid = require('uuid');
21+
const version = require('../package.json').version;
2122

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

@@ -80,6 +81,14 @@ class ConnectorServer {
8081
LOG.exit(method);
8182
}
8283

84+
/**
85+
* Test the connection to the connector server.
86+
* @param {function} callback The callback to call when complete.
87+
*/
88+
async ping(callback) {
89+
callback(null, { version });
90+
}
91+
8392
/**
8493
* Handle a request from the client to get a busines network card.
8594
* @param {string} cardName The name of the card.

packages/composer-connector-server/test/connectorserver.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const ConnectorServer = require('..');
2424
const SecurityContext = require('composer-common').SecurityContext;
2525
const Logger= require('composer-common').Logger;
2626
const uuid = require('uuid');
27+
const version = require('../package.json').version;
2728

2829
const should = require('chai').should();
2930
const sinon = require('sinon');
@@ -152,7 +153,8 @@ describe('ConnectorServer', () => {
152153
'/api/connectionUpgrade',
153154
'/api/connectionManagerExportIdentity',
154155
'/api/connectionManagerRemoveIdentity',
155-
'/api/connectionCreateTransactionId'
156+
'/api/connectionCreateTransactionId',
157+
'/api/ping'
156158
].sort());
157159
mockSocket.on.args.forEach((args) => {
158160
isFunction(args[1]).should.be.true;
@@ -173,6 +175,17 @@ describe('ConnectorServer', () => {
173175

174176
});
175177

178+
describe('#ping', () => {
179+
180+
it('should ping', async () => {
181+
const cb = sinon.stub();
182+
await connectorServer.ping(cb);
183+
sinon.assert.calledOnce(cb);
184+
sinon.assert.calledWith(cb, null, { version });
185+
});
186+
187+
});
188+
176189
describe('#businessNetworkCardStoreGet', () => {
177190
it('should get a business network card', () => {
178191
mockBusinessNetworkCardStore.get.withArgs(cardName).resolves(card);

packages/composer-playground-api/cli.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ const Logger = require('composer-common').Logger;
3939
Logger.setCLIDefaults();
4040
const LOG = Logger.getLog('PlaygroundAPI');
4141

42-
43-
44-
const method = 'main';
45-
LOG.entry(method);
46-
47-
const app = require('.')(argv.port, argv.test);
48-
49-
if (process.env.COMPOSER_CONFIG) {
50-
const config = JSON.parse(process.env.COMPOSER_CONFIG);
51-
app.get('/config.json', (req, res, next) => {
52-
res.json(config);
53-
});
54-
}
42+
(async function main() {
43+
const method = 'main';
44+
LOG.entry(method);
45+
46+
const app = await require('.')(argv.port, argv.test);
47+
48+
if (process.env.COMPOSER_CONFIG) {
49+
const config = JSON.parse(process.env.COMPOSER_CONFIG);
50+
app.get('/config.json', (req, res, next) => {
51+
res.json(config);
52+
});
53+
}
54+
})();

packages/composer-playground-api/index.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@
1717
const ConnectionProfileManager = require('composer-common').ConnectionProfileManager;
1818
const ConnectorServer = require('composer-connector-server');
1919
const http = require('http');
20-
const socketIO = require('socket.io');
2120
const Logger = require('composer-common').Logger;
22-
const Util = require('./lib/util');
21+
const NetworkCardStoreManager = require('composer-common').NetworkCardStoreManager;
2322
const npmRoute = require('./routes/npm');
23+
const socketIO = require('socket.io');
24+
const Util = require('./lib/util');
2425

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

@@ -49,16 +49,23 @@ function createServer (port, testMode) {
4949
const io = socketIO(server);
5050
io.on('connect', (socket) => {
5151
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' connected`);
52+
socket.on('disconnect', (reason) => {
53+
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' disconnected (${reason})`);
54+
});
5255
new ConnectorServer(businessNetworkCardStore, connectionProfileManager, socket);
5356
});
54-
io.on('disconnect', (socket) => {
55-
LOG.info(method, `Client with ID '${socket.id}' on host '${socket.request.connection.remoteAddress}' disconnected`);
57+
58+
port = await new Promise((resolve, reject) => {
59+
server.listen(port, (error) => {
60+
if (error) {
61+
return reject(error);
62+
}
63+
resolve(server.address().port);
64+
});
5665
});
5766

58-
server.listen(port);
5967
// Save the port back into the app. If the port was 0, it will now have
6068
// been set to a dynamically assigned port.
61-
port = server.address().port;
6269
app.set('port', port);
6370
LOG.info(method, `Playground API started on port ${port}`);
6471
if(testMode) {

packages/composer-playground-api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
"mocha": "3.4.2",
7575
"nyc": "11.1.0",
7676
"proxyquire": "1.7.11",
77-
"sinon": "2.3.8"
77+
"sinon": "2.3.8",
78+
"socket.io-client": "1.7.3"
7879
},
7980
"dependencies": {
8081
"async": "2.5.0",
@@ -98,7 +99,6 @@
9899
"systest/**",
99100
"test/**",
100101
"config/environment/**",
101-
"index.js",
102102
"lib/util.js",
103103
"cli.js"
104104
],

packages/composer-playground-api/routes/npm.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,10 @@ const LOG = Logger.getLog('NPM');
3939
const sampleList = [{name : 'basic-sample-network'}];
4040
const fs = require('fs');
4141

42-
let router = null;
43-
4442
module.exports = (app, testMode) => {
4543

46-
// Did we already create a router?
47-
if (router !== null) {
48-
return router;
49-
}
50-
5144
// Create a new router.
52-
router = express.Router();
45+
const router = express.Router();
5346

5447
app.use('/', router);
5548

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
'use strict';
16+
17+
const createServer = require('..');
18+
const http = require('http');
19+
const io = require('socket.io-client');
20+
21+
const chai = require('chai');
22+
chai.should();
23+
chai.use(require('chai-as-promised'));
24+
chai.use(require('chai-http'));
25+
const sinon = require('sinon');
26+
27+
describe('#createServer', () => {
28+
29+
let sandbox;
30+
31+
beforeEach(() => {
32+
sandbox = sinon.sandbox.create();
33+
});
34+
35+
afterEach(() => {
36+
sandbox.restore();
37+
});
38+
39+
it('should start a server and return the port', async () => {
40+
const app = await createServer(0, false);
41+
app.get('port').should.be.greaterThan(0);
42+
});
43+
44+
it('should start a socket.io server', async () => {
45+
const app = await createServer(0, false);
46+
const port = app.get('port');
47+
const socket = io(`http://localhost:${port}`, {
48+
autoConnect: false
49+
});
50+
await new Promise((resolve, reject) => {
51+
socket.once('connect', () => {
52+
resolve();
53+
});
54+
socket.open();
55+
});
56+
const result = await new Promise((resolve, reject) => {
57+
socket.emit('/api/ping', (error, result) => {
58+
if (error) {
59+
return reject(error);
60+
}
61+
resolve(result);
62+
});
63+
});
64+
result.version.should.be.a('string');
65+
await new Promise((resolve, reject) => {
66+
socket.once('disconnect', () => {
67+
resolve();
68+
});
69+
socket.close();
70+
});
71+
});
72+
73+
it('should start a server in test mode', async () => {
74+
const app = await createServer(0, true);
75+
const result = await chai.request(app).get('/api/getSampleList');
76+
result.body.should.deep.equal([{ name: 'basic-sample-network' }]);
77+
});
78+
79+
it('should throw an error if listen throws an error', async () => {
80+
const server = http.createServer();
81+
sandbox.stub(http, 'createServer').returns(server);
82+
sinon.stub(server, 'listen').throws(new Error('such throw error'));
83+
await createServer(0, false)
84+
.should.be.rejectedWith(/such throw error/);
85+
});
86+
87+
it('should throw an error if listen calls the callback with an error', async () => {
88+
const server = http.createServer();
89+
sandbox.stub(http, 'createServer').returns(server);
90+
sinon.stub(server, 'listen').yields(new Error('such callback error'));
91+
await createServer(0, false)
92+
.should.be.rejectedWith(/such callback error/);
93+
});
94+
95+
});

packages/composer-playground/cli.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ if (process.env.COMPOSER_CONFIG) {
4444
config = JSON.parse(process.env.COMPOSER_CONFIG);
4545
}
4646

47-
const method = 'main';
48-
LOG.entry(method);
47+
(async function main() {
48+
const method = 'main';
49+
LOG.entry(method);
4950

50-
require('.')(argv.port, argv.test, config);
51+
await require('.')(argv.port, argv.test, config);
5152

52-
if (!isDocker()) {
53-
opener(`http://localhost:${argv.port}`);
54-
}
53+
if (!isDocker()) {
54+
opener(`http://localhost:${argv.port}`);
55+
}
56+
})();

packages/composer-playground/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ const playgroundAPI = require('composer-playground-api');
2828
* @param {Object} [config] The configuration.
2929
* @return {Object} The Express.js application.
3030
*/
31-
function createServer (port, testMode, config) {
31+
async function createServer (port, testMode, config) {
3232

3333
// Create the playground API server.
34-
const app = playgroundAPI(port, testMode);
34+
const app = await playgroundAPI(port, testMode);
3535

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

0 commit comments

Comments
 (0)