node.js http-proxy proxy based on url - node.js

I would like to create a proxy based on URL, such that you can go to: blah.com:8000/tolley-ltm and it proxies that request to my local workstation such as tolley.internal.blah.com based on the URL. So I could also do blah.com:8000/someguy-ltm and it goes to some guy's workstation at someguy-ltm.internal.blah.com. The end user will only ever see blah.com:8000/tolley-ltm
I have this sort of working, but not 100% and I would like your help! Here is the current code:
var fulltld ="internal.blah.com";
var proxyServer = httpProxy.createProxy();
var options = {
'/tolley-ltm': 'tolley-ltm',
'/someguy-ltm': 'someguy-ltm'
}
require('http').createServer(function(req, res) {
var dest = req.url;
req.url = '/';
fail = function(err){
console.log("Something bad happened: " + err);
}
proxyServer.web(req, res, {
target: {
host: options[dest] + '.' + fulltld,
port: 6109
}
},fail);
}).listen(8000);
So what happens currently is I go to blah.com:8000/tolley-ltm in my browser and it successfully goes to tolley.internal.blah.com:6109, however when I navigate since I changed the req.url to / instead of tolley-ltm every subsequent actions then goes to blah.com:8000/ instead of blah.com:8000/tolley-ltm and this causes the proxy to stop working.
Sidenote: If I don't change the req.url to '/' it ends up proxying to tolley.internal.blah.com:6109/tolley-ltm instead of just tolley.internal.blah.com:6109/
Is there a way I can keep the url to the end user looking like blah.com/8000/tolley-ltm and have all actions call back to blah.com/8000-ltm, such as if I clicked a link to go to /products, it would take me to blah.com/8000/tolley-ltm/products instead of blah.com/8000/products

Related

Url forwarding does not work when the target has a base url (NodeJs http-express-proxy)

I am calling a backend API service with this line in my NodeJs application (Angular):
this.http.get<Car[]>('/server/api/v1/cars')
But get the following error : GET http://127.0.0.1:4200/server/api/v1/cars 404 (Not Found)
I did expect the url to be translated to http://127.0.0.1:8080/api-baseurl/api/v1/cars with the following server.js (run withnode server.js) :
const proxy = require('express-http-proxy');
.
.
app.use('/server', proxy('http://localhost:8080/api-baseurl'));
But it looks like proxy does not handle forwarding when the target has a base url : using this line app.use('/server', proxy('http://localhost:8080/api-baseurl')); worked when the api did not have a base url, but that is non the case anymore.
I got the answer. What I did not understand form https://www.npmjs.com/package/express-http-proxy is that the first proxy() parameter matches the host, not the specific resource path. ProxyReqPathResolver must be used for the resource path, a function in which I can edit the path :
const proxy = require('express-http-proxy');
.
.
// Set our api routes proxy to point to backend server
app.use('/server', proxy('http://localhost:8080', {
proxyReqPathResolver: function (req) {
updatedPathComplete = '/api-baseurl' + req.url;
console.log('proxy on /server: ' + req.url + ' => ' + updatedPathComplete);
return updatedPathComplete;
}
}));
In the console log :
> node server-prod.js
API running on 4200
proxy on /server: /api/v1/cars => /api-baseurl/api/v1/cars

Simple Server Node.js return html page

