PDFKIT not starting streaming on http response till I call doc.end() - firebase

I am using pdfkit to generate pdf at runtime and returning this in http response for download. I am able to download the file at browser end but the download dialog is not opening immediately. Instead its waiting till doc.end is called. I guess pdfkit is unable to push the stream efficiently. Has anybody else faced this? If yes, please guide.
Here is the sample code which I am trying
exports.testPdfKit = functions.https.onRequest((request, response) => {
//create pdf document
doc.pipe(response);
response.set('Content-Disposition', `attachment;filename=testpdfstream.pdf`);
response.writeHead(200, { 'Content-Type': 'application/pdf' })
const bigText = "some big text"
for (var i = 0; i < 1000; i++) {
console.log('inside iteration -',i)
doc.text(bigText);
doc.addPage();
}
doc.end()
});
I am implementing this functionality on firebase functions which uses expressjs internally for processing http requests. To generate bigger files at my end, streaming is must for me.

HTTP functions can not stream the input or output of the function. The entire request is delivered in one chunk of memory, and the response is collection and send back to the client in one chunk. The maximum size of both is 10MB. There are not workarounds for this limitation of Cloud Functions (but it does help you system scale better).
If you need streaming or websockets, you'll need to use a different product, such as app engine or compute engine.

Related

Firebase push notifications always arrive as an empty message

I have been trying to get push notifications working using firebase. So far I have got as far as successfully sending an empty message "tickle". The problem is adding the message payload seems to have no affect on what the client receives. That is the service worker just sees it as another empty message.
I started by going through googles guide here - https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications
After going through how to send an empty message it says the message payload must be encrypted and suggests using an existing library to do it. To quote - "As with anything related to encryption, it's usually easier to use an actively maintained library than to write your own code".
I tried to use web-push-php which is one of the libraries recommended by googles guide. After having trouble with that i discovered web-php-push doesn't actually support firebase.
Looking on here i find examples that look really simple and don't event encrypt the message payload. It is simply sent in plain json. Doing this has no affect and the receiving end still thinks it's an empty message. See my code below.
I am at a complete loss with this and i'm confused why googles guide says the message data must be encrypted but there are countless examples on SO where it is just send in plain json text.
This is what i am posting from my server to the end point.
POST https://fcm.googleapis.com/fcm/send Authorization: key=[my server
key] Content-Type: application/json {"priority":10,"to":"[subscriber
id]","notification":{"body":"test body","title":"test title"}}
Here is my event listener in my service-worker.js
self.addEventListener('push', function(e) {
var body;
if (e.data) {
body = e.data.text();
} else {
body = "No message "+JSON.stringify(e);
}
var options = {
body: body
};
e.waitUntil(
self.registration.showNotification('Launtel Residential', options)
);
});
When i run the post request above the push notification occurs and triggers the service worker 'push' event as expected but no message data is present. e.data returns null. The 'e' object always just contains a flag set to true. e.isTrusted==true

How to send delayed response (Slack api) with Google Apps Script webapp?

We have a small Google Apps Script webapp that handles Slack slash commands. It does some convenient things like adding, updating and querying records to our sheet, straight from Slack. Everything works just fine most of the time. However the Slack API expects an answer from the request in less than 3 seconds, or it will timeout. Our Google Apps Script is not always able to respond in that timeframe, which will only get worse as our sheet grows or our queries get more complicated.
The Slack API allows for the use of asynchronous calls using a delayed response, but that means that the Google Apps Script needs to respond immediately (within 3 seconds) and do some work in the background.
Now this is the problem
I can't figure out how to make an asynchronous call work in Google Apps Script
I know Workers are not supported in Google Apps Script and my solution below hits a wall because of ReferenceError: 'google' is not defined. (Just ignore the Payload class, it formats a Slack response)
function doPost(request) {
var responseUrl = request.parameter.response_url
// This is how I try to circumvent the lack of threads in Google Apps Script
google.script.run
// Send an asynchronous slack response with result
.withSuccessHandler(function(payload) {
UrlFetchApp.fetch(responseUrl, {
'method' : 'post',
'contentType': 'application/json',
'payload' : payload.toString()
});
})
// Send an asynchronous slack response with error message
.withFailureHandler(function(payload) {
UrlFetchApp.fetch(responseUrl, {
'method' : 'post',
'contentType': 'application/json',
'payload' : payload.toString()
});
})
// do work in the background
.sleep(5);
return new Payload("Let me think about this...").asResponse();
}
function sleep(seconds) {
Utilities.sleep(1000 * seconds);
return new Payload("I waited for " + seconds + " seconds");
}
Does anyone have any idea how to make this work? Are there any alternative solutions to handle an asynchronous request in Google Apps Script?
I'm not aware of any threading in Apps Script either and as you noticed google.script.run only works in the Apps Script frontend.
As a workaround you could use a Google Forms as your "task queue". I've put together a simple G-Form with one question and inspected its final version to get the appropriate parameter names and URL. Then I set an installable on-form-submit trigger to run my script. Here's my POC code:
function doPost(e) {
var form = 'https://docs.google.com/forms/d/e/1FAIpQLScWBM<my-form-id>CRxA/formResponse';
UrlFetchApp.fetch(form, {method:'POST', payload:'entry.505669405=' + e.parameter.example});
return ContentService.createTextOutput('OK');
}
function onForm(e) {
//triggered async from doPost via a Google Forms
SpreadsheetApp.getActive().getSheetByName('Sheet1').appendRow(e.values);
}
It worked fine on my tests and should suffice for your use case.

