Express Limit Request Size - node.js

i would like to limit the request size for an upload in the following case (simplified):
app = Express();
router = Express.Router();
router.use('/upload', (req, res, next) => {
const writeStream = fs.createWriteStream('filename');
req.pipe(writeStream);
});
app.use(router);
As you can see, no form or similar is used (no way to change this). Therefore, the data is provided as a raw data stream.
As I could not find any existing modules for this case (body-parser does not work for this case, raw-body can also not be used as the stream should be left untouched), my current approach would be to use the following code as a middleware for express:
function(req, res, next) {
if (req.headers['content-length'] > limit) {
res.set("Connection", "close");
res.status(413).end();
return;
}
let received = 0;
req.on('data', (chunk) => {
received += chunk.length;
if (received > limit) {
res.set("Connection", "close");
res.status(413).end();
}
})
next();
})
Is there any better way to do this? Is there any existing npm package that can do this job?

In the end I solved it by using the answer provided by #ambianBeing and rewrote the function that afterwards handles the buffer instead of a stream.

Related

Updating API response to GET request with node js express

I am a web development noob. I am building a web app in node js express, and I am able to populate a route with a dummy json. I don't want to use a database but just have live data being updated every few seconds.
app.route('/robot-data').get( async (req, res) => {
res.json([ // dummy data
{"data":0},
]);
});
My html is able to read from this API fine. The problem is, I would like to update the response, eventually with an emit event but for testing I am trying to just do it periodically.
let dataSource = 0;
const updateDataSource = () => {
const delta = Math.random();
dataSource += delta;
app.get('/robot-data', (req, res) => {
res.json([
// updated data
]);
});
}
const PORT = process.env.PORT || 8080;
app.listen(PORT, _ => {
setInterval(()=> updateDataSource(), 3000);
});
However, when I run this the json at the endpoint doesn't change when I refresh. Basically, I want to have what is happening at this api for my json. https://api.wheretheiss.at/v1/satellites/25544
I've looked into websockets etc but I really just want to do what the ISS api is doing.
Your code executes the statement app.get('/robot-data', (req, res) => {...}) repeatedly, which does not work. Middleware functions like (req, res) => {...} must be set up only once, and if their behaviour shall change over time, they can refer to variables defined outside of them, and these variables can change.
In your case, this would look so:
let dataSource = 0;
const updateDataSource = () => {
const delta = Math.random();
dataSource += delta;
};
app.get('/robot-data', (req, res) => {
res.json([
{"data":dataSource}
]);
});

How to write middleware to modify response in node js

My client given me requirement to encrypt decrypt all request response. So for all encrypted request we wrote down the express middleware to get decrypted request. which is the simple part but while sending response we also have to encrypt response.
One way to do write common function to encrypt data and call that function from all routes. which is time consuming part because we have more than 50+ routes in project. So i was thinking to write middleware like we have done for request which capture response before we send and then we encrypt response after that we send encrypt response to client.
I have searched for solution in google not got any proper solution which worked for me.
routes.js
router.post('/getUserData', verifyApiKey, async function (req, res, next) {
let user = await getUserData();
res.status(200).send(user)
});
middlware.js
class EncryptDecryptRequestResponse {
async encryptResponse(req, res, next) {
console.log('Called encryptResponse');
console.log('res.body', res.body);
res.body = encryptData(res.body)
next();
}
}
App.js
// Middleware to decrypt request
app.use(decryptRequest);
app.use('/', indexRouter);
// Middleware to encrypt response
app.use(encryptResponse);
but the problem is that i am not getting any console.log from middleware. this is the solution which i used
I tried to reproduce the problem you're having with overwriting res.send(), but it works fine for me. You need to make sure to setup the interceptor middleware before you define your routes. Consider this simple example:
const express = require('express');
const app = express();
function encryptResponseInterceptor(req, res, next) {
const originalSend = res.send;
res.send = function () {
arguments[0] = encryptResponse(arguments[0]);
originalSend.apply(res, arguments);
};
next();
}
function encryptResponse(originalData) {
// place your encryption logic here, I'm just adding a string in this example
return originalData + " modified";
}
// fake method that returns resolves after 1s just for testing
function getUserData() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000)
})
}
app.use(encryptResponseInterceptor);
app.get("/test", async (req, res, next) => {
await getUserData();
res.status(200).send("will be changed");
})
app.listen(3000, () => {
console.log("server started on 3000");
});

Getting body content from request in node-mitm

I have the following node-mitm code.
mitm = Mitm();
mitm.on("request", function(req, res) {
const body = req.body; //body is null
})
I feel this has to do with reading node's IncomingMessage events, but I don't know how to do it.
Mitm.js's request handler is just like the one you're used to on Node's side. That is, it doesn't do anything special with req.body and leaves it as a ReadableStream.
You could either get its contents with the classical on("data") pattern:
mitm.on("request", function(req, res) {
req.on("data", function(data) { data == "Hello" })
})
If you want to fake a larger service, I've sometimes used Express to create routes and then pass Express's route handler to Mitm:
var Router = require("express").Router
var router = Router().use(require("body-parser").text())
router.get("/", function(req, res) { req.end() })
mitm.on("request", route.bind(null, router))
function route(router, req, res) {
router(req, res, function(err) {
if (err == null) return
res.writeHead(502)
throw err
})
}
The last example is a summary of the pattern I've also got publicly visible at the Rahvaalgatus open source repository: https://github.com/rahvaalgatus/rahvaalgatus.
Specifically, look at the controller test of https://github.com/rahvaalgatus/rahvaalgatus/blob/6dc91b026d75879cdc552bd2e63f220235b786c0/test/controllers/home_controller_test.js and see the this.router definition at https://github.com/rahvaalgatus/rahvaalgatus/blob/6dc91b026d75879cdc552bd2e63f220235b786c0/test/mitm.js.

