http-proxy-middleware, dynamic target - node.js

I'm trying to setup a proxy in express js, i am using http-proxy-middleware, it works perfect with hardcoded target.
server.use('/login', createProxyMiddleware({
target: 'https://pretest.test.example',
changeOrigin: true,
}))
in this case if i make a post request from my local domain (https://localhost:4200/login) it will proxy to https://pretest.test.example/login
but unfortunately its a multi site setup so i need the target to change based on the incoming request i have tried multiple ways but it doesnt seem to work.
server.use('/login', (req) => {
createProxyMiddleware({
target: resolveApiUrl(req),
changeOrigin: true
})
})
server.use('/login', createProxyMiddleware({
target: '',
changeOrigin: true,
router: (req: string) => resolveApiUrl(req)
}));
resolveApiUrl is just a function and takes the incoming request and formats the hostname to what i need it to be that works, also tried hardcoding values in the last 2 examples and that still doesnt work.
has anyone successfully managed to do dynamic targets based on incoming request with http-proxy-middleware?

After creating the middleware, you must invoke it with all three arguments, req, res and next:
server.use('/login', function(req, res, next) {
createProxyMiddleware({
target: resolveApiUrl(req),
changeOrigin: true,
})(req, res, next);
});

Related

Add IP parameter to proxy with http-proxy-middleware

I am using http-proxy-middleware for a proxy to a new version of an api. But I need to apped the URL with the request client IP address.
Example:
/api?param1=ok&param2=ok proxies to newapi.example.com/request?param1=ok&param2=ok, but I need it to be proxied to newapi.example.com/request?param1=ok&param2=ok&ip=x.x.x.x
Here is what I am using now.
const { createProxyMiddleware } = require('http-proxy-middleware');
const apiProxy = createProxyMiddleware({
target: "https://newapi.example.com/request",
changeOrigin: true,
pathRewrite: {
"^/api": "" // strip "/api" from the URL
},
onProxyRes(proxyRes) {
//proxyRes.headers['x-added'] = 'foobar'; // add new header to response
//delete proxyRes.headers['x-removed']; // remove header from response
}
});
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
return apiProxy(req, res);
};
Any clue will help greatly, thank you.

I am having problem using http-proxy-middleware

I have two servers running in my backend, because of that I have to use the http-proxy-middleware package but I am encountering some problems.
This is my code in the frontend which is running on localhost:3000
axios("/api2/login",data)
.then((res) => {
});
This is my code in the backend which is running on localhost:5001
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use(createProxyMiddleware('/api2', {target: 'http://localhost:5001', changeOrigin: true}))
app.post("/login", (req, res, next) => {
res.send("Logged In");
});
This code is no working showing this error in the browser's console
GET http://localhost:3000/api2/login 404 (Not Found)
Uncaught (in promise) Error: Request failed with status code 404
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:61)
I am not able to understand where I am going wrong.
Looks like it's hitting localhost:3000 instead of localhost:5001 which is where your server is running from.
axios("http://localhost:5001/api2/login",data)
.then((res) => {
});
You can also set the baseURL in axios. HTTP get request with Axios gets sent with the local host IP at the beginning of the URL
If I understand correctly your server is listening on port 5001. So, you need to proxy your requests from 3000 to 5001. You can do that in the react application by setting the proxy value in package.json:
"proxy": "http://localhost:5001",
For more information about this subject, check the docs.
Edit for the configuration explained in the comment section
First in package.json:
"proxy": "http://localhost:5002",
Create a new server (proxyserver) which will be listening in port 5002:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// redirection for first server 5000
app.use('/api1', createProxyMiddleware({ target: 'http://localhost:5000', changeOrigin: true }));
// redirection for second server 5001
app.use('/api2', createProxyMiddleware({ target: 'http://localhost:5001', changeOrigin: true }));
app.listen(5002);
For the other servers (5000 and 5001) you don't need to redirect the requests. Just make sure they're listening on the right ports (5000 and 5001), for example:
const express = require('express');
const app = express();
app.post("/api2/login", (req, res, next) => {
res.send("Logged In");
});
app.listen(5001);
I followed the steps mentioned in this post along with some changes,
I changed my Axios request code to:
axios({
method: "POST",
data: user,
withCredentials: true,
url: "/api2/login",
}).then((res) => {})
Otherwise, the proxy server was treating it as a GET request.
Secondly, I changed the proxy endpoint code int the proxy server as:
app.use('/api2', createProxyMiddleware({
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {
[`^/api2`]: '',
},
}));
Further information about the proxy endpoint change can be found here.

