lib/sender.js (126 lines of code) (raw):
/*!
* node-gcm
* Copyright(c) 2013 Marcus Farkas <toothlessgear@finitebox.com>
* MIT Licensed
*/
var Constants = require('./constants');
var Result = require('./result');
var https = require('https');
var timer = require('timers');
function Sender(key) {
this.key = key;
}
var sendNoRetryMethod = Sender.prototype.sendNoRetry = function (message, registrationIds, callback) {
var body = {},
result = new Result(),
requestBody,
post_options,
post_req;
body[Constants.JSON_REGISTRATION_IDS] = registrationIds;
if (message.delayWhileIdle !== undefined) {
body[Constants.PARAM_DELAY_WHILE_IDLE] = message.delayWhileIdle;
}
if (message.collapseKey !== undefined) {
body[Constants.PARAM_COLLAPSE_KEY] = message.collapseKey;
}
if (message.timeToLive !== undefined) {
body[Constants.PARAM_TIME_TO_LIVE] = message.timeToLive;
}
if (message.hasData) {
body[Constants.PARAM_PAYLOAD_KEY] = message.data;
}
requestBody = JSON.stringify(body);
post_options = {
host: Constants.GCM_SEND_ENDPOINT,
port: '443',
path: Constants.GCM_SEND_ENDPATH,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-length': Buffer.byteLength(requestBody, 'utf8'),
'Authorization': 'key=' + this.key
}
};
post_req = https.request(post_options, function (res) {
var statusCode = res.statusCode,
buf = '';
res.setEncoding('utf-8');
res.on('data', function (data) {
buf += data;
});
res.on('end', function () {
if (statusCode === 503) {
console.log('GCM service is unavailable');
return callback(statusCode, null);
} else if(statusCode == 401){
console.log('Unauthorized');
return callback(statusCode, null);
} else if (statusCode !== 200) {
console.log('Invalid request: ' + statusCode);
return callback(statusCode, null);
}
// Make sure that we don't crash in case something goes wrong while
// handling the response.
try {
var data = JSON.parse(buf);
callback(null, data);
} catch (e) {
console.log("Error handling GCM response " + e);
callback("error", null);
}
});
});
post_req.on('error', function (e) {
console.log("Exception during GCM request: " + e);
callback("request error", null);
});
post_req.write(requestBody);
post_req.end();
};
Sender.prototype.send = function (message, registrationId, retries, callback) {
var attempt = 1,
backoff = Constants.BACKOFF_INITIAL_DELAY;
if (registrationId.length === 1) {
this.sendNoRetry(message, registrationId, function lambda(err, result) {
if (result === undefined) {
if (attempt < retries) {
var sleepTime = backoff * 2 * attempt;
if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
sleepTime = Constants.MAX_BACKOFF_DELAY;
}
timer.setTimeout(function () {
sendNoRetryMethod(message, registrationId, lambda);
}, sleepTime);
} else {
console.log('Could not send message after ' + retries + ' attempts');
callback(null, result);
}
attempt += 1;
} else callback(null, result);
});
} else if (registrationId.length > 1) {
this.sendNoRetry(message, registrationId, function lambda(err, result) {
if (attempt < retries) {
var sleepTime = backoff * 2 * attempt,
unsentRegIds = [],
i;
if (sleepTime > Constants.MAX_BACKOFF_DELAY) {
sleepTime = Constants.MAX_BACKOFF_DELAY;
}
if (result) {
for (i = 0; i < registrationId.length; i += 1) {
if (result.results[i].error === 'Unavailable') {
unsentRegIds.push(registrationId[i]);
}
}
}
registrationId = unsentRegIds;
if (registrationId.length !== 0) {
timer.setTimeout(function () {
sendNoRetryMethod(message, registrationId, lambda);
}, sleepTime);
attempt += 1;
} else callback(null, result);
} else {
console.log('Could not send message to all devices after ' + retries + ' attempts');
callback(null, result);
}
});
} else {
console.log('No RegistrationIds given!');
}
};
module.exports = Sender;