firebase-fcm-v1-http2
Version:
A Node.js library for sending push notifications using Firebase Cloud Messaging HTTP v1 APIs.
105 lines (104 loc) • 4.42 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processBatch = processBatch;
const http2 = __importStar(require("http2"));
const async = __importStar(require("async"));
const fcmv1Api = 'https://fcm.googleapis.com';
function processBatch(message, devices, projectId, accessToken, maxConcurrentStreamsAllowed, maxConcurrentConnections) {
return new Promise((resolve, reject) => {
const client = http2.connect(fcmv1Api, {
peerMaxConcurrentStreams: maxConcurrentConnections
});
client.on('error', (err) => reject(err));
client.on('socketError', (err) => reject(err));
client.unregisteredTokens = [];
async.eachLimit(devices, maxConcurrentStreamsAllowed, (device, doneCallback) => {
sendRequest(client, device, message, projectId, accessToken, doneCallback, 0, maxConcurrentStreamsAllowed, maxConcurrentConnections);
}, (err) => {
client.close();
if (err)
return reject(err);
resolve(client.unregisteredTokens);
});
});
}
function sendRequest(client, device, message, projectId, accessToken, doneCallback, tries, maxConcurrentStreamsAllowed, maxConcurrentConnections) {
const request = client.request({
':method': 'POST',
':scheme': 'https',
':path': `/v1/projects/${projectId}/messages:send`,
Authorization: `Bearer ${accessToken}`,
});
request.setEncoding('utf8');
const clonedMessage = Object.assign(Object.assign({}, message), { token: device });
request.write(JSON.stringify({ message: clonedMessage }));
let data = '';
request.on('data', (chunk) => {
data += chunk;
});
let retrying = false;
const errorHandler = (err) => {
if (tries <= 3) {
if (retrying)
return;
retrying = true;
if (client.destroyed) {
return processBatch(message, [device], projectId, accessToken, maxConcurrentStreamsAllowed, maxConcurrentConnections)
.finally(doneCallback);
}
return setTimeout(() => sendRequest(client, device, message, projectId, accessToken, doneCallback, tries + 1, maxConcurrentStreamsAllowed, maxConcurrentConnections), 10000);
}
err.data = data;
doneCallback(err);
};
request.on('end', () => {
var _a;
try {
if (data.toLowerCase().includes('server error')) {
return errorHandler(new Error('Internal Server Error'));
}
const response = JSON.parse(data);
const statusCode = (_a = response.statusCode) !== null && _a !== void 0 ? _a : response.status;
if (statusCode && statusCode >= 500) {
return errorHandler(new Error(`${statusCode} Internal Server Error`));
}
if (response.error) {
if (response.error.details && response.error.details[0].errorCode === 'UNREGISTERED') {
client.unregisteredTokens.push(device);
}
else {
return doneCallback(response.error);
}
}
doneCallback();
}
catch (err) {
errorHandler(err);
}
});
request.on('error', errorHandler);
request.end();
}