node server hosted on heroku not serving socket.io file - node.js

So I've made a multiplayer space shooter using node.js, socket.io and kineticJS.
My Node.js server does not actually serve the client's page. My client-side files are currently hosted in a local Apache server on my computer.
The node server is up and running on Heroku right now and I can't seem to be able to get socket.io loaded on the client-side. I keep getting the "io is not defined" error.
This is how I import the script:
<script src="http://xxx-xxx-xxxx.herokuapp.com:5000/socket.io/socket.io.js"></script>
I have followed the instructions shown here: https://devcenter.heroku.com/articles/nodejs
And my package.json file looks like this:
{
"name": "Grid-Frontier",
"version": "0.0.1",
"dependencies": {
"socket.io": "0.9.x"
},
"engines": {
"node": "0.6.x"
}
}
On localhost everything is fine and I can just do the following:
// Importing on client side
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
// Server-side
server.listen(8080);
socket = io.listen(server);

Because Heroku allows you only to communicate in port 80, you cannot use other ports therefore the address should be: http://xxx-xxx-xxxx.herokuapp.com/socket.io/socket.io.js not port 5000. Actually there is nothing on port 5000, it is internal to machine.

Related

React, proxy to websocket from package.json get local machine

On AWS ec2 instance I have 2 projects react and nodejs
Due to specific of server I can't connect toc socket by it's ip, but...
I'm trying to use localhost from react package.json proxy, but it's not working. React trying to get :3334 port on my local machine
"proxy": {
"/api": {
"target": "http://localhost:3000"
},
"/socket.io": {
"target": "http://localhost:3334",
"ws": true
}
}
React on port 8000, nodejs on port 3000, socket should be listened on port 3334. I implement connection to nodejs, but with this ws. Can't understand what an I doing wrong
in react:
const socket = openSocket("http://localhost:3334");
On other instance or local machine everything works fine, with ip, but I really need this localhost connection.
Please, help
The problem was that I define in react io socket strictly to localhost. Solution:
const socket = openSocket();

Setup of AWS ElasticBeanstalk with Websockets

