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

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;

Related

Cors error in production with nuxt js app

I have an error on my Nuxt App in production and i don't understand why it not work.
My client use http://localhost:3000 and my server use http://localhost:8080
My application is hosted in AWS EC2 Linux.
My nginx config is :
server {
listen 80;
# Your domain
server_name myservername;
# Proxy to nuxt renderer.
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;
}
# Redirect from /path/ to /path
rewrite ^/(.*)/$ /$1 permanent;
}
I use axios for call express API.
My nuxt.config.js file have this property :
publicRuntimeConfig: {
axios: {
baseURL: 'http://localhost:8080/api'
}
},
My server.js file have :
const cors = require("cors");
const app = express();
var corsOptions = {
origin: "http://localhost:3000"
};
app.use(cors(corsOptions));
When i open my page http://myadress.com/cars i have this error :
However when I'm local environment everything works perfectly.
Thanks for your help

Using Nginx as proxy for Node.js' Express and Socket.io

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
...
}

Socket.io Socket.onPacket Error with reverse proxy Nginx

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

CORS issues with Express, Socket.io and Nginx

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.

socket.io + nginx + proxy problems

I have a socket.io webservice running port 8080 on my production server, it responds to http requests, but I think its having difficulty resolving the proxy when my client is sending over websocket protocols (ws://)
My client is telling me that the server is responding with a 400 (bad request) error, so something is either wrong on my client side, or my production server. Im banking on it being my production server, but neither myself or a co worker of mine can figure out where for sure.
These are the nginx configurations we have for our node.js production box.
I have replaced the real url with someapp.com
NGINX
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name app.someapp.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:3000;
}
}
server {
listen 8080;
server_name app.someapp.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:4000;
}
}
server {
listen 80;
server_name tools.someapp.com;
root /var/www/bbclient/dist;
location / {
try_files $uri $uri/ /index.html;
}
}
its a very small and simple socket.io node.js server running on port 8080 in production
Socket.IO
const app = require('express')()
const http = require('http').Server(app)
const io = require('socket.io')(http)
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
// this is not responding from the client
// this is working on localhost, but not in production
io.on('connection', (socket) => {
socket.on('USER_ID', (userId) => {
if (socket.userId) {
socket.leave(socket.userId)
}
socket.userId = userId
socket.join(userId)
})
})
// this works over http, and responds in production
// by visiting http://app.someapp.com:8080 in the browser
app.get('/', (req, res) => {
res.send('some app')
})
http.listen(process.env.PORT || '8888', () => {
console.log('listening')
})
Here is what the network tab is saying about the request coming from my client resulting in a 400 error.
chrome browser network tab
Edit 1
NGINX error logs:
2018/02/06 17:30:39 [error] 5954#5954: *86956 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 76.218.92.156, server: app.someapp.com, request: "GET /socket.io/?EIO=3&transport=polling&t=M5iAdrw&sid=Jays0pqU3StUhdjjAACb HTTP/1.1", upstream: "http://127.0.0.1:4000/socket.io/?EIO=3&transport=polling&t=M5iAdrw&sid=Jays0pqU3StUhdjjAACb", host: "someapp.com:8080", referrer: "http://tools.someapp.com/scorecards/estimating"
First of all add socket.io proxy directives:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Here's an example.
Then try to increase proxy connection timeout:
proxy_connect_timeout 75s;
proxy_read_timeout 75s;
proxy_send_timeout 75s;

Resources