Skip to content

Commit 7286a4b

Browse files
committed
Merge pull request #248 from ToothlessGear/v1-no-send-no-retry
V1 no send no retry
2 parents 2048582 + 7e55699 commit 7286a4b

2 files changed

Lines changed: 215 additions & 302 deletions

File tree

lib/sender.js

Lines changed: 109 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ function Sender(key, options) {
99
return new Sender(key, options);
1010
}
1111

12-
this.key = key;
13-
this.options = options || {};
12+
this.requestOptions = defaultsDeep({
13+
method: 'POST',
14+
headers: {
15+
'Authorization': 'key=' + key
16+
},
17+
uri: Constants.GCM_SEND_URI
18+
}, options, {
19+
timeout: Constants.SOCKET_TIMEOUT
20+
});
1421
}
1522

1623
Sender.prototype.send = function(message, recipient, options, callback) {
@@ -23,42 +30,15 @@ Sender.prototype.send = function(message, recipient, options, callback) {
2330
}
2431
options = cleanOptions(options);
2532

26-
if(options.retries == 0) {
27-
return this.sendNoRetry(message, recipient, callback);
28-
}
29-
30-
var self = this;
31-
32-
this.sendNoRetry(message, recipient, function(err, response, attemptedRegTokens) {
33-
if (err) {
34-
if (typeof err === 'number' && err > 399 && err < 500) {
35-
debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)");
36-
return callback(err);
37-
}
38-
return retry(self, message, recipient, options, callback);
33+
getRequestBody(message, recipient, function(err, body) {
34+
if(err) {
35+
return callback(err);
3936
}
40-
if(!response.results) {
41-
return callback(null, response);
37+
if(options.retries == 0) {
38+
return sendMessage(this.requestOptions, body, callback);
4239
}
43-
checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) {
44-
if(err) {
45-
return callback(err);
46-
}
47-
if (unsentRegTokens.length == 0) {
48-
return callback(null, response);
49-
}
50-
51-
debug("Retrying " + unsentRegTokens.length + " unsent registration tokens");
52-
53-
retry(self, message, unsentRegTokens, options, function(err, retriedResponse) {
54-
if(err) {
55-
return callback(null, response);
56-
}
57-
response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens);
58-
callback(null, response);
59-
});
60-
});
61-
});
40+
sendMessageWithRetries(this.requestOptions, body, options, callback);
41+
}.bind(this));
6242
};
6343

6444
function cleanOptions(options) {
@@ -86,13 +66,86 @@ function cleanOptions(options) {
8666
return options;
8767
}
8868

89-
function retry(self, message, recipient, options, callback) {
90-
return setTimeout(function() {
91-
self.send(message, recipient, {
92-
retries: options.retries - 1,
93-
backoff: options.backoff * 2
69+
function getRequestBody(message, recipient, callback) {
70+
var body = cleanParams(message);
71+
72+
if(typeof recipient == "string") {
73+
body.to = recipient;
74+
return nextTick(callback, null, body);
75+
}
76+
if(Array.isArray(recipient)) {
77+
if(recipient.length < 1) {
78+
return nextTick(callback, new Error('Empty recipient array passed!'));
79+
}
80+
body.registration_ids = recipient;
81+
return nextTick(callback, null, body);
82+
}
83+
return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!'));
84+
}
85+
86+
function cleanParams(raw) {
87+
var params = {};
88+
Object.keys(raw).forEach(function(param) {
89+
var paramOptions = messageOptions[param];
90+
if(!paramOptions) {
91+
return console.warn("node-gcm ignored unknown message parameter " + param);
92+
}
93+
if(paramOptions.__argType != typeof raw[param]) {
94+
return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")");
95+
}
96+
params[param] = raw[param];
97+
});
98+
return params;
99+
}
100+
101+
function nextTick(func) {
102+
var args = Array.prototype.slice.call(arguments, 1);
103+
process.nextTick(function() {
104+
func.apply(this, args);
105+
}.bind(this));
106+
}
107+
108+
function sendMessageWithRetries(requestOptions, body, messageOptions, callback) {
109+
sendMessage(requestOptions, body, function(err, response, attemptedRegTokens) {
110+
if (err) {
111+
if (typeof err === 'number' && err > 399 && err < 500) {
112+
debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)");
113+
return callback(err);
114+
}
115+
return retry(requestOptions, body, messageOptions, callback);
116+
}
117+
checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) {
118+
if(err) {
119+
return callback(err);
120+
}
121+
if (unsentRegTokens.length == 0) {
122+
return callback(null, response);
123+
}
124+
125+
debug("Retrying " + unsentRegTokens.length + " unsent registration tokens");
126+
127+
body.registration_ids = unsentRegTokens;
128+
retry(requestOptions, body, messageOptions, function(err, retriedResponse) {
129+
if(err) {
130+
return callback(null, response);
131+
}
132+
response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens);
133+
callback(null, response);
134+
});
135+
});
136+
});
137+
}
138+
139+
function retry(requestOptions, body, messageOptions, callback) {
140+
setTimeout(function() {
141+
if(messageOptions.retries <= 1) {
142+
return sendMessage(requestOptions, body, callback);
143+
}
144+
sendMessageWithRetries(requestOptions, body, {
145+
retries: messageOptions.retries - 1,
146+
backoff: messageOptions.backoff * 2
94147
}, callback);
95-
}, options.backoff);
148+
}, messageOptions.backoff);
96149
}
97150

