How can I gunzip POST request data in express? - node.js

I am trying to build a server that can accept gzipped POST data with express. I think I could just write my own middleware to pipe the request stream to a zlib.createGunzip() stream. The question is, how can I achieve that, afterwards, the express.bodyParser() middleware is still able to parse my gunzipped POST data?
I tried to replace the original request stream methods by the ones of the zlib stream, but that just made the bodyParser return a "Bad Request" Error:
var express = require('express');
var app = express();
function gUnzip(req, res, next) {
var newReq;
if (req.headers['content-encoding'] === 'gzip') {
console.log("received gzipped body");
newReq = req.pipe(zlib.createGunzip());
Object.getOwnPropertyNames(newReq).forEach(function (p) {
req[p] = newReq[p];
});
}
next();
}
app.use(gUnzip);
app.use(express.bodyParser());
app.listen(8080);
Is there a way to make this work without rewriting the bodyParser() middleware within my own middleware?
EDIT:
This is the same question: Unzip POST body with node + express. But in the answer he just does in his own middleware what the express.bodyParser() should do, which is what I want to avoid. I am looking for a way to simply unzip the request data from the stream and then pass it to the bodyParser(), which expects a stream itself, as can be seen at http://www.senchalabs.org/connect/json.html.

compressed request bodies are generally not used because you can't negotiate content encodings between the client and server easily (there's another stackoverflow question about that i believe). most servers don't support compressed request bodies, and the only time you really need it is for APIs where the client will send large bodies.
body-parser, specifically raw-body, does not support it because the use-case is so minimal, though i've though about adding it. for now, you'll have to create your body-parser. fortunately, that's easy since you can just fork body-parser and leverage raw-body. the main code you'll add around https://github.com/expressjs/body-parser/blob/master/index.js#L80:
var zlib = require('zlib')
var stream
switch (req.headers['content-encoding'] || 'identity') {
case 'gzip':
stream = req.pipe(zlib.createGunzip())
break
case 'deflate':
stream = req.pipe(zlib.createInflate())
break
case 'identity':
break
default:
var err = new Error('encoding not supported')
err.status = 415
next(err)
return
}
getBody(stream || req, {
limit: '1mb',
// only check content-length if body is not encoded
length: !stream && req.headers['content-length'],
encoding: 'utf8'
}, function (err, buf) {
})

Have you tried using the built in compress middleware. It's documented in the expressjs reference documentation
app.use(express.compress());
Maybe you can find something useful here instead: Unzip POST body with node + express

Related

Concurrency with fs.writeFileSync using NodeJS and ExpressJS