Node.js express: Getting HTTP header before it is sent

I am currently making an application in Node.js, with the Express web server plug-in. I want to count the total data sent by the web server.
To do this, I need to get the 'Content-Length' field of an outgoing HTTP header. However, I need to do this right after the data is added.
If I need to alter core Express scripts, can somebody tell me which file this is contained in?
If you just want to count it, you can use a middleware for that:
var totalBytes = 0;
app.use(function(req, res, next) {
res.on('finish', function() {
totalBytes += Number(res.get('content-length') || 0);
});
next();
});
You have to include very early in the middleware stack, before any other middleware whose contents you want to count.
Also, this doesn't count any streamed data, for which no Content-Length header is set.
You could add middleware to monkey-patch the response methods. It’s ugly but better than altering core Express files.
This calculates total body bytes for standard and streamed responses. Place this before any other app.use() directives.
app.use(function(req, res, next) {
res.totalLength = 0;
var realResWrite = res.write;
res.write = function(chunk, encoding) {
res.totalLength += chunk.length;
realResWrite.call(res, chunk, encoding);
};
var realResEnd = res.end;
res.end = function(data, encoding) {
if (data) { res.totalLength += data.length; }
console.log('*** body bytes sent:', res.totalLength);
realResEnd.call(res, data, encoding);
};
next();
});
If you want to count it on each request then you can try below code!!!
var totalByte=0;
app.all('*', function(req,res, next){
res.on('finish', function() {
totalByte = parseInt(res.getHeader('Content-Length'), 10);
if(isNaN(totalByte))
totalByte = 0;
});
next();
});
Please remember totalByte is a global variable here and it added value on each request

sails.js Getting a POST payload with text/plain content type

I'm developing a sails.js (node.js framework based on express) aplication, which is going great but ]I can't solve this detail...
I need to send POST requests cross domain from internet explorer 8 and 9. For that I'm forced to use xDomainRequest object, wich doesn't allow to set a Content type header.
So, when the request gets to the server the content type is "text/plain", which doesn't fire the bodyParser express middleware, so my req.body is an empty object and I can't see the payload I'm sending from the client.
For this I've tried two things with no luck:
First I wanted to set a header to the request in my first middleware, which is of course not available:
req.set("Content-type", "Apli.....
Then, I created a middleware that listens to req.on('data'), like this:
var data = "";
req.on('data', function(chunk){
data += chunk;
});
req.on('end', function(){
req.rawBody = data;
next();
});
But the data event never fires!
Does anyone know how can I access my raw payload, so I can parse it myself.
Thanks!
with newest version of Sails, using express is deprecated.
I needed to use a specific parser to get raw data from Stripe API.
Here is my code, maybe it will help somebody :
bodyParser: function(req, res, next) {
var skipper = require('skipper')();
var rawParser = require("body-parser").raw({type: "*/*"});
// Create and return the middleware function
return function(req, res, next) {
sails.log.debug(req.headers);
if (req.headers && req.headers['stripe-signature']) {
sails.log.info('request using raw parser middleware');
return rawParser(req, res, next);
}
// Otherwise use Skipper to parse the body
sails.log.info('request using skipper middleware');
return skipper(req, res, next);
};
},
I think in this case you're going to have to implement your own body parser, which you can set as sails.config.express.bodyParser or create a config/express.js file like:
var express = require('express');
module.exports.express = {
bodyParser: function(options) {
// Get default body parser from Express
var defaultBodyParser = express.bodyParser(options);
// Get function for consumung raw body, yum.
var getBody = require('raw-body');
return function (req, res, next) {
// If there's no content type, or it's text/plain, parse text
if (!req.headers['content-type'] ||
req.headers['content-type'].match('text/plain')) {
// flag as parsed
req._body = true;
// parse
getBody(req, {
limit: 100000, // something reasonable here
expected: req.headers['content-length']
}, function (err, buf) {
if (err) return next(err);
// Make string from buffer
buf = buf.toString('utf8').trim();
// Set body
req.body = buf.length ? {content: buf} : {}
// Continue
next();
});
}
// Otherwise try the default parsers
else return defaultBodyParser(req, res, next);
};
}
You'll have to npm install express and npm install raw-body. Note that this example uses the default Express body parser as a fallback, not the default Sails body parser which isn't exposed anywhere (and is mostly the same as Express anyway, sans the JSON retry).
maxime's answer is mostly correct. minor modification needed as follows:
bodyParser: function(req, res, next) {
var skipper = require('skipper')();
var rawParser = require("body-parser").raw({type: "*/*"});
// Create and return the middleware function
sails.log.debug(req.headers);
if (req.headers && req.headers['stripe-signature']) {
sails.log.info('request using raw parser middleware');
return rawParser(req, res, next);
}
// Otherwise use Skipper to parse the body
sails.log.info('request using skipper middleware');
return skipper(req, res, next);
},

Resources