firebase functions running twice - firebase

I am using functions and hosting from firebase.
I have defined one function as shown below.
const functions = require("firebase-functions")
const cors = require('cors')
exports.hello = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true})
return cors()(request, response, () => {
response.send({data: 'hello fire functions'})
})
})
And in hosting call the function like this:
import firebase from "firebase/app"
import "firebase/functions"
const config = { ... }
firebase.initializeApp( config )
const test = firebase.functions().httpsCallable('hello')
test().then( result => console.log(result) )
Then the functions log will be written twice as follows:
2:37:07.548 PM hello: Function execution started
2:37:07.599 PM hello: Hello logs!
2:37:07.600 PM hello: Function execution took 53 ms, finished with status code: 204
2:37:07.809 PM hello: Function execution started
2:37:07.816 PM hello: Hello logs!
2:37:07.817 PM hello: Function execution took 8 ms, finished with status code: 200
It is also displayed twice in the usage graph.
This behavior means I have to pay twice as much usage. This is not normal.
If cors is not used, the log and usage graph will show that it has been executed only once.
But if you don't use cors: When you call a function in the browser, the function is executed, but the browser gets a CORS error.
How can I solve this problem? I couldn't find a solution in the official documentation. (This is a problem after hosting and functions deploying. It is not a localhost environment.)

Firstly, you are mixing up HTTP requests on the client with callable functions. That's not what you're supposed to do. Please review the documentation for both HTTP functions and callable functions to see how they are different. If you're using the callable SDK on the client, you should use a callable function on the backend.
Second, this is the normal expected behavior. Callable functions use CORS between the client and server. CORS clients issue a preflight request which causes the first request and first log. Then, the actual request which causes the second request and second log. You cannot avoid this when using CORS - that's simply how the protocol works.
See also:
Firebase Cloud Function executing twice when triggered over HTTP
Enabling CORS in Cloud Functions for Firebase
Cloud Functions for Firebase triggering function on CORS preflight request

If you're using Firebase Hosting, you probably don't need CORS.
You can avoid preflight requests by adding rewrites to firebase.json.
However, there are conditions such as the position of the function must be us-central1.
https://firebase.google.com/docs/hosting/functions

Related

How to restrict a Google Cloud function to be invoked only from the Google Cloud Console?

I have a Firebase function to do some data processing, and I only want to invoke it through Google Cloud Console manually.
Just a simple function like this (using node.js):
import * as functions from 'firebase-functions'
// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
//
export const test = functions.https.onRequest(async (req, res) => {
res.send({
query: req.query,
body: req.body,
})
})
The way I did it is to deploy the function via the firebase-cli then remove the Cloud Function Invoker role in the permission tab from GUI.
It seems to work.
But I noticed one thing, when I send the request with Postman
when you send a GET, the error is 400,
But for any request that's not a GET, you get a proper 403
My questions are:
why GET is 400 while the others are 403?
am i doing it right in terms of my requirement?
what's the correct way of doing it?
does the function get invoked while sending a request like POST?

firebase callable functions returning CORS error and not being called from client

I have been using firebase functions for quite some time and all deployments of functions have been going quite smoothly. All of a sudden any new functions deployed have stopped working and any calls from the client return a CORS error. If I check the functions list in the firebase dashboard I can't see the functions being called which is similar to what I would expect if the functions simply didn't exist at all.
I am now just trying a simple basic function like below:
exports.createSession = functions.region('europe-west2').https.onCall(async (data, context) => {
return ({status: 200})
});
On the frontend I am doing a simple call like below:
const createSessionFunction = functions.httpsCallable('createSession');
const response = await createSessionFunction({});
All the other functions that were create and deployed prior to this week are working fine. Any new functions are not.
The error I get is below:
Access to fetch at 'https://europe-west2-xxxxxxx.cloudfunctions.net/createSession' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
index.cjs.js:614 POST https://europe-west2-xxxxxxxz.cloudfunctions.net/createSession net::ERR_FAILED
My function list on the firebase GUI show this function does exist:
createSession - Request - https://europe-west2-xxxxxxxx.cloudfunctions.net/createSession-europe-west2-Node.js 8 -256 MB - 60s
However the logs show that it is never called from the client when I'm trying to test it which means the client might not be detecting this function at all.
I have tried the following steps with no luck:
Delete and redeploy the functions
Rename the function and redeploy
Deploy the same new function on different applications (dev/test etc)
Any ideas?
This was resolved on the google cloud dashboard by granting all my functions public access. The default permissions has changed from public to private.
https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation

firebase functions - detect head requests

I have some firebase cloud functions set up like this:
const app = express();
app.get('/', (req, res) => res.send('404'));
app.head('/:userId/:slug', trackFile);
app.get('/:userId/:slug', trackFile);
app.use('/api', app);
export const api = functions.https.onRequest(app);
As you can see I have defined a route for HEAD requests, but in the trackFile function, I am writing the request method (req.method) to firestore, and it's always coming back as 'GET'.
I am testing it with curl to make the head request: curl -I https://myfirebaseapp.com/etc/etc/
Is there some gotcha with firebase functions where it always passes HEAD requests to GET routes? I need to know when a request is a HEAD request to properly track it.
Update: I opened an issue in the firebase function github repo.
This is standard Express behavior, as detailed here: https://github.com/expressjs/expressjs.com/issues/748

Firebase Callable Function: Response for preflight in invalid

I have created a Firebase callable function, with a simple text return, but am receiving an error when I call the function, both on local and on my deployed app.
The callable function is a simple function to return some text for now:
exports.getSomeInfo = functions.https.onCall(async (data, context) => {
return 'some info';
});
In my app I load the function with:
const getSomeInfo = firebase.functions().httpsCallable('getSomeInfo');
And call it in the app with:
getSomeInfo();
This produces an error of:
Failed to load https://us-central1-[project-ID].cloudfunctions.net/getSomeInfo: Response for preflight is invalid (redirect)
This error occurs when calling the function on local using firebase serve and on the deployed app.
Viewing the logs in the Firebase Console shows no logs or errors.
Other issues mention this could be a CORS issue, or an incorrect Firebase config. I've ensured the Firebase config is correct. And tried a few of the CORS solutions, but continue to get the error above.
Using Firebase#5.5.2.
What else could be causing this error?
As indicated in the documentation, for an HTTPS Callable function you need to "return data that can be JSON encoded".
So if you do something like the following, it should work.
exports.getSomeInfo = functions.https.onCall((data, context) => {
return {result: 'some info'};
});
Update: removed the async
April 2020, I just learned the hard way that callable functions have their module name prepended...
In index.js:
const functions = require('firebase-functions')
// ...
exports.callable = require('./callable')
In callable.js:
const functions = require('firebase-functions');
// ... other stuff
exports.myCloudFunction = functions.https.onCall((data, context) => {
// ...
The way to call this "myCloudFunction" from a JS client is to use its name, prepended with its module name, like this
const fn = firebase.functions().httpsCallable("callable-myCloudFunction")
fn().then(result => { //...
This is documented nowhere, as far as I have found, and, as others have mentioned, almost any error that occurs prior to actually executing the cloud function ends up mislabeled as a CORS error.
After trying across 2 days a variety of refreshes/clean-ups and stuff with CORS, finally found it working after first deleting the function via Firebase console and then deploying the function.
In my case it seems the deployed version got corrupted somehow. It started yesterday when deploy for functions was getting stuck - it would hang and never exit - even though Firebase Status page said all is well. It lasted all day, and I finally let it go to see if it will work today. I thought it was my code, but the deploy is back to working today.
This happened to me a couple days ago. The problem was that when i ran firebase deploythe functions in my src directory were not being compiled. There were typescript errors which stopped it from compiling. You can see if it compiled by checking your lib folder and index.ts inside there I believe.

Gateway timeout for Https Functions proxied through Firebase Hosting

Problem:
We are using Https Functions that are taking longer than 60 sec to respond (this is a necessary requirement for our use case and cannot be replaced with async execution). We configured the functions timeout, such that they succeed after e.g. 75 sec, as indicated by direct execution from Chrome or Postman through e.g. https://us-central1-<project-id>.cloudfunctions.net/test.
We have also configured Hosting proxy to rewrite all requests for /api to these functions. However, when submitting identical requests through these "hosting" endpoints (e.g. https://<project-id>.firebaseapp.com/api/test), we receive a 504 (Gateway timeout) error after only 60 sec, both in Chrome and Postman. This suggests that the Hosting proxy itself times out before a function succeeds (which still happens, as indicated in Firebase Console log).
Question:
Is it possible to set Hosting timeout to a higher value (e.g. 120 sec)?
Possible solutions/workarounds:
tried to set Connection: keep-alive and Keep-Alive: timeout=120 headers in client requests, however that didn't seem to have any effect on the Hosting proxy; or maybe we're not doing it right.
the only workaround for now is to not use rewrite rules, instead relying on the "direct" URLs (from cloudfunctions.net). However, that entails dealing with CORS in browser clients and more importantly, changing our URL resolution scheme and organization of functions' code (e.g. we cannot use a long path for each function, such as /api/some/path/to/test, because only the first part of that path, such as api, will be considered as the function name).
Thanks!
It is possible to write your function in such a way as to maintain a persistent connection:
https://firebase.google.com/docs/functions/networking#https_requests
const http = require('http');
const functions = require('firebase-functions');
const agent = new http.Agent({keepAlive: true});
exports.function = functions.https.onRequest((request, response) => {
req = http.request({
host: '',
port: 80,
path: '',
method: 'GET',
agent: agent,
}, res => {
let rawData = '';
res.setEncoding('utf8');
res.on('data', chunk => { rawData += chunk; });
res.on('end', () => {
response.status(200).send(`Data: ${rawData}`);
});
});
req.on('error', e => {
response.status(500).send(`Error: ${e.message}`);
});
req.end();
});
Just ran into this very same problem with a Cloud Scheduler Job that hits my API endpoint (which is connected via Firebase Hosting). It takes around 80 seconds to complete.
But it seems that there is no workaround to this. At least not while using Firebase Hosting. Maybe you can hit the functions' URL directly instead of doing it through Firebase Hosting. But if those request are coming from the browser, you'd have to deal with configuring CORS.
From: https://firebase.google.com/docs/hosting/functions
Text version:
Note: Firebase Hosting is subject to a 60-second request timeout. Even if you configure your HTTPS function with a longer request timeout, you'll still receive an HTTPS status code 504 (request timeout) if your function requires more than 60 seconds to run. To support dynamic content that requires longer compute time, consider using an App Engine flexible environment.

Resources