Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apn

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apn - npm Package Compare versions

Comparing version 1.4.4 to 1.5.1

test/connection.js

14

ChangeLog.md
## Changelog
1.5.0/1.5.1:
* **NOTE**: This version introduces a change to default behaviour: node-apn will now connect to the sandbox environment by default. Production mode must be explicitly specified. (Fixes #50, #146)
* Added option to disable Nagle Algorithm.
* Fixed #147: Error is not raised correctly when connection cannot be established.
* Fixed #151: Smarter certificate/key loading and simplified configuration parameters.
* Fixed #152: Exponential backoff when connect fails.
* Fixed #159: Alert Title and Alert Label are necessary for Safari Push Notifications.
* Fixed #161: Make `#setAlertText` chainable.
* Starting to create some tests. Lots more required!
This has been release as 1.5.1 due to a mistake with NPM.
1.4.4:

@@ -4,0 +18,0 @@

207

lib/connection.js

@@ -26,11 +26,8 @@ var Errors = require('./errors');

* @param {Object} [options]
* @config {String} [cert="cert.pem"] The filename of the connection certificate to load from disk
* @config {Buffer|String} [certData] The certificate data. If supplied, will be used instead of loading from disk.
* @config {String} [key="key.pem"] The filename of the connection key to load from disk
* @config {Buffer|String} [keyData] The key data. If supplied will be used instead of loading from disk.
* @config {Buffer[]|String[]} [ca] An array of strings or Buffers of trusted certificates. If this is omitted several well known "root" CAs will be used, like VeriSign. - You may need to use this as some environments don't include the CA used by Apple.
* @config {String} [pfx] File path for private key, certificate and CA certs in PFX or PKCS12 format. If supplied will be used instead of certificate and key above
* @config {Buffer|String} [pfxData] PFX or PKCS12 format data containing the private key, certificate and CA certs. If supplied will be used instead of loading from disk.
* @config {Buffer|String} [cert="cert.pem"] The filename of the connection certificate to load from disk, or a Buffer/String containing the certificate data.
* @config {Buffer|String} [key="key.pem"] The filename of the connection key to load from disk, or a Buffer/String containing the key data.
* @config {Buffer[]|String[]} [ca] An array of trusted certificates. Each element should contain either a filename to load, or a Buffer/String to be used directly. If this is omitted several well known "root" CAs will be used. - You may need to use this as some environments don't include the CA used by Apple (entrust_2048).
* @config {Buffer|String} [pfx] File path for private key, certificate and CA certs in PFX or PKCS12 format, or a Buffer/String containing the PFX data. If supplied will be used instead of certificate and key above.
* @config {String} [passphrase] The passphrase for the connection key, if required
* @config {String} [address="gateway.push.apple.com"] The gateway server to connect to.
* @config {Boolean} [production=(NODE_ENV=='production')] Specifies which environment to connect to: Production (if true) or Sandbox. (Defaults to false, unless NODE_ENV == "production")
* @config {Number} [port=2195] Gateway port

@@ -54,10 +51,8 @@ * @config {Boolean} [rejectUnauthorized=true] Reject Unauthorized property to be passed through to tls.connect()

cert: 'cert.pem',
certData: null,
key: 'key.pem',
keyData: null,
ca: null,
pfx: null,
pfxData: null,
passphrase: null,
address: 'gateway.push.apple.com',
production: (process.env.NODE_ENV === "production"),
address: null,
port: 2195,

@@ -72,3 +67,4 @@ rejectUnauthorized: true,

fastMode: false,
legacy: false
legacy: false,
disableNagle: false
};

@@ -78,7 +74,16 @@

this.certData = null;
this.keyData = null;
this.pfxData = null;
if (this.options.gateway != null) {
this.options.address = this.options.gateway;
}
this.deferredInitialize = null;
if (this.options.address == null) {
if (this.options.production) {
this.options.address = "gateway.push.apple.com";
}
else {
this.options.address = "gateway.sandbox.push.apple.com";
}
}
this.initializationPromise = null;
this.deferredConnection = null;

@@ -91,2 +96,4 @@

this.failureCount = 0;
// when true, we end all sockets after the pending notifications reach 0

@@ -101,11 +108,2 @@ this.shutdownPending = false;

