I'm running a React app with node/express on an AWS EC2 instance using an Elastic Load Balancer.
Socket io runs fine when I access the EC2 instance directly but as soon as I access the app using the ELB url (http or https) it returns with GET current url net::ERR_CONNECTION_REFUSED pinged every second or so.
I have used AWS Certificate Manager to create a public certificate for the load balancer in order to be able to access the app via https. Here are the listeners I have enabled for ELB:
Listeners on ELB
On the client side I have the socket set up like so (used with react context):
let socket
const getMsg = (dispatch) => {
if(!socket){
socket = io(':3000', {secure: true})
socket.on('chat msg', function(msg){
dispatch({type:'RECEIVE_MSG', payload: msg})
})
}
}
const sendChat = (dispatch) => {
return async (value) => {
socket.emit('chat msg', value)
getMsg()
}
Here's part of the back-end code:
const app = express()
const http = require('http').createServer(app)
const io = require('socket.io')(http);
app.use(cors())
app.use(bodyParser.json())
app.use(methodOverride('_method'))
io.on('connection', socket =>{
console.log("new connection...")
socket.on('chat msg', async (msg) => {
io.emit('chat msg', msg)
});
})
const mongoUri = //hidden
mongoose.connect(mongoUri, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
mongoose.connection.on('connected', () => {
console.log('you are connected')
})
mongoose.connection.on('error', (err) => {
console.log('connection error, please check your network settings')
})
http.listen(3000, ()=>{
console.log('Listening on 3000')
})
Do I need to setup my backend to accept https requests? Everything else within the app works fine using http on the node/express side even though I'm accessing the app using an https url. The only issue I'm having with requests to the express server is just with socket.io.
I'll add a note here, because I think this is valuable info: I ran into a similar issue a while back, you cannot use HTTP(s) listeners when trying to support web sockets on Classic ELBs.
Instead you can setup TCP SSL termination, and send the TCP to your backend which would allow regular http and ws traffic to work.
(Described here: https://www.built.io/blog/websockets-on-aws-s-elb#:~:text=AWS%20ELB%20doesn't%20support,IP%20information%20is%20not%20obtained)
Otherwise, you could opt for an ALB which does support the listeners as configured. I think ALB is likely the best option here, as it's sort of the "supported" solution from a infrastructure POV.
Related
I'm using socket.io to communicate between a server and many clients. Locally everything works fine. However, on production it doesn't work as desired. We use microservices, and this specific microservice is part of a suite of services that run together using webpack module federation.
When accessing the app directly through the k8s ingress, I can see that the clients are connecting to the server. However, clients running through module federation (in the bigger app that consists of my microapp and others), do not connect to the server, and every few seconds a 404 error is printed to the console for a GET request to:
http://<BASE_URL>/socket.io/?EIO=4&transport=polling&t=XXXXXX
where XXXXXX is some random string. I believe that I need to redirect the clients' sockets somehow to reach the server, but I don't know how to do so.
Relevant Client Code
const socket = io.connect("");
socket.on("someEvent", (param) => {
doSomething()
});
Relevant Server Code
let server = app.listen(port, (err) => {
if (err) throw err;
// Express server Ready on http://localhost:port
});
...
let io;
const initSocket = (server) => {
io = require("socket.io")(server, {
cors: { origin: "*" }
});
io.on('connection', (socket) => {
logging.mainLogger.info(`successfully connected to socket
${JSON.stringify(socket.id)}`);
});
io.listen(server);
}
const emitSomeEvent = (param) => {
io.emit("someEvent", param);
}
Im have hetzner server on which im trying to run WebSocket. Unfortunately I got stack so here is my code from test.js)
const https = require('https');
const fs = require('fs');
const ws = require('ws');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
};
let server = http.createServer(options, (req, res) => {
console.log(req);
res.writeHead(200);
res.end();
});
server.addListener('upgrade', (req, res, head) => console.log('UPGRADE:', req.url));
server.on('error', (err) => console.error(err));
server.listen(8080, () => console.log('started on 8080'));
const wss = new ws.Server({ server, path: '/echo' });
wss.on('connection', (ws) => {
console.log('client connected');
ws.send('Hello');
ws.on('message', (data) => ws.send('Receive: ' + data));
});
wss.on('error', (e) => console.log(e));
* I got this code from some sources
After running the server I got message in console started on 8080. Nothing else... I tried to test it but I always got errors with no code. It looks like service where I tested it cannot find WebSocket. It is possible that problem actually in connecting to the serer.
Im not shure which path string should I use. I have hostname ubuntu-s**** and server IP 49.****. I did lots of attempts with 'wss://ubuntu-s***:8080/echo' and 'wss://49.***:8080/echo' but none of them gave any result
I still have no messages in console that cliend tried to connect :( Moreover I tried to run it on the local server (of course I removed SSL sertificates connecthion and changed server protocol to HTTP) and it works perfectly!!!
Thanks a lot for urs replies
UPD: message I got when trying to connect ws WebSocket connection to 'ws://49.***:8080/echo' failed:
First double check if the IP address is correct then change protocol to HTTP because you are using it on top of HTTP:
http://49.***:8080/echo
I'm using nuxt-socket-io along with an Express.js server with socketio as well.
When I start up the client/server, the server-side socket.io connects and console for the server will print "connected").
When I try to connect with nuxt (the client-side part of socket.io), nothing happens. Mounted() is called correctly (the "hm" console log prints out), but the socket never seems to be made. I tried testing this.socket.on('connect-error') and this.socket.on('connect-timeout') for the CLIENT side (the server-side socket.io connects properly), but nothing was ever emitted after about 5 minutes of waiting. The persist: true isn't the issue either; I tried to remove it and had the same issue. I initially didn't have this.socket.open() and had the same problems, so I don't think that line does anything, either.
NuxtJS frontend
mounted() {
console.log("hm");
this.socket = this.$nuxtSocket({
channel: '/profile',
persist: true
})
this.socket.open();
this.socket.on('connection', (socket) => {
console.log("connected")
})
//Listens for the SERVER-EMITTED event 'send'
this.socket.on('send', (message) => {
console.log("client received event!")
console.log(message);
});
},
methods: {
sendMessage() {
// This method IS CALLED correctly with a button (I checked), but the emit is not transmitting
// sends a CLIENT-EMITTED event to the server
this.socket.emit('send-message', {
message: "hey!"
}, (res) => {console.log(res)})
},
nuxt.config.js
io: {
sockets: [{
name: 'main',
default: true,
url: 'http://localhost:3000'
}]
},
My Express Backend (port is 8080)
import express from "express";
import { db } from "./app/config/db.config";
import { authRouter } from "./app/routes/auth.router";
import * as dotenv from "dotenv";
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: '*',
methods: ["GET", "POST"]
}
});
// run on connection to socket
io.on('connection', (socket: any) => {
console.log("connected")
})
// listens for the CLIENT-EMITTED event 'send-message'
io.on('send-message', (message: any) => {
console.log(message + "server received!");
// sends a SERVER-EMITTED event "send" to be received by nuxt client
io.emit('send', "message!")
})
server.listen(process.env.PORT, () => {
console.log(`Server is running on port ${process.env.PORT}`);
});
Axios is also running on port 8080, I don't know if that would cause any issues but I don't get any errors when I try to run my whole program (which includes login/registration with axios).
Anyone know why my events aren't transmitting? Thank you!
In your server code, you're adding your 'send-message' event listener to the io object, which is your main socket.io Server instance. However, event listeners should be added to the socket object you get from the connection event. Something like:
// A new connection comes in
io.on('connection', (socket: Socket) => {
// Now we listen for the event that comes from this particular socket
socket.on('send-message', (message: any) => {
// You also use the Socket instance to send events back to clients, not to the `io` Server.
socket.emit('send', "message!");
});
});
The Socket instance docs have more info on what you can do with these sockets.
Server code:
import http from 'http';
import Koa from 'koa';
import { Server } from 'socket.io';
(async () => {
const app = new Koa();
var server = http.createServer(app.callback());
var io = new Server(server, {
path: '/seacher',
transports: ['websocket'],
});
io.on('connection', (socket) => {
setTimeout(() => socket.emit('message', { say: 'hello' }), 1000);
socket.on('message', (msg) => {
console.log('[msg]', msg);
});
});
server.listen(3000)
})();
Client code:
var socket = io('http://localhost:3000/seacher', {
path: '/seacher',
autoConnect: false,
transports: ['websocket'],
});
socket.on('error', (err) => console.log('error', err));
socket.on('connect', () => console.log('connect'));
socket.connect();
No any messages in browser / nodejs console.
In the Network tab in browser a lot of connections with messages like
Change the client code to this:
var socket = io('http://localhost:3000', { // note changed URL here
path: '/seacher',
autoConnect: false,
transports: ['websocket'],
});
The path option specifies what URL socket.io is going to use internally. You put that in the path option as you have already done in both client and server.
If you put something in the URL you specify like you had 'http://localhost:3000/seacher', then that is the namespace /seacher that you are trying to connect, but your server does not support that namespace.
This is a confusing part of socket.io's design, but path and namespace are not the same thing in socket.io. Do not confuse or mix them.
FYI, there is rarely a reason to customize the path option the way you have done unless you are trying to run more than one socket.io server shared on the same http server (something it does not appear you are doing). By default, the path is /socket.io which makes a lot of sense in logs and debuggers and when accessing the client-side library. I would suggest you remove the path option from both client and server and let it default to /socket.io. And, don't use a path in your connection URL either as that specifies a namespace, not a path.
I am trying to build a two way socket.io server/client connection. The server will remain behind one IP/domain and the client will behind a different IP. The point is to notify me when the server goes offline, in case of power outage or server failure. The issue I am having, is I am trying to secure the socket so not just anyone can connect to the socket. Socket.IO has a server.origins function that will return the origin of socket trying to connect. Their API documentation explains it like this.
io.origins((origin, callback) => {
if (origin !== 'https://foo.example.com') {
return callback('origin not allowed', false);
}
callback(null, true);
});
The issue I am having is whenever I connect to the socket.io server with socket.io-client the origin is always '*'.
Under potential drawbacks in there API is says:
"in some situations, when it is not possible to determine origin it may have value of *"
How do I get socket.io it see the IP where the socket connection request is coming from?
Once the connection is established I can use the socket information and see the IP where the socket lives, but the connection is already made. I am trying to stop rouge connections.
# Server
const express = require('express');
const app = express();
const chalk = require('chalk')
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const cors = require('cors');
const port = 4424;
app.use(cors());
io.origins((origin, callback) => {
console.log(origin);
if (origin !== '*') {
return callback('origin not allowed', false);
}
callback(null, true);
});
io.on('connection', (socket) => {
console.log('Client connected...');
socket.on('join', (data) => {
console.log(data);
socket.emit('messages', 'Hello from server');
});
})
server.listen(port, () => console.log(chalk.blue(`Express started on port ${port}!`)));
Client:
# Client
const io = require('socket.io-client');
const socket = io('https://"MY DOMAIN THAT THE SERVER IS BEHIND"', { reconnect: true })
socket.on('connect', (data) => {
console.log("Connection successful");
socket.emit('join', 'Hello World from client');
});
socket.on('connect_error', (error) => {
console.log("Connection error");
});
socket.on('disconnect', (timeout) => {
console.log("Connection disconnected");
})
socket.on('messages', (data) => {
console.log(data);
});
I have the server behind a NGINX server using SSL, and connected to the server with the client on a different IP and it goes through and creates the connection, but the Origin is always "*".
Actually I found out you can use middleware with Socket.io with the io.use() function. I just wrote a simple middleware that checks the incoming socket ip with a list of approved ips.
io.use((socket, next) => {
const ip = socket.handshake.headers['x-forwarded-for']
if (firewall(ip))
{
return next();
}
})
And firewall is a function that checks if the ip is in the array of approved ips.