I am trying to run a local server on Node.js that returns a simple html page. The difficulty for me at this point is understanding how to make the file system handle function correctly.. I am looking for the right code to use when the /recipe extension is called in the browser.
I get the error "no such file or directory", while the path they specify is correct. There is a file in there with the correct name..
Do I have to manually add "fs" in npm?
Is there another mistake in my code?
Am I forgetting something?
I have the following code:
// strict mode catches javascript errors better..
"use strict";
// localhost port on which you can access the application in DEV
// http status codes accesses npm package that contains main API status codes
// fs is the file system handler to handle the html files
const port = 3000,
http = require("http"),
httpStatus = require("http-status-codes"),
app = http.createServer(),
fs = require("fs");
// set up route mapping for html file
const routeResponseMap = {
"/recipe": "view/recipe.html",
"/index": "<h2>this is the index page</h2>"
};
// need to open your browser localhost:port for the request to be made..
app.on("request", (request,response) => {
response.writeHead(httpStatus.OK, {"Content-Type": "text/html"});
if(routeResponseMap[request.url]){
response.end(routeResponseMap[request.url]);
if(routesResponse[request.url]){
// the error is here, file does not get read
// WHAT CODE DO I NEED HERE?
fs.readFile(routesResponse[request.url]), (error, data) => {
response.write(data);
response.end();
}
console.log("route in mapping");
}
else{
response.end("<h3>Sorry not found</h3>");
}
})
app.listen(port);
console.log("The server has started and is listening on port " + port);

req.url not showing the full URL

I'm using Restify and for some reason req.url is only showing the URL up to the first query parameter. req.query is also only showing queryStartDate.
http://localhost:6001/myapp/v1/filter/path1/path2/v4.0/mhs/query/path3/path4/path5?queryStartDate=19000101&queryEndDate=21000101&requestSource=source&includeSources=1&excludeSources=2
Code:
//Breakpoint in my first handler:
HttpHandlers.prototype.readHttpRequest = function (req, res, next) {
req.locals = {};
res.locals = {};
...
var httpHandlers = new HttpHandlers();
server.get('/hello/:name', httpHandlers.readHttpRequest );
This turned out to be caused by my sending the URL with curl and not surrounding the URL with double quotes. Linux see the "&" and runs the preceding command in the background, so Node.js only see everything before the first "&".

How do I fix connect.router?

I'm working my way through Node for Front End devs, and as people on SO have already pointed out, Connect no longer has a module for routing. Some people have advised using Express, but I'm unsure of the exact syntax.
The example I am working through is hosted here:
github.com/garann/node-for-frontend-devs/blob/master/03-03.js
I would in turn like to finish the tutorial on templating:
js: github.com/garann/node-for-frontend-devs/blob/master/04-02.js
html: github.com/garann/node-for-frontend-devs/blob/master/public/parent.html
and was wondering whether people think it might be better to ditch these Connect based tutes and just learn how to use Express? As routing will most likely require Express anyway..
Thanks to SO excellent spam protection, I've had to remove the https:// portion of the github links.
Thanks for any help.
Try use this: https://github.com/baryshev/connect-route
UPDATE
In your project folder execute:
npm install connect-route
Updated code from your example:
var connect = require("connect");
var connectRoute = require("connect-route");
connect(
connect.static(__dirname + "/public"),
connectRoute(function(app) {
app.get("/sayHello/:firstName/:lastName", function(req, res) {
var userName = req.params.firstName + " " + req.params.lastName,
html = "<!doctype html>" +
"<html><head><title>Hello " + userName + "</title></head>" +
"<body><h1>Hello, " + userName + "!</h1></body></html>";
res.end(html);
});
})
).listen(8000);
Point your browser to:
http://[your_host_here]:8000/sayHello/nick/name
Summary
connect.router was removed in version 2.x. I took the code from 1.x and I published it as connect_router.
Install
npm install --save connect_router
Usage
if (!connect.router) {
connect.router = require('connect_router');
}
function route(rest) {
rest.get('/api/hello', function (req, res) {
res.end('hello');
});
}
app = connect()
.use(connect.router(route))
;
server = app.listen(port, function () {
console.log('listening on', server.address());
});
On Github
https://github.com/coolaj86/connect_router taken from Connect 1.x's router with documentation essentially being the tests
The original documentation is... somewhere.

Node.js www - non www redirection

Is there a chance to somehow redirect www to non-www URLs in node.js? Since there is no htaccess in node web server I am curious how to do that.
Even though this question is nearly 3 years old, there are a few subtle issues with the previously posted answers (and their comments), as well as some good advice in the comments that didn't make it back into the answers themselves. Here's a few important things to note:
Don't hardcode the http:// or https:// in the redirect URI; this makes life suck when switching between dev and prod environments - use req.protocol instead.
Also note that in order to use req.protocol reliably behind a proxy performing SSL termination (such as Elastic Load Balancer), you need to make sure that the trust proxy setting is enabled. This will ensure that req.protocol returns the protocol that the browser sees, not the protocol that finally made it to your app server.
The accepted answer has a logic bug, as it matches on /^www/, but formats the redirect URI with /^www./. In practice that probably won't bite anyone, but it would result in infinite redirect loops for something like wwwgotcha.example.com.
Be sure to use req.headers.host instead of req.host, as the latter strips out the port number. So, if you were to handle a request for www.example.com:3000, you'd redirect the user to www.example.com, minus the correct port number.
As Dário pointed out, you'll typically want to use a 301 redirect when going from www to non-www (or vice versa) for SEO purposes.
The last issue is the most minor, but it's generally safer to use req.originalUrl when creating redirect URIs, just in case you happen to be running in a mounted "sub app".
All that being said, here's my recommended approach that takes the above into consideration:
function wwwRedirect(req, res, next) {
if (req.headers.host.slice(0, 4) === 'www.') {
var newHost = req.headers.host.slice(4);
return res.redirect(301, req.protocol + '://' + newHost + req.originalUrl);
}
next();
};
app.set('trust proxy', true);
app.use(wwwRedirect);
You're using express, right? If so you can make a route handler that all GET requests go through, checks if they're to a 'www' URL, and redirects to the appropriate non-www URL if appropriate.
app.get('/*', function(req, res, next) {
if (req.headers.host.match(/^www/) !== null ) {
res.redirect('http://' + req.headers.host.replace(/^www\./, '') + req.url);
} else {
next();
}
})
An updated version of jmar777's answer:
Using express
server.use((req, res, next) => {
if (req.headers.host.startsWith('www.')) {
const newHost = req.headers.host.slice(4)
return res.redirect(
301,
`${req.protocol}://${newHost}${req.originalUrl}`,
)
}
next()
})
This is a basic exemple of how you could mimic the behavior of the redirect directive of apache in nodejs.
The function redirect takes either a RegExp or a string.
var http, redirect;
http = require("http");
redirect = function(host, res, pattern, redirect){
if (host == pattern || (pattern instanceof RegExp && host.match(pattern))) {
console.log("Redirected " + host);
res.writeHead(302, {
'location': redirect
});
res.end();
}};
http.createServer(function(req, res){
redirect(req.headers.host, res, /^www/, 'http://plouf.url');
redirect(req.headers.host, res, 'www.plouf.url', 'http://plouf.url');
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello World\n');
}).listen(8000, '127.0.0.1');
In the world of DevOps and the increasing adoption of hosted platforms, this type of thing should never be handled by code. You should handle the redirect using infrastructure instead. Putting this code into the application means that issues cannot be mitigated by the Operations team should something fail in the apex entry. In addition, while it is fancy and shiny and hip, using the apex as your site url limits the ability of Operations to redirect the site should something go wrong and causes issues when you want to do a blue/green, A/B testing or geo-redirection (in some cases). Your code and app should be url-agnostic.
I agree with Sebastian above with a minor tweak and if you are using Express. I would just make it a middleware it will be processed on all requests.
function removeWWW(req, res, next){
if (req.headers.host.match(/^www/) !== null ) {
res.redirect('http://' + req.headers.host.replace(/^www\./, '') + req.url);
} else {
next();
}
}
app.use(removeWWW);

Resources