-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathapp.js
92 lines (78 loc) · 4.01 KB
/
app.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
const https = require('https'),
http = require('http'),
WebSocket = require('ws'),
zlib = require('zlib'),
url = require('url'),
Rewriter = require('./rewriter');
module.exports = class {
constructor(passthrough = {}) {
this.wsPrefix = passthrough.wsPrefix,
this.httpPrefix = passthrough.httpPrefix;
Object.assign(globalThis, this);
};
http(req, resp) {
const clientProtocol = req.connection.encrypted ? 'https' :
!req.connection.encrypted ? 'http' :
null;
try {
this.baseUrl = new URL(clientProtocol + '://' + req.headers.host),
this.clientUrl = new URL(req.url.slice(this.httpPrefix));
} catch (err) {
resp.writeHead(200, { 'content-type': 'text/plain' })
.destroy(err);
}
const rewriter = new Rewriter({
httpPrefix: this.httpPrefix,
wsPrefix: this.wsPrefix,
baseUrl: this.baseUrl,
clientUrl: this.clientUrl,
}),
client = (clientProtocol == 'https' ? https : clientProtocol == 'http' ? http : null).request(this.clientUrl.href, {
headers: Object.entries(req.headers).map(([key, value]) => [key, rewriter.header(key, value)]),
method: req.method,
followAllRedirects: false
}, (clientResp, streamData = [], sendData = '') => clientResp.on('data', data => streamData.push(data)).on('end', () => {
if (typeof (enc = clientResp.headers['content-encoding'] || clientResp.headers['transfer-encoding']) != 'undefined') enc.split('; ')[0].split(', ').forEach(encType => {
sendData = encType == 'gzip' ? zlib.gunzipSync(Buffer.concat(streamData)).toString() :
encType == 'deflate' ? zlib.inflateSync(Buffer.concat(streamData)).toString() :
encType == 'br' ? zlib.brotliDecompressSync(Buffer.concat(streamData)).toString() :
Buffer.concat(streamData).toString();
})
if (typeof (type = clientResp.headers['content-type']) != 'undefined') {
const directive = type.split('; ')[0];
sendData = directive == 'text/html' ? rewriter.html :
directive == 'text/css' ? rewriter.css :
['text/javascript', 'application/x-javascript', 'application/javascript'].includes(directive) ? rewriter.js :
sendData;
}
resp.writeHead(200, Object.entries(clientResp.headers).filter(([key, value]) => !['content-encoding', 'content-length', 'forwarded'].includes(key) && !key.startsWith('x-') ? [key, rewriter.header(key, value)] : null))
.end(sendData)
}));
client.on('error', err => {
resp.writeHead(200, { 'content-type': 'text/plain' })
.destroy(err)
});
req.on('data', data => client.write(data))
.on('end', () => client.end());
};
ws(server) {
new WebSocket.Server({ server: server }).on('connection', (client, req) => {
try {
this.baseUrl = new URL(req.headers[] + '://' + req.headers.host),
this.clientUrl = new URL(req.url.slice(this.wsPrefix));
} catch (err) {
req.terminate(err);
}
let msgParts = [];
sendReq = new WebSocket(this.clientUrl.href, {
headers: req.headers
}).on('message', msg => client.send(msg))
.on('open', () => sendReq.send(msgParts.join('')))
.on('error', () => client.terminate())
.on('close', () => client.close());
client.on('message', msg => sendReq.readyState == WebSocket.open ? sendReq.send(msg) : msgParts.push(msg))
.on('error', () => sendReq.terminate())
.on('close', () => sendReq.close());
});
};
}