Multiple subdomains on 1 node.js server [duplicate] - node.js

This question already has answers here:
How do I host multiple Node.js sites on the same IP/server with different domains?
(13 answers)
Closed 4 years ago.
I would like to create a system of multiple web applications (probably around 3-5 node.js + express applications) on one server. I also only have one domain. So I figured that I need to create subdomains for each of my application apart from the main one.
My question is - how do I redirect users coming to certain subdomains to the right application? Do I need to use virtual machines and then redirect each user to a different VM (ip address) depending on their subdomain? How would I even do that?
Or could I just run every application on the same server just with a different port number? Or is there any other way that I'm not really thinking of?
Which way would be the cleanest and how would I implement it?

A very common way to achieve this is to run each of your node servers on a different port, and then set up a reverse proxy like nginx to have it forward requests based on matching the host header of incoming HTTP requests.
You could of course handle this manually with node, by checking the host header yourself and forwarding each request to the proper node server on the associated port.
Here is some Node code which illustrates what I'm referring to:
const http = require('http')
const url = require('url')
const port = 5555
const sites = {
exampleSite1: 544,
exampleSite2: 543
}
const proxy = http.createServer( (req, res) => {
const { pathname:path } = url.parse(req.url)
const { method, headers } = req
const hostname = headers.host.split(':')[0].replace('www.', '')
if (!sites.hasOwnProperty(hostname)) throw new Error(`invalid hostname ${hostname}`)
const proxiedRequest = http.request({
hostname,
path,
port: sites[hostname],
method,
headers
})
proxiedRequest.on('response', remoteRes => {
res.writeHead(remoteRes.statusCode, remoteRes.headers)
remoteRes.pipe(res)
})
proxiedRequest.on('error', () => {
res.writeHead(500)
res.end()
})
req.pipe(proxiedRequest)
})
proxy.listen(port, () => {
console.log(`reverse proxy listening on port ${port}`)
})

Related

How to name variable Inside of for loop

I wanted to create a loop that creates HTTP servers in Node.JS, where it creates server1, server2, etc. as const's. My problem is that I cannot get it to create a new server with a specific name based on a var. This is what I have right now.
const http = require('http')
const server=new http.createServer(function(res,req){res.write('Hi');res.end()})
for (let i=1001;i<9999;i++){
if (i=8080||i==3000||) return; //I'm already using 8080, 3000, and I don't want to use less than 1000.
const server${i}=new http.createServer(function(res,req){res.write('Hi');res.end()})
}
Obviously the ${i} thing won't work,
One approach would be to create an array of servers, and push to it a server object like:
const servers = []
for (let i=1001;i<9999;i++){
servers.push({
name: `server${i}`,
server: new http.createServer(function(res,req){res.write('Hi');res.end()})
})
}
now to access a specific server you could do servers.find(s => s.name === 'server999')?.server

How to determine http vs https in nodejs / nextjs api handler

