Node TLSSocket emit function does not work - node.js

I am setting up a TLS IPC server and client, using the following code:
//server.ts
import fs from "fs";
const tls = require("tls");
var serverSocket = undefined;
var tlsServer = tls.createServer(
{
key: fs.readFileSync(`${process.cwd()}/cert/server.key`),
cert: fs.readFileSync(`${process.cwd()}/cert/server.pub`),
},
function (socket) {
serverSocket = socket;
// Print the data that we received
socket.on("data", function (data) {
console.log(
"Received: %s [it is %d bytes long]",
data.toString().replace(/(\n)/gm, ""),
data.length
);
});
// Let us know when the transmission is over
socket.on("end", function () {
console.log("EOT (End Of Transmission)");
});
}
);
tlsServer.on("secureConnection", (socket) => {
socket.emit("getInfo");
});
tlsServer.listen(8000, "localhost");
//client.ts
const tls = require("tls");
var client = tls.connect(
8000,
"localhost",
{
rejectUnauthorized: false,
},
function () {
// Send a friendly message
client.write("I am the client sending you a message.");
}
);
client.on("getInfo", function () {
console.log("client side -- Sending info!");
client.write("Second message!");
});
However, it seems that the line socket.emit("getInfo"); on server.ts does not work, even though it's a TLSSocket, which should inherrit the emit function. everything works fine until the final step where the emit should send the event to the client and the client should respond.
Does anyone know how to properly send event via a TLSSocket?

Related

How to write a code to simulate the same as echo server from Websocket.org

i need to create a server based on the same logic as the echo server on websocket.org.
The difference is that the server will not echo exactly what the user typed, but will return a response that varies with the received string.
I've been looking for more than a week and I only found n examples that only explain the client, some of which include an example of a server that does NOT answer wss:// calls.
Everything I found responds only to calls http://192.168.1.1:3000 or https://192.168.1.1:3000, but when I use wss: //192.168.1.1:3000, Firefox says, it was unable to establish a connection with the wss server: //192.168.1.1:3000/.
The client works when I call wss: //echo.websocket.org/.
Where do I find the code for an echo server that responds to wss?
Above I list the code I found on github. The nodejs server code I'm trying is:
const http = require('http');
const ws = require('ws');
const wss = new ws.Server({noServer: true});
console.log("Script has started");
if (!module.parent) {
console.log("Listening on port 3000");
http.createServer(accept).listen(3000);
} else {
exports.accept = accept;
}
function accept(req, res) {
console.log("accept event started");
// all incoming requests must be websockets
if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {
console.log("This is no websocket!!! Return");
res.end();
return;
}
// can be Connection: keep-alive, Upgrade
if (!req.headers.connection.match(/\bupgrade\b/i)) {
res.end();
return;
}
console.log("Handle upgrade");
wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect);
}
function onConnect(ws) {
console.log("onConnect event started");
ws.on('message', function (message) {
let name = message.match(/([\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]+)$/gu) || "Guest";
console.log("Send Hello");
ws.send(`Hello from server, ${name}!`);
setTimeout(() => ws.close(1000, "Bye!"), 5000);
});
}
This code, responds "This is no websocket!!! Return" if I call the server with http://192.168.1.1: 3000/
Thank you in advance.
After a while I found the solution:
// Basic Websocket (ws) echo server
const WebSocket = require('ws');
const ws_server = new WebSocket.Server({ port: 81 });
ws_server.on('connection', function connection(ws) {
console.log("A client connected");
ws.on('message', function incoming(message) {
ws.send('Hi, you sent me ' + message);
});
});
This works for my tests.

Why does Socket.io emit empty events at random intervals?

