Emit event to websocket from http request handler - node.js

Using ws for NodeJS.
const wss = new WebSocket.Server({ port: 8001 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('{"message":"something"}');
});
Connection from the client works great.
var connection = new WebSocket('ws://localhost:8001');
connection.onopen = function(){
console.log("opened");
var msg = "client message";
connection.send(msg);
}
This is logged as expected by the message handler on the NodeJS server.
I am then making a post request from another server to the endpoint relay, set up like this (this part of the NodeJS server running on express):
const app = express();
app.post('/relay', (request, response) =>{
console.log("relay hit.");
wss.emit('message', 'server message');
console.log("test message sent probably");
response.send({ result: 'OK', message: 'good hits' });
});
const server = http.createServer(app);
server.listen(8000, () => console.log('Listening on http://localhost:8000'));
The calling application gets the good response.
The problem is that wss.emit doesn't trigger the on('message') callback. wss.send throws a "not a function" error. I'm imagining this is a scope issue, but I'm not sure, and haven't been able to figure out what scope or object I would actually need if it is.
How does one trigger an event for the WebSocket Server internally? The only place I can even find emit in the docs is here and it's not explained at all.

Related

how to send a message through socket connection from server when a endpoint of server is hit?

i need to send the message through socket connection that is already created when the endpoint of server is hit what is the way to do so, as I cannot send message directly because client from which socket connection is established is on localhost
edit - typo corrected
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function (socket){
console.log('connection');
socket.on('CH01', function (from, msg) {
console.log('MSG', from, ' saying ', msg);
socket.emit('CH01', 'me', 'test from server msg');
});
});
app.post('/getUser', (req, res) => {
return res.status(200).send({
success: 'false',
message: 'title is required'
});
})
http.listen(9000, function () {
console.log('listening on *:9000');
});
Above code is correct but there is a typo error in socket1.emit('CH01', 'me', 'test from server msg').
socket1.emit should be socket.emit
As you are getting socket objects in the socket variable and socket1 is undefined.

HTTP connection gets prematurely terminated in nodejs app hosted on Amazon EC2

I have a REST api hosted on Amazon EC2, which is written with Nodejs (Express).
In a particular REST call, a reply of about 5MB is sent to the client. Before the client completely receives the reply, client prints following error message.
Premature end of Content-Length delimited message body
I added a connection listener in nodejs server like below to check what is going on the server.
var app = express();
var server = http.createServer(app);
var port = app.get('port');
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
server.on('connection', function (socket) {
log.debug('SOCKET OPENED' + JSON.stringify(socket.address()));
socket.setTimeout(300000); //5 minute timeout
socket.on('end', function () {
log.debug('SOCKET END: other end of the socket sends a FIN packet');
});
socket.on('timeout', function () {
log.warn('SOCKET TIMEOUT');
});
socket.on('error', function (error) {
log.warn('SOCKET ERROR: ' + JSON.stringify(error));
});
socket.on('close', function (had_error) {
log.debug('SOCKET CLOSED. IT WAS ERROR: ' + had_error);
});
});
I observed that SOCKET TIMEOUT gets logged in backend. In above code, I have increased the socket timeout to 5 minutes, but it doesn't seem to have any effect.
Earlier I had the REST API hosted in Google compute engine, and I didn't have this problem back then.
What could be the problem here?
Edit: Here is the code of REST API call.
I have following code in my app.js
require('./routes/index')(app);
Following is the index.js of routes directory.
var changeCase = require('change-case');
var express = require('express');
var routes = require('require-dir')();
module.exports = function (app) {
Object.keys(routes).forEach(function (routeName) {
var router = express.Router();
require('./' + routeName)(router);
app.use('/api/' + changeCase.paramCase(routeName), router);
});
};
As it can be seen, it loops through all the js files in the routes directory and registers the file name as the URL path in app.
Here is the code of this particular route for which I face this problem.
module.exports = function (router) {
router.get("/fetch", function (req, res, next) {
itemModel.fetch(req.user.clientId, function (error, items) {
if (error) {
res.status(500).json({error: error});
} else {
res.json(items); //items is a JSON array
}
});
});
}
Setting timeout for the HTTP server resolved the issue.
var server = http.createServer(app);
var port = app.get('port');
server.listen(port);
server.setTimeout(300000, function (socket) {
});

Websockets in NodeJS. Calling WebSocketServer from server-side WebSocket client