I have the following code written with NodeJS and ExpressJS:
const express = require("express");
const fs = require("fs");
const bodyParser = require("body-parser");
const jsonParser = bodyParser.json();
const hostname = "127.0.0.1";
let port = 3001;
const app = express();
app.use(express.static(__dirname + "/answers"));
const answersPath = __dirname + "/answers/answers.json";
app.patch("/new/answer", jsonParser, function (req, res) {
try {
const questionId = req.body.questionId;
const answer = req.body.answer;
const answersJson = JSON.parse(fs.readFileSync(`${answersPath}`, "utf8"));
if (answersJson[questionId]) {
answersJson[questionId] = [...answersJson[questionId], answer];
} else {
answersJson[questionId] = [answer];
}
fs.writeFileSync(`${answersPath}`, JSON.stringify(answersJson));
res.sendStatus(200);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
app.listen(port);
console.log(`Server running at http://${hostname}:${port}/`);
What it basically does, it has an endpoint (/new/question), on which it receives as a JSON format, a question and an answer.
If the question exists already in the answers.json file, it adds the new answer to the list of answers for that question. If not, it creates a new question with a list of the answer.
Now, I've read the following article: https://www.geeksforgeeks.org/how-to-handle-concurrency-in-node-js/
And what I understood from here, is that even though the endpoint would get called at the same time by two clients, both of the responses will be saved, one after the other - one of them will wait for the other one, i.e. the file will not get overwritten.
So my question is, is this true? NodeJS deals with concurrency on its own, or do I need to implement something to prevent this from happening?
Thank you, and sorry if this is a dumb question 😞.
Although readFileSync() and writeFileSync() might do what you want to achieve, you should avoid using synchronous functions in Node.js.
Synchronous functions will block the entire Node.js process, not just the a single Express route. This means your server will become unresponsive while reading or writing the file. This will become an issue if the file gets bigger.
Instead of using a file, you could keep the data only in memory. If you need to persist the data between server restarts, you can read it when the server starts and write it when the server stops. In this case it might be okay to use synchronous functions.

Node js Converting pdf Buffer back to pdf

I have created a pdf with the browser in Javascript and sent it via post to the server using this code:
var blob = pdf.output('blob')
var xhr = new XMLHttpRequest();
xhr.open('post','/upload', true);
xhr.setRequestHeader("Content-Type", "application/pdf");
xhr.send(blob);
I would like to save as pdf on the server running Node with express. I have come up with the following code using express and body-parser package:
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ limit: '1gb', extended: false }));
app.use(bodyParser.raw({ limit: '1gb', type: 'application/pdf' }));
app.post('/upload', function(req, res){
console.log(req.body);
}
req.body is a Buffer, Uint8Array[653120]:
I need help converting it back to pdf before saving in on the server. Any help would be appreciated. Thanks.
A buffer is a literal binary representation. Just write it to a file directly without .toString() and it should be the file you want.
e.g. to try fs.writeFileSync('some.pdf', req.body)
I do not actually recommend using writeFileSync - instead use writeFile which is async and needs a callback, but won't block other http requests from being accepted.
A Buffer is just a sequence of bytes without any encoding. If you expect body to look like xml when you log it out, try .toString('utf8') on it. hex/utf8/base64 are just representations of binary. They're like a function to unpack, or pack data. In this case you want the sequence of bytes in your buffer to exist on disk as-they-are; so messing with the encoding is undesirable.

Node.JS GET / Parameters

For exemple this is my server with a simple API :
var express = require('express');
var rzServer = express();
rzServer.use(bodyParser.urlencoded({extended:true}));
rzServer.use(bodyParser.json());
app.get('/url', function(req, res) {
console.log(req.query.data); // String
console.log(JSON.parse(req.query.date)); // Object
});
req.query.data is interpreted as a string but it's a JSON Object.
Is it possible with the body-parser package to parse the querystring ?
Thanks.
body-parser is a middleware to parse body (it's its name). If you want to parse the query string, so you need another middleware for that.
Another thing : GET requests normally don't take any JSON parameters (no body). If you need to send a true JSON, perhaps you're not using the good HTTP method. Try to use a POST request, or create a true query string (http://expressjs.com/fr/api.html#req.query).

node express body-parser for application/logplex-1

I am using node express to process POST requests of heroku logging data with body data that is in the application/logplex-1 format (apparently syslog formatted).
In particular, I am using the body-parser module as middleware to parse the POST body.
It works OK to specify app.use(bodyParser.text({ type: 'application/logplex-1' })) to force body-parser to parse the body as text, but the text is just a big block of space-separated information without much structure other than that. Therefore I need to parse the body data further to find and extract what I want.
This is OK, but I'm wondering if there is, perhaps, a better way of parsing the logplex-1 body more directly into something more structured and easier to work with, like JSON. I'm not familiar with logplex-1 or the syslog format, and whether it does indeed have anything more useful structure/metadata in it than is apparent from the text block I'm currently getting.
Any ideas?
I have no experience with logplex or Heroku, but this seems to be working:
var syslogParser = require('glossy').Parse;
var express = require('express');
var app = express();
var server = app.listen(3012);
// Express allows arrays-of-middleware to act as a "single" middleware.
var logplexMiddleware = [
// First, read the message body into `req.body`, making sure it only
// accepts logplex "documents".
require('body-parser').text({ type: 'application/logplex-1' }),
// Next, split `req.body` into separate lines and parse each one using
// the `glossy` syslog parser.
function(req, res, next) {
req.body = (req.body || '').split(/\r*\n/).filter(function(line) {
// Make sure we only parse lines that aren't empty.
return line.length !== 0;
}).map(function(line) {
// glossy doesn't like octet counts to be prepended to the log lines,
// so remove those.
return syslogParser.parse(line.replace(/^\d+\s+/, ''));
});
next();
}
];
// Example endpoint:
app.post('/', logplexMiddleware, function(req, res) {
console.log(req.body);
return res.sendStatus(200);
});
It uses glossy to parse the syslog messages into Javascript objects.
If the amount of data being posted is considerable (>hundreds of K's), it might be better to implement a streaming solution as the code above will first read the entire message body into memory.

Secure file uploader by express js and bodyParser

I google a lot for finding how to secure file uploading in express js,at end I develop following code to do it.
app.use(express.json());
app.use(express.urlencoded());
app.post('/',express.bodyParser({
keepExtensions: true,
uploadDir: __dirname + '/faxFiles',
limit: '20mb'
}),function(req,res){
checkFile(req.files.faxFile);
});
as you see I can limit file size and set uploadDir in bodyParser,now I need to allow user to upload image and pdf only,the way I used is checkFile function which contains following code.
var fs = require('fs');
var checkFile = function(faxFile){
if (faxFile.type != "image/jpeg" || faxFile.type != "application/pdf" || faxFile.type != "image/gif"){
fs.unlink(faxFile.path, function(err){
});
}
}
but I think it's not best way,is there any alternative way to do it?such as set file extension in bodyParser constructor?
You can use mmmagic for strictly checking the extensions. It is an async libmagic binding for node.js for detecting content types by data inspection.
Express uses formidible (https://github.com/felixge/node-formidable) for parsing form data, including file uploads.
I don't see an option in formidible to restrict file types, so I'm suggesting Express likely wouldn't have one either.
I created a little gist to show how to check the mime type using mmmagic while streaming the file:
https://gist.github.com/chmanie/8520572
This is more likely to function in a streaming environment like multiparty or busboy.

Resources