2013-07-31 7 views
9

Nel codice seguente non riesco a capire perché req.pipe (res) non funzioni, e tuttavia non genera neanche un errore. Un'intuizione mi dice che è dovuto al comportamento asynch del nodo, ma questo è un caso molto semplice senza callback.NodeJS - Come eseguire lo streaming del corpo della richiesta senza buffering

Cosa mi manca?

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); 

Ecco il riccio:

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

ecco l'output di debug (vedere che il corpo è stato caricato):

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 

E il servizio risponde senza riecheggiando il corpo della richiesta:

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" 
} 

... e ultimo ricciolo debug è

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

Inoltre, quando sottolineo prova con grande corpo della richiesta, ottengo un errore EPIPE. Come posso evitare questo?

- EDIT: Grazie a tentativi ed errori, ho fatto in modo che tutto funzionasse, e continua a essere un problema di temporizzazione. Anche se è ancora strano, poiché il timeout fa sì che il carico utile venga restituito, ma la durata del timeout non viene presa in considerazione. In altre parole, se imposto il timeout a 5 secondi o 500 secondi, il carico utile viene correttamente reindirizzato alla richiesta e la connessione viene interrotta.

Ecco la modifica:

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

Nota che verrà visualizzata una richiesta su 9002. Questo è un proxy inverso (semplice nodo-http-proxy su 8000, il target). Colpire direttamente il bersaglio produce gli stessi risultati. –

risposta

6

Req a res. Req è flusso leggibile e la risposta è uno stream.It scrivibile dovrebbe funzionare

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

Funzionerà ... * a volte * ... c'è qualcosa di asincrono sulla chiamata pipe. Più veloce è la macchina, più spesso questo non funziona affatto. Sono in grado di farlo funzionare aspettando prima di chiamare res.end(). La taglia è per chi può spiegare perché sono stato in grado di risolvere questo problema aggiungendo un sonno. (vedere la mia modifica in fondo alla domanda) –

+1

La chiamata PIPE si occupa di chiamare il res.end quando il flusso req chiama close/end. Non è necessario richiamare nuovamente res.end dopo il piping. Si è tentato il codice sopra senza utilizzare res.end()? – Chandu

+0

Il motivo per cui il codice deve funzionare dopo l'aggiunta di sleep (non una terminologia corretta in nodejs :-)) è che la pipe sta effettivamente ricevendo il tempo per chiudere il flusso di risposta dopo req streaming ends.res.end() in settimeout sta semplicemente chiudendo il flusso chiuso . – Chandu

5

Quindi, prima, sembra che la vostra arricciatura è spento, il nome del file dei dati pubblicati dovrebbe essere preceduta da una @as shown here. Dovresti semplicemente postare il nome del file altrimenti.

A parte questo, Chandu ha ragione nel dire che la chiamata a res.end() è il problema qui.

Dato che IO è asincrono nel nodo, quando si emette il comando .pipe, il controllo viene immediatamente restituito al contesto corrente, mentre la pipa funziona in background. Alla successiva chiamata res.end(), si chiude lo stream, preventing any more data to be written.

La soluzione qui è di lasciare .pipe terminare lo stream stesso, which is the default.

Immagino che la temporizzazione sia entrata in gioco perché su macchine diverse e con dimensioni di dati differenti, l'I/O asincrono potrebbe teoricamente finire (I/O veloce di piccolo set di dati) prima che l'evento finale sul flusso scrivibile sia completamente elaborato.

Suggerirei this blog post per un altro contesto.

+0

Questo ha perfettamente senso. Grazie per la spiegazione. –

+0

Re il post di curl. È stato intenzionale mentre stavo facendo entrambe le cose .. ho tolto la @ per testare solo alcuni caratteri rispetto al payload relativamente grande contenuto nel file. –

+0

Quindi, se potessi dare un senso al "divertente" comportamento di timeout ... pipe impiega dire 500 ms e poi chiude la connessione. Il timeout si verifica ancora (anche se impostato su 500 secondi nel futuro), ma semplicemente non fa nulla quando viene chiamato res.close, poiché res è già chiuso. Mi aspetterei che venisse segnalato qualche tipo di errore. –