How to make Socket IO work over HTTPS using node.js - node.js

This is frustrating me like crazy!
i'm using a proxy so that any requests that go to myurl/APP/ are answered by node.js at myurl:8001
I now need this to work over https. easy.... i thought.
i have Apache serving up the old version from the public folder. That is stand alone, and when i'm done building this, it will just be removed. but for now needs to remain in tact and accessible. Lets encrypt is setup on this. and https://myurl works fine, showing content from the public folder of course.
if i go to https://myurl:8001 then chrome says "site can't be reached". If i go to http://myurl:8001 it works fine. I think this is because https default port is 443. I have VPS not dedicated so i don't think i can alter that. And surely if i did alter the ssl port then it wont work for the public folder??
i'll show you the basics of whats going on;
app.js;
var express = require('express');
var app = express();
var serv = require('http').Server(app);
app.get('/',function(req, res) {
res.sendFile(__dirname + '/client/index.html');
});
app.use('/client',express.static(__dirname + '/client'));
serv.listen(8001);
console.log("Server started.");
var SOCKET_LIST = {};
var io = require('socket.io')(serv,{});
io.sockets.on('connection', function(socket){
socket.id = Math.random();
socket.x = 0;
socket.y = 0;
socket.number = "" + Math.floor(10 * Math.random());
SOCKET_LIST[socket.id] = socket;
socket.on('disconnect',function(){
delete SOCKET_LIST[socket.id];
});
});
setInterval(function(){
var pack = [];
for(var i in SOCKET_LIST){
var socket = SOCKET_LIST[i];
socket.x++;
socket.y++;
pack.push({
x:socket.x,
y:socket.y,
number:socket.number
});
}
for(var i in SOCKET_LIST){
var socket = SOCKET_LIST[i];
socket.emit('newPositions',pack);
}
},1000/25);
client/index.html;
<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script>
var ctx = document.getElementById("ctx").getContext("2d");
ctx.font = '30px Arial';
var socket = io.connect('http://www.myurl:8001', {path: "/socket.io"});
socket.on('newPositions',function(data){
ctx.clearRect(0,0,500,500);
for(var i = 0 ; i < data.length; i++)
ctx.fillText(data[i].number,data[i].x,data[i].y);
});
</script>
It works fine as the code is but only over http. I need this to work over SSL
i need this line to work when its https;
var socket = io.connect('https://www.myurl:8001', {path: "/socket.io"});
How is this possible?
any help is greatly appreciated.

Your server code is only creating an http server, not an https server and since socket.io uses your http server, it will only run on http. http and https servers are different (the https server implements certificate verification and data encryption which is not present with the http server).
To make an https server, change this:
var express = require('express');
var app = express();
var serv = require('http').Server(app);
to this:
const express = require('express');
const app = express();
const options = {...}; // fill in relevant certificate info here
const serv = require('https').createServer(options, app);
And, you will have to fill in the appropriate certificateinfo in the options data structure.
https.createServer() code examples are here.
You can then run your https server on any port you want as long as you connect to it with an https URL and the right port number. You are correct that the default port for an https URL is 443 so if not port number is specified, that's what a browser will attempt to use.

Ok this was a bit embarrasing... the actual problem i had was because of the following.
If you can follow this lol...
So in dreamhost they allow you to run apache and serve content in the usual way. there is a /public folder for this. fairly standard stuff.
Because i'm trying to branch out into being able to use sockets (please don't lecture me on using sockets with PHP... let it go!) So i was using dreamhost which allows a person to proxy a node.js app through a port. eg. port xxxx ends up at thewebsiteurl/whateverfolderyouchoose/
ok so in my node.js i was using express. now in express you can specify routing really easy. For some bizzare reason, i cant be bothered to actually find out why, if you know please enlighten me... i had to put double forward slash in the routing, like so "//whateverroute/". all the examples i was learning from use a single forward slash. eg "/whateverroute/" but that didn't work. if you know why, please tell us. So before i figured this out, i couldn't actually route anything correctly, hence having to use the url with the port number at the end. of course that messes up the https. example, https://www.theurl.com:xxxx wasnt secure, because the node.js app wasnt running an https res req server. Actually i did set up the https node server and thought that was the solution to the problem at first.
So the solution to this bizarre problem was the node.js routing. ofcourse its stupidly complicated because, hey life's like that when your learning from google and not someone who actually knows your exact setup.
What turns out to be funny is, i cant stand node.js. I can see the benefit to using it. But it seems like alot of ball ache for something as simple as what i'm making. Also i have now discovered phone/gap so php actually is ok for what i want to do. Ok so i'm hitting the server way to many times with AJAX requests but it's coping with a few hundred users right now and when it hits the 10'000 mark when the site probably wont work very well, i should be making enough to get a real node.js programmer in to learn from.... hopefully.
I do appreciate the help tho guys.

Related

Nodejs SSL using CloudFlare not working over https

