Skip to content

Instantly share code, notes, and snippets.

@h-mikisato
Last active February 12, 2016 05:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save h-mikisato/80083da7427d994c924a to your computer and use it in GitHub Desktop.
Save h-mikisato/80083da7427d994c924a to your computer and use it in GitHub Desktop.
APNs Provider API in NodeJS
var fs = require('fs');
var path = require('path');
var http2 = require('http2');
var uuid = require('node-uuid');
var logger = require("log4js").getLogger();
var argparse = require('argparse');
/*!
* From Countly Team fork
* https://github.com/Countly/node-http2/tree/custom
*/
http2.Agent.prototype.destroy = function(error) {
if (this._httpsAgent) {
this._httpsAgent.destroy();
}
for (var key in this.endpoints) {
this.endpoints[key].close(error);
}
};
/*!
End quotation
*/
/* Parse Arguments */
var parser = new argparse.ArgumentParser({
version: '0.0.2',
addHelp:true,
description: 'HTTP/2 APNs Provider API Sender'
});
parser.addArgument(['device_tokens'], {
nargs: '+',
help: "Device Tokens"
});
parser.addArgument(['-m', '--message'], {
required: true,
help: "Push Message"
});
parser.addArgument(['-c', '--cert'], {
required: true,
help: "Certification File Path"
});
parser.addArgument(['-p', '--prod'], {
action: 'storeTrue',
help: "Send Production"
});
var args = parser.parseArgs();
/* Main Routine */
var apn_cert = fs.readFileSync(args.cert);
var agent = new http2.Agent({
key: apn_cert,
cert: apn_cert,
});
var apn_host = args.prod ? "api.push.apple.com" : "api.development.push.apple.com";
var tokens_num = args.device_tokens.length;
var recv_count = 0;
function buildPayload(message) {
return JSON.stringify({"apn": message});
}
var push_map = {};
var retry_counter = {};
function deal_response(res) {
res.setEncoding('utf8');
var response_uuid = res.headers["apns-id"];
var device_token = push_map[response_uuid][0];
var payload_str = push_map[response_uuid][1];
delete push_map[response_uuid];
if (res.statusCode == 200) {
logger.info("Notification is successfully sent: " + response_uuid + " " + device_token);
cleanup(response_uuid);
return;
}
var bufs = [];
res.on("error", function (err) {
logger.error("Response Error: " + err.message + " " + response_uuid);
});
res.on('data', function(d) {
bufs.push(d);
});
res.on('end', function() {
var res_json = JSON.parse(bufs.join(""));
logger.info("Error Code: " + res.statusCode + " Reason: " + res_json.reason + " " + response_uuid + " " + device_token);
if (res.statusCode == 410) {
logger.info("This device token must be inactive: " + device_token);
} else if (res.statusCode == 500) {
if (!retry_counter[response_uuid]) {
retry_counter[response_uuid] = 0;
}
if (++retry_counter[response_uuid] < retryMax) {
logger.error("Retry Sending...: " + response_uuid + " " + device_token);
setTimeout(
send(response_uuid, device_token, payload_str),
1000 * Math.pow(2, retry_counter[response_uuid] - 1)
);
return;
}
}
cleanup(response_uuid);
});
}
function send(request_uuid, device_token, payload_str) {
push_map[request_uuid] = [device_token, payload_str];
logger.info("Next Request: " + request_uuid + " " + device_token);
var options = {
'host': apn_host,
'method': 'POST',
'path': '/3/device/' + device_token,
'headers': {
'apns-id': request_uuid,
'content-type': "application/json",
}
};
var req = agent.request(options, deal_response);
req.on("error", function (err) {
logger.error("Request Error: " + err.message + " " + request_uuid);
});
req.write(payload_str);
req.end();
}
function cleanup(apns_id) {
if (retry_counter[apns_id]) {
delete retry_counter[apns_id];
}
if (++recv_count >= tokens_num) {
logger.info("Process Exit");
agent.destroy();
process.exit();
}
}
for (var i = 0; i < tokens_num; i++) {
var request_uuid = uuid.v4();
send(request_uuid, args.device_tokens[i], buildPayload(args.message));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment