Using node, socket.io, ember.js and nginx - node.js

I have a Nginx Serving my ember build
as follows:
server {
listen 80;
root /home/ubuntu/my-app/dist;
index index.html;
location /api {
proxy_pass http://127.0.0.1:3000;
}
location / {
try_files $uri $uri/ /index.html;
}
}
I want to add a chat using socket.io, but I already have REST api on port 3000.
I'm wondering what the best way to architect this.
I thought I could add another location as follows:
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://socket_nodes;
}
But it's not working. If someone could point me in the right direction, that'd be really great.
thanks in advance.

If you're using node as your web server, then socket.io shares the same port and IP as the web server and your REST API.
A socket.io connection starts out as a regular http request to the /socket.io path. The socket.io library hooks into your web server to handle that specific http request. After a couple back and forth, the two ends agree to "upgrade" the protocol from http to webSocket and then the conversation continues as the webSocket protocol but still on the same IP and port as your webServer operates on.
All this can work fine with nginx as a proxy if you configure nginx as specified in the configuration link I gave you earlier so that it proxies all the right things and if socket.io is configured properly with your nodejs server to hook into it properly.
There's really no architectural changes to make as the web requests and socket.io connections both operate through the same web server without you having to do anything. The socket.io connection just makes an http request to the /socket.io path with some special HTTP headers set. The socket.io server code just hooks into your web server to handle that specific request and take it from there. The rest of your REST API calls are just handled by the same mechanism you already have. So, as long as you don't try to define an API call for /socket.io, the two will happily stay out of each other's way, just like the handlers for two different routes on your web server stay out of each other's way. You can see a lot more about how incoming socket.io calls work in this answer.

