Skip to content

Commit

Permalink
feat(app): add close method for closing server
Browse files Browse the repository at this point in the history
  • Loading branch information
m4w4q7 authored and drawain committed Aug 7, 2017
1 parent c5efb39 commit db8422d
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 6 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Boar Server
# Boar Server

## Example usage for app

Expand Down Expand Up @@ -28,6 +28,19 @@ put these lines in your server.js
app.addMiddleware(cors());
```

## Graceful shutdown
You can stop the server from recieving new connections with `app.close()`. It returns a Promise that resolves when all existing connections are ended.
``` javascript
var app = new App(koaApp);
app.listen(config.port);
process.on('SIGTERM', () => {
app.close().then(() => {
// additional cleaning (e.g. closing db connection)
process.exit(0);
})
})
```

## HTTPS support
To enable HTTPS support, simple create `SERVE_HTTPS` environment variable with value `true`.
The port for https will be the port of the application increased with 10000 (10k).
Expand Down Expand Up @@ -208,7 +221,7 @@ Provides middlewares for setting up various security related HTTP headers.

```javascript
var clearCollections = require('boar-server').lib.clearCollections(mongoose);

clearCollections(); // returns a promise
```

Expand All @@ -218,7 +231,7 @@ This will _drop_ all your collections.

```javascript
var truncateCollections = require('boar-server').lib.truncateCollections(mongoose);

truncateCollections(); // returns a promise
```

Expand Down
19 changes: 17 additions & 2 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var SecurityMiddlewareFactory = require('../lib/security-middleware-factory');

var App = function(koaApp) {
this.koaApp = koaApp;
this._servers = [];
};

App.prototype = {
Expand Down Expand Up @@ -117,8 +118,15 @@ App.prototype = {
},


close: function() {
const serverClosingPromises = this._servers.map(this._closeServer.bind(this));
return Promise.all(serverClosingPromises).then(() => {});
},


_startHTTPServer: function(port, env) {
http.createServer(this.koaApp.callback()).listen(port);
const server = http.createServer(this.koaApp.callback()).listen(port);
this._servers.push(server);
console.log('Application started:', { port: port, env: env });
},

Expand All @@ -130,8 +138,15 @@ App.prototype = {
httpsOptions.cert = fs.readFileSync(process.env.HTTPS_CERT);
}

https.createServer(httpsOptions, this.koaApp.callback()).listen(port);
const server = https.createServer(httpsOptions, this.koaApp.callback()).listen(port);
this._servers.push(server);
console.log('Application started (with SSL):', { port: port, env: env });
},


_closeServer: function(server) {
this._servers = this._servers.filter(storedServer => storedServer !== server);
return new Promise(resolve => server.close(resolve));
}

};
Expand Down
101 changes: 101 additions & 0 deletions app/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict';

const http = require('http');
const https = require('https');
const expect = require('chai').expect;
const sinon = require('sinon');
const App = require('./');

let sandbox;

const createFakeServer = extensions => {
const defaultFakeServer = {
listen() {
return this;
},
close: sandbox.stub()
};
return Object.assign(defaultFakeServer, extensions);
};

const createApp = () => new App({ callback: () => {} });

const tick = () => {
return new Promise(resolve => setTimeout(resolve, 0));
};


describe('App', () => {

let envSnapshot;

beforeEach(() => {
sandbox = sinon.sandbox.create();
envSnapshot = Object.assign({}, process.env);
});

afterEach(() => {
sandbox.restore();
process.env = envSnapshot;
});

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

it('should close the running servers', function() {
process.env.SERVE_HTTPS = 'true';
const httpServer = createFakeServer();
sandbox.stub(http, 'createServer').returns(httpServer);
const httpsServer = createFakeServer();
sandbox.stub(https, 'createServer').returns(httpsServer);
const app = createApp();
app.listen(3000);

app.close();

expect(httpServer.close).to.have.been.calledOnce;
expect(httpsServer.close).to.have.been.calledOnce;
});


it('should return a promise that resolves after all servers had closed', function* () {
process.env.SERVE_HTTPS = 'true';

let httpServerClosedCallback;
const httpServer = createFakeServer({ close: callback => httpServerClosedCallback = callback });
sandbox.stub(http, 'createServer').returns(httpServer);

let httpsServerClosedCallback;
const httpsServer = createFakeServer({ close: callback => httpsServerClosedCallback = callback });
sandbox.stub(https, 'createServer').returns(httpsServer);

const app = createApp();
app.listen(3000);

let closeResolved = false;
app.close().then(() => closeResolved = true);

httpServerClosedCallback();
yield tick();
expect(closeResolved).to.be.false;

httpsServerClosedCallback();
yield tick();
expect(closeResolved).to.be.true;
});


it('should not call close on already closed servers', function() {
const httpServer = createFakeServer();
sandbox.stub(http, 'createServer').returns(httpServer);
const app = createApp();
app.listen(3000);

app.close();
app.close();

expect(httpServer.close).to.have.been.calledOnce;
});

});

});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"mocha": "3.0.2",
"nsp": "2.6.1",
"semantic-release": "4.3.5",
"sinon": "1.17.5"
"sinon": "1.17.5",
"sinon-chai": "2.12.0"
},
"dependencies": {
"app-root-path": "1.3.0",
Expand Down
4 changes: 4 additions & 0 deletions setup-test.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const sinonChai = require('sinon-chai');
const chai = require('chai');

before(() => chai.use(sinonChai));

0 comments on commit db8422d

Please sign in to comment.