Comparing version 1.4.4 to 1.5.1
## 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 @@ |
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
100508
20
1518
2
12