I have a NodeJS web app running. I have a WebSocketServer running. I can communicate with my app via a WebSocket connection made from my javascript on the client machine fine. Here's the nodejs server-side code snippet of relevance:
var WebSocket = require('ws');
var WebSocketServer = require('ws').Server;
var server = app.listen(process.env.VCAP_APP_PORT || 3000, function () {
console.log('Server started on port: ' + server.address().port);
});
wss.on('connection', function (ws) {
ws.on('message', function (message, flags) {
if (flags.binary) {
var value1 = message.readDoubleLE(0);
var value2 = message.readInt16LE(8);
var value3 = message.readInt8(10);
//message.writeDoubleLE(8.5,0);
ws.send(message, {
binary: true
});
} else {
if (message == "injest") {
ws.send("requested: " + message);
} else if (message == "something") {
wss.clients[0].send('server side initiated call');
} else {
ws.send("received text: " + message);
}
}
});
// ws.send('something'); // Sent when connection opened.
});
So you see, all very simple.
Here 's my problem. How can I access this WebServer from the NodeJS code of the server-side app itself?
I tried the below:
var ws = new WebSocket("ws://localhost:443");
ws.on('message', function (message) {
wss.clients[0].send('server side initiated call 1 ');
});
ws.on('close', function (code) {
wss.clients[0].send('server side initiated call 2 ');
});
ws.on('error', function (error) {
wss.clients[0].send(error.toString());
});
ws.send("k");
The error function is triggered with ECONNREFUSED 127.0.0.1:443.
I specified no port when I set the server up. If I do then the calls to the server from my client html page fail.
So in brief how can I set up a WebSocket client in NodeJS to access a WebSocketServer created in that app?
Do not use localhost. Substitute the 127.0.0.1 for it.
Instantiate the Server
let WebSocketServer = require("ws").Server;
let ws = new WebSocketServer({port: 9090});
ws.on('connection', function (ws) {
console.log(nHelp.chalk.red.bold('Server WebSocket was connected.'));
// Add the listener for that particular websocket connection instance.
ws.on('message', function (data) {
//code goes here for what you need
});
ws.on('close', function () {
console.log('websocket connection closed!');
});
});
You can open other ports and routes (example for Express) in the same file, or other ports for WS as well btw.
The above is not code for Secure WS server for TLS. that is a bit different.

Socket.io Redis Server Broadcast

I'm missing something really basic here. I have two very standard express processes with socket io. I'm trying to get a server broadcast on one process to broadcast the same message to another servers clients. I have following setup:
var server = require('http').createServer(app);
var socketio = require('socket.io')(server, {
serveClient: (config.env !== 'production'),
path: '/socket.io-client'
});
require('./config/express')(app);
require('./routes')(app);
server.listen(config.port, config.ip, function () {
logger.info('Express server listening on %d, in %s mode', config.port, app.get('env'));
});
var redis = require('socket.io-redis');
var adapter = redis({ host: config.redis.uri, port: config.redis.port });
adapter.pubClient.on('error', function(){
logger.error('Error on pub client');
});
adapter.subClient.on('error', function(){
logger.error('Error on sub client');
});
socketio.adapter(adapter);
require('./config/socketio')(socketio);
Then my socket io file is:
module.exports = function (socketio) {
//listen to other server broadcasts
socketio.on('socket.io#/#', function(data) {
logger.debug('received message from other process');
});
//client connection code
socketio.on('connection', function (socket) {
socket.address = socket.handshake.address !== null ?
socket.handshake.address + ':' + socket.handshake.address.port :
process.env.DOMAIN;
socket.connectedAt = new Date();
// Call onDisconnect.
socket.on('disconnect', function () {
onDisconnect(socket);
logger.debug('[%s] DISCONNECTED', socket.address);
});
// Call onConnect.
onConnect(socketio, socket);
logger.debug('[%s] CONNECTED', socket.address);
});
};
From a different server I'm just calling a standard emit
socketio.emit('message', object);
In terms of debugging, I load up the redis cli and check the channels that are created:
1) "socket.io#/#"
2) "socket.io-sync#request#/#"
I even run SUBSCRIBE "socket.io#/#" in the cli and can see the message I'm trying to get on the other server displayed via the cli.
This seems so basic, how do I properly subscribe to events on the redis channel 'socket.io#/#'?
EDIT: So I figured out that I receive messages on my other process if I do
socketio.sockets.adapter.subClient.on('message', function (channel, message) {
logger.warn('GOT STUFF ');
});
However here I'm just going straight to the redis sub client. The channel and message are just buffers of non usable data. Isn't this suppose to provide a higher level of abstraction?
EDIT 2: I've got it "mostly" working now. Doing this:
socketio.sockets.adapter.subClient.on('message', function (channel, message) {
logger.warn('GOT STUFF ');
socketio.sockets.in(channel).emit(message);
});
Turns on the socket interface will take the channel and message buffers directly. Now to my next issue, instead of doing socketio.emit(message, doc); I want to do this socketio.sockets.in(doc._id).emit('message;, doc);. Basically i only want it to end in clients that are a part of that room. Doesn't quite seem to be working as is...

How to handle io.use middleware to capture errors on client?

I need to check authentication for my socket connection and drop one if they failed. My server and client codes looks like the following;
// server.js
var http = require('http');
var sio = require('socket.io');
var app = require('express')();
var server = require('http').createServer(app).listen(3000, function () {
console.log('Server listening on 3000 port');
});
var io = sio.listen(server);
io.use(function (socket, next) {
return next(new Error('Not Authenticated'));
});
// client.js
var socket = io.connect(window.location.origin);
socket.on('connect', function () {
console.log('socket connected')
});
My problem is I can't capture Not Authenticated error on the client side to handle it and show some popup for example.
Now if Not Authenticated error get executed on server side then socket connection still persists but none of socket.emit('message') don't any work.
How can I capture this error on client side to make some decision?
You should catch error events clientside:
socket.on('error', function(err) {
console.log('The server sent an error', err);
});
2021 update: for Socket.io v3 and up, errors that get passed to middleware callbacks trigger a connect_error event client side, as documented here.

Resources