Sending http request on node process exit - node.js

I have written sample node application to handle error data such as database connection error, port conflict error, process uncaught exception. When error occurrs an http request is made to process the error. In such case when node process exist abnormally, I am able to handle exist in process.on('exit') function but I am not able to send http request, the process is exiting quickly.
Can any one suggest how to send http request and get the response on Node.js before process exit. Below is the sample code for sending http request on process exists
var http = require('http');
var errorData=null;
var sendErrorReport = function(data,callback){
var options = {
host : connection.host,
path : "/api/errorReport",
port : connection.port,
method : 'POST',
timeout: connection.timeInterval,
headers:{
'Content-Type':'application/json',
'Content-Length': Buffer.byteLength(data)}
}
var request = http.request(options,function(response){
callback(response);
});
request.on('error',function(err){
console.log("On Error");
callback(err);
});
request.on('timeout', function(err){console.log("On Timeout");
callback(err);});
request.write(data);
request.end();
}
process.on('uncaughtException', function ( err ) {
errorData = err;
});
process.on('exit',function(code){
sendErrorReport(errorData,function(err,res){
console.log(res);
});
})

In process.on('exit', [fn]) you cannot do any asynchronous action as stated in the docs. However, this is an anti-pattern also discovered in many libraries.
You need to rely on process.on('uncaughtException', [fn]) or any signal handlers like SIGTERM.

