CORS issues with Express, Socket.io and Nginx - node.js

I have adapted a tutorial to get a simple Socket.io chat going in Node. It works when hosted locally, but after pushing it to a test server I can't get the socket connection to be accepted. Seems to be a cross-origin related matter, though I'm slightly confused about how to route things in Nginx also. Following the advice in the related questions hasn't helped.
Client script:
var socket = io.connect('http://localhost/socket.io');
Index.js:
const express = require('express');
const app = express();
const path = require('path');
const httpServer = require('http').createServer(app);
const io = require('socket.io')(httpServer, {
cors:true,
origins:["*"],
// origins:["http://xxx.xxx.xxx.xxx:8080"],
// transports: ['websocket'],
});
const views_path = (__dirname + '/views');
app.set('views',views_path);
app.use(express.static(__dirname + '/public'));
app.get('/', function(req,res){
console.log('render request received');
res.render('startPage.ejs');
});
io.sockets.on('connection', socket => {
console.log('connection received.')
socket.on('username', function(username) {
socket.username = username;
io.emit('is_online', socket.username);
});
//...
});
httpServer.listen(8080);
nginx sites-available:
server {
server_name campfire;
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/campfire/html;
index index.html index.htm index.nginx-debian.html;
location ^~ /assets/ {
gzip_static on;
expires 12h;
add_header Cache-Control public;
}
location / {
proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8080;
}
location /socket.io/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'Upgrade';
proxy_http_version 1.1;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:8080/socket.io/;
}
}
Any insights welcome!

Hope this will help with all the CORS error
Because it will handle it for you
const cors = require('cors');
app.use(cors());
Docs CORS

Making this change fixed the issue:
Client script: var socket = io.connect();
This way uses the default connection destination with socket.

Related

Docker + Node.JS + Socket.IO + Nginx (405 & Bad handshake method)

I have a docker containerized application with react front-end and nodeJS/PHP backend (working on different containers). I've successfully installed https with an intermediate container (let'sencrypt certbot) for my front-end build and PHP backend, but have some problem with socket pooling to nodejs backend. When socket.io pooling starts I got the error on POST request:
[POST] https://my.domain/socket.io/?EIO=3&transport=polling&t=MlE0IBv
405 Not Allowed
When I tried to prevent this by next Nginx construction:
error_page 405 #nodejs;
I got the same error code with the next message:
code: 2 message: "Bad handshake method"
There is part of my Nginx configuration (nginx is separate docker container):
upstream node {
ip_hash;
server node:4000; //nodejs container
}
server {
listen 80;
// ...redirect to https
}
server {
listen 443 ssl;
// .... cert's and other settings
// front-end static react build
location / {
try_files $uri /index.html =404;
}
location /static {
try_files $uri #nodejs;
}
location #nodejs {
proxy_pass http://node;
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;
}
// without this string i just got "405 Not Allowed" nginx error page
// with this string i got probably nodejs "Bad handshake method" error
error_page 405 #nodejs;
}
My app.js server code:
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const socketRouter = require('./sockets/index');
const app = express();
const server = http.Server(app);
const io = socketIO(server, {origins: '*:*'});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
io.on('connection', socketRouter.bind({}, io));
module.exports = server;
And index.js:
require('dotenv').config();
const app = require('./src/app');
const PORT = process.env.APP_PORT || 4000;
app.listen(PORT);
console.log('Application started on Port ' + PORT);
console.log('APP_ENV ' + process.env.APP_ENV);
Problem was solved. The correct config is:
location /static {
try_files $uri =404;
}
location /socket.io {
proxy_pass http://node;
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;
add_header Front-End-Https on;
}
error_page 405 #nodejs;

How can I get the client IP address with socket.io?

I've searched the whole SO but I couldn't find my answer. I want to get client IP address with socket io. v2.0.3
app.js
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
/// other imports ...
//with or without this line nothing changes
io.set('transports', ['websocket']);
app.use(function(req, res, next) {
req.db = db;
next();
});
require('./sockets')(io, db);
sockets.js
module.exports = function(io, db) {
io.on('connection', function(socket) {
var ip = socket.request.socket.remoteAddress ; // undefined
//var ip = socket.handshake.headers["x-forwarded-for"]; undefined
//var ip = socket.handshake.headers["X-Forwarded-For"]; undefined
});
});
so this code does not work:
var ip = socket.request.socket.remoteAddress
this one does not work eather:
var ip = socket.handshake.headers["x-forwarded-for"];
//var ip = socket.handshake.headers["X-Forwarded-For"];
with this nginx config:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
is there anyway to get the client IP address with socket.io?
I changed nginx config and the problem solved:
server {
listen 80;
server_name example.com;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Real-IP;
real_ip_recursive on;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}
and in sockets.js
module.exports = function(io, db) {
io.on('connection', function(socket) {
var ip = socket.handshake.headers["x-real-ip"];
});
});
You can try socket.handshake.address or socket.handshake.address.substr(7)

