node.js http-proxy custom routing - node.js

I'm having troubles trying use http-proxy to route to localhost.
I'm using IISNODE but from a Console App is not working neither.
If "target" is set to google for example, it also works with local:9000 that is created in this snippet but it doesn't work with sites running in my local IIS
Any ideas?
UPDATE: the code snippet posted now worked for me, lot of work still missing tho.
var port = process.env.PORT;
var http = require('http'),
httpProxy = require('http-proxy'),
url = require('url');
// http Server
var proxy = new httpProxy.createServer({});
var httpServer = http.createServer(function (req, res) {
console.log('request received: ' + req.path);
var target = 'http://myapp';
if (!req.url.toString().startsWith('/')) {
target = target + '/';
}
target = target + req.url;
console.log('routing request to ' + target);
var urlObj = url.parse(req.url);
req.headers['host'] = urlObj.host;
req.headers['url'] = urlObj.href;
proxy.web(req, res, {
host: urlObj.host,
target: target,
enable: { xforward: true }
});
});
httpServer.listen(port);
String.prototype.endsWith = function (s) {
return this.length >= s.length && this.substr(this.length - s.length) == s;
}
String.prototype.startsWith = function (str) {
return this.indexOf(str) == 0;
};

I tested a simple node http-proxy outside iisnode and it works fine routing request to any sites, localhost iis or www.
But in iisnode, I can't make it work properly either. The request headers are not exactly the same and we're running on a named pipe instead of a tcp port, it's difficult to find what is causing the bad routing.
For instance, if we change req.url to '/', then it sends the request to the same domain the node app is running on, instead of the target domain.
A possible solution would be to use IIS reverse proxy (using ARR and URL Rewrite) to forward requests to your node proxy app running in standalone on a tcp port.
I have tested it and it works fine. I can give config example if needed.
Update: Here's how I made it work using IIS reverse proxy and a node proxy app. It doesn't use iisnode though:
A simple node proxyServer.js app:
var http = require('http')
var httpProxy = require('http-proxy')
var proxy = httpProxy.createProxyServer({})
var server = require('http').createServer(function(req, res) {
console.log(req.url)
proxy.web(req, res, { target: 'http://nodejs.org' })
}).listen(3000)
Running it using node.exe proxyServer.js
In IIS, we need to activate the ReverseProxy in Application Request Routing:
IIS server -> ARR:
Proxy settings...:
Enable the proxy like this:
And then I create a dedicated website node.mydomain.com and a rewrite rule for this website in IIS:
<rule name="ReverseProxyInboundNodeProxy" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="{C:1}://localhost:3000/{R:1}" />
</rule>
It's kind a "double proxy" solution but it works. I still have issue making a node proxy server runs through iisnode. Of course you need ARR and URL Rewrite installed in IIS.

Related

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 } }
}

WSS over Secure SSL connection fails only on FireFox with error 1006