98151
function checkForBadTokens(results, originalRecipients, callback) {
@@ -125,86 +178,26 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) {
125178
response.failure -= unsentRegTokens.length - retriedResponse.failure;
126179
}
127180

128-
Sender.prototype.sendNoRetry = function(message, recipient, callback) {
129-
if(!callback) {
130-
callback = function() {};
131-
}
132-
133-
getRequestBody(message, recipient, function(err, body) {
134-
if(err) {
181+
function sendMessage(requestOptions, body, callback) {
182+
requestOptions.json = body;
183+
request(requestOptions, function (err, res, resBodyJSON) {
184+
if (err) {
135185
return callback(err);
136186
}
137-
138-
//Build request options, allowing some to be overridden
139-
var request_options = defaultsDeep({
140-
method: 'POST',
141-
headers: {
142-
'Authorization': 'key=' + this.key
143-
},
144-
uri: Constants.GCM_SEND_URI,
145-
json: body
146-
}, this.options, {
147-
timeout: Constants.SOCKET_TIMEOUT
148-
});
149-
150-
request(request_options, function (err, res, resBodyJSON) {
151-
if (err) {
152-
return callback(err);
153-
}
154-
if (res.statusCode >= 500) {
155-
debug('GCM service is unavailable (500)');
156-
return callback(res.statusCode);
157-
}
158-
if (res.statusCode === 401) {
159-
debug('Unauthorized (401). Check that your API token is correct.');
160-
return callback(res.statusCode);
161-
}
162-
if (res.statusCode !== 200) {
163-
debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON);
164-
return callback(res.statusCode);
165-
}
166-
callback(null, resBodyJSON, body.registration_ids || [ body.to ]);
167-
});
168-
}.bind(this));
169-
};
170-
171-
function getRequestBody(message, recipient, callback) {
172-
var body = cleanParams(message);
173-
174-
if(typeof recipient == "string") {
175-
body.to = recipient;
176-
return nextTick(callback, null, body);
177-
}
178-
if(Array.isArray(recipient)) {
179-
if(recipient.length < 1) {
180-
return nextTick(callback, new Error('Empty recipient array passed!'));
187+
if (res.statusCode >= 500) {
188+
debug('GCM service is unavailable (500)');
189+
return callback(res.statusCode);
181190
}
182-
body.registration_ids = recipient;
183-
return nextTick(callback, null, body);
184-
}
185-
return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!'));
186-
}
187-
188-
function cleanParams(raw) {
189-
var params = {};
190-
Object.keys(raw).forEach(function(param) {
191-
var paramOptions = messageOptions[param];
192-
if(!paramOptions) {
193-
return console.warn("node-gcm ignored unknown message parameter " + param);
191+
if (res.statusCode === 401) {
192+
debug('Unauthorized (401). Check that your API token is correct.');
193+
return callback(res.statusCode);
194194
}
195-
if(paramOptions.__argType != typeof raw[param]) {
196-
return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")");
195+
if (res.statusCode !== 200) {
196+
debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON);
197+
return callback(res.statusCode);
197198
}
198-
params[param] = raw[param];
199+
callback(null, resBodyJSON, body.registration_ids || [ body.to ]);
199200
});
200-
return params;
201-
}
202-
203-
function nextTick(func) {
204-
var args = Array.prototype.slice.call(arguments, 1);
205-
process.nextTick(function() {
206-
func.apply(this, args);
207-
}.bind(this));
208201
}
209202

210203
module.exports = Sender;

0 commit comments

Comments
 (0)