Getting CORS error while using socket.io / nginx / node

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!

nodejs & express https server with NGINX

I'm trying to setup a nodejs server with https to make REST calls to.
Connecting directly to ip + port through http works fine. When using https with ip and port my android app complains that "the Hostname xx.xx.xx.xx not verified". So I figured I should setup a domain for it to make my certificate match.
With NGINX I made the following:
server {
listen 80;
server_name rest-ssl.mydomain.com;
location / {
proxy_pass https://xx.xx.xx.xx:4443;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
server {
listen 80;
server_name rest-normal.mydomain.com;
location / {
proxy_pass http://xx.xx.xx.xx:4080;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
And restarted my nginx service. In this config file I have two other server configs listening on port 80 that works fine.
My nodejs app looks like the following:
var express = require('express'),
http = require('http'),
https = require('https'),
fs = require('fs'),
Security = require('./security.js'),
json = require('express-json'),
stylus = require('stylus'),
nib = require('nib'),
path = require('path'),
bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.use(json());
app.use(authChecker);
app.use(stylus.middleware({
src: __dirname + '/public',
compile: compile,
keepExtensions: true,
uploadDir: __dirname + '/public/images'}));
app.use(express.static(path.join(__dirname, 'public')));
require('./routes-v1.js')(app);
require('./routes-v2.js')(app);
var hskey = fs.readFileSync('certificates/key.pem');
var hscert = fs.readFileSync('certificates/key-cert.pem');
var options = {
key: hskey,
cert: hscert
};
http.createServer(app).listen(4080);
console.log('Listening on port 4080...');
https.createServer(options, app).listen(4443);
console.log('Listening on port 4443...');
function authChecker(req, res, next) {
console.log("authChecker");
res.setHeader('Content-Type', 'application/json');
if (!Security.checkHMAC(req)) {
res.json({unauthorized:true});
} else {
next();
}
}
function compile(str, path) {
return stylus(str).set('filename', path).use(nib());
}
My problem is that the newly defined urls in NGINX doesn't work. The IP's work fine. What am I missing here? Is it something in NGINX or in node? My other subdomains through NGINX works flawlessly.

Error in socket.io connection - 'Session ID unknown'

I have an "Elastic Beanstalk" environment with the scalable server running 2 Ec2 instances.
On my root, I am serving angular 7 content using PHP API (using Codeigniter from API folder). I have socket.io chat module in my Node folder.
I am using Nginx to set up the Node and PHP project in a single EB environment.
I tried different options by modifying my Nginx .conf file as per some research but not able to fix the issue.
Here my Nginx .CONF setting file for SOCKET.IO.
server {
listen 80;
server_name sub.domain.com;
root /var/app/current/node;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 3600;
proxy_connect_timeout 3600;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header Host $host;
}
}
My socket.io code.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 3000;
var socketController = require('./socketController.js')
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
var nsp = io.of('/namespace');
/**
* Socket for chat message
*/
io.on('connection', function (socket) {
socket.on('client_user_join', socketController.userJoin.bind(null, socket, nsp, io));
socket.on('client_send_chat_message', socketController.sendChatMessage.bind(null, socket, nsp, io));
socket.on('client_received_chat_message', socketController.receivedChatMessage.bind(null, socket, nsp, io));
socket.on('disconnect', socketController.disconnect.bind(null, socket));
});
http.listen(port, function () {
console.log('listening on *:' + port);
});
I am getting Session ID unknown at intial lever and after few minutes I am getting failed: Error during WebSocket handshake: Unexpected response code: 400
My socket connection link
https://sub.domain.com/socket.io/?EIO=3&transport=polling&t=Mtzwcpj&sid=TSMAaNI0KxDuzcIEAA4s
I already try following option.
Try to connect socket server using different transports.
this.socket = io('https://sub.domain.com/namespace', {transports: ['websocket']});
At the AWS side, enable Stickiness to 1 day.
I do not understand whether it blocking by Nginx configuration or I have to use Radis to fix this as I have two EC2 instances running.

Resources