Express-Socket.IO App isn't working with my Azure WebApp - node.js

For educational purposes I try to deploy an Express Server that is using Socket.IO. The Server should be able to deliver a static HTML Site that was built with React, answer with a "Hello Azure!" message whenever I make a GET Rest Call to http://localhost:4000/api/azure and whenever a new client connects to the site, all the other clients get a message announcing the new client.
const path = require('path');
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const router = require('./api/azure');
const PORT = process.env.PORT || 4000;
io.on('connection', () => {
console.log('A new user has connected!')
io.emit('broadcast', 'A new user has connected');
});
app.use(express.json());
app.use('/api/azure', router);
app.use(express.static(path.join(__dirname, 'build')));
app.use(express.static('public'));
app.use('/', (_, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
server.listen(PORT, () => {
console.log(`Listening to http://localhost:${PORT}`);
});
All this tasks are fulfilled without problems in localhost. The problem begins after this app is uploaded to one of my Azure WebApps.
Instead of delivering the message "Hello Azure!" when I call the https://mydomain.azurewebsites.net/api/azure it responses back with the HTML file.
The typical Socket.IO GET method for polling
https://mydomain.azurewebsites.net/socket.io/?EIO=4&transport=polling&t=SomeString
responses back with the HTML file, too.
Everything url extension that I give, gives me back the HTML file.
I barely know the basic stuff about WebApps. Maybe there is a configuration that I am forgetting? By the way I haven't done anything in the configuration except that I enabled the Websockets in the WebApp config.
This never happened before. The only difference is that right now I am using a free-tier just to test. Could it be that? If not, what am I doing wrong?
Thank you for your time!

To begin with, try turning the Web Socket config off as it applies to an IIS setting which tends to contradict with the Node.js websocket implementation.
If this doesn't help, try and force the transport layer to use Websockets and SSL.
io.configure(function() {
// Force websocket
io.set('transports', ['websocket']);
// Force SSL
io.set('match origin protocol', true);
});
Also, you cannot use arbitrary ports (port 4000 in your case) on services like App Service. Your app will be provided a port via process.env.PORT. So ensure that you are refering to the correct port from your log message. You should be able to see these in your log stream.
Also note, that Azure has launched a fully managed service called Web PubSub to power your apps with Web Sockets. The app service web socket implementation does not scale horizontally, this where Web PubSub will help you.
https://azure.microsoft.com/en-in/blog/easily-build-realtime-apps-with-websockets-and-azure-web-pubsub-now-in-preview/

Related

My node.js-powered app can only be accessed from some remote clients but not others. How can I get all clients to access it?

I am developing a multi-user, interactive, game-like collective behavior experiment that uses node.js, socket.io, and express. The site for it is:
http://pcl.cogs.indiana.edu:8080/
When I am at my university, the same university where the server is deployed, or when I am elsewhere but first VPN into my university, the site works fine and I am immediately put into the experiment. When I am not at my university and don’t VPN in, usually Chrome spins around for awhile and then eventually delivers the error message:
This site can’t be reached
pcl.cogs.indiana.edu took too long to respond.
ERR_CONNECTION_TIMED_OUT
When I am away from my university and don’t VPN in, the site still sometimes works, perhaps depending on how the client’s network is set up? How do I troubleshoot where the problem is? That is, how do I figure out if the problem is with the network settings at the client end, or at the server end at my university? Is there a way to inspect some log that tells me what clients attempted to reach the server but were rejected? Most pragmatically, what kinds of fixes should I be looking at in my server code or server settings that will let all remote clients into the experiment? I’m including the beginning of my app.js code in case that helps:
var express = require('express');
var fs = require("fs");
var app = express();
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/public/index.html');
});
app.set('view engine', 'pug');
const server = require('http').Server(app);
const io = require('socket.io')(server);
if (module === require.main) {
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
}

404 - from nginx application to remote servers localhost?

