I have a small angular app who comunicate with a node.js server. Both are deployed on aws and I use Nginx reverse proxy to acess the node.js server at the port 4000.
All end-points of the nodejs.server works fine, except the socket.io connection.
When I run both apps (front end app and the node.js server) in my machinine the socket.io connection works fine, but when I try to deploy it on aws I get this error in the front-end app:
Error: server error
at Socket.onPacket (socket.js:401)
at XHR.<anonymous> (socket.js:216)
at XHR.push.dMso.Emitter.emit (index.js:145)
at XHR.onPacket (transport.js:103)
at callback (polling.js:101)
at Array.forEach (<anonymous>)
at XHR.onData (polling.js:105)
at Request.<anonymous> (polling-xhr.js:94)
at Request.push.dMso.Emitter.emit (index.js:145)
at Request.onData (polling-xhr.js:236)
This is my Nginx configuration in /etc/nginx/sites-available/default:
server{
charset utf-8;
listen 80 default_server;
server_name 18.231.153.164;
# angular app & front-end files
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
root /opt/front-end/dist/admin;
try_files $uri /index.html;
}
# node api reverse proxy
location /api/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:4000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# node api reverse proxy
location /socket.io {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:4000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
As you can see, my node.js is running at port 4000, so everytime someone try to access the web site with the path /api/ it will be redirected to the node.js server.
Here is a piece of my node.js code:
const socketio = require('socket.io');
const express = require('express');
const app = express();
const port = 4000;
const httpServer = app.listen(port, () => {
console.log('Server listening on port ' + port);
});
let io = socketio(httpServer, {
cors: {
origin: "http://localhost:5200",
},
});
// auth moddleware
io.use((socket, next, err) => {
const jwtToken = socket.handshake.query.jwtToken;
// console.log('token --> ', jwtToken);
const user = socket.handshake.query.user;
if (!user) {return};
socket.user = JSON.parse(user);
next();
});
io.on('connection', (socket) => {
socket.emit('messageFromServer', {data: 'Welcome to server'});
})
Here is a piece of my front-end code:
import {io} from 'socket.io-client';
...
// Connect to server (When I am running locally I change '/api' to 'http://localhost:4000'
this.socket = io('/api', {secure: true, reconnection: true, rejectUnauthorized: false, query: {user: user, jwtToken: this.authService.authValue.jwtToken}});
// Listen to events
this.socket.on('usersOnline', (usersOnline) => {
console.log('Users online now:', usersOnline);
});
this.socket.on('connect_error', (err) => {
console.log('ERR::', err);
});
The version of socket.io in the server is "^4.0.0", and the version of the socket.io-client in front and is "^4.0.0".
I believe this issue is related with the reverse proxy and socket.io somehow. As I told before, the socket.io connection works fine in local envoriment.
Can someone please help me?
I found the solution.
I need to add proxy_redirect off; on the socket.io reverse proxy.
location /socket.io {
proxy_pass http://localhost:4000;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Then I had another problem, in the font end I start to get an error or invalid namespace.
So I change the client connection to / instead od /api:
this.socket = io('/', {query: {user: user, jwtToken: this.authService.authValue.jwtToken}});
Just check and install same version of socket.io and socket.io-client
Related
I want to run an express server and a socket.io server on an node.js instance which are reachable through nginx. If possible, I would favor to run them both on the same port, but I did not reach that goal either.
I can reach the webserver locally and from extern.
I can only reach the socket server locally.
When I call the page, my browsers log shows the following error:
GET https://example.com/socket.io/?EIO=3&transport=polling&t=O6qCW5Y
400 Bad Request
So I think the problem lies somewhere in the nginx config.
My app.js code for the socket.io server:
const http = require('http').createServer();
const io = require('socket.io')(http);
http.listen(3001, () => {
console.log('Socket-Server listening on port: 3001');
});
io.on('connection', (socket) => {
...
});
My code for the client:
window.onload = function() {
const socket = io.connect('https://box-api.com/');
socket.on('connect', () => {
...
}
}
My nginx config:
server {
server_name example.com;
root /nodejs/example.com;
include static_files.conf;
index app.js;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
}
location /socket.io {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Certbot stuff
...
}
I have MERN app, the api build with express and the client build with reactjs.
In my local computer (windows), the client can connect to socket server. The onConnection event is triggered.
1. Local Computer (Windows)
Client Side:
socket.on('connect', () => {
console.log('connected to the socket server');
}) // triggered and i can see the console.log in broser console
Server Side:
io.on('connection', () => {
console.log('client connected to the socket server');
}) // triggered and i can see the console.log in terminal
2. VPS (Ubuntu 20, Nginx)
When i deploy the server and the client to vps, the socket can't connect. on connection event not triggered.
But the client is trigger connect_error event:
socket.on("connect_error", (error) => {
console.log('socket connection error:', error.message) // socket connection error: server error
}); // this event is triggered
This is my code:
Server side:
const server = app.listen(3000)
const io = socketio(server)
Client side:
const socket = io("http://103.150.60.132/api")
NGINX Config:
server {
listen 80;
location / {
root /var/www/bacotin/client; //reactjs build location
index index.html index.htm;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://103.150.60.132:3000; // express api
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ~* \.io {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy false;
proxy_pass http://localhost:3000;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
UPDATE: the problem is solve, now the socket client can connect to the socket server.
Just modify the client code:
From this:
const socket = io("http://103.150.60.132/api")
To this:
const socket = io("http://103.150.60.132")
But why?
I have a node app that is running in a dockerized environment just fine. It runs all fine over HTTP and locally. Only when I use Nginx reverse proxy, the socket.emit doesn't return anything.
My Nginx config:
location /verification/ {
proxy_pass http://127.0.0.1:3005/;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
my socket connection (client):
const socket = io(
'https://mywebsite.com',
{ path: + '/verification/socket.io/',
secure: true,
rejectUnauthorized: false
}, fn)
My server config:
const io = socket(http, {path: '/socket.io/'});
Actually I've come to a solution with this. I've implemented the room scenario myself and it's been working well. For so, I just implemented the following:
const clients = {};
io.to('/verification/').connection(function(){
clients[socket.id] = {room: null, id: socket.id};
socket.on('message', function(payload){
if(payload.intent === 'join'){
clients[socket.id] = {...clients[socket.id], room: payload.room};
}
});
//and to broadcast messages:
Object.values(clients)
.map(client => {
socket.to(client.id).emit('message', 'foo message')
})
})
I am trying to set up my node server that uses express to serve files on port 3000 and the net library to serve a TCP server on port 5052, so:
const express = require('express');
const app = express();
const httpServer = require('http').Server(app);
const io = require('socket.io').listen(httpServer);
const path = require('path');
const net = require('net');
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, './public/index.html'))
});
let server = net.createServer(function(socket) {
// Left out for brevity
}
server.listen(5052, 'localhost');
httpServer.listen(3000, () => {
console.log('Ready on port 3000');
});
Locally, this all works very well. I can load up localhost:3000 and I get my HTML served and it connects to socket.io fine. I can also connect to the server on port 5052 perfectly and life is good. I just can't get nginx to serve it all correctly. Here's what I have:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name mycoolproject.com www.mycoolproject.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/mycoolproject.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mycoolproject.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
server {
listen 5053;
server_name mycoolproject.com www.mycoolproject.com;
location /{
proxy_pass http://localhost:5052;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
When I navigate to mycoolproject.com I get the site loaded fine, so the express side is working fine. I just can't connect to my server on 5053. Any ideas?
You need to configure a different port for Nginx, 5052 is busy by Node.js.
server {
listen 5053;
server_name mycoolproject.com www.mycoolproject.com;
location /{
proxy_pass http://localhost:5052;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Then you can connect to mycoolproject.com:5053
I'm getting this error log from chrome's console
XMLHttpRequest cannot load https://subgroup.domain.com/socket.io/?EIO=3&transport=polling&t=Lpgp_mu. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 500.
I am using Node.js, socket.io to talk between my node and react.js, with a digitalocean's droplet using nginx to host them.
I've been reading a lot about CORS errors and I am unsure where to fix the error. I've been trying to allow them in my NGINX
location /server {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
And from my node.js, server side:
var express = require("express");
var app = express();
var http = require("http");
var port = 8080;
var io = require("socket.io").listen(app.listen(port));
app.use("/", function (req, res, next) {
res.status(200).send("Online |" + " Version : [" + AppVersion + "]");
res.setHeader("Access-Control-Allow-Origin","*");
res.setHeader("Access-Control-Allow-Headers","X-Requested-With,content-type");
res.setHeader("Access-Control-Allow-Methods","GET,POST, OPTIONS, PUT, PATCH, DELETE");
next();});
And I connect on the client side using:
const socket = io.connect("https://subgroup.domain.com/server")
I am not really sure where and what I should look for. Any kind of help would help. Thanks!
After a long research and executing multiple tests I got this working. Here is what I did,
NodeJS
const server = require('http').createServer();
const io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('A user connected!');
io.emit('welcome', 'Hello there!');
});
server.listen(3001);
console.log('Socket server started on port 3001');
Nginx
upstream websocket1 {
server 127.0.0.1:3001;
}
server {
listen 80;
listen [::]:80;
root /var/www/html;
server_name mydomain.com
location /ws/ {
proxy_pass http://websocket1/socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Finally on the client side
const socket = io.connect('http://yourdomain.com/', {path: '/ws/'})
Here is a screenshot from Chrome console.
Please do not ignore the / after specifying the location in Nginx, it must be /ws/ it is not working otherwise. Currently I have a node balancer added to this socket service with Nginx.
Cheers!