/**
* @private
*/
Connection.prototype.checkInitialized = function () {
if ((this.keyData && this.certData) || this.pfxData) {
this.deferredInitialize.resolve();
}
};
/**
* You should never need to call this method, initialization and connection is handled by {@link Connection#sendNotification}

@@ -115,58 +113,82 @@ * @private

Connection.prototype.initialize = function () {
if (this.deferredInitialize) {
return this.deferredInitialize.promise;
if (this.initializationPromise) {
return this.initializationPromise;
}
debug("Initialising module");
this.deferredInitialize = q.defer();
var readFile = q.nfbind(fs.readFile);
if(this.options.pfx !== null || this.options.pfxData !== null) {
// Prepare PKCS#12 data if available
var pfxPromise = null;
if(this.options.pfx != null || this.options.pfxData != null) {
if(this.options.pfxData) {
this.pfxData = this.options.pfxData;
pfxPromise = this.options.pfxData;
}
else if(Buffer.isBuffer(this.options.pfx)) {
pfxPromise = this.options.pfx;
}
else {
fs.readFile(this.options.pfx, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.pfxData = data;
this.checkInitialized();
}.bind(this));
pfxPromise = readFile(this.options.pfx);
}
}
// Prepare Certificate data if available.
var certPromise = null;
if (this.options.certData) {
certPromise = this.options.certData;
}
else if(Buffer.isBuffer(this.options.cert) || checkPEMType(this.options.cert, "CERTIFICATE")) {
certPromise = this.options.cert;
}
else {
if (this.options.certData) {
this.certData = this.options.certData;
}
else {
fs.readFile(this.options.cert, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.certData = data.toString();
this.checkInitialized();
}.bind(this));
}
// Nothing has matched so attempt to load from disk
certPromise = readFile(this.options.cert);
}
if (this.options.keyData) {
this.keyData = this.options.keyData;
// Prepare Key data if available
var keyPromise = null;
if (this.options.keyData) {
keyPromise = this.options.keyData;
}
else if(Buffer.isBuffer(this.options.key) || checkPEMType(this.options.key, "PRIVATE KEY")) {
keyPromise = this.options.key;
}
else {
keyPromise = readFile(this.options.key);
}
// Prepare Certificate Authority data if available.
var caPromises = [];
if (this.options.ca != null && !sysu.isArray(this.options.ca)) {
this.options.ca = [ this.options.ca ];
}
for(var i in this.options.ca) {
var ca = this.options.ca[i];
if(Buffer.isBuffer(ca) || checkPEMType(ca, "CERTIFICATE")) {
caPromises.push(ca);
}
else {
fs.readFile(this.options.key, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.keyData = data.toString();
this.checkInitialized();
}.bind(this));
caPromises.push(readFile(ca));
}
}
if (caPromises.length == 0) {
caPromises = undefined;
}
else {
caPromises = q.all(caPromises);
}
this.checkInitialized();
return this.deferredInitialize.promise;
this.initializationPromise = q.all([pfxPromise, certPromise, keyPromise, caPromises]);
return this.initializationPromise;
};
function checkPEMType(input, type) {
var matches = input.match(/\-\-\-\-\-BEGIN ([A-Z\s*]+)\-\-\-\-\-/);
if (matches != null) {
return matches[1].indexOf(type) > 0;
}
return false;
}
/**

@@ -183,13 +205,9 @@ * You should never need to call this method, initialisation and connection is handled by {@link Connection#pushNotification}

this.deferredConnection = q.defer();
this.initialize().then(function () {
this.initialize().spread(function (pfxData, certData, keyData, caData) {
var socketOptions = {};
if(this.pfxData) {
socketOptions.pfx = this.pfxData;
}
else {
socketOptions.key = this.keyData;
socketOptions.cert = this.certData;
socketOptions.ca = this.options.ca;
}
socketOptions.pfx = pfxData;
socketOptions.cert = certData;
socketOptions.key = keyData;
socketOptions.ca = caData;
socketOptions.passphrase = this.options.passphrase;

@@ -204,3 +222,3 @@ socketOptions.rejectUnauthorized = this.options.rejectUnauthorized;

this.options['port'],
this.options['gateway'] || this.options['address'],
this.options['address'],
socketOptions,

@@ -214,4 +232,7 @@ function () {

this.socket.setNoDelay(false);
this.socket.setTimeout(this.options.connectionTimeout);
socketOptions.socket.setNoDelay(this.options.disableNagle);
socketOptions.socket.setKeepAlive(true);
if (this.options.connectionTimeout > 0) {
socketOptions.socket.setTimeout(this.options.connectionTimeout);
}

@@ -222,3 +243,2 @@ this.socket.on("error", this.errorOccurred.bind(this, this.socket));

this.socket.on("drain", this.socketDrained.bind(this, this.socket, true));
this.socket.on("clientError", this.errorOccurred.bind(this, this.socket));
this.socket.once("close", this.socketClosed.bind(this, this.socket));

@@ -229,6 +249,6 @@

if ("function" == typeof this.socket.connect ) {
this.socket.connect(this.options['port'], this.options['gateway'] || this.options['address']);
this.socket.connect(this.options['port'], this.options['address']);
}
else {
socketOptions.socket.connect(this.options['port'], this.options['gateway'] || this.options['address']);
socketOptions.socket.connect(this.options['port'], this.options['address']);
}

@@ -244,4 +264,2 @@ }.bind(this)).fail(function (error) {

}
this.raiseError(error);
this.emit('error', error);
this.deferredConnection.reject(error);

@@ -259,2 +277,4 @@ this.deferredConnection = null;

this.connect().then(function () {
this.failureCount = 0;
this.socket.socketId = this.socketId++;

@@ -264,7 +284,11 @@ this.socket.currentId = 0;

this.deferredConnection = null;
this.sockets.push(this.socket);
this.socket = undefined;
this.serviceBuffer();
}.bind(this)).fail(function (err) {
}.bind(this)).fail(function (error) {
// Exponential backoff when connections fail.
var delay = Math.pow(2, this.failureCount++) * 1000;
this.raiseError(error);
this.emit('error', error);
return q.delay(delay);
}.bind(this)).finally(function () {
this.deferredConnection = null;

@@ -368,3 +392,3 @@ this.socket = undefined;

Connection.prototype.socketAvailable = function(socket) {
if (!socket || !socket.writable || socket.busy) {
if (!socket || !socket.writable || socket.busy || socket.transmissionErrorOccurred) {
return false;

@@ -407,3 +431,3 @@ }

this.emit('timeout');
socket.end();
this.destroyConnection(socket);
};

@@ -511,4 +535,4 @@

Connection.prototype.handleTransmissionError = function (socket, data) {
socket.destroy();
if (data[0] == 8) {
socket.transmissionErrorOccurred = true;
if (!this.options.enhanced) {

@@ -565,5 +589,6 @@ return;

}
socket.transmissionErrorOccurred = true;
}
else {
debug("Unknown data received: ", data);
}
};

@@ -752,3 +777,3 @@

Connection.prototype.shutdown = function () {
console.log("Shutdown pending");
debug("Shutdown pending");
this.shutdownPending = true;

@@ -755,0 +780,0 @@ };

@@ -18,3 +18,4 @@ /**

}
else {
if (!this.token || this.token.length == 0) {
throw new Error('Invalid Token Specified, must be a Buffer or valid hex String');

@@ -21,0 +22,0 @@ }

@@ -24,9 +24,6 @@ var Device = require('./device');

* @param {Object} [options]
* @config {String} [cert="cert.pem"] The filename of the connection certificate to load from disk
* @config {Buffer|String} [certData] The certificate data. If supplied, will be used instead of loading from disk.
* @config {String} [key="key.pem"] The filename of the connection key to load from disk
* @config {Buffer|String} [keyData] The key data. If supplied will be used instead of loading from disk.
* @config {Buffer[]|String[]} [ca] An array of strings or Buffers of trusted certificates. If this is omitted several well known "root" CAs will be used, like VeriSign. - You may need to use this as some environments don't include the CA used by Apple.
* @config {String} [pfx] File path for private key, certificate and CA certs in PFX or PKCS12 format. If supplied will be used instead of certificate and key above
* @config {Buffer|String} [pfxData] PFX or PKCS12 format data containing the private key, certificate and CA certs. If supplied will be used instead of loading from disk.
* @config {Buffer|String} [cert="cert.pem"] The filename of the connection certificate to load from disk, or a Buffer/String containing the certificate data.
* @config {Buffer|String} [key="key.pem"] The filename of the connection key to load from disk, or a Buffer/String containing the key data.
* @config {Buffer[]|String[]} [ca] An array of trusted certificates. Each element should contain either a filename to load, or a Buffer/String to be used directly. If this is omitted several well known "root" CAs will be used. - You may need to use this as some environments don't include the CA used by Apple (entrust_2048).
* @config {Buffer|String} [pfx] File path for private key, certificate and CA certs in PFX or PKCS12 format, or a Buffer/String containing the PFX data. If supplied will be used instead of certificate and key above.
* @config {String} [passphrase] The passphrase for the connection key, if required

@@ -45,10 +42,7 @@ * @config {String} [address="feedback.push.apple.com"] The feedback server to connect to.

cert: 'cert.pem', /* Certificate file */
certData: null, /* Certificate data */
key: 'key.pem', /* Key file */
keyData: null, /* Key data */
ca: null, /* Certificate Authority */
pfx: null, /* PFX File */
pfxData: null, /* PFX Data */
passphrase: null, /* Passphrase for key */
address: 'feedback.push.apple.com', /* feedback address */
production: (process.env.NODE_ENV === "production"),
port: 2196, /* feedback port */

