I'm trying to get a node.js server (using express) working using websockets in elastic beanstalk (EB) using application load balancer (ALB) but without using socket.io (because peerjs-server is the server I'm trying to get running and it's not written with socket.io).
I've seen a couple of articles suggesting you have to use socket.io (or another lib that doesn't just rely on websockets), but Amazon says ALB supports websockets directly.
My server is both a create-react-app server and a peerjs-server. It runs fine in dev on port 9000 for both the web UI and the peerjs ws connections.
I've tried all of the different approaches I've found, but I haven't gotten this to work, and I've even seen things written that suggest it can't be done, but it seems like I'm close. Has anyone gotten this to work, and if so, how?
Okay, I got it to work. Here's what I've done to get everything working on port 9000.
In EB, create an application, then begin creating an environment.
In the configuration of the environment, go into the Software section and tell it that you're going to use npm run prod to start your server.:
Now, go into the Load Balancers section and, as shown in the pic below:
add a listener on port 9000
create a process on port 9000 and enable stickiness (I called mine peerjsServer)
add Rules for each URL path you want to use to access the server, and have each of those rules assigned to the process you created (peerjsServer). Also point the default to that process, so that health checks on 80 get through successfully to your server
You may need to hop over to the EC2 dashboard in the AWS UI in order to make sure that the necessary Security Groups are defined there. I think I created the first two on the and the last two were default creations, but I don't recall. Anyway, they need to have port 9000 open for inbound and outbound (port 80 is there by default all the time):
Back to the EB configuration, go to the Instances section and make sure that your instance has the Security Groups assigned to it:
I ran react-scripts build to make the /build directory containing the production version of the server UI(this is create-react-apps stuff that I'm not covering here).
In my code, I start the server using a server.js that makes a server that runs both the peerjs-server ws server and an http server.
const express = require("express");
const bodyParser = require("body-parser");
const path = require('path');
const passport = require("passport");
const users = require("./routes/api/users");
const games = require("./routes/api/games");
const Game = require("./src/models/Game");
const WebSocket = require('ws');
const ExpressPeerServer = require('peerjs-server').ExpressPeerServer;
const app = express();
const url = require('url');
const port = process.env.PEERSERVERPORT || 9000;
// WebSocket for making db updates to client.
const wsserver = require('./wsserver').wsserver
// Server that gets all requests: lobby UI, peerserver, db websocket
const server = require('http').createServer(app);
wsserver.on('connection', function connection(ws) {
ws.on('message', msg => {
ws.send(msg)
})
ws.on('close', () => {
console.log('WebSocket was closed')
})
});
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === '/ws') {
wsserver.handleUpgrade(request, socket, head, function done(ws) {
wsserver.emit('connection', ws, request);
});
}
});
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
// Routes -- the /api/* path is defined in the EB load balancer
app.use("/api/users", users);
app.use("/api/games", games);
// This is the create-react-apps /build directory of UI code
app.use(express.static(path.join(__dirname, 'build')));
app.get('/login', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// These are the paths that are defined in the EB load balancer
app.get('/logout', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/register', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/dashboard', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// Peer server for making WebRTC connections between game clients.
const options = {
debug: true
}
const peerserver = ExpressPeerServer(server, options);
app.use('/peerserver', peerserver);
app.use(express.static(path.join(__dirname, 'src')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'src', 'index.html'));
});
peerserver.on('disconnect', (client) => {
console.log('Delete the game if the host client disconnects');
Game.deleteMany({ hostPeerId: client })
.then(() => {
wsserver.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send("refreshGames");
}
})
})
.catch(err => console.log(err));
});
server.listen( port, () => console.log(`Server is up and running on port ${port} !`))
And then in my package.json, I set the script for prod to run the above server.js to start the server, and the npm run prod I put in the Software section earlier is what calls this to make the server go:
...
"scripts": {
"start": "set PORT=8081&& react-scripts start",
"prod": "node server.js",
...
After doing all that, I now have a running EB server using ALB and handling both websocket (ws) and UI (http) traffic on port 9000.
Related
I have created 2 server with express and node.js and now I want to run both the server on the same port which is localhost:8000 with different end points.
How it can be done or what can be the approach for it?
Attaching the server code for reference:-
Server1:-
const express = require("express");
const cors = require("cors");
const app = express();
const axios = require('axios');
app.use(cors());
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
const PORT = 8000;
app.get("/WeatherForcast", function (req, res) {
axios.get('https://localhost:7173/WeatherForecast')
.then(response => {
res.status(200).json({ success: true, data: response.data});
})
.catch(error => {
console.log(error);
});
});
app.listen(PORT, function () {
console.log(`Server is running on ${PORT}`);
});
Server2:-
const express = require("express");
const cors = require("cors");
const app = express();
const axios = require('axios');
app.use(cors());
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
const PORT = 8000;
app.get("/UserData", function (req, res) {
axios.get('https://localhost:7173/UserData')
.then(response => {
res.status(200).json({ success: true, data: response.data});
})
.catch(error => {
console.log(error);
});
});
app.listen(PORT, function () {
console.log(`Server is running on ${PORT}`);
});
Currently when I run it, one server runs and for other server an error is displayed that port 8000 is already in use.
You can't run two servers on the same port. The OS and TCP stack won't allow it.
The easiest solution is to use two endpoints on one server.
If you have to have two separate servers, then you would run them both on separate ports (neither of which is the public port) and then use something like nginx to proxy each separate path to the appropriate server.
So, the user's request goes to the proxy and the proxy examines the path of the request and then forwards it to one of your two servers based on the path of the request (as setup in the proxy configuration).
Two different servers can not be hosted on same port as it will give the error i.e "this port is currently is use" something like this.
The thing that can be done is instead of creating multiple server you can create a single server and define different endpoints to manage the code flow.
it is not possible to run different servers on same port
I have created a REST server using Node.js and express, but i am having trouble connecting to the server on a separate machine. The server machine and client machine are on the same local network.
This is my code for the server
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const PORT = process.env.PORT || 3000;
const imageUpload = require("./routes/imageUpload");
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', '*');
next();
});
app.use("/imageUpload", imageUpload);
app.listen(PORT, "0.0.0.0", () => {
console.log("Running at port " + PORT);
})
router.get("/getFilename", (req, res) => {
res.status(200).json({
status: "recieved"
});
});
Upon using postman on the server machine the get request works fine even if using the machine IP address in the network(192.168.0.104:3000/imageUpload/getFilename).
On a separate machine in the network the postman will give an error of There was an error connecting to 192.168.0.104:3000/imageUpload/getFilename. so i am unable to connect to the API, How do i fix this? Thank you very much
Your problem is rather in network settings, not in nodejs or express.
Did you try ping 192.168.0.104 your server? If it works most likely you need to manually open port 3000.
Try google how to open a port on a server for the OS your are using on that machine.
I have an express server setup online which loads multiple ports and those ports are setup on subdomains for example. port 9000 loads the main domain.com port 8000 loads the main application at "app.domain.com" port 1000 loads "signup.domain.com" and the build version of the app is on port 8500 "build.domain.com".
The application is an Angular application however when I go to load the Angular app it loads on port 4200 or it says 8500 is in use. So currently I am loading that in express like so:
// Build Application - In Development
var appbuild = express();
appbuild.get('/', function (req, res){
res.sendFile('/app/build/myapp/src/index.html', { root: '.' })
});
var port = 8500;
appbuild.listen(port);
console.log('Build App Listening on port', port);
So my question is in Express how can I instead of writing sendfile command make it launch the angular app in that location on port 8500 so my subdomain names will work. The reason I'm asking this is because right now all it does is load the index file but angular or the app isn't running so i just see source code that says app-root and a blank white page.
Thank you in advance.
Robert
--- Update. I've decided to post the entire Express file. My issue is trying to load a angular app on port 8500 from the subfolder upon booting of express. Here is the full server.js code:
// server.js
const express = require('express'),
path = require('path'),
bodyParser = require('body-parser'),
cors = require('cors'),
mongoose = require('mongoose'),
config = require('../config/DB');
mongoose.Promise = global.Promise;
mongoose.connect(config.DB).then(
() => {console.log('Database is connected') },
err => { console.log('Can not connect to the database'+ err)}
);
// Main Website
var web = express();
web.get('/', function (req, res){
res.sendFile('/web/index.html', { root: '.' })
});
var port = 9000;
web.listen(port);
console.log('Web Listening on port', port);
// Main Application
var app = express();
app.get('/', function (req, res){
res.sendFile('/app/index.html', { root: '.' })
});
var port = 8000;
app.listen(port);
console.log('Main App Listening on port', port);
// Build Application - In Development
var appbuild = express();
appbuild.get('/', function (req, res){
res.sendFile('/app/build/myapp/src/index.html', { root: '.' })
});
var port = 8500;
appbuild.listen(port);
console.log('Build App Listening on port', port);
// Sign up Portal
var sign = express();
sign.get('/', function (req, res){
res.sendFile('/signup/index.html', { root: '.' })
});
var port = 10000;
sign.listen(port);
console.log('Sign Up Portal Listening on port', port);
Refer to this link https://malcoded.com/posts/angular-backend-express
Update your code to the following:
const express = require('express');
const app = express();
app.listen(8500, () => {
console.log('Server started!');
});
You need to build the angular app if your angular version not 1.x
ng build
Also, I think this question is similar to your question:
Not able to view Angular app via express/heroku?
There is a React app that has express requesting api/login information from Mongodb and checking the password input against it, otherwise it doesn't allow you to access the website.
Locally everything works great. When we moved all the build files to the apache server the console returns POST https://websitepath.com/api/login 404 (Not Found)
Any idea of what could be a problem and why it works locally but doesn't work on apache? Node is installed and Express is running there successfully on port 4000.
Here is the code for index.js
var express = require('express');
var bodyParser= require('body-parser')
var MongoClient = require('mongodb').MongoClient
var sha1 = require('sha1');
var db;
const PORT = 4000;
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use('/api/login', function (req, res) {
if (!req.body.password) return res.status(400).send('bad_request!')
db.collection('user').find().toArray(function(err, results) {
if (err) return res.status(500).send('something_wrong!');
var checker = false;
results.forEach(function (entry) {
if (entry.password === sha1(req.body.password)) checker = true;
})
if (checker) {
res.send('success')
} else {
return res.status(403).send('Unfortunately password is incorrect. Please try again.');
}
})
})
MongoClient.connect('mongodb://localhost:27017/test', (err, database) => {
if (err) return console.log(err)
db = database
app.listen(PORT, function() {
console.log('Express server is up on port ' + PORT);
});
})
Here is the code for the AuthService.js
import axios from 'axios';
import qs from 'qs';
const AuthService = {
isLogged: false,
login(data, cb) {
axios.post('/api/login', qs.stringify(data)).then(
(res) => {
this.isLogged = true;
cb(null, res);
}
).catch((error) => {
console.error('error occured', error);
cb(error.response.data);
})
},
}
export default AuthService;
Your question doesn't mention proxying the node.js application, so I'm guessing that's where the problem is - specifically, the node application is not being proxied.
In short, what you appear to be trying to do is something like this:
Apache is listening on port 443 (the HTTPS port) and serving web pages at various paths (presumably, everything except paths starting with /api).
You want the web server to also serve the paths used by your node.js API (eg. /api/login and others) on port 443.
But two distinct applications (Apache and your node.js app) cannot both listen on port 443 - Apache is binding it and serving its own pages. If you try to change the port on your node.js application, it will fail to start and give you an error indicating that port 443 is already bound by another application.
There is a simple test for this: navigate to http://websitepath.com:4000/api/login. If you can see your API login page (ie. the node.js application is listening on port 4000), that means the problem is NOT with your node application, it's with Apache's proxy configuration.
The solution to this is setting up Apache as a proxy. This will allow Apache to serve its own pages and forward the request to another service based on the path. So you could certainly set it up so paths that start with /api/... are forwarded to http://localhost:4000/api/... and any other paths are served by Apache directly.
Setting up a proxy is not terribly difficult, but it depends a lot on your specific circumstances, so I'm not going to attempt to explain all the ins & outs. I'd recommend starting with the mod_proxy documentation. There are also about a million tutorials out there; Digital Ocean's documentation is good - I've used it in the past.
I'm using iisnode to host a node app. I'm having trouble actually deploying it under my domain name. Here's the main file with two different starting points. The un-commented code is just a simple server that works correctly when accessed via my domain (so iisnode is mapping and handling the node app correctly). The commented code is the entry point for the express app I am working on, and this works when I view from a local host, but when attempting to access via my domain I receive a 'cannot GET application.js' error.
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, world!');
}).listen(process.env.PORT);
//require('./app/init');
//var server = require('./app/server');
//module.exports = server.start(process.env.NODE_ENV);
Here is my server.js file. I think its a routing issue, I've substitued a console.log function for the indexRoute function, and it never fires. But I still don't understand why this works correctly accessing via localhost but not under my domain.
var express = require('express');
var routes = require('./routes');
var app = express();
function createApplication(environment) {
app.get('/', routes.indexRoute);
app.listen(process.env.PORT);
return app;
}
module.exports.start = createApplication;
I can message a git link for full app if anyone is interested.
Try specifying that you want to listen from all IP addresses, not just localhost by adding '0.0.0.0' as a parameter to listen. Also add a callback to see what happened.
app.listen(process.env.PORT, '0.0.0.0', function(err) {
console.log("Started listening on %s", app.url);
});