-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathproxy.js
124 lines (110 loc) · 3.48 KB
/
proxy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const assert = require('assert');
const net = require('net');
const { request } = require('http');
const { HTTP_REQ_FLAG, formatPatterns, checkPatterns, getHost } = require('./util');
const HOST_RE = /^\s*[\w.-]+\s*$/;
let globalProxy;
const checkHost = host => HOST_RE.test(host);
const checkPort = port => port > 0 && port <= 65535;
const formatProxy = (proxy) => {
if (!proxy || typeof proxy === 'function') {
return proxy;
}
let { host, port, socket, headers, filterRequest, allowlist, blocklist } = proxy;
assert(checkHost(host), 'Enter the correct host.');
assert(checkPort(port), 'Enter the correct port.');
filterRequest = typeof filterRequest === 'function' ? filterRequest : undefined;
allowlist = formatPatterns(allowlist);
blocklist = formatPatterns(blocklist);
proxy = { host, port, headers, filterRequest, allowlist, blocklist };
if (socket === false) {
proxy.socket = false;
} else if (socket && (socket.host || socket.port)) {
const { host: h, port: p } = socket;
assert(checkHost(h), 'Enter the correct host.');
assert(checkPort(p), 'Enter the correct port.');
proxy.socket = { host: h, port: p, headers: socket.headers || headers };
} else {
proxy.socket = { host, port, headers };
}
return proxy;
};
const getProxy = (options, isHttps) => {
if (!globalProxy || !options) {
return globalProxy;
}
let proxy;
if (typeof globalProxy === 'function') {
proxy = globalProxy(Object.keys({}, options), isHttps);
if (!proxy || typeof proxy === 'function') {
return;
}
proxy = formatProxy(proxy);
} else {
proxy = Object.assign({}, globalProxy);
}
let host = getHost(options);
if (!checkPatterns(host, proxy)) {
return;
}
proxy.headers = proxy.headers || {};
if (!proxy.headers.host && /^([.\w-]+)(?::(\d+))?$/.test(host)) {
if (!RegExp.$2) {
host = `${RegExp.$1}:${options.port || (isHttps ? 443 : 80)}`;
}
proxy.headers.host = host;
}
return proxy;
};
exports.setProxy = (proxy) => {
globalProxy = formatProxy(proxy);
return globalProxy;
};
exports.getProxy = getProxy;
exports.hasProxy = () => globalProxy;
exports.removeProxy = () => {
globalProxy = null;
};
const createConnection = (options) => {
const { proxy } = options;
if (!proxy) {
return new Promise((resolve, reject) => {
const socket = net.connect(options, () => resolve(socket));
socket.on('error', reject);
});
}
const headers = Object.assign({}, options.proxy && options.proxy.headers, options.headers);
const path = options.url || `${options.host}:${options.port}`;
headers.host = path;
if (!headers['x-whistle-policy']) {
headers['x-whistle-policy'] = 'tunnel';
}
headers['x-whistle-request-tunnel-ack'] = 1;
if (!headers['x-lack-proxy-proto']) {
headers['x-lack-proxy-proto'] = options.isSocket === 2 ? 'h2' : 'socket';
}
const proxyOpts = {
[HTTP_REQ_FLAG]: true,
method: 'CONNECT',
agent: false,
host: proxy.host,
port: proxy.port,
path,
headers,
};
return new Promise((resolve, reject) => {
const req = request(proxyOpts);
req.on('connect', (res, socket) => {
if (res.statusCode !== 200) {
return reject(new Error(`Tunneling socket could not be established, statusCode=${res.statusCode}`));
}
if (res.headers['x-whistle-allow-tunnel-ack']) {
socket.write('1');
}
resolve(socket);
});
req.on('error', reject);
req.end();
});
};
exports.createConnection = createConnection;