New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.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.6.0 to 1.6.1

10

ChangeLog.md
## Changelog
1.6.1:
* Increased default cache length to 1000; Previous value of 100 was overly cautious.
* Added a delay of 100ms to connection to ensure notifications aren't lost if wrong certificates are used.
* Fixes #195: Better handling of socket creation
* Fixes #200: Improved buffer handling code when multiple connections are enabled.
* Minor optimisation to Notification processing, removing the need for 2 `JSON.stringify` calls
* Fixes #196: Check cache has contents before trying to access. Also ensure minimum size is "1" to allow "transmitted" events to be emitted.
* Fixes #199: Emit a "drain" event when no further notifications require sending. Most useful in a batch environment.
1.6.0:

@@ -4,0 +14,0 @@

178

lib/connection.js

@@ -13,5 +13,7 @@ var Errors = require('./errors');

var debug = function() {};
var trace = function() {};
if(process.env.DEBUG) {
try {
debug = require('debug')('apn');
trace = require('debug')('apn:trace');
}

@@ -38,3 +40,3 @@ catch (e) {

* @config {Function} [errorCallback] A callback which accepts 2 parameters (err, notification). Use `transmissionError` event instead.
* @config {Number} [cacheLength=100] Number of notifications to cache for error purposes (See doc/apn.markdown)
* @config {Number} [cacheLength=1000] Number of notifications to cache for error purposes (See doc/apn.markdown)
* @config {Boolean} [autoAdjustCache=false] Whether the cache should grow in response to messages being lost after errors. (Will still emit a 'cacheTooSmall' event)

@@ -62,5 +64,6 @@ * @config {Number} [maxConnections=1] The maximum number of connections to create for sending messages.

enhanced: true,
cacheLength: 100,
cacheLength: 1000,
autoAdjustCache: true,
maxConnections: 1,
connectTimeout: 10000,
connectionTimeout: 0,

@@ -107,2 +110,6 @@ connectionRetryLimit: 10,

// Set cache length to 1 to ensure transmitted notifications can be sent.
this.options.cacheLength = Math.max(this.options.cacheLength, 1);
this.options.maxConnections = Math.max(this.options.maxConnections, 1);
this.initializationPromise = null;

@@ -117,2 +124,3 @@ this.deferredConnection = null;

this.failureCount = 0;
this.connectionTimer = null;

@@ -122,2 +130,6 @@ // when true, we end all sockets after the pending notifications reach 0

// track when notifications are queued so transmitCompleted is only emitted one when
// notifications are transmitted rather than after socket timeouts
this.notificationsQueued = false;
this.terminated = false;

@@ -176,4 +188,20 @@

this.deferredConnection.resolve();
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
}.bind(this));
this.socket.on("error", this.errorOccurred.bind(this, this.socket));
this.socket.on("timeout", this.socketTimeout.bind(this, this.socket));
this.socket.on("data", this.handleTransmissionError.bind(this, this.socket));
this.socket.on("drain", this.socketDrained.bind(this, this.socket, true));
this.socket.once("close", this.socketClosed.bind(this, this.socket));
if (this.options.connectTimeout > 0) {
this.connectionTimer = setTimeout(function () {
this.deferredConnection.reject(new Error("Connect timed out"));
this.socket.end();
}.bind(this), this.options.connectTimeout);
}
}.bind(this)).fail(function (error) {

@@ -195,10 +223,22 @@ debug("Module initialisation error:", error);

Connection.prototype.createConnection = function() {
this.connect().then(function () {
if (this.initialisingConnection() || this.sockets.length >= this.options.maxConnections) {
return;
}
// Delay here because Apple will successfully authenticate production certificates
// in sandbox, but will then immediately close the connection. Necessary to wait for a beat
// to see if the connection stays before sending anything because the connection will be closed
// without error and messages could be lost.
this.connect().delay(100).then(function () {
if (this.socket.apnRetired) {
throw new Error("Socket unusable after connection. Hint: You may be using a certificate for the wrong environment");
}
this.failureCount = 0;
this.socket.socketId = this.socketId++;
this.socket.currentId = 0;
this.socket.cachedNotifications = [];
this.socket.apnSocketId = this.socketId++;
this.socket.apnCurrentId = 0;
this.socket.apnCachedNotifications = [];
this.sockets.push(this.socket);
trace("connection established", this.socketId);
}.bind(this)).fail(function (error) {

@@ -208,2 +248,4 @@ // Exponential backoff when connections fail.

trace("connection failed", delay);
this.raiseError(error);

@@ -223,2 +265,3 @@ this.emit('error', error);

}.bind(this)).finally(function () {
trace("create completed", this.sockets.length);
this.deferredConnection = null;

@@ -247,2 +290,3 @@ this.socket = undefined;

var repeat = false;
var socketsAvailable = 0;
if(this.options.fastMode) {

@@ -252,40 +296,49 @@ repeat = true;

do {
socket = null;
if (this.notificationBuffer.length === 0) break;
for (var i = this.sockets.length - 1; i >= 0; i--) {
if(this.socketAvailable(this.sockets[i])) {
socket = this.sockets[i];
break;
socketsAvailable = 0;
for (var i = 0; i < this.sockets.length; i++) {
socket = this.sockets[i];
if(!this.socketAvailable(socket)) {
continue;
}
}
if (socket !== null) {
debug("Transmitting notification from buffer");
if(this.transmitNotification(socket, this.notificationBuffer.shift())) {
if (this.notificationBuffer.length === 0) {
socketsAvailable += 1;
continue;
}
// If a socket is available then transmit. If true is returned then manually call socketDrained
if (this.transmitNotification(socket, this.notificationBuffer.shift())) {
// Only set socket available here because if transmit returns false then the socket
// is blocked so shouldn't be used in the next loop.
socketsAvailable += 1;
this.socketDrained(socket, !repeat);
}
}
else if (!this.initialisingConnection() && this.sockets.length < this.options.maxConnections) {
this.createConnection();
repeat = false;
} while(repeat && socketsAvailable > 0 && this.notificationBuffer.length > 0);
if (this.notificationBuffer.length > 0 && socketsAvailable == 0) {
this.createConnection();
}
if (this.notificationBuffer.length === 0 && socketsAvailable == this.sockets.length){
if (this.notificationsQueued) {
this.emit('completed');
this.notificationsQueued = false;
}
else {
repeat = false;
}
} while(repeat);
debug("%d left to send", this.notificationBuffer.length);
if (this.shutdownPending) {
debug("closing connections");
if (this.notificationBuffer.length === 0 && this.shutdownPending) {
debug("closing connections");
for (var i = this.sockets.length - 1; i >= 0; i--) {
var socket = this.sockets[i];
if (!socket.busy) {
for (var i = 0; i < this.sockets.length; i++) {
var socket = this.sockets[i];
// We delay before closing connections to ensure we don't miss any error packets from the service.
setTimeout(socket.end.bind(socket), 2500);
this.retireSocket(socket);
}
if (this.sockets.length == 0) {
this.shutdownPending = false;
}
}
if (this.sockets.length == 0) {
this.shutdownPending = false;
}
}
debug("%d left to send", this.notificationBuffer.length);
};

@@ -297,3 +350,3 @@

Connection.prototype.errorOccurred = function(socket, err) {
debug("Socket error occurred", socket.socketId, err);
debug("Socket error occurred", socket.apnSocketId, err);

@@ -313,5 +366,5 @@ if(socket.transmissionErrorOccurred && err.code == 'EPIPE') {

if(socket.busy && socket.cachedNotifications.length > 0) {
if(socket.apnBusy && socket.apnCachedNotifications.length > 0) {
// A notification was in flight. It should be buffered for resending.
this.bufferNotification(socket.cachedNotifications[socket.cachedNotifications.length - 1]);
this.bufferNotification(socket.apnCachedNotifications[socket.apnCachedNotifications.length - 1]);
}

@@ -326,3 +379,3 @@

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

@@ -337,6 +390,6 @@ }

Connection.prototype.socketDrained = function(socket, serviceBuffer) {
debug("Socket drained", socket.socketId);
socket.busy = false;
if(this.options.enhanced) {
var notification = socket.cachedNotifications[socket.cachedNotifications.length - 1];
debug("Socket drained", socket.apnSocketId);
socket.apnBusy = false;
if((!this.options.legacy || this.options.enhanced) && socket.apnCachedNotifications.length > 0) {
var notification = socket.apnCachedNotifications[socket.apnCachedNotifications.length - 1];
this.emit('transmitted', notification.notification, notification.recipient);

@@ -361,3 +414,3 @@ }

Connection.prototype.socketTimeout = function(socket) {
debug("Socket timeout", socket.socketId);
debug("Socket timeout", socket.apnSocketId);
this.emit('timeout');

@@ -373,3 +426,3 @@ this.destroyConnection(socket);

Connection.prototype.destroyConnection = function(socket) {
debug("Destroying connection", socket.socketId);
debug("Destroying connection", socket.apnSocketId);
if (socket) {

@@ -385,3 +438,3 @@ this.retireSocket(socket);

Connection.prototype.socketClosed = function(socket) {
debug("Socket closed", socket.socketId);
debug("Socket closed", socket.apnSocketId);

@@ -404,4 +457,5 @@ if (socket === this.socket && this.deferredConnection.promise.isPending()) {

Connection.prototype.retireSocket = function(socket) {
debug("Removing socket from pool", socket.socketId);
debug("Removing socket from pool", socket.apnSocketId);
socket.apnRetired = true;
var index = this.sockets.indexOf(socket);

@@ -431,2 +485,3 @@ if (index > -1) {

this.notificationBuffer.push(notification);
this.notificationsQueued = true;
};

@@ -482,6 +537,6 @@

Connection.prototype.cacheNotification = function (socket, notification) {
socket.cachedNotifications.push(notification);
if (socket.cachedNotifications.length > this.options.cacheLength) {
debug("Clearing notification %d from the cache", socket.cachedNotifications[0]['_uid']);
socket.cachedNotifications.splice(0, socket.cachedNotifications.length - this.options.cacheLength);
socket.apnCachedNotifications.push(notification);
if (socket.apnCachedNotifications.length > this.options.cacheLength) {
debug("Clearing notification %d from the cache", socket.apnCachedNotifications[0]['_uid']);
socket.apnCachedNotifications.splice(0, socket.apnCachedNotifications.length - this.options.cacheLength);
}

@@ -496,3 +551,3 @@ };

socket.transmissionErrorOccurred = true;
if (!this.options.enhanced) {
if (!this.options.enhanced && this.options.legacy) {
return;

@@ -509,4 +564,4 @@ }

while (socket.cachedNotifications.length) {
notification = socket.cachedNotifications.shift();
while (socket.apnCachedNotifications.length) {
notification = socket.apnCachedNotifications.shift();
if (notification['_uid'] == identifier) {

@@ -527,6 +582,6 @@ foundNotification = true;

else {
socket.cachedNotifications = temporaryCache;
socket.apnCachedNotifications = temporaryCache;
if(socket.cachedNotifications.length > 0) {
var differentialSize = socket.cachedNotifications[0]['_uid'] - identifier;
if(socket.apnCachedNotifications.length > 0) {
var differentialSize = socket.apnCachedNotifications[0]['_uid'] - identifier;
this.emit('cacheTooSmall', differentialSize);

@@ -542,7 +597,7 @@ if(this.options.autoAdjustCache) {

var count = socket.cachedNotifications.length;
var count = socket.apnCachedNotifications.length;
if(this.options.buffersNotifications) {
debug("Buffering %d notifications for resending", count);
for (var i = 0; i < count; ++i) {
notification = socket.cachedNotifications.shift();
notification = socket.apnCachedNotifications.shift();
this.bufferNotification(notification);

@@ -591,5 +646,5 @@ }

notification._uid = socket.currentId++;
if (socket.currentId > 0xffffffff) {
socket.currentId = 0;
notification._uid = socket.apnCurrentId++;
if (socket.apnCurrentId > 0xffffffff) {
socket.apnCurrentId = 0;
}

@@ -686,3 +741,3 @@ if (this.options.legacy) {

socket.busy = true;
socket.apnBusy = true;
return socket.write(data);

@@ -701,3 +756,2 @@ };

}
notification.compile();
return true;

@@ -704,0 +758,0 @@ };

@@ -5,2 +5,4 @@ var CredentialLoader = require('./credentials');

var createSocket = require('./socket');
var q = require('q');

@@ -124,14 +126,2 @@ var tls = require('tls');

function checkPEMType(input, type) {
if(input == null) {
return false;
}
var matches = input.match(/\-\-\-\-\-BEGIN ([A-Z\s*]+)\-\-\-\-\-/);
if (matches != null) {
return matches[1].indexOf(type) >= 0;
}
return false;
}
/**

@@ -151,2 +141,4 @@ * You should call {@link Feedback#start} instead of this method

socketOptions.port = this.options.port;
socketOptions.host = this.options.address;
socketOptions.pfx = pfxData;

@@ -159,6 +151,3 @@ socketOptions.cert = certData;

this.socket = tls.connect(
this.options['port'],
this.options['address'],
socketOptions,
this.socket = createSocket(this, socketOptions,
function () {

@@ -165,0 +154,0 @@ debug("Connection established");

@@ -287,3 +287,3 @@ /**

Notification.prototype.length = function () {
return Buffer.byteLength(JSON.stringify(this), this.encoding || 'utf8');
return Buffer.byteLength(this.compile(), this.encoding || 'utf8');
};

@@ -301,2 +301,3 @@

}
this.compiled = false;
var length;

@@ -303,0 +304,0 @@ var encoding = this.encoding || 'utf8';

var tls = require('tls');
var net = require('net');
var debug = function() {};
if(process.env.DEBUG) {
try {
debug = require('debug')('apn:socket');
} catch (e) {}
}
function destroyEPIPEFix(e) {

@@ -36,2 +43,4 @@ // When a write error occurs we miss the opportunity to

debug("connecting to: ", socketOptions['host'] + ":" + socketOptions['port']);
socketOptions.socket.setNoDelay(socketOptions.disableNagle);

@@ -43,8 +52,2 @@ socketOptions.socket.setKeepAlive(true);

socket.on("error", connection.errorOccurred.bind(connection, socket));
socket.on("timeout", connection.socketTimeout.bind(connection, socket));
socket.on("data", connection.handleTransmissionError.bind(connection, socket));
socket.on("drain", connection.socketDrained.bind(connection, socket, true));
socket.once("close", connection.socketClosed.bind(connection, socket));
// The actual connection is delayed until after all the event listeners have

@@ -72,7 +75,3 @@ // been attached.

socket.on("error", connection.errorOccurred.bind(connection, socket));
socket.on("timeout", connection.socketTimeout.bind(connection, socket));
socket.on("data", connection.handleTransmissionError.bind(connection, socket));
socket.on("drain", connection.socketDrained.bind(connection, socket, true));
socket.once("close", connection.socketClosed.bind(connection, socket));
debug("connecting to: ", socketOptions['host'] + ":" + socketOptions['port']);

@@ -83,6 +82,8 @@ return socket;

if (tls.TLSSocket) {
debug("Using 0.12 socket API");
module.exports = apnSocket;
}
else {
debug("Using legacy socket API");
module.exports = apnSocketLegacy;
}
{
"name": "apn",
"description": "An interface to the Apple Push Notification service for Node.js",
"version": "1.6.0",
"version": "1.6.1",
"author": "Andrew Naylor <argon@mkbot.net>",

@@ -6,0 +6,0 @@ "contributors": [

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