So the problem I'm having is that the client won't connect with the server.js when the server.js is using https.
if I go to "https://mydomainame.com" I get this error in the console of every other browser than brave browser
index.js:83 GET https://serverip:8081/socket.io/?EIO=3&transport=polling&t=NK0oCD6 net::ERR_CERT_AUTHORITY_INVALID
(The blacked out is the IP address of the server)
the weird thing is that in the brave browser the domain changes to "http://mydomainame.com" and the client then is connected to server.js
I'm using free Cloudflare with Full end to end encryption
server.js code:
var express = require('express'),
https = require('https');
var app = express();
var fs = require('fs');
var httpsOptions = {
key: fs.readFileSync('/var/www/ssl/sitename.com.key'),
cert: fs.readFileSync('/var/www/ssl/sitename.com.pem')};
var server = https.createServer(httpsOptions,app);
var io = require('socket.io').listen(server);
const port = 8081;
server.listen(port);
And client.js connection code:
socket = io.connect('https://serverip:8081', {secure: true});
I am using the same Origin Certificates for the server and for the nodejs code.
The server is using Apache2 with PHPMyAdmin and is configured to make the domain only work using https.
I read somewhere something Cloudflare not being able to use other ports than 443 and some other but I did not really understand it, And I can't get the server.js to work over port 443.
I'm thankful for any information or help I can get! :)
So I figured it out, big thanks to Eric Wong for pointing out the biggest problem that I was trying to connect to the server using its IP there for not going thru Cloudflare.
Then in this article Identifying network ports compatible with Cloudflare's proxy
you can see what ports Cloudflare allows connections on then, I changed my code to used the https port 8443.
socket = io.connect('https://domainname.com:8443',{secure: true});
then the only thing I had to do was to port forward the new port and everything worked fine!

App using socket.io and Node.js works locally but not deployed to heroku

Tried other solutions I could find such as adding process.env.PORT and also looked at heroku's guide on socket.io.
The Phaser.io game my group is making works perfectly locally, however when we deploy to heroku there is a bug where only the first player connected will see the other players, and only for a brief moment as well. After that short period, the players freeze.
My question is, does anyone have any clue what could potentially be causing the issue? Is it something wrong with our implementation of socket.io?
Thank you in advance for your help. Extremely confused as to what is causing this issue.
Here is a deployed version: https://death-road-to-toronto.herokuapp.com/
Here is the repo if you'd like: https://github.com/Road-To-Capstone/CapstoneMultiplayer/
Here are the code snippets relevant to socket and express
Index.js
const config = require('./config');
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use('/', express.static(config.publicDir));
//-socket
const players = require('./players.js');
const missiles = require('./missiles.js');
const zombies = require('./zombies.js');
io.on('connection', socket => {
-------
});
//=socket
server.listen(process.env.PORT || config.port, () => {
console.log(`Listening on ${config.port}`);
});
//creating new zombie id
function newZombieId() {
let id = new Date();
return id.getTime();
}
config.js
const path = require('path');
module.exports = {
port: 3000,
publicDir: path.join(__dirname, '../public'),
game: {
start: {
x: 400,
y: 400
}
}
}
EDIT1 : I made a funny discovery while playing around with socket.io and heroku. So if I connect with another computer to the deployed version on heroku, then click on console (just to freeze the game). Anyone at that point can join and everything works perfectly, just like in the local copy.
Does anyone know why? If not what can I do as a work around? Perhaps implement a lobby system and allow all players to join and start at the same time?
Solved my problem! Going to leave my question and the solution here for prosperity, incase other people want to implement a multiplayer game using Phaser and socket.io on Heroku and also run into issues.
We added this to the index.html file </script type ="text/javascript" src="/socket.io/socket.io.js"> </script> which allows heroku to access the socket library.
It actually wasn't a setup issue with socket.io, it was our logic. When a player detects movement it instantly tries to socket.broadcast that a player moved. However, when other players first connect, they don't have time to retrieve all the players information from the server side. Thus the broadcast would cause an error where "player" is undefined. Adding a conditional statement fixed our issue.
The code in it's entirety is going to be left on github if you want to investigate further.

Socket.io, server not responding to connection event

