diff --git a/README.md b/README.md index 7b2f3eb..318255e 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,16 @@ apn.send({ Additional fields can be found in [node-apn documentation](https://github.com/argon/node-apn/blob/master/doc/apn.markdown#class-apnnotification). +#### Closing connection + +APN use a socket and keep it connected, so if we want to gracefully stop a process, you will need to close this connection. A close method is avalaible on the APN sender. + +```js +apn.close(function () { + // the connection is closed +}); +``` + #### Events ##### transmitted diff --git a/lib/protocols/apn.js b/lib/protocols/apn.js index b60a10e..a528227 100644 --- a/lib/protocols/apn.js +++ b/lib/protocols/apn.js @@ -51,6 +51,24 @@ Sender.prototype.send = function (data) { this.connection.pushNotification(notification, data.token); }; +/** + * Close all connections. + * + * @param {function} cb + */ + +Sender.prototype.close = function (cb) { + if (! this.connection) { + if (cb) process.nextTick(cb); + return ; + } + + this.connection.shutdown(); + // node-apn use a delay of 2500ms before really closing the connection + // https://github.com/argon/node-apn/blob/3731fae9fefccdb7964606d819561058debf4b70/lib/connection.js#L355 + if (cb) setTimeout(cb, 2600); +}; + /** * Create a new APN connection and proxify events. * diff --git a/test/unit/protocols/apn.js b/test/unit/protocols/apn.js index 9abb045..31cd397 100644 --- a/test/unit/protocols/apn.js +++ b/test/unit/protocols/apn.js @@ -6,7 +6,7 @@ var apn = require('apn'); var notify = require('../../../'); describe('APN', function () { - var pushNotificationFn; + var pushNotificationFn, apnSender; beforeEach(function () { pushNotificationFn = sinon.spy(function (notification) { @@ -32,6 +32,8 @@ describe('APN', function () { util.inherits(Connection, EventEmitter); sinon.stub(apn, 'Connection', Connection); + + apnSender = notify.apn({foo: 'bar'}); }); afterEach(function () { @@ -39,12 +41,6 @@ describe('APN', function () { }); describe('#send', function () { - var apnSender; - - beforeEach(function () { - apnSender = notify.apn({foo: 'bar'}); - }); - it('should create notification and send it', function () { apnSender.send({ token: 'myToken', @@ -126,4 +122,52 @@ describe('APN', function () { expect(errorSpy).to.not.be.called; }); }); + + describe('#close', function () { + describe('without active connection', function () { + it('should do nothing if there is no connection', function () { + apnSender.close(); + }); + + it('should accept a callback', function (done) { + apnSender.close(done); + }); + }); + + describe('with an active connection', function () { + var clock; + + beforeEach(function () { + clock = sinon.useFakeTimers(0, 'setTimeout'); + apnSender.connection = { + shutdown: sinon.spy() + }; + }); + + afterEach(function () { + clock.restore(); + }); + + it('should work without callback', function () { + apnSender.close(); + expect(apnSender.connection.shutdown).to.be.called; + }); + + it('should wait 2600ms if there is a callback', function () { + var spy = sinon.spy(); + apnSender.close(spy); + + expect(spy).to.not.be.called; + + clock.tick(1000); + expect(spy).to.not.be.called; + + clock.tick(1000); + expect(spy).to.not.be.called; + + clock.tick(1000); + expect(spy).to.be.called; + }); + }); + }); }); \ No newline at end of file