In order to properly build my urls in my xml sitemaps and rss feeds I want to determine if the webpage is currently served over http or https, so it also works locally in development.
export default function handler(req, res) {
const host = req.headers.host;
const proto = req.connection.encrypted ? "https" : "http";
//construct url for xml sitemaps
}
With above code however also on Vercel it still shows as being served over http. I would expect it to run as https. Is there a better way to figure out http vs https?
As Next.js api routes run behind a proxy which is offloading to http the protocol is http.
By changing the code to the following I was able to first check at what protocol the proxy runs.
const proto = req.headers["x-forwarded-proto"];
However this will break the thing in development where you are not running behind a proxy, or a different way of deploying the solution that might also not involve a proxy. To support both use cases I eventually ended up with the following code.
const proto =
req.headers["x-forwarded-proto"] || req.connection.encrypted
? "https"
: "http";
Whenever the x-forwarded-proto header is not present (undefined) we fall back to req.connection.encrypted to determine if we should serve on http vs https.
Now it works on localhost as well a Vercel deployment.
my solution:
export const getServerSideProps: GetServerSideProps = async (context: any) => {
// Fetch data from external API
const reqUrl = context.req.headers["referer"];
const url = new URL(reqUrl);
console.log('====================================');
console.log(url.protocol); // http
console.log('====================================');
// const res = await fetch(`${origin}/api/projets`)
// const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}

net module "socket.remoteAddress" value is wrong

I create a TCP IVP4 socket server in Node.js using the 'net' module, and it works great for almost all of my clients, but one client has an IP similar to (I won't disclose the real one):
180.190.154.97
but when they connect to my socket server and I console.log(socket.remoteAddress), the value is this:
180.190.193.2
Why would this be happening?
// Import dependencies
let net = require('net')
// Create socket server.
const SERVER = net.createServer(socketConnection)
function socketConnection(client) {
console.log(client.remoteAddress)
}
// Server code snippet
let SERVER_LISTEN_ADDRESS = '0.0.0.0'
if (Game.local) {
SERVER_LISTEN_ADDRESS = '127.0.0.1'
Game.port = 42480
}
SERVER.listen(Game.port, SERVER_LISTEN_ADDRESS, () => {
console.log('Listening on port ' + Game.port)
if (!Game.local)
postServer()
else
console.log('Running server locally.')
})
Expected result is that it should print their IPV4 value from a site like:
https://whatismyipaddress.com/
But instead it says something completely different.
The result shown by whatipmyaddress it is not always the public IP of the single host. It could be rather also the IP of the subnet or if there's a proxy the IP of the proxy server. The fact that the address differs by a few units suggests that those particular hosts are in a subnet where a NAT service is active, or protected by a proxy or where the router acts as a VPN to the outside, encapsulating and sending the message to the server. In this case probably the entire subnet is seen as a single host.
In case you're using Express, you may want to use request-ip package in order to retrieve the IP in a more robust way. Since it seems you're not using express you should implement something similar to the first example shown in the link. For your specific case according the code you give:
const requestIp = require('request-ip');
function socketConnection(client) {
const clientIp = requestIp.getClientIp(client);
console.log(client);
}

How to redirect multiple subdomains to the same running express app

I'm building a SaaS app in NodeJS and using the Express framework. The individual members of the website has a url with a custom subdomain to login.
For example, a company called ABC Corp may login at abc.example.com and another company called Sony may login at sony.example.com
Any idea how I can redirect/route multiple subdomains to the same app instance?
You can use the express-subdomain package. Assuming you have a routes folder, containing abc.js and sony.js files that respectively export login routes for the abc and sony subdomains, you could have the following in index.js or whatever file from which your express server is listening.
const subdomain = require('express-subdomain');
const express = require('express');
const app = express();
const abcRoutes = require('./routes/abc');
const sonyRoutes = require('./routes/sony');
const port = process.env.PORT || 3000;
// use the subdomain middleware
app.use(subdomain('abc', abcRoutes));
app.use(subdomain('sony', sonyRoutes));
// a simple get route on the top-level domain
app.get('/', (req, res) => {
res.send('Welcome to the Home Page!');
});
// add any other needed routes
module.exports = app.listen(port, () => {
console.log('Server listening on port ' + port);
});
Your server will then be live and working as expected
http://example.com/ --> Welcome to the Home Page!
http://abc.example.com/login --> (Your login page for abc)
http://sony.example.com/login --> (Your login page for sony)
To tests subdomains locally you need to add them to your /etc/hosts file. (it requires sudo permissions)
127.0.0.1 localhost
127.0.0.1 example.com
127.0.0.1 abc.example.com
127.0.0.1 sony.example.com
The equivalent for /etc/hosts file on windows is at %systemroot%\system32\drivers\etc
For better details on setting up localhost domains locally check here
You can do more with the subdomain package. It accepts wildcards and you can use it to check API keys if you need such a feature.
Checkout the documentation for the express-subdomain package at https://npmjs.com/package/express-subdomain
You can actually handle that particular route or a wide range then go for Reg Exp (which allows you to do this app.get(new RegExp('(your|string)\/here'), function…) which you want to redirect and then follow the redirecting action something like below code is doing:
response.writeHead(302, {
'Location': 'yourpath/page.html'
//add other headers here...
});
response.end();
Update 1 : [as per the comments and other updates]
Then you try to handle all requests with the following app:
express()
.use(express.vhost('abc.example.com', require('/path/to/loginApp').app))
.use(express.vhost('sony.example.com', require('/path/to/loginApp').app))
.listen(80)
where /path/to/loginApp can be absolute paths or relative paths.
I hope this solves your problem.
Update 2:
Actually when a request comes in the request event is raised on a HTTP Server. So basically it is handled by express, express.vhost is a middle-ware function which raises the request event on another instance of a HTTP Server, that is how it works.
Below is the code:
function vhost(req, res, next){
if (!req.headers.host) return next();
var host = req.headers.host.split(':')[0];
if (req.subdomains = regexp.exec(host)) {
req.subdomains = req.subdomains[0].split('.').slice(0, -1);
server.emit('request', req, res);
} else {
next();
}
};

Change port without losing data

I'm building a settings manager for my http server. I want to be able to change settings without having to kill the whole process. One of the settings I would like to be able to change is change the port number, and I've come up with a variety of solutions:
Kill the process and restart it
Call server.close() and then do the first approach
Call server.close() and initialize a new server in the same process
The problem is, I'm not sure what the repercussions of each approach is. I know that the first will work, but I'd really like to accomplish these things:
Respond to existing requests without accepting new ones
Maintain data in memory on the new server
Lose as little uptime as possible
Is there any way to get everything I want? The API for server.close() gives me hope:
server.close(): Stops the server from accepting new connections.
My server will only be accessible by clients I create and by a very limited number of clients connecting through a browser, so I will be able to notify them of a port change. I understand that changing ports is generally a bad idea, but I want to allow for the edge-case where it is convenient or possibly necessary.
P.S. I'm using connect if that changes anything.
P.P.S. Relatively unrelated, but what would change if I were to use UNIX server sockets or change the host name? This might be a more relevant use-case.
P.P.P.S. This code illustrates the problem of using server.close(). None of the previous servers are killed, but more are created with access to the same resources...
var http = require("http");
var server = false,
curPort = 8888;
function OnRequest(req,res){
res.end("You are on port " + curPort);
CreateServer(curPort + 1);
}
function CreateServer(port){
if(server){
server.close();
server = false;
}
curPort = port;
server = http.createServer(OnRequest);
server.listen(curPort);
}
CreateServer(curPort);
Resources:
http://nodejs.org/docs/v0.4.4/api/http.html#server.close
I tested the close() function. It seems to do absolute nothing. The server still accepts connections on his port. restarting the process was the only way for me.
I used the following code:
var http = require("http");
var server = false;
function OnRequest(req,res){
res.end("server now listens on port "+8889);
CreateServer(8889);
}
function CreateServer(port){
if(server){
server.close();
server = false;
}
server = http.createServer(OnRequest);
server.listen(port);
}
CreateServer(8888);
I was about to file an issue on the node github page when I decided to test my code thoroughly to see if it really is a bug (I hate filing bug reports when it's user error). I realized that the problem only manifests itself in the browser, because apparently browsers do some weird kind of HTTP request keep alive thing where it can still access dead ports because there's still a connection with the server.
What I've learned is this:
Browser caches keep ports alive unless the process on the server is killed
Utilities that do not keep caches by default (curl, wget, etc) work as expected
HTTP requests in node also don't keep the same type of cache that browsers do
For example, I used this code to prove that node http clients don't have access to old ports:
Client-side code:
var http = require('http'),
client,
request;
function createClient (port) {
client = http.createClient(port, 'localhost');
request = client.request('GET', '/create');
request.end();
request.on('response', function (response) {
response.on('end', function () {
console.log("Request ended on port " + port);
setTimeout(function () {
createClient(port);
}, 5000);
});
});
}
createClient(8888);
And server-side code:
var http = require("http");
var server,
curPort = 8888;
function CreateServer(port){
if(server){
server.close();
server = undefined;
}
curPort = port;
server = http.createServer(function (req, res) {
res.end("You are on port " + curPort);
if (req.url === "/create") {
CreateServer(curPort);
}
});
server.listen(curPort);
console.log("Server listening on port " + curPort);
}
CreateServer(curPort);
Thanks everyone for the responses.
What about using cluster?
http://learnboost.github.com/cluster/docs/reload.html
It looks interesting!

Resources