I'm trying to setup Websockets in order to send messages to AWS, so I can then process the message and send some payload to other resources at cloud and deliver custom responses to client part.
But, I cannot get that to work.
The main target is to send messages to AWS through WSS://, first approach with WS:// (in case that's possible), depending on payload content, it shall return a custom response. Then close the connection if no further operation is needed.
I've tried the suggestions posted here, here and here. But, either my lack of knowledge about Load Balancing, Websockets, TCP and HTTP is not letting me see pieces of solution missing, I'm doing everything wrong or both.
As for now, I have an Elastic Beanstalk example project structure like this:
+ nodejs-v1
|--+ .ebextensions
| |--- socketupgrade.config
|
|--+ .elasticbeasntalk
| |--- config.yaml
|
|--- .gitignore
|--- app.js
|--- cron.yaml
|--- index.html
|--- package.json
The Elastic Beanstalk environment and application are standard created, and also made sure that the Balancer is application, not classic, hence the Application Load Balancer can work with Websockets out of the box as many sources and documentation state.
It's setup with HTTP at port 80. Stickiness is enabled for a day.
Here's the code being used:
app.js:
'use strict';
const express = require('express');
const socketIO = require('socket.io');
const path = require('path');
const PORT = process.env.PORT || 3000;
const INDEX = path.join(__dirname, 'index.html');
const serber = express()
.use((req, res) => res.sendFile(INDEX) )
.listen(PORT, () => console.log(`Listening on ${ PORT }`));
const io = socketIO(serber);
io.on('connection', (socket) => {
console.log('Client connected');
socket.on('disconnect', () => console.log('Client disconnected'));
});
setInterval(() => io.emit('time', new Date().toTimeString()), 1000);
index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
});
</script>
package.json:
{
"name": "Elastic-Beanstalk-Sample-App",
"version": "0.0.1",
"private": true,
"dependencies": {
"express":"*",
"socket.io":"*"
},
"scripts": {
"start": "node app.js"
}
}
.ebextensions/socketupgrade.config:
container_commands:
enable_websockets:
command: |
sed -i '/\s*proxy_set_header\s*Connection/c \
proxy_set_header Upgrade $http_upgrade;\
proxy_set_header Connection "upgrade";\
' /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf
I'm only getting 504, 502, sometimes, when tweaking configurations randomly at pointless tries, it gives me 200 and at other attempts, no protocol error, but messages like disconnection and stuff...
I appreciate your time and attention reading this desperate topic! Any hint will be appreciated as well... Just, anything... T-T
Thanks for your time and attention!
Kind regards,
Jon M.
Update 1
I'll start quoting #RickBaker:
Personally, what I would do first is remove the load balancer from the equation. >If your ec2 instance has a public ip, go into your security groups and make sure >the proper port your app is listening to is open to the public. And see if you >can at least get it working without the load balancer complicating things. – >Rick Baker 21 hours ago
Changed the scaling feature of the Elastic Beanstalk environment's application from Load Balancing, Auto Scaling Environment Type to Single Instance Environment. Important to know, that I changed it from Elastic Beanstalk web page console, not from EC2 directly, since I think that it can break the Elastic Beanstalk environment application as a whole.
Anyway, changed it, after the environment and environment's application finished setting up again, changed and deployed the following:
index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
After everything got running, tested with a call via webpage to the index page. And the logs from node shows life:
-------------------------------------
/var/log/nodejs/nodejs.log
-------------------------------------
Listening on 8081
Client connected
Client disconnected
Client connected
Then I started to search for Server to Server setup found this docs and
then started to dig up a bit in order to connect to a WSS server.
So, the main goal is to stablish, and mantain a session from AWS EB application to another server that accepts WSS connections. The AWS EB should be responsible of stablish and mantain that connection, so when events happen at Network Server, the application at EB can send responses to the requests of events happening.
So then I read this topic, and realized that the NodeJS - socket.io approach won't work based on the posts read. So, I don't know what to do now. ( '-')
AWS EB can setup environment with Python with WSGI but, geez... Don't know what to do next. I'll try things in order to connect to WS if possible, if not then WSS, and see if something works out. So I'll Update right after I have results, whether possitive or not.
Jon over and out.
After combining previous iterations with some more documentation reading, I came to realize that, the connection indeed starts from AWS, via NodeJS using ws.
So I'm able to communicate with Network Server via WSS and request and provide data.
The app.js:
var WebSocket = require('ws');
var wss = new WebSocket('wss://example.com');
wss.on('open', function connection() {
console.log("WSS connection opening")
});
wss.on('message', function incoming(data) {
console.log("Jot:")
console.log(data)
setTimeout(function timeout() {
console.log("Sending response")
wss.send(JSON.stringify(
{
"key": "Hi there"
}
));
},
500);
});
The package.json:
{
"name": "Elastic-Beanstalk-Sample-App",
"version": "0.0.1",
"private": true,
"dependencies": {
"express":"*",
"socket.io":"*",
"ws": "*"
},
"scripts": {
"start": "node app.js"
}
}
The structure of the project remains almost the same:
+ nodejs-v1
|--+ .ebextensions
| |--- socketupgrade.config
|
|--+ .elasticbeasntalk
| |--- config.yaml
|
|--- .gitignore
|--- app.js
|--- cron.yaml
|--- package.json
As you can see, there's no index.html since it's not used.
From here, now it's up to the solution requirements the usage of sending/receiving data. And to make sure the connection is established/recovered.

React UI and Node.js/Express app served/monitored with webpack

I am new to React and Node, trying to setup an environment to work.
I worked a little with Node.js/Express before and I used nodemon to monitor file changes and restart my app.
Also I recently started to work with React and I use webpack dev server (specifically webpack-dev-server --content-base src --inline --hot --history-api-fallback) setup that monitors file changes and reload the UI.
The problem
Now, I want to tie it together.
First solution I found was: Start my node app on specific port, say 3000 then start webpack dev server on 8080 port and proxy requests to specific urls to my backend app.
devServer: {
proxy: {
'/backend-api/': {
target: {
"host": "localhost",
"protocol": 'http:',
"port": 3000
},
ignorePath: false,
changeOrigin: true,
secure: false
}
}
},
It should work but i do not feel right starting two servers for single application.
Is there any way (and simple example i could understand) to start single server that would serve both: a server side app and React UI?
And of course, it should track file changes and restart server app if server code changes OR reload UI if client side code changes.
You can use webpack through Express middleware using this package: https://github.com/webpack/webpack-dev-middleware
You can use it by simply adding it to the app:
var webpackMiddleware = require("webpack-dev-middleware");
app.use(webpackMiddleware(webpack({/* webpack options */})));
In addition there's a package that builds on this to offer hotloading and "Create React App" style terminal display: https://www.npmjs.com/package/webpack-express-middleware

How do I deploy socket.io to Google App Engine?

