[Messaging] Unable to send APN (Apple Push Notifications) with Firebase Messaging and Cloud Functions.
[READ] Step 1: Are you in the right place?
- For issues related to the code in this repository file a Github issue.
- If the issue pertains to Cloud Firestore, read the instructions in the "Firestore issue" template.
- For general technical questions, post a question on StackOverflow with the firebase tag.
- For general Firebase discussion, use the firebase-talk google group.
- For help troubleshooting your application that does not fall under one of the above categories, reach out to the personalized Firebase support channel.
[REQUIRED] Step 2: Describe your environment
- Operating System version: iOS 17.4+
- Firebase SDK version: v12.3.0
- Firebase Product: Cloud Functions | Firebase Messaging
- Node.js version: 20.16.0
- NPM version: 10.8.2
[REQUIRED] Step 3: Describe the problem
When trying to send an iOS push notification (APN) through firebase cloud functions using firebase messaging, it fails with an error message (summarized from cloud functions log):
cause: HTTPParserError: Response does not match the HTTP/1.1 protocol (Expected HTTP/)
code: 'HPE_INVALID_CONSTANT',
data: '\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x10\x00\x00\x03\x00\x00\x00\x01\x00\x05\...'
Sending an APN request via cURL through the terminal, using http/2 gives a valid response and the notification shows. It also works via Firebase Messaging dashboard when testing a push notification.
All APN certificates (the method I'm using) have been added to Firebase in project settings, and they have all been verified.
Steps to reproduce:
- Create a cloud function to send a push notification.
- Send the notification to an IOS device.
Relevant Code:
Index.ts file
import {onDocumentUpdated} from "firebase-functions/v2/firestore";
import {initializeApp} from "firebase-admin/app";
initializeApp();
const path = "";
exports.change = onDocumentUpdated(path, async (event) => {
if (!event.data) {
error("No data associated with the event");
return;
}
debug("", {
docRef: event.data.after.ref,
exists: event.data.after.exists,
createdTime: event.data.after.createTime,
});
// Would get data user tokens here from firestore, irrelevant code"
const promises = [];
const promise = await sendiOSNotification(deviceToken, fcmToken, "Title Test", "Body Test");
promises.push(promise);
return await Promise.all(promises)
});
APN notification file.ts
import {getMessaging} from "firebase-admin/messaging";
/**
* A simple function to deliver iOS push notifications
* @param {string} deviceToken the user's device token
* @param {string} fcmToken the user's firebase fcm token
* @param {string} alertTitle the notification title
* @param {string} alertBody the notification body
*/
export async function sendiOSNotification(
deviceToken: string,
fcmToken: string,
alertTitle: string,
alertBody: string) {
const payload = {
token: fcmToken,
notification: {
title: alertTitle,
body: alertBody,
},
};
return await getMessaging().send(payload);
}
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Thanks for reporting this. Are you able to reproduce this issue on your local development environment or does this only happen on Cloud Functions?
So I'm using Node.js and tried to replicate the function on a custom node from scratch (making a request to Apple's APN server). Using http2 gave me the same error warning. Perhaps it's JavaScript's http2 library that's causing the issue but I'm not a JS expert so maybe there was something I missed.
The code sample you provided shows that you are using the send() API. Are you sending a single message? If so, any reason you would prefer to use http/2 for that?
I'm sending a single notification, yes. And the reason for http2 is the apple requirement for sending and receiving APNs (as they will only accept requests that way). I noticed Firebase Messaging Nodejs uses http/2 by default and has an option for enabling legacy requests. I tried both, but both failed.
Tested this on the latest build and is now working.