Тело POST в Node.js и некоторая асинхронная проверка состояния - PullRequest
1 голос
/ 09 июня 2010

Я хочу получить тело запроса (POST) от http.ServerRequest, но не прямо при вызове моей функции запроса, а только через некоторое время (запрос Redis).

У меня есть очень простой пример для иллюстрации:

var http = require('http'),
    sys = require('sys');

http.createServer(function (req, res) {
    sys.puts("Got request");
    req.pause();
    setTimeout(function() { // In real code I'm accessing Redis here.
        var body = "";
        req.addListener('data', function (chunk) {
            sys.puts("Got 'data' event");
            body += chunk;
        });
        req.addListener('end', function () {
            sys.puts("Got 'end' event");
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end(body);
        });
        req.resume();
    }, 10);
}).listen(8000);

В этом примере события data и end полностью пропущены, хотя я звоню req.pause() / req.resume(), поэтому запрос "застрял".

Если я закомментирую вызов setTimeout() и все сделаю прямо в функции запроса, все будет работать как положено. Но я хочу запросить Redis, чтобы увидеть, когда запрос действителен. POST - это большие загрузки файлов, и я не хочу тратить время на ожидание неудачной загрузки.

Простой способ сделать это - задержать POST на несколько миллисекунд (в моем случае POST займет не менее нескольких секунд, поэтому такая крошечная задержка довольно незначительна). Более поздний - начать анализ входящих данных, одновременно подтвердить запрос и сбросить соединение, если запрос будет признан недействительным.

Вопрос в том, как бы я это сделал? Я использую Node.js версии 0.1.97-14-g0055dd1, если это имеет значение.

Спасибо за любые предложения.

1 Ответ

3 голосов
/ 09 июня 2010

Не знаю, когда я делаю это правильно, но мне кажется, что это правильно:

var http = require('http'),
    sys = require('sys');

http.createServer(function (req, res) {
    var body = "",
        body_complete = false,
        wait_complete = false;
    sys.debug("Got request");

    // This will be called on each process' completion
    var final_cb = function(err) {
        if (err) throw new Error(err);
        if (body_complete && wait_complete) {
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end(body);
        }
    };

    // Async process one: get POST body
    req.addListener('data', function (chunk) {
        sys.debug("Got 'data' event");
        body += chunk;
    });
    req.addListener('end', function () {
        sys.debug("Got 'end' event");
        body_complete = true;
        final_cb(null)
    });

    // Async process two: wait 10ms
    setTimeout(function() { // In real code I'm accessing Redis here.
        sys.debug("Timeout passed");
        if (false) { // Okay, timeout can't go wrong
            res.writeHead(403);
            res.end('Sorry');
        }
        wait_complete = true;
        final_cb(null);
    }, 10);
}).listen(8000);

Добавление: Когда промежуточное ПО Connect хочет выполнить некоторые асинхронные запросы (т.е.передать управление обратно в цикл обработки событий) оно работает следующим образом:

var pause = utils.pause(req);
fs.readFile(path, function(){
    next();
    pause.resume();
});

Где utils.pause() - это хак для буферизации ожидающих событий .

...