I am new to react, and wanted to deploy a site to my domain with Nginx. I need to make the application to be able to fetch from client side, to the localhost of the remote server hosting the site with Nginx. I know exposing this many details might make security experts and hackers either drool or shake their heads. But I am losing my sanity from this.
This is a filtered version of my Node.js express service running on the remote server:
const express = require("express")
const cors = require("cors")
const app = express();
const PORT = 1234;
const spawn = require("child_process").spawn;
app.use(cors())
app.listen(PORT, function(){
console.log(`listening on port:${PORT}...`)
})
app.get("/api/play/:choice", function(req,res){
pythonProcess = spawn('python',["./script.py", req.params.choice]);
pythonProcess.stdout.on('data', function(data) {
res.status(200).send(data.toString('utf-8'))})
})
this is how I am fetching from the deployed react application. The public IP of the droplet I am using
fetch(`104.248.28.88/1234/api/play/rock`)
Change the fetch to replace the / with a : to indicate port, rather than directory
fetch("104.248.28.88:1234/api/play/rock")

Socket IO net::ERR_CONNECTION_REFUSED

I am trying to implement socket.io into my application which is hosted at Azurewebsites. webapp
Here is the server.js
var app = require('express')();
var server = require('http').createServer(app);
server.listen(process.env.PORT || 3001)
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
var io = require('socket.io')(server);
io.on('connection', function (socket) {
console.log("Socket connected :"+socket.id);
socket.emit('news', { hello: 'world' });
});
And Here is the client side socket. index.html
<script src="socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:3001');
console.log("scoekt connect",socket)
socket.on('connect', function(){ console.log('connected to socket'); });
socket.on('error', function(e){ console.log('error' + e); });
socket.on( 'news', function( data ){
console.log("socket data",data);
});</script>
I am getting the below error
I am not really sure whats is going wrong. Here is the structure of file-ing system
ROOT
app/
index.html
server.js
web.config
PS: this is an Angular2 application
PS: I have checked all the suggested question based on this error but none solved my issue, thus i am posting this question.
Per my experience, Azure Web App doesn't bind loaclhost or 127.0.0.1 to your website, and only ports 80 and 443 are public-facing. This maps to a specific port for your app to listen to, retrievable via process.env.PORT. So you'd need to replace
var socket = io('http://localhost:3001');
with
var socket = io('http://<your app name>.azurewebsites.net');
And if your server side and client side in the different domain, you'd also need to enable CORS on the server side. In Azure, we can enable it with the Azure portal.
In a browser, go to the Azure portal, and navigate to your App Service.
Click CORS in the API menu.
Enter each URL in the empty Allowed Origins text box. A new text box is created. As an alternative, you can enter an asterisk (*) to specify that all origin domains are accepted.
Click Save.
Socket.IO uses WebSockets, which are not enabled by default on Azure. You can also enable WebSocket support using the Azure Portal. Please see the steps below.
In the Azure portal, click Application settings in the SETTINGS menu.
Under Web Sockets click On
Click Save.
For more info, please refer to this documentation.

Socket.io + Azure web sockets issue

I am working on a multiplayer chess game with NodeJS and socket.IO.
I have problem hosting it on Azure tho.. I tried many different approaches, a few mentioned:
Forcing the application to only use WebSockets by adding the code below:
io.configure(function() {
io.set('transports', ['websocket']);
});
Added <webSocket enabled="false"/> in web.config file..
Note: This disables the IIS WebSockets module, which includes its own implementation of WebSockets and conflicts with Node.js specific WebSocket modules such as Socket.IO. If this line is not present, or is set to true, this may be the reason that the WebSocket transport is not working for your application.
Matching origin protocol to ensure no SSL issues.
io.configure(function() {
io.set('match origin protocol', true);
});
I now started from scratch, since I thought my server-side part was corrupt, and tried Socket.io chat example instead.
I followed the steps.
Created a new web app on Azure.
Published my files through FileZilla FTP.
Enabled Web Sockets on Azure for my app (disabled by default).
STILL THE SAME ERROR! See picture below.
Anyone? I am unsure if it's a client-side or server-side issue. It seems like it's trying to XHR-poll instead of using web sockets..
Thanks in advance.
I got it working, thank you Chris Anderson-MSFT for your help.
The weird thing that occurred for me when deploying with FTP was that my node_modules folder differed with version(s) specified in my package.json.
I solved this by connecting my web app on Azure to a local Git repository and deploying the app through git. This connects my packages recursively and matches correct versions.
I also needed to enforce my client-side socket-io to use web sockets by specifying transport method:
var socket = io({transports:['websocket']});
And this is what my server-side file ended up looking like:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
app.use(express.static('public'));
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/default.html');
});
io.on('connection', function(socket) {
io.set('transports', ['websocket']);
console.log('new connection on socket.io');
socket.on('move', function(msg) {
socket.broadcast.emit('move', msg);
});
});
server.listen(port, function () {
console.log('Server listening at port %d', port);
});

