Catching Illegal JSON POST Data in Express? - node.js

When creating a POST request with valid JSON, the bodyParser parses the body of the POST request correctly. However, if I submit an invalid JSON string as the body, I receive the error:
SyntaxError: Unexpected token ILLEGAL
at parse (native)
at IncomingMessage.<anonymous>(/home/.../middleware/bodyParser.js:69:15)
...
So, it appears that the body parser is failing during the parsing of the body. However, I would like to catch this failure and return an error. I'm unsure what I can do to catch it, so any help would be appreciated. Thanks.

This is in the connect.js bodyParser middleware. It DOES do a try/catch and then calls next(err). You should be able to catch this error and handle with additional custom code using the app.error() callback hook. http://expressjs.com/guide.html#error-handling

For some reason, when using express/connect, JSON.parse doesn't throw exceptions, which is why your error handler doesn't fire.
I've logged an issue with express to find out what's going on, but in the meantime you can use this workaround:
express.bodyParser.parse['application/json'] = function(data) {
var result = JSON.parse(data)
if (typeof result != 'object') {
throw new Error('Problems parsing JSON')
}
return result;
}
app.use(express.bodyParser());
update: this issue is not familiar to the author of express, so I am wondering if it's another library causing it. Will have to dismantle my code piece by piece to figure out where this behaviour is being introduced.

Try to put your
app.use(express.bodyParser());
after
app.use(express.errorHandler(...))
solved it for me.
You may also adapt the following code to manage the error
express.bodyParser.parse['application/json'] = function(req, options, fn){
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
try {
req.body = buf.length
? JSON.parse(buf)
: {};
fn();
} catch (err){
fn(new Error('Problems parsing JSON'));
}
});
};

bodyParser must be above app.use(app.router), it doesn't matter relative location to error handler as Perki

Related

q-io/fs in express request handler

I a trying to solve a programming problem in node js that needs to a rad a file.
What is the right way to do this using qio.
Here is my program
var express = require('express')
var qfs = require('q-io/fs')
var q = require('q')
var fs = require('fs')
var app = express()
app.get('/books', function(req, res){
qfs.read(process.argv[3])
// .then( function(buf){res.json(JSON.parse(buf))})
// .done()
.then(res.send).done()
/* .then(null, function(abc, err){
res.json(err)
console.log("Error handler")
res.status(500)
})*/
})
app.listen(process.argv[2])
I know I can read file synchronously, also the following code works
qfs.read(process.argv[3])
.then( function(buf){res.json(JSON.parse(buf))})
.done()
but the main code gives error, I understand this is because the app object has gone out of scope because the request handler has probably already returned.
/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:155
throw e;
^
TypeError: Cannot read property 'req' of undefined
at send (/home/ubuntu/mahesh/node_tries/node_modules/express/lib/response.js:103:17)
at _fulfilled (/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:834:54)
at self.promiseDispatch.done (/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:863:30)
at Promise.promise.promiseDispatch (/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:796:13)
at /home/ubuntu/mahesh/node_tries/node_modules/q/q.js:604:44
at runSingle (/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:137:13)
at flush (/home/ubuntu/mahesh/node_tries/node_modules/q/q.js:125:13)
What is happening here?? At what point does the handler for express.js return when res.end is called?
A little late, but I stumbled upon this problem myself and wanted to help anyone who encounters this in the future.
The line on which your program fails, in response.js, is this one:
var req = this.req;
When res.send() is invoked, the function send is invoked with the context of res (so "this" is res), and this.req is the request object.
However, when you passed the function as a variable to the promise, it lost the context and became just a function. So, when it was invoked, "this" was undefined and this.req became an error. So to fix your problem, replace
.then(res.send).done()
with
.then(books => res.send(books)).done()

How to test error in request with Nock?

