I am developing an http "middleman" (aka reverse proxy) using nodejs. So far I made a basic server listening to http and https:
const http = require('node:http');
let https = require('node:https');
const app = (req,res)=>{
// Forward the request to respecitve http
};
http.createServer(app).listen(80);
https.createServer({
// typical https options ommited for siplicity
},app).listen(443);
In my case, I just want to forward the incomming request to actual server. I mean, if I receive a http GET request for yahoo.com
just forward it to actual yahoo.
Is there a way to do it?
Yes there is:
const http = require('node:http');
let https = require('node:https');
const getProtocol = (req) => {
if(req.protocol) return req.protocol;
return req.secure ? 'https':'http';
};
const app = (req,res)=>{
const http_handler = getProtocol(req) == 'http'?http:https;
const http_client = http_handler.request({
host: req.headers.host,
path: req.url,
method: req.method,
headers: req.headers,
body: req.body
},(resp)=>{
res.writeHead(resp.statusCode,resp.headers);
resp.pipe(res);
});
req.pipe(http_client);
};
http.createServer(app).listen(80);
https.createServer({
// typical https options ommited for siplicity
},app).listen(443);
As you can see both http
and https
can be used as clients as well. I can detect if http or https using the getProtocol
that can be used both at expressjs and other frameworks (such as connect).
Using that at section:
const http_handler = getProtocol(req) == 'http'?http:https;
I decide whether to use http or https client for better emulation. Then I make the client using http_handler.request
and I pipe the request using req.pipe(http_client);
At callback function provided in http_handler.request
I also writhe the appropriate response and heads because by default resp.pipe(res);
will send 200
status code, a thing that might not always be true (for example http redirects).
The resp.pipe(res);
forwards only the body instead of the headers as well.Therefore, we must send them first.
Using curl you can test it like this:
curl --resolve yahoo.com:443:172.21.0.2 --resolve yahoo.com:80:172.21.0.2 -vvv https://yahoo.com -k -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Camino/2.2.1"
Using --resolve
I temporally override the dns resolution and I provide manually the appropriate IPS. Also, using the -k
parameter I accept any certificate (it's ok for local development). With that, you can test a reverse proxy using custom ssl/tls certificates.