Socket.io-based app running through node proxy server disconnecting all sockets whenever one disconnects

I made a basic chat app using node.js, express and socket.io. It's not too different from the tutorial chat app for socket.io, it simply emits events between connected clients. When I ran it on port 3001 on my server, it worked fine.
Then I made a proxy server app using node-http-proxy which listens on port 80 and redirects traffic based on the requested url to various independent node apps I have running on different ports. Pretty straightforward. But something is breaking. Whenever anyone disconnects, every single socket dis- and re-connects. This is bad for my chat app, which has connection-based events. The client consoles all show:
WebSocket connection to 'ws://[some socket info]' failed: Connection closed before receiving a handshake response
Here's what I think are the important parts of my code.
proxy-server.js
var http = require('http');
var httpProxy = require('http-proxy');
//create proxy template object with websockets enabled
var proxy = httpProxy.createProxyServer({ws: true});
//check the header on request and return the appropriate port to proxy to
function sites (req) {
//webapps get their own dedicated port
if (req == 'mychatwebsite.com') {return 'http://localhost:3001';}
else if (req == 'someothersite.com') {return 'http://localhost:3002';}
//static sites are handled by a vhost server on port 3000
else {return 'http://localhost:3000';}
}
//create node server on port 80 and proxy to ports accordingly
http.createServer(function (req, res) {
proxy.web(req, res, { target: sites(req.headers.host) });
}).listen(80);
chat-app.js
/*
...other modules
*/
var express = require("express");
var app = exports.app = express(); //I probably don't need "exports.app" anymore
var http = require("http").Server(app);
var io = require("socket.io")(http);
io.on("connection", function (socket) {
/*
...fun socket.on and io.emit stuff
*/
socket.on("disconnect", function () {
//say bye
});
});
http.listen(3001, function () {
console.log("listening on port 3001");
});
Now from what I've read on socket.io's site, I might need to use something to carry the socket traffic through my proxy server. I thought that node-http-proxy did that for me with the {ws: true} option as it states in their docs, but apparently it doesn't work like I thought it would. socket.io mentions three different things:
sticky session based on node's built in cluster module
socket.io-redis, which allows separate socket.io instances to talk to each other
socket.io-emitter, which allows socket.io to talk to non-socket.io processes
I have exactly no idea what any of this means or does. I am accidentally coding way above my skill level here, and I have no idea which of these tools will solve my problem (if any) or even what the cause of my problem really is.
Obligatory apology: I'm new to node.js, so please forgive me.
Also obligatory: I know other apps like nginx can solve a lot of my issues, but my goal is to learn and understand how to use this set of tools before I go picking up new ones. And, the less apps I use, the better.
I think your intuition about needing to "carry the socket traffic through" the proxy server is right on. To establish a websocket, the client makes an HTTP request with a special Upgrade header, signalling the server to switch protocols (RFC 6455). In node, http.Server instances emit an upgrade event when this happens and if the event is not handled, the connection is immediately closed.
You need to listen for the upgrade event on your http server and handle it:
var proxy = httpProxy.createProxyServer({ws: true})
var http = http.createServer(/* snip */).listen(80)
// handle upgrade events by proxying websockets
// something like this
http.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head, {target:sites(req.headers.host)})
})
See the node docs on the upgrade event and the node-http-proxy docs for more.

Resources