How to run ElasticSearch on user's devices AND keep ElasticSearch server safe?

Writing a mobile app with Firebase being my backend, also using ES to power my search. I'm completely new to ES.
Suppose each user can publish articles, each of which contains some number of tags, which denotes what this article is about, kind of like questions asked here. Users can search for articles, with tags, and articles containing that tag will be displayed. I manage to do that with Cloud Function, so, the Cloud Function basically looks like this:
exports.articleSearch = functions.https.onRequest((req, res) => {
const { tag } = req.query;
const ElasticSearchConfig = {
uri: '..<my elastic cloud url>/articles/article/_search...',
method: 'GET',
body: ...,
json: true,
auth: {
username: '...<my elastic cloud username>...',
password: '...<my elastic cloud password>...'
}
};
// If succeeds, send results back to user, if not, send error back
request(ElasticSearchConfig).then((results) => ...)
.catch((error) => ...);
});
This works, however, it's a bit slow, because I'm not running ElasticSearch on user's devices, instead, through a cloud function. But if I do run the above code on user's devices, you noticed auth property of ElasticSearchConfig object, I'm basically giving everybody permissions to access and manipulate my ES server. How can I run the above code on user's devices and at the same time, prevent them from reading or writing anything without proper permission?
There's no secure way to do what your asking. Even if it was possible, you don't want that kind of processing client side draining the battery, especially on mobile. Your slow response from cloud functions may be caused from the function entering a timeout state, meaning it hasn't been called in a while.

Meteor - should inserting data from an api go into a method?

Real simple and short question here, should I put an API into a method when trying to upload files or keep it on the client side? And also what is the point of methods, I know it's to keep your app safe, but I am not sure how a user would change break the app. Also, can you explain when to use methods?
readImage(e){
let file = e.target.files[0];
const CLOUDINARY_URL = "my_URL";
const CLOUDIARY_UPLOAD_PRESET = "my_Upload_Preset"
let formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", CLOUDIARY_UPLOAD_PRESET)
axios({
url: CLOUDINARY_URL,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: formData
}).then(function(res){
console.log(res)
console.log(res.data.secure_url);
}).catch(function(err){
console.log(err);
})
console.log(file);
}
The file upload itself needs to happen on the client, but it is good practice to put any processing into a server method. If you do all the processing on the client, it means that you need to enable database access from the client, meaning that a malicious user could modify your database from the browser console.
In the server you should check if the logged in user has permission to do the operation requested. These methods can also be called by entering commands from the console, but you make the attack surface much smaller by reducing the number of operations available.

Node.js sending binary data of HTTP over a socket

I am writing my own http module (I know that node.js includes one)
I am using the net module
when I am getting a request for a static binary(picture) file, how do I generate HTTP response with the binary file?
When I do exactly that for text file(e.g. html file) it just works..
If you are using the net module, you are probably using a code such as :
var server = net.createServer(function (socket) {
});
What is a socket? it represents the stream of data over the network.
In objects, Socket is a WriteableStream, read more here: http://nodejs.org/docs/v0.6.5/api/streams.html
When you are reading a file you can get the content as String, Buffer or as a ReadableStream
The easiest way to read file as a stream is by using the function: http://nodejs.org/docs/v0.6.5/api/fs.html#fs.createReadStream
e.g.
var fileAsAstream = fs.createReadStream(filePath);
In order to transfer the content of a binary read-stream to a write-stream you can use the pipe function http://nodejs.org/docs/v0.6.5/api/streams.html#stream.pipe functions
e.g.
fileAsAstream.pipe(socket);

Resources