My website runs on https/ssl (valid CA certificate) on IIS 10.0 (iisnode 0.2.21) / node/express. I am using a websocket server on top of https server (same port)
However the websocket connection works very well for CHrome and IE11. It fails only for Firefox (version 80) with error code 1006 (error details)
in Inspector / Network console, it gives the following errorerror as shown in network under inspector console
In the web.config file, I have added the following line
<webSocket enabled="false"/>
app.js code is as below:
var cors = require('cors');
var createError = require('http-errors');
var cookieParser = require('cookie-parser');
const https = require("https");
// var app = express();
var app = require('express-ws-routes')();
.....
var wsRouter = require("./routes/wsRoute"); // route for Websocket
....
app.use('/ws', wsRouter);
....
app.listen(process.env.PORT || 443);
In my Websocket route, I use the following code
router.websocket('/', function(info, cb, next) {
var connid = info.req.query.connid;
var pgid = info.req.query.pageid;
cb(function(socket) {
// socket is connected
socket.send(JSON.stringify({'response_type':'WSConnActive',
'response':{"status":"success","details":"Welcome Message","reqOrigin":"Home","welcomeMsg":'Hello '+usr+', Welcome! you are now connected!'}}));
....
socket.on('message', function incoming(message) { // handle message }
socket.on("close", function socketClose(ws){
console.log(info.req.query.usr+" closed session at "+moment().format("YYYY-MM-DD HH:mm:ss"));
var usrClose = info.req.query.usr;
...}
}); });
At the client end I connect
var socket = new WebSocket('wss://<host>:443/ws?request_type=init&usr='+UserID+'&connid='+connectTime+'&pageid='+page_ID);
All this works as expected for Chrome and IE11 except firefox
I am not sure where the issue is.. any help is appreciated!
Finally got the WSS working on Firefox with help of the note found in iisnode githun ([https://github.com/tjanczuk/iisnode/issues/497][1]) !. Issue seemed to be with iisnode configuration, In addition t , i had create a HTTP_CONNECTION server variable and add rewrite rules as below,
<match url="ws/*" />
<serverVariables>
<set name="HTTP_CONNECTION" value="Upgrade" />
</serverVariables>
<action type="Rewrite" url="websocket-server.js" />
</rule> ```
[1]: https://github.com/tjanczuk/iisnode/issues/497

Can't get IISNode,socket.io and express to work together

Please read my question,even a small piece of advice will be received with gratitude.
I am getting the following error in Google Chrome:
GET http://localhost/socket.io/?EIO=3&transport=polling&t=1419089743449-2 404 (Not Found)
My folder setup is as follows:
localhost
pro
public
socket.io/socket.io.js
cssfile.css
jsfile.js
app.js
node_ modules
It seems to me that the request made by the client for the handshake is wrong because the error should be localhost/pro/public/socket.io/blah blah .
I am using the following setup:
web.config :
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<rule name="DynamicContent">
<match url="/pro/" negate='True'/>
<!--<conditions>
<add input="pro" matchType="IsFile" negate="True"/>
</conditions>-->
<action type="Rewrite" url="app.js"/>
</rule>
<rule name="LogFile" patternSyntax="ECMAScript">
<match url="socket.io"/>
<action type="Rewrite" url="app.js"/>
</rule>
</rules>
</rewrite>
client side js:
var socket = io.connect('http: //localhost', {resource: 'pro/public/socket.io' });
server side js(node):
var http = require('http');
var express = require('express');
var app = express();
var port = process.env.PORT || 3002;
var server = http.createServer(app).listen(port);
io = require('socket.io').listen(server, { resource : '/pro/public/socket.io' });
I get html as expect and the static files are served as well; I just can't get socket.io to work.
SOLUTION:
CHANGE :
io = require('socket.io').listen(server, { resource : '/pro/public/socket.io' });
TO
io = require('socket.io').listen(server, { path : '/pro/public/socket.io' });
THAT is what worked for me I hope it works for you too! :)
Drizo, I think your 404 on the socket.io is a mixture of things, but I'm not a huge expert here! :)
Firstly, the port is missing from your connection string, you'll need this.
Secondly, I'm not 100% sure your socket.io initialisation is correct, though I use namespaces and hadn't heard of resources before, mine looks something like:
var app = require('express')();
var http = require('http').Server(app);
var config = require('./config')();
var server = http.listen(config.server.port, function () {
console.log('listening on *:' + config.server.port);
});
var socketIo = require('socket.io');
var io = socketIo(http);
var namespaceIo = io.of('/namespace');
namespaceIo.on('connection', function (socket) {
//configuration for events follows
socket.on('someEvent', function (input) {
...
}
}
With these lines you can namespace out a socket.io implementaton, and the client can connect with something like
function initSocket(__bool){
if(__bool == true){
io(serverAddress + '/namespace');
if ( !socket ) {
socket = io.connect(serverAddress + '/namespace');
} else {
socket.socket.connect(serverAddress + '/namespace'); // Yep, socket.socket ( 2 times )
}
// now that we have the socket we can bind events to it
bindSocketEvents();
}else{
socket.disconnect();
}
}

How do you get RequireSSL in NodeJS on an Azure Website?

So a little background first: A NodeJS server running in an Azure Website will automatically have all HTTPS requests directed to the http endpoint. This allows the following code to work for both HTTP and HTTPS
var http = require('http');
var express = require('express');
var app = express();
// *snip*
http.createServer(app).listen(process.env.PORT);
// can go to http://*.azurewebsites.net or https://*.azurewebsites.net without issue
From here I decided to create a "RequireSSL" middleware
/* snip */
if (req.protocol === 'http') {
var origFullUrl: string = 'http://' + req.get('host') + req.originalUrl;
var u: url.Url = url.parse(origFullUrl, true);
u.host = null; // nead to clear so 'port' is used
u.protocol = 'https';
u.port = '443';
res.redirect(url.format(u));
}
/* snip */
Here's where the background comes into play. Because Azure redirects all HTTPS to the HTTP protocol the req.protocol always equals 'http' creating a redirect loop.
Anyone know of a way to get this to work in an Azure Website?
You can detect this using x-arr-ssl header.. Please go through this : https://coderead.wordpress.com/2014/09/05/redirecting-to-https-in-node-js-on-azure-websites/

Proxy server with Node.js on Heroku

I'm trying to build a proxy server on with Node.js on Heroku using http-proxy.
Everything works fine locally, but I'm having some troubles on Heroku.
var http = require('http');
var httpProxy = require('http-proxy');
settings = {
"localhost": process.env.LOCALHOST,
"devices": process.env.DEVICES_URI
}
var options = { router: { } }
options.router[settings.localhost + '/devices'] = settings.devices + '/devices';
var port = process.env.PORT || 8000;
var server = httpProxy.createServer(options).listen(port);
As you can see the in the example I set a routing object. What I say is this:
when a request matches '/devices' then route the request to the the device service.
(identified by the DEVICES_URI environmental var)
In development I set
LOCALHOST = 'localhost'
DEVICES_URI = 'http://localhost:3000'
This means that all requests going to localhost:8000/devices are proxied to
localhost:3000/devices which is what I want. All works perfectly.
The problem is in production. It gives me a timeout error repeated multiple times
for every request.
2012-08-23T20:18:20+00:00 heroku[router]: Error H12 (Request timeout) -> GET lelylan-api.herokuapp.com/devices dyno=web.1 queue= wait= service=30000ms status=503 bytes=0
In the production the environment vars are configured to the app names.
LOCALHOST = 'lelylan-api.herokuapp.com'
DEVICES_URI = 'lelylan-devices.herokuapp.com/'
I guess I'm wrong is some configurations, but after the whole day I'm still
not able to figure it out.
Update
I've continued with my tests and I've seen that the proxy is not able to reach the proxied service which totally stops me.
In development I set:
LOCALHOST = 'localhost'
DEVICES_URI = 'lelylan-devices.herokuapp.com/'
If I call http://lelylan-devices.herokuapp.com/devices everything works fine.
If I call localhost:8000/devices (which points to http://lelylan-devices.herokuapp.com/devices) Heroku tells me there is no such an app. I guess the problem is somehow in the routing system.
Here you can access at the source code.
Here the configuration vars for Heroku.
NODE_ENV => production
LOCALHOST => lelylan-api.herokuapp.com
DEVICES_URI => lelylan-devices.herokuapp.com
TYPES_URI => lelylan-types.herokuapp.com
LOCATIONS_URI => lelylan-locations.herokuapp.com
I finally made it work using a slightly modified version proxy-by-url. The final code looks something like this and works fine.
var httpProxy = require('http-proxy');
var port = process.env.PORT || 8000;
var routing = {
'/devices': { port: process.env.DEVICES_PORT || 80, host: process.env.DEVICES_URI }
}
var server = httpProxy.createServer(
require('./lib/uri-middleware')(routing)
).listen(port);
One note to remember. The plugin sets the header HOST to the destination application uri. If you do not do so, Heroku will not recognize the app and will not find it, as its internal routing system seems to be based on the HOST header.
I use http-proxy successfully on Heroku. The first thing I noticed in your log err is the url it is GETting:
GET lelylan-api.herokuapp.com/tdevices
There is a typo... instead of '/devices' it shows '/tdevices'. Before continuing can you confirm this is the actual log message?

Resources