官术网_书友最值得收藏!

Proxying and tunneling

Sometimes, it is useful to provide a means for one server to function as a proxy, or broker, for other servers. This would allow one server to distribute a load to other servers, for example. Another use would be to provide access to a secured server to users who are unable to connect to that server directly. It is also common to have one server answering for more than one URL—using a proxy, that one server can forward requests to the right recipient.

Because Node has a consistent streams interface throughout its network interfaces, we can build a simple HTTP proxy in just a few lines of code. For example, the following program will set up an HTTP server on port 8080 which will respond to any request by fetching the front page of a website and piping that page back to the client:

const http = require('http');
const server = new http.Server();

server.on("request", (request, socket) => {
console.log(request.url);
http.request({
host: 'www.example.org',
method: 'GET',
path: "/",
port: 80
}, response => response.pipe(socket))
.end();
});

server.listen(8080, () => console.log('Proxy server listening on localhost:8080'));

Go ahead and start this server, and connect to it. Once this server receives the client socket, it is free to push content from any readable stream back to the client, and here, the result of GET of www.example.org is streamed. One can easily see how an external content server managing a caching layer for your application might become a proxy endpoint, for example.

Using similar ideas, we can create a tunneling service, using Node's native CONNECT support. Tunneling involves using a proxy server as an intermediary to communicate with a remote server on behalf of a client. Once our proxy server connects to a remote server, it is able to pass messages back and forth between that server and a client. This is advantageous when a direct connection between a client and a remote server is not possible, or not desired.

First, we'll set up a proxy server responding to HTTP CONNECT requests, then make a CONNECT request to that server. The proxy receives our client's Request object, the client's socket itself, and the head (the first packet) of the tunneling stream:

const http = require('http');
const net = require('net');
const url = require('url');
const proxy = new http.Server();

proxy.on('connect', (request, clientSocket, head) => {
let reqData = url.parse(`http://${request.url}`);
let remoteSocket = net.connect(reqData.port, reqData.hostname, () => {
clientSocket.write('HTTP/1.1 200 \r\n\r\n');
remoteSocket.write(head);
remoteSocket.pipe(clientSocket);
clientSocket.pipe(remoteSocket);
});
}).listen(8080);

let request = http.request({
port: 8080,
hostname: 'localhost',
method: 'CONNECT',
path: 'www.example.org:80'
});
request.end();

request.on('connect', (res, socket, head) => {
socket.setEncoding("utf8");
socket.write('GET / HTTP/1.1\r\nHost: www.example.org:80\r\nConnection: close\r\n\r\n');
socket.on('readable', () => {
console.log(socket.read());
});
socket.on('end', () => {
proxy.close();
});
});

Once we make a request to our local tunneling server running on port 8080 it will set up a remote socket connection to our destination and maintain this "bridge" between the remote socket and the (local) client socket. The remote connection of course only sees our tunneling server, and in this way clients can connect in a sense anonymously to remote services (which isn't always a shady practice!).

主站蜘蛛池模板: 北流市| 鹤峰县| 玉树县| 新密市| 罗平县| 万安县| 阳新县| 祁东县| 贺兰县| 康乐县| 清丰县| 临邑县| 明光市| 吉安县| 社旗县| 汕尾市| 仙桃市| 卫辉市| 周宁县| 延庆县| 梅州市| 乐业县| 阿荣旗| 万宁市| 正镶白旗| 黑龙江省| 栖霞市| 宁陵县| 噶尔县| 盱眙县| 青阳县| 紫金县| 镇宁| 许昌县| 丰原市| 垦利县| 定边县| 浦城县| 银川市| 乐亭县| 水富县|