Node.js app fails to connect using socket.io-client - node.js

Background
I have a Node.js server using socket.io that accepts connections from clients via HTTPS.
I know this server works as I am able to connect to it via browser.
Problem
The problem is that I can't create a node app to connect to this server as a client.
I am using the following code:
const io = require("socket.io-client");
const socket = io.connect("https://my.website.com:3002", { secure: true, reconnect: true });
socket.on("connect", function(){
console.log("connected");
});
socket.on("disconnect", function(){
console.log("disconnected");
});
socket.on("error", console.error);
The server registers no connections, and this app logs no errors. It would seem that I am connecting to the wrong server, but this same URL works just fine when I use a browser.
Research
I have searched github and the official docs for an answer. Even similar questions from stackoverflow seem to not work:
Node.js client for a socket.io server
https://www.npmjs.com/package/socket.io-client
https://github.com/socketio/socket.io-client/issues/828
Question
What am I doing wrong ?

Answer
After realising, that not all errors feed into the "error" event ( special thanks to #RolandStarke ) I found that I was having a consistent XHR pool request:
{ Error: xhr poll error
at XHR.Transport.onError (/Users/pedro/Workspace/backend-stresser/node_modules/engine.io-client/lib/transport.js:64:13)
at Request.<anonymous> (/Users/pedro/Workspace/backend-stresser/node_modules/engine.io-client/lib/transports/polling-xhr.js:128:10)
at Request.Emitter.emit (/Users/pedro/Workspace/backend-stresser/node_modules/component-emitter/index.js:133:20)
at Request.onError (/Users/pedro/Workspace/backend-stresser/node_modules/engine.io-client/lib/transports/polling-xhr.js:310:8)
at Timeout._onTimeout (/Users/pedro/Workspace/backend-stresser/node_modules/engine.io-client/lib/transports/polling-xhr.js:257:18)
at ontimeout (timers.js:469:11)
at tryOnTimeout (timers.js:304:5)
at Timer.listOnTimeout (timers.js:264:5) type: 'TransportError', description: 503 }
Once I had this information, I made a quick search and found a solution to this issue, which seems to be a bug:
https://github.com/socketio/socket.io-client/issues/1097
The code I am now using is:
const socket = io.connect("https://my.website.com:3002", { secure: true, reconnection: true, rejectUnauthorized: false });
And it works as expected.

I have used this code in my client side and it worked:
import io from "socket.io-client"
const SERVER = "http://localhost:5000"
const socket = io(SERVER, { transports: ["websocket"] })

Related

Nodejs socketio: The emit does not seem to work on my Ubuntu server

I'm working on a game project. I use socketio for communication between server and client.
My problem is the server on Ubuntu18.04 can receive the connection and disconnection request from the Windows client(as the corresponding console.log works). But the server always disconnects from the client as a result of ping timeout, and when I emit any messages from either server or client, the other side receive nothing.
I consider the compatibility of different versions of socketio, but I check socketio and socket.io in package-lock.json and find they are of the same version (I just don't know what is the difference between socketio and socket.io). I also check the firewall, the port 30001 I use is opened. And actually when I run the server locally on the same Windows PC with the client, it works well.
The following is the source code. Here the client cannot receive a connected message from the Ubuntu server.
Server:
const io = socketIO(httpServer, {
path: '/',
pingInterval: 10000,
pingTimeout: 30000,
})
io.on('connect', async(socket)=>{
console.log('connect from ' + socket.account)
socket.emit('connected', {status: 'ok'})
socket.on('disconnect', async(reason)=>{
console.log('disconnect from ' + socket.account, reason)
let tableID = Table.findTable(socket.account)
if(tableID !== undefined){
Table.leave(socket.account, tableID)
io.to(tableID).emit('updateTable', OK(res.table.getCurrentInfo()))
}
})
//other listeners
})
Client:
socket = io(socketAddress, {
query: {
token: store.state.playerInfo.token
},
})
socket.on('connected', (data)=>{
console.log('socket connected successfully', data)
})
I'm new in socketio and I have no idea for how to deal with it. I will be very appreciate if anyone can help me.

Redis Cloud and connect-redis: the client is closed

I currently build a website using Express and want to use redis cloud database to save userID in session. The redisClient is created in redisClient.js and after that i pass it to redisStore in session in app.js. Here is the code:
redisCLient.js
const redis = require("redis");
let redisClient = redis.createClient({
host: process.env.REDIS_HOSTNAME,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD
});
redisClient.on('error', function(err) {
console.log('*Redis Client Error: ' + err.message);
});
redisClient.on('connect', function(){
console.log('Connected to redis instance');
});
(async () => {
await redisClient.auth(process.env.REDIS_PASSWORD)
.catch(err => {console.log('Redis auth error: ' + err.message)});
await redisClient.connect()
.catch(err => {console.log('Redis connect error: ' + err.message)});
})();
module.exports = redisClient;
app.js
const session = require("express-session");
const redisStore = require('connect-redis')(session);
const redisClient = require('./session-store/redisClient');
...
app.use(cookieParser());
app.use(session({
store: new redisStore({client: redisClient, ttl: 3600 * 24 * 30}),
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false
}));
The problem is: upon starting the server i got error messages log in console like this:
Redis auth error: The client is closed
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
*Redis Client Error: connect ECONNREFUSED 127.0.0.1:6379
...
I used this guide to set up redis cloud and assign dotenv variables (host, port and password). I have debugged and the dotenv is working fine and I have host, port and password variables correct.
But the problem still remains. I still get The client is closed and connect ECONNREFUSED 127.0.0.1:6379 error as in console log above. How can i fix this?
was stuck on same issue and found some luck. have used 'ioredis' module instead of redis which worked seamlessly.
const redis = require('ioredis');
const redisClient = redis.createClient({host:'your host address',port:your port,username:'',password:''});
redisClient.on('connect',() => {
console.log('connected to redis successfully!');
})
redisClient.on('error',(error) => {
console.log('Redis connection error :', error);
})
module.exports = redisClient;
When you create a client using redis.createClient, you need to use url instead of host, port.
refer to basic-example
it showing url format
redis[s]://[[username][:password]#][host][:port][/db-number]
in your case, it might be like this
var url = `redis://<YourUsername>:${process.env.REDIS_PASSWORD}#${process.env.REDIS_HOSTNAME}:${parseInt(process.env.REDIS_PORT)}`
redis.createClient({
url: url
});
Using host, port option must be the old version.
Option 1: switch the order of the calls to auth and connect
From the Node Redis client documentation:
When connecting to a Redis server that requires authentication, the AUTH command must be sent as the first command after connecting.
You should therefore switch the order of the calls to redisClient.auth and redisClient.connect.
Option 2: remove the call to auth
However, the documentation for the password property of createClient options states:
If set, client will run Redis auth command on connect.
As you are supplying password to createClient, you could alternatively just remove the explicit call to auth.
You must do await redisClient.connect() before you access the client. Try to move your redisClient.connect() just after you create it.
try this -->
var options = {
client: redis.createClient({
url : process.env.REDIS_URL,
legacyMode: true,
})};
you can make your url as 'redis://host:port'
import { createClient } from "redis";
const redisClient = createClient({
url: "redis://localhost:6379",
});
const start = async () => {
await redisClient.connect();
};
start();
export default redisClient;

Proxied websocket connection is immediately closed

I have a create-react-app dev server proxying backend connections (as one does). Suddenly websocket proxying stopped working.
My setupProxy.js looks like this:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
const port = process.env.BACKEND_PORT || '8080';
const target = `http://localhost:${port}`;
app.use(proxy(['/path/to/socket'], {
target,
ws: true,
onProxyReqWs: function(proxyReq, req, socket) {
socket.on('error', err => console.log(err));
console.log('socket is destroyed', socket.destroyed)
},
logLevel: 'debug',
}));
app.use(proxy(shouldProxy, {
target,
logLevel: 'debug',
}));
(where shouldProxy is a function, since my logic for when to proxy is... non-trivial).
When the browser (Firefox 71 or Chrome 79) creates a websocket connection, I can see that the backend gets the request and responds normally, but the browser gets a 400 Bad request and the dev-server console has this:
[HPM] GET /path/to/socket -> http://localhost:8080
socket is destroyed true
[HPM] Upgrading to WebSocket
Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
at Socket.Writable.write (_stream_writable.js:321:17)
at ClientRequest.<anonymous> ([...]/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:143:14)
at ClientRequest.emit (events.js:305:20)
at Socket.socketOnData (_http_client.js:508:11)
at Socket.emit (events.js:305:20)
at addChunk (_stream_readable.js:341:12)
at readableAddChunk (_stream_readable.js:316:11)
at Socket.Readable.push (_stream_readable.js:250:10)
at TCP.onStreamRead (internal/stream_base_commons.js:186:23) {
code: 'ERR_STREAM_DESTROYED'
}
[HPM] Client disconnected
So something seems to be destroying the socket very early in the proxying process, but I cannot fathom what.
I currently run with node 13.5.0, http-proxy 1.18.0 and http-proxy-middleware 0.20.0; I've tried downgrading node to 12.14.0 and HPM to 0.19.1, to no avail.
This was an issue with create-react-app 3.3.0, caused by this bug in webpack-dev-server. Adding "webpack-dev-server": "3.10.1" to the resolutions section of package.json and SKIP_PREFLIGHT_CHECK=true to .env fixed it.

socket.io client behind proxy works fine in browser, not in nodejs application

I'm stuck with a Socket.io exchange. If the javascript code is hosted inside a browser ( Chrome/Firefox) the connection is working with or without proxy in the middle.
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script>
<script>
var socket = io('https://uri', { secure: true, reconnect: true, rejectUnauthorized: false });
socket.on('connect', function () {
console.log('Connected to server!');
socket.emit('register', 'ClientName');
});
</script>
On the contrary, the same code run on nodejs, using node v10.4.0 and the module "socket.io": "^2.1.1" is working ONLY if the connection is direct.
I've tried to use socket.io-proxy (quite old), but is seems it is not aligned with socket.io-client, and it does not work, or I'm missing something.
It is clear that the "in browser script" can access to the proxy settings/channel...whatever, or to some other setting that node runtime is not aware of.
Thanks for any suggestion.
Lorenzo
Did you manage to solve the problem?
If you are behind a simple http(s) proxy you can try with the https-proxy-agent package..
var HttpsProxyAgent = require('https-proxy-agent');
let p = 'http://my.proxy.address:8080';
let agent = new HttpsProxyAgent(p);
let opts = {
secure: true,
rejectUnauthorized: false,
reconnect: true,
agent: agent
};
let socket = require('socket.io-client').connect('https://my.socket.io.server', opts);

How to make 2 nodejs servers connect with SSL websocket?

I am trying to make 2 servers communicate via socket.io library and SSL.
This used to work until an upgrade of socket.io package (can't tell you which).
I have managed to fix secure connection with a browser. I have also made it work between unsecure (http) servers. But the secure (https) servers refuse to connect between themselves. You may argue that socket.io is not made for server to server communications, but it would save me lots of work to fix it.
I am now running:
node: 7.5.0
express: 4.16.2
socket.io (and socket.io-client): 2.0.3
I cannot even make simple examples below work (removing all my middleware).
node server
// Use SSL certificate
const cert_path = "..";
const fs = require('fs');
const https_options = {
key: fs.readFileSync(cert_path+'/privkey.pem'),
cert: fs.readFileSync(cert_path+'/cert.pem')
};
const app = require('express')();
const https = require('https');
const server = https.createServer(https_options, app);
const io = require('socket.io')(server);
server.listen(8000);
io.on('connection', function (socket) {
console.log("connected");
});
node client
const io = require('socket.io-client');
const socket = io.connect(
'https://localhost:8000',
{secure: true}
);
socket.on("connect", function () {
console.log("connected");
});
Nothing happens, none of them connect. Any idea why?
EDIT: I'm getting both connect_error and reconnect_error that pop every 5s on client side:
{ Error: xhr poll error
at XHR.Transport.onError (../node_modules/engine.io-client/lib/transport.js:64:13)
at Request.<anonymous> (../node_modules/engine.io-client/lib/transports/polling-xhr.js:128:10)
at Request.Emitter.emit (../node_modules/component-emitter/index.js:133:20)
at Request.onError (../node_modules/engine.io-client/lib/transports/polling-xhr.js:310:8)
at Timeout._onTimeout (../node_modules/engine.io-client/lib/transports/polling-xhr.js:257:18)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5) type: 'TransportError', description: 503 }
Digging further in the errors, I see it may come from the certificate. But while I apply several workarounds of SO, I'm getting consecutively ECONNREFUSED, UNABLE_TO_VERIFY_LEAF_SIGNATURE, and finally DEPTH_ZERO_SELF_SIGNED_CERT...
After trying hard:
re-generate my Let's Encrypt certificate
re-generate my self-signed certificates (openssl) and use them by server+client
tinker with socket.io connect options (secure, rejectUnauthorized, ..)
tinker with nodejs global setup even (process.env['NODE_TLS_REJECT_UNAUTHORIZED'])
I finally stumbled on this page of github. It solved my issue and it's worth sharing it.
node client
const https = require('https');
https.globalAgent.options.rejectUnauthorized = false;
const io = require('socket.io-client');
const sockets = io.connect('https://localhost:8001', {agent: https.globalAgent});
Even if I would have preferred getting my connection authorized in the first place, this will work for me.

Resources