I created my first node.js app using socket.io. Specifically I implemented the chat example published by socket.io. It works perfectly, locally. And then I tried deploying it to Google App Engine (making some code tweaks for node to work).
Everything shows up indicating that the node part is working well. However the chat doesn't work indicating that socket.io part isn't working. You can see the deployed app (and page source) here.
Do I have to do anything additional? Something in the yaml or json files?
yaml content:
runtime: nodejs
vm: true
skip_files:
- ^(.*/)?.*/node_modules/.*$
json content:
{
"name": "Chaty",
"description": "chatrooms app",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "McChatface",
"engines": {
"node": "~4.2"
},
"scripts": {
"start": "node app.js",
"monitor": "nodemon app.js",
"deploy": "gcloud preview app deploy"
},
"dependencies": {
"express": "^4.13.4",
"socket.io": "^1.4.6"
}
}
In short this cannot be done on production and it appears to be work in process. The right architecture is to have a chat server on google compute engine as outlined here.
But as a proof of concept to use socket.io on google app engine is very similar to that shown in google appengine samples for websockets.
In case of socket.io do the following steps on server side. Code snippet below.
Create second express middleware and server.
Attach/use socket.io with new server.
Listen to port (65080).
Open firewall for port (65080) on google compute engine.
Link to working repository.
socket.io changes on server side
var app_chat = require('express')();
var server1 = require('http').Server(app_chat);
var io = require('socket.io')(server1);
server1.listen(65080);
io.on('connection', function (socket) {
console.log('user connected');
socket.on('chat_message', function (data) {
console.log('client sent:',data);
socket.emit('chat_message', 'Server is echoing your message: ' + data);
});
});
open firewall by command
gcloud compute firewall-rules create default-allow-websockets \
--allow tcp:65080 \
--target-tags websocket \
--description "Allow websocket traffic on port 65080"
I hope Google comes up with a production-ready solution soon enough on as this will become a key armour in any PaaS-arsenal.
GAE support for persistent socket connections arrived in February 2019!
To make this work, you'll need to be using the flex environment and modify your app.yaml to include session_affinity:
network:
session_affinity: true
Note that I still had to open port 65080 to get this working, but no other changes were required for me.
Read the deets at:
https://cloud.google.com/appengine/docs/flexible/nodejs/using-websockets-and-session-affinity
Google has an example app using WebSockets here, you need to do the following to get it working correctly:
Open up a firewall port for the server so clients can reach your server
Fetch your internal IP in Google App Engine, so clients know what IP to connect to
Echo out your IP from your server via something like a rest API or a HTML page
That should be it (don't take my word for it though, this is what I've been able to find out after doing some research on the docs), hope it helps!
Fetching your external IP from within Google App Engine
var METADATA_NETWORK_INTERFACE_URL = 'http://metadata/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip';
function getExternalIp (cb) {
var options = {
url: METADATA_NETWORK_INTERFACE_URL,
headers: {
'Metadata-Flavor': 'Google'
}
};
request(options, function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.log('Error while talking to metadata server, assuming localhost');
return cb('localhost');
}
return cb(body);
});
}
Opening the firewall port
gcloud compute firewall-rules create default-allow-websockets \
--allow tcp:65080 \
--target-tags websocket \
--description "Allow websocket traffic on port 65080"
This app.yaml configuration worked for me:
runtime: nodejs
env: flex
manual_scaling:
instances: 1
network:
session_affinity: true
And I enabled the firewall rules by this command:
gcloud compute firewall-rules create default-allow-websockets --allow
tcp:65080 --target-tags websocket --description "Allow websocket
traffic on port 65080"

HTML5 application on Heroku

I wish tho host my HTML5 application on Heroku. But I must choose one of the platforms (Java, Python, Node.js, etc) for my app. How can I run my application under Node.js? I have been able to create and deploy a simple Node.js app in Heroku however I am yet to figure out how can I merge my HTML5 app into this Node.js app.
I hope I make sense.
Tanks
"HTML5 app" is very non-descriptive and generic. If you mean an entirely client-side HTML application with no real backend logic, than that's trivial with node.js on heroku. If you put this in the root of your git repo in a file called server.js:
var app = require('express')();
app.use(express.static('app'));
app.listen(process.env.PORT);
Then put all of your HTML5 app in a directory called 'app' and node will serve it up.
Also in the root of your repo you'll need a Heroku Procfile with the following:
web: node server.js
Finally, you'll also want a package.json in the root of your repo so that Heroku can install your dependencies:
{
"name": "my-app",
"description": "My application",
"version": "0.0.1",
"dependencies": {
"express": "3.4.0"
},
"engines": {
"node": ">=0.10.0"
}
}
you can use ruby as well. rack based app Heroku rack based app
You need to create a config.ru file and put your HTML content into public folder
Here are more details about creating a static site based on rack app. Creating Static Sites in Ruby with Rack
Hope this helps!

Resources