forked from http-party/node-http-proxy
/
interceptor.js
116 lines (90 loc) · 4.52 KB
/
interceptor.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
'use strict';
const PerMessageDeflate = require('ws/lib/PerMessageDeflate');
const Extensions = require('ws/lib/Extensions');
const Receiver = require('ws/lib/Receiver');
const Sender = require('ws/lib/Sender');
const acceptExtensions = ({extensions, isServer}) => {
const {extensionName} = PerMessageDeflate;
const extension = extensions[extensionName];
if (!extension) {
return {};
}
const perMessageDeflate = new PerMessageDeflate({}, isServer);
perMessageDeflate.accept(extension);
return {[extensionName]: perMessageDeflate};
};
module.exports = class Interceptor {
static create(opts = {}) {
return new this(opts);
}
constructor({socket, options, req, proxyReq, proxyRes, proxySocket}) {
this._socket = socket;
this._options = options;
this._req = req;
this._proxyReq = proxyReq;
this._proxyRes = proxyRes;
this._proxySocket = proxySocket;
this._isClientSocketOpen = true;
this._isServerSocketOpen = true;
this._configure();
}
_configure() {
this._proxySocket.on('close', () => this._isClientSocketOpen = false);
this._socket.on('close', () => this._isServerSocketOpen = false);
const secWsExtensions = this._proxyRes.headers['sec-websocket-extensions'];
const extensions = Extensions.parse(secWsExtensions);
this._isCompressed = secWsExtensions && secWsExtensions.includes('permessage-deflate');
// need both versions of extensions for each side of the proxy connection
this._clientExtensions = this._isCompressed ? acceptExtensions({extensions, isServer: false}) : null;
this._serverExtensions = this._isCompressed ? acceptExtensions({extensions, isServer: true}) : null;
}
_getDataSender({sender, dataSendCond, event, options}) {
return ({data, binary = false}) => {
const opts = Object.assign({fin: true, compress: this._isCompressed, binary}, options);
dataSendCond() && sender.send(data, opts);
this._proxyReq.emit(event, {data, binary});
};
}
_getMsgHandler({interceptor, dataSender, binary}) {
return (data, flags) => {
if (typeof interceptor !== 'function') {
dataSender({data});
return;
}
const modifiedData = interceptor(data, {req: this._req, flags});
// if interceptor does not return data then nothing will be sended to the server
if (modifiedData) {
dataSender({data: modifiedData, binary});
}
}
}
_interceptClientMessages() {
const receiver = new Receiver(this._clientExtensions);
const sender = new Sender(this._proxySocket, this._serverExtensions);
this._proxyReq.emit('clientSenderInited', sender);
// frame must be masked when send from client to server - https://tools.ietf.org/html/rfc6455#section-5.3
const options = {mask: true};
const dataSendCond = () => this._isClientSocketOpen;
const dataSender = this._getDataSender({sender, dataSendCond, event: 'wsClientMsg', options});
receiver.ontext = this._getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: false});
receiver.onbinary = this._getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: true});
receiver.onclose = (code, msg, {masked: mask}) => this._isClientSocketOpen && sender.close(code, msg, mask);
this._socket.on('data', (data) => receiver.add(data));
}
_interceptServerMessages() {
const receiver = new Receiver(this._serverExtensions);
const sender = new Sender(this._socket, this._clientExtensions);
this._proxyReq.emit('serverSenderInited', sender);
const options = {mask: false};
const dataSendCond = () => this._isServerSocketOpen;
const dataSender = this._getDataSender({sender, dataSendCond, event: 'wsServerMsg', options});
receiver.ontext = this._getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: false});
receiver.onbinary = this._getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: true});
receiver.onclose = (code, msg, {masked: mask}) => this._isServerSocketOpen && sender.close(code, msg, mask);
this._proxySocket.on('data', (data) => receiver.add(data));
}
startDataTransfer() {
this._interceptClientMessages();
this._interceptServerMessages();
}
};