Hit on same problem,
according to the doc https://nodejs.org/api/process.html#process_event_exit
"Event: 'exit' Listener functions must only perform synchronous operations." make it hard to send request to other system.
A possible workaround method is to to execute another script/cmd, eg.
import exec from 'child_process'
mocha.run(failures => {
process.on('exit', () => {
exec.execSync(some cmd, function (error, stderr) {
if (error) {
throw (error);
}
}
}

Related

NodeJS http: Wait for another response in a request listener before returning

Basically, it is a web proxy. In a request listener, I create another http request, read its response and pass it to the response. But I need to wait for the other request to end. Please see the function structure below.
I have searched for existing answers, but the existing answers were using await or Promise etc, which I think, do not fit my structure. I think I need something like the ManualResetEvent of C#. Right after sending the request (POS 1), I need to mark the thread to block so that before finishing the response (POS 3) it can be blocked. When the request's response ends (POS 2), I need to mark the thread to continue. How do I do this in TypeScript/NodeJS?
function onRequest(req: http.IncomingMessage, res: http.ServerResponse)
{
....
if(arguments are valid)
{
... prepare options for request
try
{
const remoteReq = https.request(options, (remoteRes) =>
{
remoteRes.on('data', (d) =>
{
... pass it to the response.
});
remoteRes.on('end', (d) =>
{
//POS 2: resetevent.Set() allow thread to proceed
});
});
remoteReq.end();
//POS 1:resetevent.Reset() block thread
}
}
catch
{
}
}
else
{
}
//POS 3: resetevent.WaitOne() wait for the remote response to end.
res.end("");
}
You don't "wait" in nodejs. You register a listener for an event and you finish the request when that listener is called. You move the res.end() and and res.write() into the listener that tells you you're done or you have data. Nodejs is a non-blocking, event driven, asynchronous I/O model. You have to program it that way.
You don't show enough of your real code for us to write something that would actually work, but the general scheme would be like this where you listen for the data, end and error events on the http request you sent and you handle the original request in those event handlers. There is no crying in baseball. There is no "waiting" in nodejs:
function onRequest(req: http.IncomingMessage, res: http.ServerResponse) {
....
if(arguments are valid) {
...prepare options
try {
const remoteReq = https.request(options, (remoteRes) => {
remoteRes.on('data', (d) => {
...pass it to the response.
res.write(...)
});
remoteRes.on('end', (d) => {
res.end(...);
});
});
remoteReq.on('error', err => {
console.log(err);
if (res.headersSent) {
// not much to do other than just hangup
res.end();
} else {
res.statusCode = 500;
res.end();
}
});
remoteReq.end();
} catch (e) {
// highly unlikely anything gets here because this is all
// asynchronous. Instead, you need to listen for the 'error' event.
}
}
else {
// need to send some sort of response here, probably a 400 status
// if arguments are invalid
}
}

How to send response back to client using socket.io?

I have socket.emit call from client to server in response i want to have filename to the client that is not happening with below code not sure what is implemented wrong any idea, I do not see any error. How can i get response fro server using socket.emit ?
client.js
socket.emit('startRecording',function (response) {
console.log('start recording emit response',response);
});
server.js
socket.on('startRecording',function () {
var response;
logsRecording(function (filename) {
response = filename;
return response;
//socket.emit('filename',filename);
});
To acknowledge the message, your handler for the startRecording event needs to accept an acknowledgement callback as a parameter. You can then call that with your desired data. See Sending and getting data (acknowledgements)
socket.on('startRecording',function (socket, ackFn) {
var response;
logsRecording(function (filename) {
ackFn(filename);
});
});
Alternatively, you could add a listener for that filename event you have commented out, in the client.js:
socket.emit('startRecording');
socket.on('filename', function(filename) {
console.log('Filename received: ' + filename);
});
It might be helpful to run through Get Started: Chat application starting at the heading "Integrating Socket.IO" to get a more general understanding of Websockets.
Your server code should look like this:
socket.on('startRecording',function (callbackFn) {
var response;
logsRecording(function (filename) {
callbackFn(filename);
});
If you want to pass in data from your client:
socket.emit('startRecording', {someData: 'value'}, function (response) {
then server will be :
socket.on('startRecording',function (dataFromClient, callbackFn) {
Thank you for this helpfull hint
Here an 2020 "call" example call possible to use with moleculer microservices with four arguments:
The server responds in the callback function with two arguments err and res for the angular promise.
Angular 9 socket io
protected call(method: string, param?: any) {
try {
return new Promise((resolve, reject) => {
this.socket.emit("call", method, param, (err: any, res: unknown) => {
console.log(res);
if (err) { return reject(err); }
return resolve(res);
});
});
} catch (err) {
console.error(err);
}
}
Socket IO server response
socket.on('call', function(method, param, callbackFn){ // call method, param,
console.log(param);
switch (method) {
case "test":
console.log("test detected");
callbackFn(null , {name:"test",email:"test"});
break;
}
});

Cancelling piping files with request module throw ESOCKETTIMEDOUT

I'm doing a simple request with express & the request module and piping its response to res:
var pipe = request.get({
url: 'url-to-file'
});
pipe.on('response', function (response) {
req.on('close', function () {
// these methods won't work:
// pipe.unpipe();
// pipe.end();
// pipe.finish();
// pipe.close();
});
pipe.on('end', function () {
// this will never fire if I cancel the request
});
res.writeHead(response.statusCode, response.headers);
pipe.pipe(res);
});
This works like a charm, except if I cancel downloads. The end event won't fire and some seconds later, an ESOCKETTIMEDOUT error gets thrown.
How can I close the pipe? These node docs claim that I can call .unpipe, but all node gives is pipe.unpipe is not a funtion (tested with v0.12.7 & 4.2.2 & & 5.0.0), probably because it's not an original node stream.
I also tried using events like end, finish and close, but neither of them work.
request.get() does not return a pure node.js stream but rather a Request object which inherits from the native Stream class and adds some custom methods. The method you are looking for is Request#abort() (Source link).
Your code example would look like the following:
var pipe = request.get({
url: 'url-to-file'
});
pipe.on('response', function (response) {
req.on('close', function () {
pipe.abort();
});
pipe.on('end', function () {
// this will never fire if I cancel the request
});
res.writeHead(response.statusCode, response.headers);
pipe.pipe(res);
});

Why is server.close callback never invoked?

I am trying to 'gracefully' close a net.Server instance (created with app.listen()) if an un-handled error is thrown. Server creation occurs in my bin/www script. All error handling and routing middleware configuration is defined in index.js.
In my application configuration module (index.js) I have error handling middleware that checks that each error is handled. If the error is not handled then a 'close' event is emitted.
Note: Each req and res is wrapped in a domain. I am using express-domain-middleware middleware module to listen for error events on each req domain and route the error to my error handling. I only mention this in case it might be the culprit.
The 'close_server' event handler should:
Close the server so new connections are not accepted.
Close the current process once all open connections have completed.
If after 10 seconds the server has not closed, force the process to close.
The optional callback provided to server.close() never seems to be invoked and I'm not sure why. To test this I am making a single request which throws an error. The process is only closed after the timer expires (10 seconds has elapsed).
Could there be something holding open a connection in the server? Why is the server.close() callback never called?
Thanks!
Update
I was using Chrome to make a request to the server. It appears that the browser is holding open a connection. If I make the request using curl it works as expected.
See this issue
index.js
app.use(function (err, req, res, next) {
if (typeof err.statusCode !== 'undefined') {
if (err.statusCode >= 500) {
Logger.error({error: err});
return next(err);
} else {
Logger.warn({warn: err});
return next(err);
}
} else {
//The error is un-handled and the server needs to go bye, bye
var unhandledError = new UnhandledError(util.format('%s:%s', req.method, req.originalUrl), 'Server shutting down!', err.stack, 500);
Logger.fatal({fatal: unhandledError});
res.status(500).send('Server Error');
app.emit('close_server', unhandledError);
}
});
bin/www
#!/usr/bin/env node
var app = require('../index');
var port = config.applicationPort;
app.set('port', port);
var server = app.listen(app.get('port'));
/*
* Wait for open connections to complete and shut server down.
* After 10 seconds force process to close.
* */
app.on('close_server', function () {
server.close(function () {
console.log('Server Closed.');
process.exit()
});
setTimeout(function () {
console.log('Force Close.');
process.exit()
}, 10 * 1000);
});
server.close() does not close open client connections, it only stops accepting new connections.
So most likely Chrome is sending a request with Connection: keep-alive which means the connection stays open for some time for efficiency reasons (to be able to make multiple requests on the same connection), whereas curl is probably using Connection: close where the connection is severed immediately after the server's response.
As #mscdex mentioned server.close never runs its callback when the browser sends the request Connection: keep-alive, because server.close only stops the server from accepting new connections.
Node v18.2.0 introduced server.closeAllConnections() and server.closeIdleConnections()
server.closeAllConnections() closes all connections connected to the server, and server.closeIdleConnections() closes all connections connected to the server but only the ones which are not sending a request or waiting for a response.
Before Node v18.2.0 I tackled this problem by waiting 5 seconds for the server to shutdown, after which it would force exit.
The following code contemplates both situations
process.on('SIGINT', gracefulShutdown)
process.on('SIGTERM', gracefulShutdown)
function gracefulShutdown (signal) {
if (signal) {
console.log(`\nReceived signal ${signal}`)
}
console.log('Gracefully closing http server')
// closeAllConnections() is only available after Node v18.02
if (server.closeAllConnections) server.closeAllConnections()
else setTimeout(() => process.exit(0), 5000)
try {
server.close(function (err) {
if (err) {
console.error(err)
process.exit(1)
} else {
console.log('http server closed successfully. Exiting!')
process.exit(0)
}
})
} catch (err) {
console.error('There was an error', err)
setTimeout(() => process.exit(1), 500)
}
}

nodejs socket hang up error in nested callback

I try to put many callbacks in a callback, but the program will shut down after the return of several success requests and post "socket hang up" error. I try to collect data from response and output them at once, can someone tell me which part goes wrong...
By the way, I hide the details of request method, I promise the request method works on http call.
http.request(options1,function(data){
var condition=data.length;
var result=[];
data.foreach(item,function(data){
http.request(options2, function(data){
if(data) result.push(data);
condition--;
if(condition<=0) console.log(result);
}
});
});
for my http.request method
var request=function(options,callback){
http.request(options,function(res){
var body;
res.on('data',function(chunk){
body+=chunk;
});
res.on('end',function(){
callback(JSON.parse(body));
});
request.end();
};
That's not the correct usage of http.request().
The http.request() callback is passed an IncomingMessage object, not buffered response data.
EDIT: Your custom request() should look something like this, although you should handle errors too. Note the proper placement of request.end() and initializing var body = '':
function request(options, callback) {
http.request(options,function(res) {
var body = '';
res.setEncoding('utf8');
res.on('data',function(chunk) {
body += chunk;
}).on('end',function() {
callback(JSON.parse(body));
});
}).end();
}
You're missing .end() for your requests so that node.js knows you are ready to actually send the HTTP request: http.request(..., ....).end();. This is the cause of your particular error... the server hangs up the connection because it got tired of waiting for your request after the TCP connection was opened.

Resources