如何在 node.js 中创建一个简单的 http 代理?

我试图创建一个代理服务器传递 HTTP GET请求从客户端到第三方网站(如谷歌)。我的代理只需要将传入请求镜像到目标站点上对应的路径,因此,如果我的客户请求的 URL 是:

127.0.0.1/images/srpr/logo11w.png

应提供下列资源:

http://www.google.com/images/srpr/logo11w.png

以下是我的想法:

http.createServer(onRequest).listen(80);


function onRequest (client_req, client_res) {
client_req.addListener("end", function() {
var options = {
hostname: 'www.google.com',
port: 80,
path: client_req.url,
method: client_req.method
headers: client_req.headers
};
var req=http.request(options, function(res) {
var body;
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
client_res.writeHead(res.statusCode, res.headers);
client_res.end(body);
});
});
req.end();
});
}

它可以很好地处理 html 页面,但是对于其他类型的文件,它只返回一个空白页面或来自目标站点的一些错误消息(这在不同的站点中有所不同)。

215074 次浏览

I don't think it's a good idea to process response received from the 3rd party server. This will only increase your proxy server's memory footprint. Further, it's the reason why your code is not working.

Instead try passing the response through to the client. Consider following snippet:

var http = require('http');


http.createServer(onRequest).listen(3000);


function onRequest(client_req, client_res) {
console.log('serve: ' + client_req.url);


var options = {
hostname: 'www.google.com',
port: 80,
path: client_req.url,
method: client_req.method,
headers: client_req.headers
};


var proxy = http.request(options, function (res) {
client_res.writeHead(res.statusCode, res.headers)
res.pipe(client_res, {
end: true
});
});


client_req.pipe(proxy, {
end: true
});
}

Your code doesn't work for binary files because they can't be cast to strings in the data event handler. If you need to manipulate binary files you'll need to use a buffer. Sorry, I do not have an example of using a buffer because in my case I needed to manipulate HTML files. I just check the content type and then for text/html files update them as needed:

app.get('/*', function(clientRequest, clientResponse) {
var options = {
hostname: 'google.com',
port: 80,
path: clientRequest.url,
method: 'GET'
};


var googleRequest = http.request(options, function(googleResponse) {
var body = '';


if (String(googleResponse.headers['content-type']).indexOf('text/html') !== -1) {
googleResponse.on('data', function(chunk) {
body += chunk;
});


googleResponse.on('end', function() {
// Make changes to HTML files when they're done being read.
body = body.replace(/google.com/gi, host + ':' + port);
body = body.replace(
/<\/body>/,
'<script src="http://localhost:3000/new-script.js" type="text/javascript"></script></body>'
);


clientResponse.writeHead(googleResponse.statusCode, googleResponse.headers);
clientResponse.end(body);
});
}
else {
googleResponse.pipe(clientResponse, {
end: true
});
}
});


googleRequest.end();
});

Here's a proxy server using request that handles redirects. Use it by hitting your proxy URL http://domain.com:3000/?url=[your_url]

var http = require('http');
var url = require('url');
var request = require('request');


http.createServer(onRequest).listen(3000);


function onRequest(req, res) {


var queryData = url.parse(req.url, true).query;
if (queryData.url) {
request({
url: queryData.url
}).on('error', function(e) {
res.end(e);
}).pipe(res);
}
else {
res.end("no url found");
}
}

Here's an implementation using node-http-proxy from nodejitsu.

var http = require('http');
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});


http.createServer(function(req, res) {
proxy.web(req, res, { target: 'http://www.google.com' });
}).listen(3000);

I juste wrote a proxy in nodejs that take care of HTTPS with optional decoding of the message. This proxy also can add proxy-authentification header in order to go through a corporate proxy. You need to give as argument the url to find the proxy.pac file in order to configurate the usage of corporate proxy.

https://github.com/luckyrantanplan/proxy-to-proxy-https

Super simple and readable, here's how you create a local proxy server to a local HTTP server with just Node.js (tested on v8.1.0). I've found it particular useful for integration testing so here's my share:

/**
* Once this is running open your browser and hit http://localhost
* You'll see that the request hits the proxy and you get the HTML back
*/


'use strict';


const net = require('net');
const http = require('http');


const PROXY_PORT = 80;
const HTTP_SERVER_PORT = 8080;


let proxy = net.createServer(socket => {
socket.on('data', message => {
console.log('---PROXY- got message', message.toString());


let serviceSocket = new net.Socket();


serviceSocket.connect(HTTP_SERVER_PORT, 'localhost', () => {
console.log('---PROXY- Sending message to server');
serviceSocket.write(message);
});


serviceSocket.on('data', data => {
console.log('---PROXY- Receiving message from server', data.toString();
socket.write(data);
});
});
});


let httpServer = http.createServer((req, res) => {
switch (req.url) {
case '/':
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<html><body><p>Ciao!</p></body></html>');
break;
default:
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('404 Not Found');
}
});


proxy.listen(PROXY_PORT);
httpServer.listen(HTTP_SERVER_PORT);

https://gist.github.com/fracasula/d15ae925835c636a5672311ef584b999

Here's a more optimized version of Mike's answer above that gets the websites Content-Type properly, supports POST and GET request, and uses your browsers User-Agent so websites can identify your proxy as a browser. You can just simply set the URL by changing url = and it will automatically set HTTP and HTTPS stuff without manually doing it.

/* eslint-disable @typescript-eslint/no-var-requires */
// https://stackoverflow.com/a/63602976/470749
const express = require('express');


const app = express();
const https = require('https');
const http = require('http');
// const { response } = require('express');


const targetUrl = process.env.TARGET_URL || 'https://jsonplaceholder.typicode.com'; // Run localtunnel like `lt -s rscraper -p 8080 --print-requests`; then visit https://yourname.loca.lt/todos/1 .


const proxyServerPort = process.env.PROXY_SERVER_PORT || 8080;


// eslint-disable-next-line max-lines-per-function
app.use('/', function (clientRequest, clientResponse) {
const parsedHost = targetUrl.split('/').splice(2).splice(0, 1).join('/');
let parsedPort;
let parsedSSL;
if (targetUrl.startsWith('https://')) {
parsedPort = 443;
parsedSSL = https;
} else if (targetUrl.startsWith('http://')) {
parsedPort = 80;
parsedSSL = http;
}
const options = {
hostname: parsedHost,
port: parsedPort,
path: clientRequest.url,
method: clientRequest.method,
headers: {
'User-Agent': clientRequest.headers['user-agent'],
},
};


const serverRequest = parsedSSL.request(options, function (serverResponse) {
let body = '';
if (String(serverResponse.headers['content-type']).indexOf('text/html') !== -1) {
serverResponse.on('data', function (chunk) {
body += chunk;
});


serverResponse.on('end', function () {
// Make changes to HTML files when they're done being read.
// body = body.replace(`example`, `Cat!`);


clientResponse.writeHead(serverResponse.statusCode, serverResponse.headers);
clientResponse.end(body);
});
} else {
serverResponse.pipe(clientResponse, {
end: true,
});
clientResponse.contentType(serverResponse.headers['content-type']);
}
});


serverRequest.end();
});


app.listen(proxyServerPort);
console.log(`Proxy server listening on port ${proxyServerPort}`);


enter image description here

enter image description here

here is one that I made:

var http = require("http")
var Unblocker = require("unblocker")
var unblocker = Unblocker({})
http.createServer(function(req,res){
unblocker(req,res,function(err){
var headers = {"content-type": "text/html"}
if(err){
res.writeHead(500, headers)
return res.end(err.stack || err)
}
if(req.url == "/"){
res.writeHead(200, headers)
return res.end(
`
<title>Seventh Grade by Gary Soto</title>
<embed src="https://www.cforks.org/Downloads/7.pdf" width="1500" height="1500"/>
`
)
}else{
res.writeHead(404, headers)
return res.end("ERROR 404: File Not Found.");
}
})
})
.listen(8080)

demo: view the demo: