firebase / firebase/firebase-functions

The HTTP request cancellation event is not fired in the Firebase HTTP Function

Open
#1,585 6 comments 2 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
1.1k
Forks
232
Avg merge
20h 46m
Merged PRs (30d)
15

Description

### Related issues

### [REQUIRED] Version info

**node:**

v20.13.1

**firebase-functions:**

5.0.1

**firebase-tools:**

13.9.0

**firebase-admin:**
12.1.0

**express**

4.19.2

### [REQUIRED] Test case

* `frontend.html`
```l

send to express app
send to function

abort

const callApi = (url) => {
controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal })
.then((response) => {
console.log("Request complete", response);
})
.catch((err) => {
console.error(`Request error: ${ err.message }`);
});
}

let controller;
const expressUrl = "http://127.0.0.1:3001";
const fnUrl = "http://127.0.0.1:5001/dev-guestacount/europe-west1/firebaseFunction";

const callExpressBtn = document.getElementById("sendExpressApp");
const callFunctionBtn = document.getElementById("sendToFn");
const abortBtn = document.getElementById("abort");

callExpressBtn.addEventListener("click", () => callApi(expressUrl));
callFunctionBtn.addEventListener("click", () => callApi(fnUrl));
abortBtn.addEventListener("click", () => {
if (controller) {
controller.abort();
console.log("Aborted");
}
});

```

* `firebase-function.ts`

```
import express, { Request, Response } from 'express';
import cors from 'cors';
import { https, region } from 'firebase-functions/v1';

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const app = express();
app.use(cors());

app.use('/*', async (req: Request, res: Response) => {
console.log('start processing...');

// Detecting close event
req.on('close', function() {
const { destroyed } = req;
console.log('Client connection close....!', { destroyed });
});

await delay(5000);

if (!req.destroyed) {
console.log('sending response');
res.send('Hello World!');
}
});

export const firebaseFunction = region('europe-west1').runWith({ invoker: 'public' })
.https.onRequest((request: https.Request, response: Response) => app(request, response));

```

* `express-server.js`

```
const express = require('express');
const cors = require('cors');

const app = express();
const port = 3001;
app.use(cors());

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

app.get('/', async (expressRequest, expressResponse) => {
console.log('start processing...');

// Detecting close event
expressRequest.on('close', function () {
const { destroyed } = expressRequest;
console.log('Client connection close....!', { destroyed });
});

await delay(5000);

if (!expressRequest.destroyed) {
console.log('sending response');
expressResponse.send('Hello World!')
}
})

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${ port }`)
})
```

### [REQUIRED] Steps to reproduce

Run the Firebase function defined in the `firebase-function.ts` file locally in the emulator, via `firebase emulators:start --only functions`, and the native Express application, via `node express-server.js`.
Open the `frontend.html` file in your browser. When the `firebase-function` function is loaded by the emulator, click the `send to function` button in your browser and then (after about a second) the `abort` button.
Notice in the network tab of the browser's developer tools that the function call was canceled by the browser, but no information about it appeared in the emulator console.
Then click the `send to express app` button in your browser and, as before, after about a second, click `abort`. Similarly to the previous case, in the network dev tools tab of the browser you will see that the call has been canceled, but also in the console where the express application is running (`express-server.js`) you can see a log proving that the event informing about the request cancellation has been received by the application.

### [REQUIRED] Expected behavior

The expected behavior is for the `req.on('close')` event to be triggered when the client (browser) cancels the HTTP request also for the requests received by Firebase Functions, just as it is for the native Express.js application.

### [REQUIRED] Actual behavior

Currently, for calls received by the Firebase Function, the `req.on('close')` event fires only when the call completes, and nothing happens when the client cancels the call.

### Were you able to successfully deploy your functions?

There are no problems with deploy

Contributor guide

Open the contributing guide

Research direction

Start by running the firebase-function.ts example with `firebase emulators:start --only functions` and compare its cancellation behavior with express-server.js running via `node express-server.js`. Trace how the Firebase HTTP Function handles the request lifecycle and verify completion by confirming that client cancellation triggers the expected `req.on('close')` behavior before the delayed response.

Written by the indexing model from the issue text.

Assessment

Tech stack
express, node.js, typescript
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.