2013-07-31 3 views
9

В приведенном ниже коде я не могу понять, почему req.pipe (res) не работает, и все же не выдает ошибки. Догадка говорит мне, что это связано с асинхронным поведением nodejs, но это очень простой случай без обратного вызова.NodeJS - Как передать тело запроса без буферизации

Что мне не хватает?

http.createServer(function (req, res) { 

    res.writeHead(200, { 'Content-Type': 'text/plain' }); 

    res.write('Echo service: \nUrl: ' + req.url); 
    res.write('\nHeaders:\n' + JSON.stringify(req.headers, true, 2)); 

    res.write('\nBody:\n'); 

    req.pipe(res); // does not work 

    res.end(); 

}).listen(8000); 

Вот завиток:

➜ ldap-auth-gateway git:(master) ✗ curl -v -X POST --data "test.payload" --header "Cookie: token=12345678" --header "Content-Type:text/plain" localhost:9002 

Вот вывод отладки (видеть, что тело было загружено):

About to connect() to localhost port 9002 (#0) 
    Trying 127.0.0.1... 
    connected 
    Connected to localhost (127.0.0.1) port 9002 (#0) 
    POST/HTTP/1.1 
    User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5 
    Host: localhost:9002 
    Accept: */* 
    Cookie: token=12345678 
    Content-Type:text/plain 
    Content-Length: 243360 
    Expect: 100-continue 

    HTTP/1.1 100 Continue 
    HTTP/1.1 200 OK 
    Content-Type: text/plain 
    Date: Sun, 04 Aug 2013 17:12:39 GMT 
    Connection: keep-alive 
    Transfer-Encoding: chunked 

И сервис отвечает без вторя тело запроса:

Echo service: 
Url:/
Headers: 
{ 
    "user-agent": "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5", 
    "host": "localhost:9002", 
    "accept": "*/*", 
    "cookie": "token=12345678", 
    "content-type": "text/plain", 
    "content-length": "243360", 
    "expect": "100-continue" 
} 

... и последний локон отладка

Body: 
Connection #0 to host localhost left intact 
Closing connection #0 

Кроме того, когда я подчеркиваю тест с большим телом запроса, я получаю сообщение об ошибке EPIPE. Как я могу избежать этого?

- EDIT: через пробную версию и ошибку я получил это, чтобы работать, и это все еще указывает на то, что это проблема времени. Хотя все еще странно, поскольку таймаут заставляет вернуть полезную нагрузку, но продолжительность таймаута не имеет значения. Другими словами, установлен ли тайм-аут на 5 секунд или 500 секунд, полезная нагрузка возвращается обратно в запрос и соединение прекращается.

Вот редактировать:

http.createServer(function (req, res) { 

    try { 
     res.writeHead(200, { 'Content-Type': 'text/plain' }); 
     res.write('Echo service: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); 
     res.write('\nBody:"\n'); 
     req.pipe(res); 
    } catch(ex) { 
     console.log(ex); 
     // how to change response code to error here? since headers have already been written? 
    } finally { 
     setTimeout((function() { 
     res.end(); 
     }), 500000); 
    } 

}).listen(TARGET_SERVER.port); 

?

+0

Обратите внимание, что вы увидите, что запрос сделан на 9002. Это обратный прокси (простой узел-http-прокси до 8000, цель). Удар по цели напрямую дает те же результаты. –

ответ

6

Pipe req to res. Req читается поток и ответ записываемый stream.It должен работать

http.createServer(function (req, res) { 

     res.writeHead(200, { 'Content-Type': 'text/plain' });  
     res.write('Echo service: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); 

     // pipe request body directly into the response body 
     req.pipe(res);  

    }).listen(9002); 
+0

Это будет работать ... * иногда * ... есть что-то асинхронное о вызове канала. Чем быстрее машина, тем чаще это вообще не работает. Я могу заставить его работать, ожидая, прежде чем вызвать res.end(). Щедрость для тех, кто может объяснить, почему я смог исправить эту проблему, добавив сон. (см. мое редактирование внизу вопроса) –

+1

Звонок PIPE заботится о вызове res.end, когда поток req вызывает закрытие/завершение. Нет необходимости снова вызывать res.end после piping. Вы пытаетесь использовать вышеуказанный код без использования res.end()? – Chandu

+0

Причина, по которой ваш код будет работать после добавления сна (не правильная терминология в nodejs :-)), заключается в том, что на самом деле труба набирает время для закрытия потока ответов после того, как поток req ends.res.end() в settimeout просто закрывается закрытый поток . – Chandu

5

Так первый, это выглядит как ваш локон выключен, имя файла посланных данных должно предшествовать @as shown here. В противном случае вы просто отправляете имя файла.

Кроме того, Chandu правильно сказал, что проблема здесь res.end().

Поскольку IO является асинхронным в узле, когда вы выдаете команду .pipe, управление немедленно возвращается в текущий контекст, тогда как труба работает в фоновом режиме. При следующем вызове res.end() вы закрываете поток, preventing any more data to be written.

Решение должно содержать .pipe конец самого потока, which is the default.

Я бы предположил, что синхронизация вступила в игру, потому что на разных машинах и разных размерах данных теоретически завершение асинхронного ввода-вывода (быстрое IO небольшого набора данных) до завершения конечного события в записываемом потоке полностью обрабатывается.

Я бы рекомендовал this blog post для более контекста.

+0

Это имеет смысл. Спасибо за объяснение. –

+0

Верните локон. Это было намеренно, поскольку я делал и то, и другое. Снял @, чтобы проверить только несколько символов против относительно большой полезной нагрузки, содержащейся в файле. –

+0

Так что, если бы я мог понять «смешное» поведение тайм-аута ... труба занимает 500 мс, а затем закрывает соединение. Тайм-аут все еще происходит (даже если он установлен в 500 секунд в будущем), но просто ничего не делает, когда вызывается res.close, поскольку res уже закрыт. Я ожидал, что какая-то ошибка будет сообщена. –

Смежные вопросы