How to set up http-proxy-middleware on a dynamic route?

I'm trying to proxy some asset routes where a dynamic part of the path comes from a config file. I had this working using the request library, but I can't quite get it working with http-proxy-middleware.
Here's the code that works when I use the request library:
const assets = express.Router();
app.use(`/${p.name}/assets`, assets);
assets.get('*', async (req, res) => {
return request(`${p.address}/${p.version}/assets${req.path}`).pipe(res);
});
I've tried a few different variations with http-proxy-middleware, and none of these work. Example 1:
app.use(
`/${p.name}/assets`,
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);
Example 2:
app.use(
`/${p.name}/assets`,
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: function(path) {
return path.replace(p.name, p.version);
}
})
);
I've also tried using /${p.name}/assets/** as the first argument to app.use, and I've also tried adding a /assets to the end of both the key and value in the pathRewrite object. The result is always the same: I get a 302 for the asset requested by the browser.
I've even tried adding a middleware function right before the httpProxy call that logs to the console so that I know my request is hitting the correct route:
app.use(
`/${p.name}/assets`,
(req, _res, next) => {
console.log("I'm an asset proxy, short and stout: ", req.url);
next();
},
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);
But I don't ever see that output. Probably a simple mistake. Perhaps my first argument to app.use just isn't right? Help appreciated!
Update
I also tried a combination of the old way and the new. I mounted a new router on /${p.name}/assets, and after discovering the onProxyRequest option, I added a function there to log some output.
const assets = express.Router();
app.use(`/${p.name}/assets`, assets);
assets.use(
'*',
httpProxy({
target: p.address,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: p.version,
},
onProxyReq: (proxyReq, req, res) => {
console.log("Hello I'm being proxied now! ", proxyReq, req, res);
},
})
);
Still getting a 302, and I never see the output from the onProxyReq function.
Documentation has custom router option.
-
const proxyTable = {
'integration.localhost:3000': 'http://localhost:8001', // host only
'staging.localhost:3000': 'http://localhost:8002', // host only
'localhost:3000/api': 'http://localhost:8003', // host + path
'/rest': 'http://localhost:8004', // path only
};
const options = {
target: 'http://localhost:8000',
router: proxyTable,
};
const myProxy = createProxyMiddleware(options);
or
function customRouter(req) {
// I dont know what "p" stands for
// check the req object to see which property you want to modify
console.log(req)
const { hostname } = req
const hostName = hostname.split('.')[0]
return `https://${hostName}.sub.domain.com`
}
then add this option:
router: customRouter
Can you check your installed version - looks like you are using the v0.x.x.
interface to the http-proxy-middleware package. Latest is version 2.X.X and it exposes the following function
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use(
`/${p.name}/assets`,
createProxyMiddleware({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);

Websocket error with NodeBB running through a proxy server

I'm trying to run NodeBB through a node.js reverse proxy (https://docs.nodebb.org/configuring/proxies/node/)
I've followed every tutorial, hint/tip I can but am still experiencing issues with websocket connections to my NodeBB server, causing session problems, can't log in etc.
My setup is as follows:
App 1 - http://www.mywebsite.co.uk/
Node.js & Express
Routing for API & frontend website
Nothing out of the ordinary
Full code snippet at bottom of post
I am using the 'http-proxy' npm module to proxy anyone who loads http://mywebsite.co.uk/forum to http://www.myforum.co.uk/forum
This part is working, assets load as expected. However, there is a part of NodeBB which uses websockets to poll the forum for functionality purposes, user sessions. This part is not proxying correctly, or at least the NodeBB response is not correct and therefore giving me lots of errors:
"You are accessing the forum from an unknown origin. This will likely
result in websockets failing to connect. To fix this, set the "url"
value in config.json to the URL at which you access the site. For
more information, see this FAQ topic:
https://community.nodebb.org/topic/13388"
Also:
"Looks like your connection to NodeBB was lost, please wait while we
try to reconnect."
And, in the network panel, lots of 'pending' requests which eventually fail with an empty response from NodeBB.
http://mywebsite.co.uk/forum/socket.io/?EIO=3&transport=polling&t=MgJQSMk
App 2 - http://www.myforum.co.uk/forum
This app is a basic NodeBB installation running, with one plugin - (https://github.com/julianlam/nodebb-plugin-session-sharing)
The config JSON file looks like this (note the URL is my frontend app's URL as per the instructions when proxying.
{
"url": "http://www.mywebsite.co.uk/forum",
"secret": "secret",
"database": "postgres",
"port": "4567",
"postgres": {
"host": "HOST",
"port": "PORT",
"password": "PASSWORD",
"database": "DATABASE"
}
}
App 1 code:
// app
const express = require("express");
const app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'secret', resave: true, saveUninitialized: true }));
//
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE"
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
// serve the content
app.use(express.static("dist"));
// Frontend
app.set('view engine', 'pug');
// serve out the api
// app.use ...
// Server set up
const httpProxy = require('http-proxy');
const HttpProxyRules = require('http-proxy-rules');
// Forum urls
let rules = {
rules: {
'/forum': 'http://www.myforum.co.uk/forum',
'/forum/*': 'http://www.myforum.co.uk/forum',
},
};
const proxyRules = new HttpProxyRules(rules);
const proxy = httpProxy.createProxy();
app.use(function (req, res, next) {
try {
if (req.url.includes("socket.io") === true) {
// console.log("SOCKET.IO", req.url)
return proxy.web(req, res, {
target: 'wss://www.myforum.co.uk',
ws: true,
changeOrigin: true
}, function (e) {
// console.log('PROXY ERR', e)
// next();
});
} else {
var target = proxyRules.match(req);
if (target) {
// console.log("TARGET", target, req.url)
return proxy.web(req, res, {
target: target,
changeOrigin: true
}, function (e) {
// console.log('PROXY ERR', e)
});
} else {
next();
}
}
} catch (e) {
// res.sendStatus(500);
res.json({ error: e });
}
});
// Frontend routes
// app.use ...
// HTTP
const http = require('http');
// Create server
mainserver = http.createServer(app);
const PORT = process.env.PORT || 3000;
mainserver.listen(PORT);
mainserver.on('listening', onListening);
mainserver.on('error', function (error, req, res) {
let json;
console.log('proxy error', error);
if (!res.headersSent) {
res.writeHead(500, { 'content-type': 'application/json' });
}
json = { error: 'proxy_error', reason: error.message };
res.end(JSON.stringify(json));
});
function onListening() {
console.log(`Listening on :${PORT}`);
}
To resolve this, I changed the Proxy module I was using. It uses less code to implement and works with websockets. I think the NodeBB Node.js proxy documentation is out of date so I'll be updating with the new method I found.
Working code is as follows:
/**
* Proxy forum
*/
const proxy = require('http-proxy-middleware');
// Forum Proxy
app.use(
'/forum',
proxy({
target: 'http://www.myforum.co.uk/forum',
changeOrigin: true,
ws: true,
})
);
Another potential GOTCHA here is that this Prixy setup needs to be declared ABOVE the 'body-parser' module (if it's being used). The wrong order will prevent post requests from being proxied.

Unable to Proxy AWS EMR Jupyter-Notebook's socket through node application. Failed to load kernel

We have our node application which we want to use to proxy jupyter notebook running on AWS EMR. I am able to proxy all my http request from my node application using http-proxy-middleware. But for some reason I am unable to proxy web-socket requests. Because of this I am able to create a new notebook but unable to start the kernel. Here is my proxy middleware
'use strict';
const proxy = require('http-proxy-middleware');
module.exports = proxy({
target: 'http://<EMR master_node IP>:<Port>',
ws: true,
changeOrigin: true,
// onProxyReq: (proxyReq, req, res) => {
// proxyReq.removeHeader('Upgrade');
// proxyReq.removeHeader('Connection');
// proxyReq.setHeader('Upgrade', 'websocket');
// proxyReq.setHeader('Connection', 'upgrade');
// },
secure: true,
loglevel: 'debug'
});
I tried to manually add the headers as well which you see as commented but it did not help. Also for the server application I have upgrade enabled.
server.on('upgrade', proxy.upgrade);
Any help would be greatly appreciated.
Here's a working solution:
handleRequestHeaders = function (proxyRequest, request) {
proxyRequest.setHeader('Host', request.headers['host']) ;
proxyRequest.setHeader('X-Forwarded-For', request.connection.remoteAddress) ;
proxyRequest.setHeader('X-Real-IP', request.connection.remoteAddress)
if (request.headers['upgrade']) {
proxyRequest.setHeader('Upgrade', request.headers['upgrade']) ;
}
proxyRequest.setHeader('Connection', request.headers['upgrade'] ? 'upgrade' : 'close') ;
proxyRequest.setHeader('Referer', 'http://localhost') ;
proxyRequest.removeHeader('Origin') ;
}
module.exports = proxy({
target: 'http://<EMR master_node IP>:<Port>',
ws: true,
changeOrigin: true,
onProxyReq: handleRequestHeaders,
onProxyReqWs handleRequestHeaders
});

Resources