I have a managed server at cloudways. I am trying to use socket.io.
client.php:
var socket = io('http://localhost:3000');
console.log(socket);
server.js:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(3000);
io.on('connection', function(socket){
console.log('is connected');
});
When visiting the page, I see two instances of 200 OK in the network section of the console.
The console in the browser also prints the socket variable, which has "connected : true".
However, I DO NOT see "is connected" in the terminal (from server.js), it is just blank.
This site has worked locally, but these problems are appearing when trying to get it to work at Cloudways.
Any ideas?
I have tried switching "localhost" to both the domain name and the IP-address, and I have tried different ports, but 3000 is the only one that will let me start the server.js without error.
EDIT
Cloudways have confirmed that port 3000 should be used.
I have tried changing the url in io() to:
var socket = io('http://my-cloudways-url:3000'); (net::ERR_EMPTY_RESPONSE)
var socket = io('http://www.my-domain.club:3000'); (net::ERR_EMPTY_RESPONSE)
var socket = io('http://ip-adr:3000'); (net::ERR_EMPTY_RESPONSE)
And today I am getting
var socket = io('http://localhost:3000'); (net::ERR_CONNECTION_REFUSED)
I have also tried io.connect(using the same adresses / ports).
Maybe is your php index... Because you are setting the localhost and the port.
I'm not sure about how Cloudway works. But, when we run the server.js, some clouds give some url for access our projects...
In your cloudways try to use the official URL that they given to you access the project:
var socket = io.connect('url.from.cloudway');
For example, I'm using cloud9, and when I execute the server.js, return one url like:
Your code is running: https://demo-some-id.c9.io
And when I'm using socket.io, I need to set in my index the URL parameter inside the IO.
var socket = io('https://demo-some-id.c9.io');
You need to add the client-side socket.io.js version and add the script tag with the path, like:
<script src="js/socket.io.js"></script>
Obs.: Some clouds, you need to set the url in your <script> tag
<script src="https://demo-some-id.c9.io/socket.io/socket.io.js"></script>

Setting up Cloud9 SSL App with Node JS

I've been playing around with the Cloud9 IDE and am having a great time with it. However, I'm trying to setup a simple https server with node js and I can't seem to get it to work. When I run the page, Cloud9 says 'Running Node Process' but when I visit the url that the server is supposed to respond to: https://workspace.user.c9.io the page says
Service Temporarily Unavailable
The server you are trying to contact is down either because it was stopped or is unable to service your request due to maintenance downtime or capacity problems. Please try again later.
node-web-proxy/0.4 Server at project-livec9f70a01ca28.rhcloud.com Port 8000
I created a test certificate with OPENSSL and am using the following code to set up my server. I can confirm that the OPENSSL certificate was built correctly.
var https = require("https");
var fs = require("fs");
var url = require("url");
var options = {
key: fs.readFileSync('certs/cert.pem'),
cert: fs.readFileSync('certs/cert.pem')
};
// create a server
https.createServer(options, function(req, res) {
console.log("This works!");
res.writeHead(200);
res.end("Hello world from Cloud9! Url:"+req.url);
}).listen(process.env.PORT);
Thank you for your help!
you need .listen(process.env.PORT,process.env.IP);
It should say that when you start a program.

Implementing / forcing SSL for LocomotiveJS apps (NodeJS / Express)

With MVC frameworks like LocomotiveJS now available for NodeJS / Express, I'm just wondering how it would be possible to implement SSL on part of an app?
For example, an ecommerce app.
I'd need all /checkout controllers to force SSL.
I've read tutorials like this one but not sure on how to implement this with Locomotive, since Express is effectively "wrapped" ?
Currently SSL is not directly supported by Locomotive, but should be soon, according to this Google Groups posting in April by Jared Hanson, the creator of Locomotive.
Currently, I've always been putting Locomotive behind a proxy that
terminates SSL. But, I'll be adding a command line option for this
shortly, for direct support.
That said, if you want a completely node-based solution without using a proxy, then for the time being you'll need to edit the Express instance in Locomotive. I've tested the below and it's working well.
As of writing, npm install locomotive uses Express 2.x, though the latest at github has since been updated to use Express 3.x.
If you're using Locomotive with Express 2.x, then I think you have to edit /locomotive/lib/locomotive/index.js, around line 180, to look something like:
var sslOptions = {
cert : fs.readFileSync('/path/to/your/ssl-cert/dev.crt')
, key : fs.readFileSync('/path/to/your/ssl-key/dev.key')
};
var self = this
, server = express.createServer(sslOptions)
, entry;
Additionally, you will probably still want to listen on HTTP and redirect all traffic to HTTPS. Sticking with an all node-based solution, you could simply start another Express server at the end of /locomotive/lib/locomotive/cli/server.js that redirects all its traffic to HTTPS, e.g.
...
debug('booting app at %s in %s environment', dir, env);
locomotive.boot(dir, env, function(err, server) {
if (err) { throw err; }
server.listen(port, address, function() {
var addr = this.address();
debug('listening on %s:%d', addr.address, addr.port);
});
// add an http server and redirect all request to https
var httpServer = require('express').createServer();
httpServer.all('*', function(req, res) {
res.redirect('https://' + address + ':' + port + req.url);
});
httpServer.listen(80); // probably change based on NODE_ENV
});
}
Lastly, start the server:
$ lcm server -p 443 # again, probably use different port in development
All those frameworks are based on top of Express which based is on connect which has HTTPS support.
Anyway in a real life situation you might want to want to have a nginx/or nother proxy handling the https for you anyway.

Resources