I have an application that runs on Nodejs and Socket.io
The events 2, 3 seen in the image are empty events that socket.io server keeps emitting at random intervals. What's more annoying is that the time delay between the empty events is crazy, as seen in the picture there was a 19 seconds time delay between 2 and 3. And due to this, the message:delete event that was emitted by socket.io client which was supposed to immediately emit message:deleted, got delayed by 19 seconds!!
What's worse is that while these empty events are emitted, all other socket events originated from socket client are not emitted and they are stuck in limbo until the empty events are out of the way.
I suspect these empty events are socket ping and pong events. Does anyone have any idea why this happens? And how we could prevent this? Or maybe even set a priority for client socket.emit so that regardless of these empty events, the client emitted socket event takes precedence and is fired immediately?
Edit #1
Despite adding { transports : ['websocket'] } the delay was still well over 17 seconds as you can see in this image
Edit #2 - The socket.emit code
The client is made up of a lot of jQuery + vanilla JS + Angular 1.7. The client socket is a factory wrapper
app.factory('socket',['$rootScope', function ($rootScope) {
var socket = io.connect('',{
path: '/socket.io',
transports: ['websocket']
});
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
},
disconnect: function(close){
socket.disconnect(close);
},
removeAllListeners: function (eventName, callback) {
socket.removeAllListeners(eventName, function() {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
}
};
}])
And on the controller the `socket.emit' code is as follows
$scope.deleteMessage = function (message) {
socket.emit('message:delete', {
id: message
});
};
I use express + Nodejs so here's the code for initializing the server socket
Server = require('socket.io'),
io = new Server({
path: `/socket.io`,
transports: ['websocket']
});
And the server side socket code listens to the client emit as follows
io.on("connection", async function (socket) {
socket.on("message:delete", function (payload) {
if (props.connectedUsers[socket.userId].info.isAdmin) {
console.log("Received Message Deletion by",
props.connectedUsers[socket.userId].info.name);
if (payload.id == "SYS_MOTD") return;
await Message.remove({
_id: new db.Types.ObjectId(payload.id)
}).exec(function (err, message) {
if (!err) {
console.log("Emit message:deleted");
io.sockets.emit("message:deleted", {
id: payload.id
});
}
});
}
});
});
I think it's normal, according to the Socket.IO documentation:
By default, a long-polling connection is established first, then
upgraded to “better” transports (like WebSocket).
What you can do is to set the connection to use websocket only, it should stop the long-polling and you will receive the messages immediately:
socket = io.connect({transports: ['websocket']});
Update
No need to set the path to /socket.io, it's the default value.
On Client side you can also use:
const socket = io([URL], {
transports: ['websocket']
});
I've noticed you are using async/await with a callback, I would either use async/await or a callback on your server side code, for example:
try {
await Message.remove({
_id: new db.Types.ObjectId(payload.id)
})
console.log("Emit message:deleted");
io.sockets.emit("message:deleted", {
id: payload.id
});
} catch(ex) {
console.log(ex.message);
}

socket io and mqtt nodejs duplicate entry

I am using mqttjs and socketio on my nodejs backend.
I am using angular as my frontend framework.
On my frontend there are 3 routes.
All requires socket connection for real time data.
So on ngOnInit i run client side socket io connection code and on ngOnDestroy I will run socket disconnect as well.
And in my server side code (index.js) there are mainly 3 actions that is happening.
const io = require('socket.io')(server)
mqtt.createConnection();
mqtt.mqttSubscriptions(io);
mqtt.mqttMessages(io);
These are the mqtt methods:
const createConnection = () => {
let options = {
protocol: 'mqtt',
clientId: process.env.MQTT_CLIENT_ID,
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
};
client = mqtt.connect(process.env.MQTT_HOST, options);
client.on('connect', function() {
winston.info('MQTT connected');
});
client.on('error', function(err) {
winston.error(err);
});
};
const mqttSubscriptions = io => {
winston.info(`Socket connected.`);
client.subscribe([TOPICS.DATA], function(error, granted) {
if (error) {
winston.error(error);
}
winston.info('Topics: ', granted);
});
};
const mqttMessages = io => {
io.sockets.on('connection', socket => {
winston.info(`Socket connected.`);
client.on('message', function(topic, message) {
let payload = JSON.parse(message.toString());
winston.info(topic);
winston.info(payload.id);
switch (topic) {
case TOPICS.DATA:
dataController.storeData(payload, io);
break;
default:
winston.error('Wrong topic');
break;
}
});
});
};
And on the datacontroller I am running
socket.emit()
My problem is everytime I navigate to a route and come back the dataController.storeData is called multiple times.
That is when I am at route A, and then navigate to route B and then back to A and then to C, the data is multiplied that many times of my route navigation. (In this case 4 times.)
I found that it is socket io and mqtt connection problem, but I don't know how to solve, since I am new to both of these.
Any help?

Node.js - Persistent connection adapter

My requirement is a little bit different, don't know even if it's achievable.
I am using Node.js for developing backend application server. This server basically does two jobs:
(1) Serving clients: My clients are all mobile phones who will be sending HTTP(S) request and after receiving the response will close the session.
(2) Calling some other asynchronously working service: The server, on the other hand, will be connected to some other server which works over just TCP/IP connection and not HTTP. Asynchronous here means, the server will send a request and should not wait for a response. The response will be received through same TCP/IP connection.
So the flow I want to achieve is:
Mobile phone sends the HTTP request to server
Server after receiving the HTTP request, does a call to service which is on TCP/IP
Server receives the response from TCP/IP service over the TCP/IP connection
Server responds to the phone with the response.
To represent the above flow I have attached the below image.
In the above image the TCP/IP Server is managed by some other provider.
I wrote the following code in node.js which works perfectly as per our requirement some times, but some times it sends incorrect response to the HTTP request. I did not write any code to handle this issue.
var net = require('net');
var client = new net.Socket();
client.connect(2202, 'example_ip', function () {
console.log('Connected');
// client.write('Hello, server! Love, Client.');
});
//Lets require/import the HTTP module
var http = require('http');
//Lets define a port we want to listen to
const PORT = 8080;
//We need a function which handles requests and send response
function handleRequest(request, response) {
var body = '';
request.on('data', function (chunk) {
body += chunk;
});
request.on('end', function () {
console.log('Received request from JMeter------------>>>');
// console.log(body);
client.write(body);
var count = 0;
client.on('data', function (data) {
console.log('<<<------------Received from SSM: ' + data);
response.end(data);
// client.destroy(); // kill client after server's response
});
});
client.on('close', function () {
console.log('Connection closed');
});
}
//Create a server
var server = http.createServer(handleRequest);
//Lets start our server
server.listen(PORT, function () {
//Callback triggered when server is successfully listening. Hurray!
console.log("Server listening on: http://localhost:%s", PORT);
});
Please some one guide me to solve this issue.
TCP streams don't work like WebSocket streams ( as you expect ). You need to use your own protocol to communicate with a TCP server. Keep in mind that HTTP clients are many and you have only one TCP connection to handle them, so use requestIds like below, code explains itself.
Not tested, but you can get the idea.
shared.js
exports.tcp = {
host: 'example_ip',
port: 2202
};
exports.http = {
host: 'localhost',
port: 8080
};
/**
* TCP "guarantees" that a receiver will receive the reconstituted
* stream of --> BYTES <-- as it was originally sent by the sender.
*
* eg. if written message = 'How are you today?'
* the messages can come to us as follows:
*
* 'How ar'
* 'e you '
* 'today?'
*
* so we need to use a simple protocol to handle messages
*/
exports.protocol = protocol;
function protocol(options) {
if (!options) options = {};
this.END_OF_MESSAGE = options.endOfMessage || '\0';
this.END_OF_PART = options.endOfPart || '\1';
this.dataBuffer = '';
}
protocol.prototype.packMessage = function(id, body) {
return [id, body].join( this.END_OF_PART ) + this.END_OF_MESSAGE;
};
protocol.prototype.unpackMessage = function(message) {
var parts = message.toString('utf8').split( this.END_OF_PART );
return {id: parts.shift(), body: parts.shift()};
};
protocol.prototype.extractMessages = function(data, callback) {
this.dataBuffer += data.toString('utf8');
if (this.dataBuffer.indexOf(this.END_OF_MESSAGE) !== -1)
{
var messages = this.dataBuffer.split(this.END_OF_MESSAGE);
var incomplete = this.dataBuffer.slice(-1) === this.END_OF_MESSAGE
? '' : messages.pop();
messages.forEach(function(message)
{
if (message !== '') {
callback( this.unpackMessage(message) );
}
});
this.dataBuffer = incomplete;
// rest of 'data'
}
/**
if (Buffer.byteLength(this.dataBuffer, 'utf8') > 10240) { // 10KB
console.log('[!] socket flooded');
this.dataBuffer = '';
}
*/
};
protocol.prototype.reset = function() {
this.dataBuffer = '';
};
httpServer.js
var http = require('http');
var net = require('net');
var shared = require('./shared.js');
var protocol = new shared.protocol();
var server = http.createServer(handleRequest);
server.listen(shared.http.port, shared.http.host, function() {
console.log('HTTP server listening: %s:%s', shared.http.host, shared.http.port);
});
function handleRequest(request, response) {
var body = '';
var requestId = nextId++;
var eventName = 'message' + requestId;
request.on('data', function(chunk) {
body += chunk.toString('utf8');
});
request.on('end', function()
{
// ref#2
client.write( protocol.packMessage(requestId, body) );
// ref#3
client.once(eventName, function(data) {
clearTimeout(timeoutId);
response.end(data);
});
});
var timeoutId = setTimeout(function() {
client.removeListener(eventName);
response.end('timeout');
}, 10000); // 10 sec.
/**
* [!] Don't do this; you are adding just another 'data' event to
* the TCP client for EVERY http request !?
*
* request: UNIQUE obj. for every http request
* client: a PERSISTENT (TCP) stream obj.
*
client.on('data', function() { });
**/
}
var client = new net.Socket();
// ref#1
client.connect(shared.tcp.port, shared.tcp.host, function() {
console.log('TCP conn. established to: ', shared.tcp.host, shared.tcp.port);
});
var nextId = 0;
// unique per http req.
/**
* [!] Do this ( once ) ( not for every request )
*/
client.on('data', function(data)
{
protocol.extractMessages(data, function(message) {
client.emit('message' + message.id, message.body);
// ref#3
});
});
client.on('close', function()
{
console.log('TCP conn. closed');
client.removeAllListeners();
})
client.on('error', function()
{
console.log('TCP conn. error', arguments);
// client.destroy(); // and reconnect here
});
tcpServer.js
var net = require('net');
var shared = require('./shared.js');
var protocol = new shared.protocol();
var server = net.createServer(handleConnection);
server.listen(shared.tcp, function() {
console.log('TCP server listening %s:%s', shared.tcp.host, shared.tcp.port);
});
// [!] CONNECTION handler ( ref#1 )
function handleConnection(client)
{
var this.dataBuffer = '';
// [!] DATA handler ( ref#2 )
client.on('data', function(data) {
protocol.extractMessages(data, function(message)
{
var requestId = message.id;
var body = message.body;
// Do whatever you want with 'body' here
/**
* And return back to 'client' with 'requestId' using same protocol again
* so the 'client' ( from httpServer.js ) can handle your response
*/
client.write( protocol.packMessage(requestId, body) );
});
});
}
Can you instantiate a new client for each incoming request? This way the TCP connection for each request will be unique.

How to retain the values across connections in websocket server?

Below is the websocket server side code that uses "ws" plugin.
var WebSocketServer = require('ws').Server
,wsSend = new WebSocketServer({port: 8080}) //Client sends message on this port.
,wsReceive = new WebSocketServer({port: 8081}) //Response is sent on this port.
,clientMessage;
wsSend.on('connection', ReceiveSocketConnection);//From client.
//Callback function on connection with client.
function ReceiveSocketConnection(webSocket) {
webSocket.on('message', GetMessageFromClient);
}
//Handler to receive message from client.
function GetMessageFromClient(messageFromClient) {
clientMessage = messageFromClient; //Message from client saved into variable
}
wsReceive.on('connection', SendSocketConnection);//To client.
function SendSocketConnection(webSocket) {
webSocket.send(clientMessage);//Here clientMessage is undefined
}
Below is the client side code.
var WebSocket = require('ws')
, wsSend = new WebSocket('ws://localhost:8080') //send port
, wsReceive = new WebSocket('ws://localhost:8081'); //receive port
//Open connection on send port.
wsSend.on('open', function() {
wsSend.send('Hi I am new to websockets');
});
//Open connection on receive port.
wsReceive.on('open', function() {
//Do nothing
});
//Receive message from server via port 8081
wsReceive.on('message', function(message) {
console.log('received: %s', message);
});
Separate ports for sending and receiving messages are there because it is a design decision.
I want to echo the message by receiving it on one port and sending it on another.
Problem : The message from the client is not saved into the local variable(i.e clientMessage).Any suggestions?
this should work, however i recommend using http://socket.io/
var WebSocketServer = require('ws').Server
,wsSend = new WebSocketServer({port: 8080}) //Client sends message on this port.
,wsReceive = new WebSocketServer({port: 8081}) //Response is sent on this port.
wsReceive.broadcast = function(data) {
for(var i in this.clients)
this.clients[i].send(data);
};
wsSend.on('connection', function(ws) {
ws.on('message', function(message) {
console.log('received: %s', message);
try {
console.log('sending: %s', message);
wsReceive.broadcast(message);
} catch (e) {
console.log(e);
}
});
});

Resources