Piping Stream from Firebase Function to Client
See original GitHub issueVersion info
node: v10.13.0
firebase-functions: ^2.1.0
firebase-tools: 6.3.0
Test case
Server (Firebase Function):
app.post('/download', (req, res) => {
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-disposition': 'attachment; filename=Report.pdf',
'Access-Control-Allow-Origin' : '*'
});
const options = { format: 'letter' };
pdf.create(req.body.template, options).toStream((err, stream) => {
const watchdog = stream.pipe(res);
watchdog.on('finish', () => {
console.log('Finish event emitted');
res.end();
});
});
});
Client:
fetch('https://us-central1-[id].cloudfunctions.net/app/download', {
method: "POST",
mode: "cors",
body: JSON.stringify(data)
})
.then(response => response.body)
.then(body => {
const reader = body.getReader();
return new ReadableStream({
start(controller) {
return pump();
function pump() {
return reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
return pump();
})
}
}
})
})
.then(stream => new Response(stream))
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
.then(url => {
const link = document.createElement('a');
link.href = url;
link.download = "Report.pdf";
link.click();
})
.catch(err => console.log(err));
StackOverflow Question: https://stackoverflow.com/questions/54489268/piping-stream-to-client-express-server-to-firebase-functions-migration
Steps to reproduce
I’m not sure if this is an actual bug or if Firebase Functions does not support piping streams. Make a Fetch call after deployment to run function to reproduce.
Expected behavior
When running this code on my own isolated Node/Express Server, without Firebase, everything works fine - that is, a PDF file containing the HTML sent in the body of the POST request is downloaded.
Actual behavior
When migrating the exact same code to Firebase Functions, I see a CORS error, even though I am both manually setting the header to allow CORS and including the CORS NPM module middleware (setting origin to true in app.use()
).
Additionally, every function call is both timing out and getting stuck in an infinite loop to the extent that after only calling the function one time today, I have used up my free quota. The function times out and then seems to get called again, then times out and gets called again, and so on.
Function execution took 6002 ms, finished with status: 'timeout'
Were you able to successfully deploy your functions?
No error messages from firebase deploy
.
I hope I’m not breaking any guidelines by posting this issue here. Again, I’m not sure if the problem is arising out of a bug with Firebase Functions, whether Firebase Functions does not support piping streams, or if I’m just doing something wrong. I don’t get the why, number one, the request times out when it takes only a second on my local server - I’m using res.send()
in all the required callbacks for errors and the completion of the piping, and number two, the functions keeps getting called as if recursively. The Fetch call is inside an event handler for a button click, so it’s not like I keep making a call.
Thank you.
Issue Analytics
- State:
- Created 5 years ago
- Comments:6 (2 by maintainers)
@kevinajian
I’m not actually sure what was causing the issue. I believe it might have had something to do with sending a CORS Header in the response when accessing the URL from the same origin/domain. I didn’t think that would cause an issue.
I ended up deleting the function and creating a new one, and then I rewrote the aforementioned code, and it worked fine.
For reference, here is the code in
index.js
from Firebase Functions:This uses the html-pdf NPM Package.
The goal of the function is to take a POST Request containing a body with an HTML string, where the HTML string is the
innerHTML
of a Handlebars Template after Client-Side Rendering, convert that into a PDF, and then pipe back the reponse to the client for download.On the client, I create a utility function that returns a promise:
And then I can call that function where neccerssary:
For reference, my
firebase.json
file contains the required rewrites - giving me a/api/**
route for my Node/Express Server and a/**
route for React-Router to handle on the client:I did manage to get pdf.create(html, options).toStream((err, stream) to work from a firebase function with options = {format: ‘A4’, base: ‘file:///’ + __dirname + ‘/’} and stream.pipe(res) without a res.end(). Without this I would get invalid attachment messages in abobe reader.