I am learning Node.js and thought I had a very simple script but no about of tweaking will ever get my script from hanging forever.
Say I have a dumb server running:
$ nc -l 32001 <<EOF
HTTP/1.1 200 OK
Content-Type: text/plain
Works
EOF
And I run the following script:
var http = require('http');
var options = {
hostname: 'localhost',
port: 32001,
method: 'POST',
headers: {'Content-Type': 'text/plain'}
};
var req = http.request(options, res => {
var exitCode = res.statusCode >= 200 && res.statusCode < 300 ? 0 : 1;
res.pipe(process.stdout).on('end', () => process.exit(exitCode));
});
req.on('error', error => console.error(error));
process.stdin.pipe(req)
.on('end', () => console.log('this does trigger'));
But when I perform the following:
$ echo foobar | node my-script.js
It just hangs and never reaches the request callback. I would expect the req stream to end and the http.request callback to be called and then that outputs Works
and finally exits the process.
I've check that the end
event is indeed called from process.stdin.pipe()
and I have attempted to manually end the req
stream in the end
callback. But it just will not end. How do I pipe stdin to an http.request and still have it end the stream?
Alternatively, after typing the input into shell, you can press Ctrl + D to send EOF (end-of-file) to trigger the event handler in process. stdin. on("end", ...)
I understand that since it inherits from stream, one should know that `pause` is how to "close" it, but for the sake of clarity for people who haven't had enough of their coffee or cocoa yet that day I would suggest adding: `process. stdin. pause` will "close" `stdin`.
The stdin property of the process object is a Readable Stream. It uses on() function to listen for the event.
process. stdin. resume() A Readable Stream that points to a standard input stream (stdin). Standard input streams are paused by default, so you must call process.
You are on the right track; you just need to attach your listeners to the proper object events. In the first case (as written, not as executed):
res.pipe(process.stdout).on('end', () => process.exit(exitCode));
What you said: "Pipe the data from response to stdout, and when stdout ends, exit the process."
What you meant: "Pipe the data from response to stdout. When response ends, exit the process." Codified:
res.pipe(process.stdout);
res.on('end', () => process.exit(exitCode));
The point of clarity here is that unless you specifically close process.stdout
, it will never close/end until you exit the program. The response
however, will end when the HTTP interaction is complete.
It goes similar for the second case:
process.stdin.pipe(req)
.on('end', () => console.log('this does trigger'));
What you said: "Pipe the data from stdin to request, and when request ends, write a message."
What you meant: "Pipe the data from stdin to request. When stdin ends, write a message." Codified:
process.stdin.pipe(req);
process.stdin.on('end', () => console.log('this does trigger'));
It's slightly more nuanced here because you could listen for either stdin's end
event or the request's finish
event:
process.stdin.pipe(req).on('finish', () => console.log('Request has finished writing/sending');
process.stdin.on('end', () => console.log('Stdin has no more data'));
For completeness then, your working client back to you, with some mild text modifications for assumed pedagogical clarity:
var http = require('http');
var options = {
hostname: 'localhost',
port: 32001,
method: 'POST',
headers: {'Content-Type': 'text/plain'}
};
var req = http.request(options, res => {
var exitCode = res.statusCode >= 200 && res.statusCode < 300 ? 0 : 1;
res.pipe(process.stdout);
res.on('end', () => {
console.log('Response (IncomingMessage) has no more data; exiting with code:', exitCode);
process.exit(exitCode);
});
});
req.on('error', error => console.error(error));
process.stdin.on('end', () => console.log('Stdin has no more data.'));
process.stdin.pipe(req).on('finish', () => console.log('Request has finished writing/sending'));
Which outputs at command line:
$ echo Some text from stdin | node test.js; echo $?
Stdin has no more data.
Request has finished writing/sending
Works
Response (IncomingMessage) has no more data; exiting with code: 0
0
And at "the server":
$ nc -l 32001 <<EOF
HTTP/1.1 200 OK
Content-Type: text/plain
Works
EOF
POST / HTTP/1.1
Content-Type: text/plain
Host: localhost:32001
Connection: close
Transfer-Encoding: chunked
15
Some text from stdin
0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With