So I finally got this to work and thought I'd share my findings.
Nginx:
For my api proxy, I can actually share the same port as my node API. I just needed to add version and headers.
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
server.js
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket) {
console.log("[NOTIFICATION] - New connection");
io.sockets.emit("message", { message: "New connection" });
socket.on("send", function(data) {
io.sockets.emit("message", { message: data.message });
});
http.listen(3000);
Ember:
https://github.com/keydunov/emberjs-socketio-chat
is a pretty good example, and use of socket.io

Related

How do I connect Vue application to server (Node, MongoDB)?

I've been learning frontend development only and just recently went over basics of Nodejs. I know that I would connect to certain port number when developing in Nodejs alone. However, I'm confused about how I would connect Vue application (built with Vue CLI) to backend since npm run serve will automatically connect to port 8080 by default.
My ultimate goal is to connect MongoDB to my application. Current error I'm getting is Error: Can't resolve 'dns'.
TLDR: Could someone please explain in newbie term how I can connect Vue application with MongoDB?
In my opinion, you have two ways of solving this:
First, there is a field called devServer through which you can tweak the configuration of the dev server that starts up when you run npm run serve. Specifically, you want to pay attention to proxy field, using which you can ask the dev server to route certain requests to your node backend.
Second, depending on your setup, you could use a different host altogether to handle backend calls. For example, as you mentioned, the dev server runs on 8080 by default. You could set up your node backend to run on, say, 8081 and all backend requests that you make in your VueJS app will explicitly use the host of <host>:8081. When you decide to move your code into production, and get SSL certificates, you can have a reverse-proxy server like Nginx redirect all requests from say, api.example.com to port 8081.
As for connections to MongoDB, IMO, here's a question you should be asking yourself:
Is it safe to provide clients direct access to the database?
If the answer is yes, then by all means, ensure the mongoDB server starts with its HTTP interface enabled, set up some access restrictions, update the proxy and/or nginx and you're good to go.
If the answer is no, then you're going to have to write light-weight API endpoints in your NodeJS app. For example, instead of allowing users to directly talk to the database to get their list of privileges, you instead make a request to your NodeJS app via GET /api/privileges, and your NodeJS app will in turn communicate with your database to get this data and return it to the client.
Another added benefit to having the backend talk to your database rather than the client, is that your database instance's details are never exposed to malicious clients.
Here's a sample vue.config.js setup that I have on one of my websites:
const proxyPath = 'https://api.example.com'
module.exports = {
devServer: {
port: 8115, // Change the port from 8080
public: 'dev.example.com',
proxy: {
'/api/': {
target: proxyPath
},
'/auth/': {
target: proxyPath
},
'/socket.io': {
target: proxyPath,
ws: true
},
'^/websocket': {
target: proxyPath,
ws: true
}
}
}
}
Here's the nginx config for the same dev server. I quickly pulled what I could from our production config and obscured certain fields for safety. Consider this as pseudo-code (pseudo-config?).
server {
listen 443 ssl;
server_name dev.example.com;
root "/home/www/workspace/app-dev";
set $APP_PORT "8115";
location / {
# Don't allow robots to access the dev server
if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|Googlebot") {
return 404;
}
# Redirect all requests to the vue dev server #localhost:$APP_PORT
proxy_pass $scheme://127.0.0.1:$APP_PORT$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 443 ssl;
server_name api.example.com;
set $APP_PORT "8240";
location / {
# Don't allow robots to access the dev server
if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|Googlebot") {
return 404;
}
# Redirect all requests to NodeJS backend #localhost:$APP_PORT
proxy_pass $scheme://127.0.0.1:$APP_PORT$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

502 Bad Gateway when connecting to Nodejs app running express through Nginx

I'm having issues connecting to my node app that is running on port 8081.
My setup is as follows (everything runs on a Raspberry Pi):
NGINX
events {
worker_connections 1024;
}
http {
server {
root /data/web;
location / {
}
location /pub {
proxy_pass http://localhost:8081;
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;
}
}
}
I'm serving static files with the first location (which seems to be working fine), and I would like the second location to reroute to my node app. which is running on port 8081.
My node app looks like this:
app.get('/', function(req, res){
res.send("Hello World!");
});
var server = app.listen(8081, '192.168.0.178');
And I'm testing my connection using a simple wget from another pc in the LAN:
wget http://192.168.0.178/pub
The full error I get is this:
http://192.168.0.178/pub
Connecting to 192.168.0.178:80... connected.
HTTP request sent, awaiting response... 502 Bad Gateway
2018-01-14 15:42:27 ERROR 502: Bad Gateway.
SOLUTION
The accepted answer was indeed the problem I was having.
Another thing I added was a rewrite in my /pub location because '/pub' needs to be cut off from the url going to the Node app. So the final nginx conf looks like this:
http {
access_log /data/access_log.log;
error_log /data/error_log.log debug;
upstream backend {
server localhost:8081;
}
server {
root /data/web;
location / {
}
location /pub {
proxy_pass http://localhost:8081;
rewrite /pub(.*) /$1; break;
}
}
}
The problem seems related to the network interface you are exposing the nodejs app. You have setup the app to listen to port 8081 on the interface with ip 192.168.0.178, but the nginx is proxying trough the loopback interface, given the instruction
proxy_pass http://localhost:8081;
You can solve this issue exposing the nodejs app on the loopback interface:
var server = app.listen(8081, 'localhost');
The node app should be no more reachable directly on port 8081 from any other machine except the one the app is running

express-ws doesn't handle call when deployed to VPS server

I have back-end written using node.js + express with express-ws depedency.
Locally everything works like it should. Previously it was deployed to red hat open shift, also haven't had any problem. Yesterday I bought VPS configured it and deployed there. Everything works except websockets.
I have nginx with enabled SSL that has the next lines in config related to the server
server {
listen ipaddresshere:80 default;
server_name _;
location / {
proxy_pass http://ipaddresshere:8080;
}
location /ws {
proxy_pass http://ipaddresshere:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
I have other config places but they were generated by VestaCP and https://certbot.eff.org/
What I know that request to /ws route is coming to node.js app (I am logging it). But it doesn't go to that handler
app.ws('/ws', SocketsHandler.registerWs);
In the end it matches with my last handler and returns 404
app.get('*', ErrorHandler.notFound);
The question: What it can be that WS library doesn't work in VPS environment but I don't see any error in console... ?
P.S. Localy I run app without SSL and nginx
wsServer.on('connection', function (socket) {...})
I found that my config was overridden by some other file. So instead of Connection "upgrade"; server was receiving Connection "close"..

nginx+nodejs+socket.io ERR_CONNECTION_TIMED_OUT

I almost tried any solution I can find in forums and blogs but I have no luck that's why I'm asking for any help right now.
Here's the situation, I am currently using Ubuntu and I'm running 2 sockets in it before which running perfectly but when I tried to add another 1 more socket the problem arise (The ERR_CONNECTION_TIMED_OUT).
Here is my set up on NGINX for my third socket
upstream stream {
server localhost:3210;
}
server {
location /socket.io {
proxy_pass http://stream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
This is the same exact nginx setup that I have had with my first 2 app, that's why I'm having a hard time to debug it, also with the nodejs server.
http.listen(3210, function(){
console.log('Listening on Port 3210');
});
and on front-end
var socket = io.connect('http://testapp.com:3210');
This seems incorrect:
var socket = io.connect('http://testapp.com:3210');
Port 3210 is what Express is listening on, and given that you're proxying using Nginx I'd expect that the client should connect to Nginx, not Express:
var socket = io.connect('http://testapp.com');
(provided that Nginx is running on port 80)

Mixed content error when proxying websocket through nginx with SSL

I am working on a node.js application using express to serve content and socket.io for websocket communication. The setup has been working fine, but now I want to be able to access the websocket via SSL, too. I thought using nginx (which we already used for other stuff) as a proxy was a good idea, and configured it like this:
upstream nodejs {
server 127.0.0.1:8080;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
server_name _;
location / {
proxy_http_version 1.1;
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 Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://nodejs;
proxy_redirect off;
}
}
The node.js server is set up like this:
var express = require('express'),
app = express();
// some app configuration that I don't think matters
server = http.createServer(app).listen(8080);
var io = require('socket.io').listen(server);
io.configure(function() {
io.set('match original protocol', true);
io.set('log level', 0);
io.set('store', redisStore); // creation of redisStore not shown
}
Both nginx and node.js run inside a Vagrant box which forwards port 443 (which nginx listens on) to port 4443 on the host system.
With this setup, navigating to http://localhost:4443 (using Firefox 23) gives me access to the files served by Express, but when socket.io tries to connect to the socket, it throws the following error:
Blocked loading mixed active content "http://localhost:4443/socket.io/1/?t=1376058430540"
This outcome is sadly obvious, as it tries to load the JS file via HTTP from inside an HTTPS page, which Firefox does not allow. The question is why it does so in the first place.
Socket.io tries to determine which protocol is used to access the web page, and uses the same protocol in the construction of the above URL. In this case, it thinks it is being accessed over HTTP, which may be the result of being proxied. However, as I understand, setting match original protocol to true in the socket.io config is supposed to help in situations like this, but it does not in my case.
I have found numerous questions and answers here about websocket proxying, but none that deal with this particular issue. So I'm pretty much at wit's end, and would really appreciate some advice.
Change match original protocol to match origin protocol:
io.configure(function() {
//io.set('match original protocol', true);
io.set('match origin protocol', true);
...
}

Resources