@@ -65,7 +59,12 @@ rejectUnauthorized: true, /* Set this to false incase using a local proxy, reject otherwise */

this.certData = null;
this.keyData = null;
this.pfxData = null;
if (this.options.address == null) {
if (this.options.production) {
this.options.address = "feedback.push.apple.com";
}
else {
this.options.address = "feedback.sandbox.push.apple.com";
}
}
this.deferredInitialize = null;
this.initializationPromise = null;
this.deferredConnection = null;

@@ -98,70 +97,86 @@

/**
* You should never need to call this method, initialization and connection is handled by {@link Connection#sendNotification}
* @private
*/
Feedback.prototype.checkInitialized = function () {
if ((this.keyData && this.certData) || this.pfxData) {
this.deferredInitialize.resolve();
}
};
/**
* @private
*/
Feedback.prototype.initialize = function () {
if (this.deferredInitialize) {
return this.deferredInitialize.promise;
if (this.initializationPromise) {
return this.initializationPromise;
}
debug("Initialising module");
this.deferredInitialize = q.defer();
var readFile = q.nfbind(fs.readFile);
if (this.options.pfx !== null || this.options.pfxData !== null) {
if (this.options.pfxData) {
this.pfxData = this.options.pfxData;
// Prepare PKCS#12 data if available
var pfxPromise = null;
if(this.options.pfx !== null || this.options.pfxData !== null) {
if(this.options.pfxData) {
pfxPromise = this.options.pfxData;
}
else if(Buffer.isBuffer(this.options.pfx)) {
pfxPromise = this.options.pfx;
}
else {
fs.readFile(this.options.pfx, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.pfxData = data;
this.checkInitialized();
}.bind(this));
pfxPromise = readFile(this.options.pfx);
}
}
// Prepare Certificate data if available.
var certPromise = null;
if (this.options.certData) {
certPromise = this.options.certData;
}
else if(Buffer.isBuffer(this.options.key) || checkPEMType(this.options.cert, "CERTIFICATE")) {
certPromise = this.options.cert;
}
else {
if (this.options.certData) {
this.certData = this.options.certData;
}
else {
fs.readFile(this.options.cert, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.certData = data.toString();
this.checkInitialized();
}.bind(this));
}
// Nothing has matched so attempt to load from disk
certPromise = readFile(this.options.cert);
}
if (this.options.keyData) {
this.keyData = this.options.keyData;
// Prepare Key data if available
var keyPromise = null;
if (this.options.keyData) {
keyPromise = this.options.keyData;
}
else if(Buffer.isBuffer(this.options.key) || checkPEMType(this.options.key, "PRIVATE KEY")) {
keyPromise = this.options.key;
}
else {
keyPromise = readFile(this.options.key);
}
// Prepare Certificate Authority data if available.
var caPromises = [];
if (this.options.ca != null && !sysu.isArray(this.options.ca)) {
this.options.ca = [ this.options.ca ];
}
for(var i in this.options.ca) {
var ca = this.options.ca[i];
if(Buffer.isBuffer(ca) || checkPEMType(ca, "CERTIFICATE")) {
caPromises.push(ca);
}
else {
fs.readFile(this.options.key, function (err, data) {
if (err) {
this.deferredInitialize.reject(err);
return;
}
this.keyData = data.toString();
this.checkInitialized();
}.bind(this));
caPromises.push(readFile(ca));
}
}
if (caPromises.length == 0) {
caPromises = undefined;
}
else {
caPromises = q.all(caPromises);
}
this.checkInitialized();
return this.deferredInitialize.promise;
this.initializationPromise = q.all([pfxPromise, certPromise, keyPromise, caPromises]);
return this.initializationPromise;
};
function checkPEMType(input, type) {
var matches = input.match(/\-\-\-\-\-BEGIN ([A-Z\s*]+)\-\-\-\-\-/);
if (matches != null) {
return matches[1].indexOf(type) > 0;
}
return false;
}
/**

@@ -178,13 +193,9 @@ * You should call {@link Feedback#start} instead of this method

this.deferredConnection = q.defer();
this.initialize().then(function() {
this.initialize().spread(function(pfxData, certData, keyData, caData) {
var socketOptions = {};
if (this.pfxData !== null) {
socketOptions.pfx = this.pfxData;
}
else {
socketOptions.key = this.keyData;
socketOptions.cert = this.certData;
socketOptions.ca = this.options.ca;
}
socketOptions.pfx = pfxData;
socketOptions.cert = certData;
socketOptions.key = keyData;
socketOptions.ca = caData;
socketOptions.passphrase = this.options.passphrase;

@@ -191,0 +202,0 @@ socketOptions.rejectUnauthorized = this.options.rejectUnauthorized;

@@ -118,5 +118,30 @@ /**

}
return this;
};
/**
* Set the alert title for the notification - used with Safari Push Notifications
* @param {String} alertTitle The title for the alert.
* @see The <a href="https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NotificationProgrammingGuideForWebsites/PushNotifications/PushNotifications.html#//apple_ref/doc/uid/TP40013225-CH3-SW12">Pushing Notifications</a> in the Notification Programming Guide for Websites
* @since v1.5.0
*/
Notification.prototype.setAlertTitle = function(alertTitle) {
this.prepareAlert();
this.alert['title'] = alertTitle;
return this;
}
/**
* Set the alert action label for the notification - used with Safari Push Notifications
* @param {String} alertLabel The label for the alert action button.
* @see The <a href="https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NotificationProgrammingGuideForWebsites/PushNotifications/PushNotifications.html#//apple_ref/doc/uid/TP40013225-CH3-SW12">Pushing Notifications</a> in the Notification Programming Guide for Websites
* @since v1.5.0
*/
Notification.prototype.setAlertAction = function(alertAction) {
this.prepareAlert();
this.alert['action'] = alertAction;
return this;
}
/**
* Set the action-loc-key property on the alert object

@@ -123,0 +148,0 @@ * @param {String} [key] If a string is specified, displays an alert with two buttons, whose behavior is described in Table 3-1. However, iOS uses the string as a key to get a localized string in the current localization to use for the right button’s title instead of “View”. If the value is null, the system displays an alert with a single OK button that simply dismisses the alert when tapped.

{
"name": "apn",
"description": "An interface to the Apple Push Notification service for Node.js",
"version": "1.4.4",
"version": "1.5.1",
"author": "Andrew Naylor <argon@mkbot.net>",

@@ -32,2 +32,9 @@ "contributors": [

},
"devDependencies": {
"mocha": "*",
"should": "3.x.x"
},
"scripts": {
"test": "node_modules/.bin/mocha -w"
},
"engines": {

@@ -34,0 +41,0 @@ "node": ">= 0.6.6"

@@ -34,6 +34,6 @@ #node-apn

### Connecting
Create a new connection to the APN gateway server using a dictionary of options. If you name your certificate and key files appropriately (`cert.pem` and `key.pem`) then the defaults should be suitable to get you up and running, the only thing you'll need to change is the `gateway` if you're in the sandbox environment.
Create a new connection to the APN gateway server, passing a dictionary of options to the constructor. If you name your certificate and key files appropriately (`cert.pem` and `key.pem`) then the defaults should be suitable to get you up and running. By default the module will connect to the sandbox environment unless the environment variable `NODE_ENV=production` is set. For more information consult the documentation (in doc/apn.markdown).
```javascript
var options = { "gateway": "gateway.sandbox.push.apple.com" };
var options = { };

@@ -40,0 +40,0 @@ var apnConnection = new apn.Connection(options);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc