Search code examples
mysqlnode.jsexpressmultipart

node.js: database query + multipart form data locks up


I'm still reasonably new to the whole node thingie, so please forgive me if this is rampantly stupid, but:

If I call query() on a node-mysql client object, I can no longer get the body of a request object from within my web app (express).

my basic POST / PUT pattern requests requiring user auth is:

_db_client = connect_to_db_server();
app.post("/some/path", function (req, res) {
    _db_client.query("SELECT * FROM Users WHERE username = ?",
                     [ http_basic_auth.username ], function (err, results) {
      if (err != null || resp == null) {
         res.send("AUTH FAILURE: sorry, you can't do that", 403);
      } else {
         get_request_body(req, function (body) {
            do_something();
            res.send("yay, I'm happy", 200);
         });
      }
   });
});

Now, the problem is that the get_request_body function simply never comes back. It seems as though it's just "blocked" on teh request object.

Here is a sample program:

var express = require('express'),
    mysql = require('mysql');

var MySQLClient = mysql.Client;
var client = new MySQLClient();

client.user = 'root';
client.password = '';

client.connect(function(err, res) {
    client.query("USE Gazelle", function (qerr, qresp) {
    });
});

var app = express.createServer(  );
app.use(express.bodyParser());

app.post("/test", function (req, res) {
    client.query('SELECT * FROM Users', function (err, results) {
        get_request_body(req, function (body) {
            console.log("wooo");
            console.log(body);
            res.send("cool", 200);
        });
    });
});


app.listen(8080);


function get_request_body (req, callback) {
    if (req.rawBody != undefined) {
        callback(req.rawBody);
    } else {
        var data = '';
        req.setEncoding('binary');

        req.on('data', function(chunk) {
            data += chunk;
        });

        req.on('end', function() {
            callback(data);
        });
    }
}

The trick: It only fails if I'm using multipart form POST data, not regular POST data:

curl -u un:pw -X POST -d ' { "cat" : "meow" }' http://localhost:8080/test

works

curl -u un:pw -X POST -F "[email protected]" http://localhost:8080/test

locks up and never returns. I've seen the same error with connect-form and multipart-js. Kind of stumped here now.

Am i a complete idiot? Is this not the correct way of doing things?


Solution

  • I think by the time you add your end listener to the request object, the end event has already fired. You need to add your IO event listeners to the req object immediately in the main app.post function scope, not in an async callback when your DB query returns. Here's a version with some log statements that show the sequence of events causing this issue. The req.emit('end'); at the bottom is just a hack to prove why the request is locking up. Your end event handler never gets called, but synthesizing a duplicate end event does allow the request to complete (but the body has not been properly parsed).

    var express = require('express');
    var app = express.createServer(  );
    app.use(express.bodyParser());
    
    app.post("/test", function (req, res) {
      req.on('data', function(){
        console.log("data fired but your listener is not yet added");
      });
      req.on('end', function() {
        console.log('end fired but your listener is not yet added');
      });
      setTimeout(function() { //Simulating your async DB query
        console.log("setTimeout finished and fired callback");
            get_request_body(req, function (body) {
                console.log("wooo");
                console.log(body);
                res.send("cool", 200);
            });
          }, 50);
    });
    
    
    app.listen(8081);
    
    
    function get_request_body (req, callback) {
        if (req.rawBody != undefined) {
            console.log('express.bodyParser parsed it already');
            callback(req.rawBody);
        } else {
            console.log('get_request_body going to parse because ' + req.rawBody);
            var data = '';
            req.setEncoding('binary');
    
            req.on('data', function(chunk) {
                console.log('get_request_body got data event');
                data += chunk;
            });
    
            req.on('end', function() {
                console.log('get_request_body parsed it');
                callback(data);
            });
            req.emit('end');//BUGBUG
        }
    }