Skip to content

Commit bf2dbdf

Browse files
committed
Add support for legacyHttpTransport flag
1 parent 1929762 commit bf2dbdf

4 files changed

Lines changed: 146 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ This repository implements a Node.js module for sending push notifications acros
6767
- `databaseAuthVariableOverride` (optional) - Auth override for RTDB rules
6868
- `httpAgent` (optional) - HTTP proxy agent for network requests
6969
- `httpsAgent` (optional) - HTTPS proxy agent for network requests
70+
- `legacyHttpTransport` (optional) - Enable HTTP/1.1 transport instead of HTTP/2 (for compatibility with older Node.js or network restrictions)
7071

7172
All optional properties are dynamically added to Firebase initialization if defined.
7273

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const settings = {
6161
serviceAccountId: 'your-email@your-project.iam.gserviceaccount.com', // Service account email (optional)
6262
httpAgent: undefined, // HTTP Agent for proxy support (optional)
6363
httpsAgent: undefined, // HTTPS Agent for proxy support (optional)
64+
legacyHttpTransport: false, // Enable HTTP/1.1 instead of HTTP/2 (optional)
6465
},
6566
apn: {
6667
token: {
@@ -476,6 +477,7 @@ The following Firebase Admin SDK `AppOptions` are supported and can be passed in
476477
- `databaseAuthVariableOverride` - Auth variable override for Realtime Database (optional)
477478
- `httpAgent` - HTTP Agent for proxy support (optional, see [Proxy](#proxy) section)
478479
- `httpsAgent` - HTTPS Agent for proxy support (optional, see [Proxy](#proxy) section)
480+
- `legacyHttpTransport` - Enable HTTP/1.1 transport instead of HTTP/2 (optional, for compatibility with older Node.js versions or network restrictions)
479481

480482
```js
481483
const tokens = ['e..Gwso:APA91.......7r910HljzGUVS_f...kbyIFk2sK6......D2s6XZWn2E21x'];

src/sendFCM.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ const sendFCM = (regIds, data, settings) => {
8989
});
9090

9191
const firebaseApp = firebaseAdmin.initializeApp(opts, appName);
92+
93+
// Enable legacy HTTP/1.1 transport if requested
94+
if (settings.fcm.legacyHttpTransport) {
95+
firebaseAdmin.messaging(firebaseApp).enableLegacyHttpTransport();
96+
}
97+
9298
firebaseAdmin.INTERNAL.appStore.removeApp(appName);
9399

94100
const promises = [];

test/send/sendFCM.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,141 @@ describe('push-notifications-fcm', () => {
469469
});
470470
});
471471
});
472+
473+
describe('Legacy HTTP transport support', () => {
474+
it('should enable legacyHttpTransport when configured', (done) => {
475+
const mockEnableLegacyHttpTransport = sinon.stub();
476+
477+
// Create mock messaging instance
478+
const mockMessagingInstance = {
479+
enableLegacyHttpTransport: mockEnableLegacyHttpTransport,
480+
sendEachForMulticast: () =>
481+
Promise.resolve({
482+
successCount: 1,
483+
failureCount: 0,
484+
responses: [{ error: null }],
485+
}),
486+
};
487+
488+
// Stub initializeApp and INTERNAL.appStore.removeApp
489+
const initStub = sinon.stub(firebaseAdmin, 'initializeApp').returns({});
490+
const removeStub = sinon.stub(firebaseAdmin.INTERNAL.appStore, 'removeApp');
491+
492+
// Use sinon.stub with an object and property descriptor
493+
const getMessagingStub = sinon.stub();
494+
getMessagingStub.returns(mockMessagingInstance);
495+
496+
// Replace the getter for messaging
497+
const descriptor = Object.getOwnPropertyDescriptor(
498+
Object.getPrototypeOf(firebaseAdmin),
499+
'messaging'
500+
);
501+
const originalMessaging = descriptor.get;
502+
Object.defineProperty(firebaseAdmin, 'messaging', {
503+
value: getMessagingStub,
504+
configurable: true,
505+
});
506+
507+
const fcmOptsWithLegacy = {
508+
fcm: {
509+
name: 'testAppNameLegacy',
510+
credential: { getAccessToken: () => Promise.resolve({}) },
511+
legacyHttpTransport: true,
512+
},
513+
};
514+
515+
const pnWithLegacy = new PN(fcmOptsWithLegacy);
516+
517+
pnWithLegacy
518+
.send(regIds, message)
519+
.then(() => {
520+
expect(mockEnableLegacyHttpTransport.called).to.be.true;
521+
// Restore
522+
initStub.restore();
523+
removeStub.restore();
524+
Object.defineProperty(firebaseAdmin, 'messaging', {
525+
get: originalMessaging,
526+
configurable: true,
527+
});
528+
done();
529+
})
530+
.catch((err) => {
531+
// Restore
532+
initStub.restore();
533+
removeStub.restore();
534+
Object.defineProperty(firebaseAdmin, 'messaging', {
535+
get: originalMessaging,
536+
configurable: true,
537+
});
538+
done(err);
539+
});
540+
});
541+
542+
it('should not enable legacyHttpTransport when not configured', (done) => {
543+
const mockEnableLegacyHttpTransport = sinon.stub();
544+
545+
// Create mock messaging instance
546+
const mockMessagingInstance = {
547+
enableLegacyHttpTransport: mockEnableLegacyHttpTransport,
548+
sendEachForMulticast: () =>
549+
Promise.resolve({
550+
successCount: 1,
551+
failureCount: 0,
552+
responses: [{ error: null }],
553+
}),
554+
};
555+
556+
// Stub initializeApp and INTERNAL.appStore.removeApp
557+
const initStub = sinon.stub(firebaseAdmin, 'initializeApp').returns({});
558+
const removeStub = sinon.stub(firebaseAdmin.INTERNAL.appStore, 'removeApp');
559+
560+
// Use sinon.stub with an object and property descriptor
561+
const getMessagingStub = sinon.stub();
562+
getMessagingStub.returns(mockMessagingInstance);
563+
564+
// Replace the getter for messaging
565+
const descriptor = Object.getOwnPropertyDescriptor(
566+
Object.getPrototypeOf(firebaseAdmin),
567+
'messaging'
568+
);
569+
const originalMessaging = descriptor.get;
570+
Object.defineProperty(firebaseAdmin, 'messaging', {
571+
value: getMessagingStub,
572+
configurable: true,
573+
});
574+
575+
const fcmOptsWithoutLegacy = {
576+
fcm: {
577+
name: 'testAppNameNoLegacy',
578+
credential: { getAccessToken: () => Promise.resolve({}) },
579+
},
580+
};
581+
582+
const pnWithoutLegacy = new PN(fcmOptsWithoutLegacy);
583+
584+
pnWithoutLegacy
585+
.send(regIds, message)
586+
.then(() => {
587+
expect(mockEnableLegacyHttpTransport.called).to.be.false;
588+
// Restore
589+
initStub.restore();
590+
removeStub.restore();
591+
Object.defineProperty(firebaseAdmin, 'messaging', {
592+
get: originalMessaging,
593+
configurable: true,
594+
});
595+
done();
596+
})
597+
.catch((err) => {
598+
// Restore
599+
initStub.restore();
600+
removeStub.restore();
601+
Object.defineProperty(firebaseAdmin, 'messaging', {
602+
get: originalMessaging,
603+
configurable: true,
604+
});
605+
done(err);
606+
});
607+
});
608+
});
472609
});

0 commit comments

Comments
 (0)