How to properly shutdown an ExpressJs server? - javascript

I have an ExpressJs (version 4.X) server, and I need to correctly stop the server.
As some requests can take on long time (1-2 seconds), I must reject new connections and wait the end of all the requests in progress. Kill the server in the middle of some tasks could put the server in an unstable state.
I've tried the following code:
//Start
var app = express();
var server = https.createServer(options, app);
server.listen(3000);
//Stop
process.on('SIGINT', function() {
server.close();
});
However, this code doesn't close keep-alive connections, and some clients can keep the connexion for a long time.
So, how can I properly close all connections ?

You could use some middleware to block new requests if the server is being shut down.
var app = express(),
shuttingDown = false;
app.use(function(req, res, next) {
if(shuttingDown) {
return;
}
next();
});
var server = https.createServer(options, app);
server.listen(3000);
process.on('SIGINT', function() {
shuttingDown = true;
server.close(function(){
process.exit();
});
});

Related

Problem with socket when using it in express

I have a problem with the socket in my program. export the "io" connection and although I can broadcast from any part of the normal program (just by referring to "io"), I cannot use "socket" and broadcast with it unless it is within the same connection of "io." I would like to know how to use socket from any other part of the program, with functions such as "socket.emit ()" or "socket.broadcast.emit" etc. Or how to call this one.
This is my index.js:
const express = require('express');
const restApi = express();
const cors = require('cors');
const server = require('http').createServer(restApi);
const rutas = require('./src/routes/Routes.js');
const io = require('./src/socket/SocketManager.js');
io.attach(server);
restApi.use(express.json());
restApi.use(express.static('public'));
restApi.use(cors());
restApi.options('*', cors());
server.listen(4040, () => console.log('Listening server in port 4040'));
restApi.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
restApi.use('/api',rutas);
my managersocket.js:
const io = require('socket.io')();
io.on('connection', function (socket) {
console.log('NUEVA CONEXION: ',socket.id);
socket.emit('recoger',socket.id);
});
module.exports = io;
This is where I would like to use the socket or at least call the function.
and User.js:
var io = require('../../socket/SocketManager');
var RouterUser = function(){
this.practicafuncion = function (req,res){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
console.log('probando entrar por rest y que salga un mensaje por socket');
res.send({
status:'Aprobado',
msj:'Enviado por rest'
});
//this works correctly. io.sockets.emit('mensaje','**MENSAJESERVIDORPORSOCKET**');
//This is not the problem when using "socket". socket.broadcast.emit('mensaje','**MENSAJESERVIDORPORSOCKET**');
};
}
module.exports = function(){
var instancia = new RouterUser();
return instancia;
};
same is the repository where the whole code is
https://github.com/proxirv/Socket.io-router
socket is a temporary object that exists only for the duration of one particular client connection. And, there can be zillions of them (one or more for each connected client). As such, you don't just export one socket or stuff it in a global and try to use that everywhere.
So, if what you're trying to do is to access the socket.io connection for the user that you just received an http request for, that's a bit more complicated and there are several different ways to approach it.
One approach, I've used before is shown below:
const express = require('express');
const app = express();
const server = app.listen(80);
const io = require('socket.io')(server);
const expsession = require('express-session');
const path = require('path');
// install session middleware
const sessionMiddleware = expsession({
secret: 'random secret',
saveUninitialized: true,
resave: true
});
// run session middleware for regular http connections
app.use(sessionMiddleware);
// run session middleware for socket.io connections
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
// when a socket.io connection connects, put the socket.id into the session
// so it can be accessed from other http requests from that client
io.on('connection', function(socket) {
// socket.handshake.headers
console.log(`socket.io connected: ${socket.id}`);
// save socket.io socket in the session
console.log("session at socket.io connection:\n", socket.request.session);
socket.request.session.socketio = socket.id;
socket.request.session.save();
});
// any arbitrary express route definition
// Note: you can't send socket.io data in a request that loads an HTML page
// because that client hasn't yet established the socket.io connection
// for that page. The socket.io connections will be created after
// the browser loads this page.
app.get("/", function(req, res) {
const session = req.session;
console.log("\n\npage load\n---------------------------\n");
console.log("session:\n", session);
res.sendFile(path.join(__dirname, "socket-io-session.html"));
});
// Upon an API call from a page that already has a socket.io connection,
// respond to the API call and send something to that page's socket.io socket
app.get("/api/test", function(req, res) {
const session = req.session;
io.sockets.connected[session.socketio].emit('show', "sending some data");
console.log("session:\n", session);
console.log("session.socketio:\n", session.socketio);
res.json({greeting: "hello"});
});
Here are the steps in that concept:
Set up express-session for regular http connections. This gives you a place to store stuff that belongs to one particular client (keyed by a cookie)
Set up express-session for the socket.io connection so you can also have access to express-session data in socket.io connections.
When a socket.io connection happens, store the socket.id in the sesssion. This makes the socket.id available for future http requests from that specific client
When some future http request comes in from that client, you can reach into the session, get the socket.id value (which is just a string) and then use that to get the socket for that user and once you have the socket, you can use socket.emit() to send data just to that user.
If you didn't have other reasons for using express-session, you could also just put the socket.id into a cookie all by itself when the socket.io connection connects and then get the socket.id from the cookie during your http request.

How to check if WebSocket.server connection is open or closed?

Im using node js and I cant figure out how to check if my WebSocket.Server connection is Open or Closed, is there any function like socket.readyState?
Im asking because I have a problem, when me + someone else reloads the 192....../xxx in the same moment I get an error Error: listen EADDRINUSE :::3001 and I cant figure out where it blows up..
Also Id like to mention that I DO close the connection but only in one spot, here is my code example;
router.get('/', function(req, res, next) {
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('Client: Im on... /zzz');
})
});
wss.on('error', function error(err) {
console.log(wss.clients);
next(err);
});
//??
//wss.clients.clear();
db.all('SELECT rowid, * FROM ZZZZZZZZ', (err, rows) => {
//idk if its ok
if (err) next(err);
if (rows.length == 0) {
res.render('xxxxx/index', {
AAA: 'empty'
});
} else {
wss.close(function(err) {
if (err) next(err);
console.log('closing websocket at /zzz');
server.close();
setTimeout(function() {
wss = new WebSocket.Server({
port: 3001
});
}, 100);
});
res.render('xxx/index', {
AAA: rows
});
}
});
});
And heres what I have above my router.get
let express = require('express');
let router = express.Router();
let sqlite3 = require('sqlite3').verbose();
let db = new sqlite3.Database('./xx/xx/xx/xxx/xx/xxx.db');
let WebSocket = require('ws');
let wss = new WebSocket.Server({ port: 3001 });
let server = wss._server;
Looks to me like you have the websocket server depend on a specific endpoint. The error you're getting is trying to tie multiple websocket servers to the same port. You only really need one websocket server to handle multiple client connections. I suggest that you would make them separate and have the websocket server run alongside of the express app.
Having endpoints to perform certain actions like closing the websocket is cool, but I would suggest authenticating them if you're doing something like that.
The error you are having is 100% server side, port 3001 is in use. It's trying to start up a websocket server on port 3001 and there is already one running. You can either use different ports, or just use the original websocket server to serve multiple clients.
To actually answer the question you asked, I believe the proper way for doing so would be to implement a heartbeat for your websocket server. That should send after n amount of minutes/seconds/milliseconds (I'd probably go with minutes). It's a good idea to have it bidirectional so your server and clients know when a connection has been dropped.

Javascript Websocket server message broadcast to clients

I am trying to create a dummy websocket server in javascript to send some message to my android client app. The messages will be injected to the server using a html page( javascript ), which will further be passed on to the android client. I am able to connect these two clients (web and android) individually with the server, however, unable to achieve the flow I want, i.e. Web based javascript sends message to running Nodejs websocket server, which broadcast this message to the android client.
This is the code I am using for server side
var WebSocketServer = require("ws").Server;
var http = require("http");
var express = require("express");
var port = 2001;
var app = express();
app.use(express.static(__dirname + "/../"));
app.get('/someGetRequest', function(req, res, next) {
console.log('receiving get request');
});
app.post('/somePostRequest', function(req, res, next) {
console.log('receiving post request');
});
app.listen(80); //port 80 need to run as root
console.log("app listening on %d ", 80);
var server = http.createServer(app);
server.listen(port);
console.log("http server listening on %d", port);
var userId;
var wss = new WebSocketServer({
server: server
});
wss.on("connection", function(ws) {
console.info("websocket connection open");
var timestamp = new Date().getTime();
userId = timestamp;
ws.send(JSON.stringify({
msgType: "onOpenConnection",
msg: {
connectionId: timestamp
}
}));
ws.on("message", function(data, flags) {
console.log("websocket received a message");
var clientMsg = data;
ws.send(JSON.stringify({
msg: {
connectionId: userId
}
}));
console.log(clientMsg);
});
ws.on("close", function() {
console.log("websocket connection close");
});
});
console.log("websocket server created");
WebClient:
< script type = "text/javascript" >
var websocketURL = 'ws://localhost:2001/';
function startWebSocket() {
try {
ws = new WebSocket(websocketURL);
} catch (e) {
alert("Unable to connect to webserver")
}
}
function sendMessage(text) {
var message = 'Test message from webclient: ' + text;
ws.send(message);
alert(message);
}
startWebSocket(); < /script>
<button onclick="sendMessage('From button1')">Button 1</button > < br >
< button onclick = "sendMessage('From button2')" > Button 2 < /button><br>
Android client:
Just using socket class and its method to do further processing
s = new Socket(HOST, TCP_PORT);
Please let me know how I can pass the message generated from the web client to my android client via websocket server.
I am using nodejs for websocket server implementation.
Thanks
From https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76
The protocol consists of an initial handshake followed by basic message framing, layered over TCP.
So, just opening a Socket on the client side isn't enough. Maybe this will help https://stackoverflow.com/a/4292671
Also take a look at http:// www.elabs.se/blog/66-using-websockets-in-native-ios-and-android-apps chapter Android client
If you really want to implement the WebSocket stuff yourself, take a look at https://stackoverflow.com/a/8125509 and https://www.rfc-editor.org/rfc/rfc6455
I guess I misread your question. Since the connection between the clients and the server already works, you just need to forward the messages.
First, you need to identify the WebSocket client type (Android or Web). Meaning, you immediately send a message what type of client the newly opened WebSocket connection is and store the WebSocket (ws) for that type in the server. Since you have identified and stored each WebSocket connection, you just forward the message to the other type.
For a more specific answer, I need more information.
Should the communication be bidirectional?
Should there be multiple web and Android connections at the same time?

Node.js / How handle connection leak?

I wrote a server application in Node.js.
When I do lsof -p 10893 (10893 is the pid of my Node.js application), I get this:
node 10893 root 19u IPv4 (num) 0t0 TCP ip-X.X.X.X->ip-X-X-X.X:45403 (ESTABLISHED)
node 10893 root 19u IPv4 (num) 0t0 TCP ip-X.X.X.X->ip-X-X-X.X:58416(ESTABLISHED)
This connections are hold and never closed, just when I restart my application.
I have the following code in node.js, which as I understand, should handle this issue:
app.use(function(err, req, res, next) {
res.setTimeout(120000);
res.status(500).send('Unexpected error');
});
But these connection are still appears, even after 120000 ms.
How can I fix it?
Pretty sure this is a Keep-Alive issue. See this question and it's answer for details, but the gist is:
res.set("Connection", "close");
If you want that behavior for all your connections, then add it as a middleware above your routes/router:
app.use(function (req, res, next) {
res.set("Connection", "close");
next();
});
Also, if you want to time out the socket itself, it's a bit trickier:
var http = require('http');
var express = require('express');
var app = express();
var svr = http.createServer(app);
svr.on('connection', function (socket) {
socket.setTimeout(120000, function () {
this.end();
});
});
// other middleware / routes here
app.use(function(err, req, res, next) {
res.status(500).send('Unexpected error');
});
svr.listen(PORT);
EDIT:
Just noticed that your function is an error handler middleware (due to the presence of the err parameter). This means that the timeout would only have been set if there was an error, and not if the request completed successfully.

Why is this code blocking another requests since I am using only async functions?

I can't figure out why this code is blocking/preventing the server to fulfill concurrent requests, since I am only using async code. Can someone throw some light?
I am using mongoose streaming feature! The following is an express route.
function getChart(req, res, next) {
var stream = Model.find({}).stream({
transform: JSON.stringify
});
stream.on('data', function(record) {
res.write(record);
});
stream.on('end', function() {
res.end();
});
}
The problem can be verified when requesting around 10000 records from database, which takes around 10 seconds. During this time I open another tab and make a request to any other route, say /home, the content of /home only arrives immediately after the first request finishes!
EDIT
I have just made some basic tests (even set mongoose connection pool to a higher value) and now I know that the problem is not with mongoose, but with nodejs itself or perhaps with express. I created the following test app:
var express = require('express'),
http = require('http'),
app = express();
app.get('/', function(req, res, next) {
console.log('> Request received!');
setTimeout(function() {
console.log('> Request sent!');
res.send(200);
}, 5000);
});
app.listen(8000, function() {
console.log(' > APP LISTENING');
console.log(' > maxSockets: ' + http.globalAgent.maxSockets);
});
Within the 5 seconds of the first request I open more 3 tabs on chrome and make more requests, message > Request received! is only shown after first ones are already sent.
I get 5 as the number of maxSockets
Can anyone help me?

Categories

Resources