I want to test the error in a request return. I'm using nock in my tests, how can I force Nock to provoke an error? I want to achieve 100% test coverage and need to test err branch for that
request('/foo', function(err, res) {
if(err) console.log('boom!');
});
Never enter in the if err branch. Even if hit err is a valid response, my Nock line in test looks like this
nock('http://localhost:3000').get('/foo').reply(400);
edit:
thanks to some comments:
I'm trying to mock an error in the request. From node manual:
https://nodejs.org/api/http.html#http_http_request_options_callback
If any error is encountered during the request (be that with DNS resolution, TCP level errors, or actual HTTP parse errors) an 'error' event is emitted on the returned request object
An error code (e.g. 4xx) doesn't define the err variable. I'm trying to mock exactly that, whatever error that defines the err variable and evaluates to true
Use replyWithError.
From the docs:
nock('http://www.google.com')
.get('/cat-poems')
.replyWithError('something awful happened');
When you initialise a http(s) request with request(url, callback), it returns an event emitter instance (along with some custom properties/methods).
As long as you can get your hands on this object (this might require some refactoring or perhaps it might not even be suitable for you) you can make this emitter to emit an error event, thus firing your callback with err being the error you emitted.
The following code snippet demonstrates this.
'use strict';
// Just importing the module
var request = require('request')
// google is now an event emitter that we can emit from!
, google = request('http://google.com', function (err, res) {
console.log(err) // Guess what this will be...?
})
// In the next tick, make the emitter emit an error event
// which will trigger the above callback with err being
// our Error object.
process.nextTick(function () {
google.emit('error', new Error('test'))
})
EDIT
The problem with this approach is that it, in most situations, requires a bit of refactoring. An alternative approach exploits the fact that Node's native modules are cached and reused across the whole application, thus we can modify the http module and Request will see our modifications. The trick is in monkey-patching the http.request() method and injecting our own bit of logic into it.
The following code snippet demonstrates this.
'use strict';
// Just importing the module
var request = require('request')
, http = require('http')
, httpRequest = http.request
// Monkey-patch the http.request method with
// our implementation
http.request = function (opts, cb) {
console.log('ping');
// Call the original implementation of http.request()
var req = httpRequest(opts, cb)
// In next tick, simulate an error in the http module
process.nextTick(function () {
req.emit('error', new Error('you shall not pass!'))
// Prevent Request from waiting for
// this request to finish
req.removeAllListeners('response')
// Properly close the current request
req.end()
})
// We must return this value to keep it
// consistent with original implementation
return req
}
request('http://google.com', function (err) {
console.log(err) // Guess what this will be...?
})
I suspect that Nock does something similar (replacing methods on the http module) so I recommend that you apply this monkey-patch after you have required (and perhaps also configured?) Nock.
Note that it will be your task to make sure you emit the error only when the correct URL is requested (inspecting the opts object) and to restore the original http.request() implementation so that future tests are not affected by your changes.
Posting an updated answer for using nock with request-promise.
Let's assume that your code calls request-promise like this:
require('request-promise')
.get({
url: 'https://google.com/'
})
.catch(res => {
console.error(res);
});
you can set up nock like this to simulate a 500 error:
nock('https://google.com')
.get('/')
.reply(500, 'FAILED!');
Your catch block would log a StatusCodeError object:
{
name: 'StatusCodeError',
statusCode: 500,
message: '500 - "FAILED!"',
error: 'FAILED!',
options: {...},
response: {
body: 'FAILED!',
...
}
}
Your test can then validate that error object.
Looks like you're looking for an exception on a nock request, this maybe can help you:
var nock = require('nock');
var google = nock('http://google.com')
.get('/')
.reply(200, 'Hello from Google!');
try{
google.done();
}
catch (e) {
console.log('boom! -> ' + e); // pass exception object to error handler
}

No error being thrown for undefined variable in node.js with express

I am running node.js with express. I wrote a node module with methods in it so when you go to
http://bla.com/module_name/method_name
it will run the method.
The method follows the typical style of
exports.method_name(req, res, next);
my main app does something like this:
app.all("*", resSetup, controller, render);
and controller is the thing that will call the method based on the path.
it seems that if there is an undefined variable error in the method, express will just hang there and not throw any error. Nothing will appear in the console log either. I can put a console message right before and after where the error occurs and the before will appear in the log, and after will not.
I can wrap it in a try/catch and get this:
[ReferenceError: blabla is not defined]
but no line numbers or anything.
My guess is that express is somehow preventing the errors from coming up. When I put the error in the function called "controller" that is directly in the route, it shows that error correctly.
It might not matter too much, but here is the code I am working on:
https://github.com/RobKohr/quick-site/blob/master/index.js
Line 189 is where the method call happens.
Building on Ruairi's comment above, I had this same issue with when using 'q' (https://github.com/kriskowal/q) and promises with express - node would hang and no error was generated.
By adding a catch to the end of the promise 'callback' chain I was able to see the error and print it to console etc.
The code ends up looking like:
export function index(req, res) {
//Create the 'promise'
var request = req.body;
var queryJobID = req.query.jobId;
console.log('queryJobID: ' + queryJobID);
var jobStatusPromsie = jobManager.job.getStatus(queryJobID);
Q.all([jobStatusPromsie])
.then(
function (result) {
var responseData = {};
console.log('Job Status Response received');
if (result != null) {
//Without the .catch below an error here will be 'silent'
console.log('jobStatus result: ' + util.inspect(result, false, null));
responseData['status'] = 'OK';
responseData['progress'] = result.progress;
res.json(responseData);
} else {
console.log('jobStatus Error');
responseData['status'] = 'Error';
}
res.json(responseData);
console.log('jobStatus Response data sent');
},
function (error) {
console.log('Error while getting job status:', error);
res.json("Error");
})
.catch(function(err) {
//handle errors
console.log('Promise error while getting job status:', err);
});
}
Express heavily relies on Nodes asynchronous nature. Seeing errors thrown like on line 30 would give me the creeps if I was maintaining this. Try refactoring your code to only use the next(err) pattern.
The reason that you app is hanging is that Express hasn't finished the HTTP response (eg: res.send()). This means you have broken plumbing where an Error has bubbled up the call stack but not redirected into the Express middleware pipeline. Try registering some error middleware to see if it gets called with your error.

Sending HTTP response in node.js

I am trying to send an http response in node to print results in the browser. The simplified source code is down below. Basically, all the variables are defined somewhere in the program, so that shouldn't be problem. When I try to run the script, I keep getting the error:
http.js:783
throw new TypeError('first argument must be a string or Buffer');
TypeError: first argument must be a string or Buffer
So can someone familiar with node.js or javascript syntax let me know what the problem is?
upload = function(req, res) {
var fileInfos = [obj, obj]; //defined as an array of objects
var counter = 0;
counter -= 1;
if (!counter) {
res.end({files: fileInfos}); //files is defined.
}
};
async.forEach(urls, downloadFile, function (err) { //all params defined.
if(err){
console.error("err");
throw err;
}
else{
http.createServer(function(req, res){
upload(req1, res); //req1 defined as an array of objects.
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
}
});
This error is often caused by an attempt to call response.write with the wrong type of parameter. Looking at the documentation it suggests:
response.end([data], [encoding])#
This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. The method, response.end(), MUST be called on each response.
If data is specified, it is equivalent to calling response.write(data, encoding) followed by response.end().
Now response.write( chunk, encoding ) expects the chunk to be a string, so it seems possible that when you are calling res.end({files: fileInfos}) it is unable to write the content of that object as a string.
You can use JSON.stringify() to convert the JavaScript object to string before sending it to the client.
res.end(JSON.stringify({files: fileInfos}));

How to use filesystem's createReadStream with Meteor router(NodeJS)

I need to allow the user of my app to download a file with Meteor. Currently what I do is when the user requests to download a file I enter into a "fileRequests" collection in Mongo a document with the file location and a timestamp of the request and return the ID of the newly created request. When the client gets the new ID it imediately goes to mydomain.com/uploads/:id. I then use something like this to intercept the request before Meteor does:
var connect = Npm.require("connect");
var Fiber = Npm.require("fibers");
var path = Npm.require('path');
var fs = Npm.require("fs");
var mime = Npm.require("mime");
__meteor_bootstrap__.app
.use(connect.query())
.use(connect.bodyParser()) //I add this for file-uploading
.use(function (req, res, next) {
Fiber(function() {
if(req.method == "GET") {
// get the id here, and stream the file using fs.createReadStream();
}
next();
}).run();
});
I check to make sure the file request was made less than 5 seconds ago, and I immediately delete the request document after I've queried it.
This works, and is secure(enough) I think. No one can make a request without being logged in and 5 seconds is a pretty small window for someone to be able to highjack the created request URL but I just don't feel right with my solution. It feels dirty!
So I attempted to use Meteor-Router to accomplish the same thing. That way I can check if they're logged in correctly without doing the 5 second open to the world trickery.
So here's the code I wrote for that:
Meteor.Router.add('/uploads/:id', function(id) {
var path = Npm.require('path');
var fs = Npm.require("fs");
var mime = Npm.require("mime");
var res = this.response;
var file = FileSystem.findOne({ _id: id });
if(typeof file !== "undefined") {
var filename = path.basename(file.filePath);
var filePath = '/var/MeteorDMS/uploads/' + filename;
var stat = fs.statSync(filePath);
res.setHeader('Content-Disposition', 'attachment; filename=' + filename);
res.setHeader('Content-Type', mime.lookup(filePath));
res.setHeader('Content-Length', stat.size);
var filestream = fs.createReadStream(filePath);
filestream.pipe(res);
return;
}
});
This looks great, fits right in with the rest of the code and is easy to read, no hacking involved, BUT! It doesn't work! The browser spins and spins and never quite knows what to do. I have ZERO error messages coming up. I can keep using the app on other tabs. I don't know what it's doing, it never stops "loading". If I restart the server, I get a 0 byte file with all the correct headers, but I don't get the data.
Any help is greatly appreciated!!
EDIT:
After digging around a bit more, I noticed that trying to turn the response object into a JSON object results in a circular structure error.
Now the interesting thing about this is that when I listen to the filestream for the "data" event, and attempt to stringify the response object I don't get that error. But if I attempt to do the same thing in my first solution(listen to "data" and stringify the response) I get the error again.
So using the Meteor-Router solution something is happening to the response object. I also noticed that on the "data" event response.finished is flagged as true.
filestream.on('data', function(data) {
fs.writeFile('/var/MeteorDMS/afterData', JSON.stringify(res));
});
The Meteor router installs a middleware to do the routing. All Connect middleware either MUST call next() (exactly once) to indicate that the response is not yet settled or MUST settle the response by calling res.end() or by piping to the response. It is not allowed to do both.
I studied the source code of the middleware (see below). We see that we can return false to tell the middleware to call next(). This means we declare that this route did not settle the response and we would like to let other middleware do their work.
Or we can return a template name, a text, an array [status, text] or an array [status, headers, text], and the middleware will settle the response on our behalf by calling res.end() using the data we returned.
However, by piping to the response, we already settled the response. The Meteor router should not call next() nor res.end().
We solved the problem by forking the Meteor router and making a small change. We replaced the else in line 87 (after if (output === false)) by:
else if (typeof(output)!="undefined") {
See the commit with sha 8d8fc23d9c in my fork.
This way return; in the route method will tell the router to do nothing. Of course you already settled the response by piping to it.
Source code of the middleware as in the commit with sha f910a090ae:
// hook up the serving
__meteor_bootstrap__.app
.use(connect.query()) // <- XXX: we can probably assume accounts did this
.use(this._config.requestParser(this._config.bodyParser))
.use(function(req, res, next) {
// need to wrap in a fiber in case they do something async
// (e.g. in the database)
if(typeof(Fiber)=="undefined") Fiber = Npm.require('fibers');
Fiber(function() {
var output = Meteor.Router.match(req, res);
if (output === false) {
return next();
} else {
// parse out the various type of response we can have
// array can be
// [content], [status, content], [status, headers, content]
if (_.isArray(output)) {
// copy the array so we aren't actually modifying it!
output = output.slice(0);
if (output.length === 3) {
var headers = output.splice(1, 1)[0];
_.each(headers, function(value, key) {
res.setHeader(key, value);
});
}
if (output.length === 2) {
res.statusCode = output.shift();
}
output = output[0];
}
if (_.isNumber(output)) {
res.statusCode = output;
output = '';
}
return res.end(output);
}
}).run();
});

Resources