From 784de0d1c85ecc8fefa4b02ffd53a1af35a8875d Mon Sep 17 00:00:00 2001 From: DudaGod Date: Tue, 25 Jun 2019 18:19:17 +0300 Subject: [PATCH 1/5] fix: listen "close" from client and from server --- lib/http-proxy/ws/interceptor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/http-proxy/ws/interceptor.js b/lib/http-proxy/ws/interceptor.js index 3e5e6edf6..4cc88f8e7 100644 --- a/lib/http-proxy/ws/interceptor.js +++ b/lib/http-proxy/ws/interceptor.js @@ -79,6 +79,7 @@ module.exports = class Interceptor { receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: false}); receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: true}); + receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); this._socket.on('data', (data) => receiver.add(data)); } @@ -92,6 +93,7 @@ module.exports = class Interceptor { receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: false}); receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: true}); + receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); this._proxySocket.on('data', (data) => receiver.add(data)); } From 046e84b32d9f1cb8e3cff273f5505e7eca54d88e Mon Sep 17 00:00:00 2001 From: DudaGod Date: Tue, 25 Jun 2019 18:28:47 +0300 Subject: [PATCH 2/5] fix: intercept every client-server messages --- lib/http-proxy/passes/ws-incoming.js | 8 ++------ lib/http-proxy/ws/interceptor.js | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/http-proxy/passes/ws-incoming.js b/lib/http-proxy/passes/ws-incoming.js index ba07c68fe..5c481d683 100644 --- a/lib/http-proxy/passes/ws-incoming.js +++ b/lib/http-proxy/passes/ws-incoming.js @@ -145,12 +145,8 @@ module.exports = { // socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers)); - if (options.wsInterceptServerMsg || options.wsInterceptClientMsg) { - WsInterceptor.create({socket, options, proxyReq, proxyRes, proxySocket}).intercept(); - } - else { - proxySocket.pipe(socket).pipe(proxySocket); - } + const wsInterceptor = WsInterceptor.create({socket, options, proxyReq, proxyRes, proxySocket}); + wsInterceptor.startDataTransfer(); server.emit('open', proxySocket); server.emit('proxySocket', proxySocket); //DEPRECATED. diff --git a/lib/http-proxy/ws/interceptor.js b/lib/http-proxy/ws/interceptor.js index 4cc88f8e7..47f645106 100644 --- a/lib/http-proxy/ws/interceptor.js +++ b/lib/http-proxy/ws/interceptor.js @@ -98,7 +98,7 @@ module.exports = class Interceptor { this._proxySocket.on('data', (data) => receiver.add(data)); } - intercept() { + startDataTransfer() { this._interceptServerMessages(); this._interceptClientMessages(); } From 006ba471f89a49131eaf9331a45a8d22b06a5333 Mon Sep 17 00:00:00 2001 From: DudaGod Date: Tue, 25 Jun 2019 18:35:11 +0300 Subject: [PATCH 3/5] fix: rename interceptor opts and evetns --- README.md | 17 +++++++++-------- lib/http-proxy/ws/interceptor.js | 18 +++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index aab604ea0..f8d38ae58 100644 --- a/README.md +++ b/README.md @@ -395,27 +395,28 @@ proxyServer.listen(8015); }; ``` -* **wsInterceptServerMsg**: Is a handler which is called when a websocket message is intercepted on its way to the server. It takes two arguments: `data` - is a websocket message and flags (fin, mask, compress, binary). If falsy value is returned then nothing will be sended to the target server. - ``` +* **wsInterceptClientMsg**: Is a handler which is called when a websocket message is intercepted on its way to the server from the client. It takes two arguments: `data` - is a websocket message and flags (fin, mask, compress, binary). If falsy value is returned then nothing will be sended to the client. + ``` const proxy = new HttpProxy({ ... - wsInterceptServerMsg: (data, flags) { + wsInterceptClientMsg: (data, flags) { return typeof data === 'string ? data.toUpperCase() : data; } ... }) ``` -* **wsInterceptClientMsg**: Is a handler which is called when a websocket message is intercepted on its way to the client. It takes two arguments: `data` - is a websocket message and flags (fin, mask, compress, binary). If falsy value is returned then nothing will be sended to the client. - ``` +* **wsInterceptServerMsg**: Is a handler which is called when a websocket message is intercepted on its way to the client from the server. It takes two arguments: `data` - is a websocket message and flags (fin, mask, compress, binary). If falsy value is returned then nothing will be sended to the target server. + ``` const proxy = new HttpProxy({ ... - wsInterceptClientMsg: (data, flags) { + wsInterceptServerMsg: (data, flags) { return typeof data === 'string ? data.toUpperCase() : data; } ... }) ``` + **NOTE:** `options.ws` and `options.ssl` are optional. `options.target` and `options.forward` cannot both be missing @@ -434,8 +435,8 @@ If you are using the `proxyServer.listen` method, the following options are also * `proxyReq`: This event is emitted before the data is sent. It gives you a chance to alter the proxyReq request object. Applies to "web" connections * `proxyReqWs`: This event is emitted before the data is sent. It gives you a chance to alter the proxyReq request object. Applies to "websocket" connections * `proxyRes`: This event is emitted if the request to the target got a response. -* `wsServerMsg`: This event is emitted after websocket message is sended to the server. -* `wsClientMsg`: This event is emitted after webscoket mesage is sended to the client. +* `wsClientMsg`: This event is emitted after webscoket mesage is sended from the client to the server. +* `wsServerMsg`: This event is emitted after websocket message is sended from the server to the client. * `open`: This event is emitted once the proxy websocket was created and piped into the target websocket. * `close`: This event is emitted once the proxy websocket was closed. * (DEPRECATED) `proxySocket`: Deprecated in favor of `open`. diff --git a/lib/http-proxy/ws/interceptor.js b/lib/http-proxy/ws/interceptor.js index 47f645106..4f5f8a1cc 100644 --- a/lib/http-proxy/ws/interceptor.js +++ b/lib/http-proxy/ws/interceptor.js @@ -69,37 +69,37 @@ module.exports = class Interceptor { }; } - _interceptServerMessages() { + _interceptClientMessages() { const receiver = new Receiver(this._clientExtenstions); const sender = new Sender(this._proxySocket, this._serverExtenstions); // frame must be masked when send from client to server - https://tools.ietf.org/html/rfc6455#section-5.3 const options = {mask: true}; - const dataSender = this._getDataSender({sender, event: 'wsServerMsg', options}); + const dataSender = this._getDataSender({sender, event: 'wsClientMsg', options}); - receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: false}); - receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: true}); + receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: false}); + receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: true}); receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); this._socket.on('data', (data) => receiver.add(data)); } - _interceptClientMessages() { + _interceptServerMessages() { const receiver = new Receiver(this._serverExtenstions); const sender = new Sender(this._socket, this._clientExtenstions); const options = {mask: false}; - const dataSender = this._getDataSender({sender, event: 'wsClientMsg', options}); + const dataSender = this._getDataSender({sender, event: 'wsServerMsg', options}); - receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: false}); - receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: true}); + receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: false}); + receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: true}); receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); this._proxySocket.on('data', (data) => receiver.add(data)); } startDataTransfer() { - this._interceptServerMessages(); this._interceptClientMessages(); + this._interceptServerMessages(); } }; From b59893d7d2b8d878ffd9e107358337f5122e84c6 Mon Sep 17 00:00:00 2001 From: DudaGod Date: Tue, 25 Jun 2019 18:41:47 +0300 Subject: [PATCH 4/5] chore: fix typo --- lib/http-proxy/ws/interceptor.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/http-proxy/ws/interceptor.js b/lib/http-proxy/ws/interceptor.js index 4f5f8a1cc..6ba640e8d 100644 --- a/lib/http-proxy/ws/interceptor.js +++ b/lib/http-proxy/ws/interceptor.js @@ -5,16 +5,16 @@ const Extensions = require('ws/lib/Extensions'); const Receiver = require('ws/lib/Receiver'); const Sender = require('ws/lib/Sender'); -const acceptExtensions = ({extenstions, isServer}) => { +const acceptExtensions = ({extensions, isServer}) => { const {extensionName} = PerMessageDeflate; - const extenstion = extenstions[extensionName]; + const extension = extensions[extensionName]; - if (!extenstion) { + if (!extension) { return {}; } const perMessageDeflate = new PerMessageDeflate({}, isServer); - perMessageDeflate.accept(extenstion); + perMessageDeflate.accept(extension); return {[extensionName]: perMessageDeflate}; }; @@ -52,12 +52,12 @@ module.exports = class Interceptor { _configure() { const secWsExtensions = this._proxyRes.headers['sec-websocket-extensions']; - const extenstions = Extensions.parse(secWsExtensions); - this._isCompressed = secWsExtensions && secWsExtensions.indexOf('permessage-deflate') != -1; + 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._clientExtenstions = this._isCompressed ? acceptExtensions({extenstions, isServer: false}) : null; - this._serverExtenstions = this._isCompressed ? acceptExtensions({extenstions, isServer: true}) : null; + this._clientExtensions = this._isCompressed ? acceptExtensions({extensions, isServer: false}) : null; + this._serverExtensions = this._isCompressed ? acceptExtensions({extensions, isServer: true}) : null; } _getDataSender({sender, event, options}) { @@ -70,8 +70,8 @@ module.exports = class Interceptor { } _interceptClientMessages() { - const receiver = new Receiver(this._clientExtenstions); - const sender = new Sender(this._proxySocket, this._serverExtenstions); + const receiver = new Receiver(this._clientExtensions); + const sender = new Sender(this._proxySocket, this._serverExtensions); // frame must be masked when send from client to server - https://tools.ietf.org/html/rfc6455#section-5.3 const options = {mask: true}; @@ -85,8 +85,8 @@ module.exports = class Interceptor { } _interceptServerMessages() { - const receiver = new Receiver(this._serverExtenstions); - const sender = new Sender(this._socket, this._clientExtenstions); + const receiver = new Receiver(this._serverExtensions); + const sender = new Sender(this._socket, this._clientExtensions); const options = {mask: false}; const dataSender = this._getDataSender({sender, event: 'wsServerMsg', options}); From 003a07e7595a35396c62644e7ddee8ff7ebf16e5 Mon Sep 17 00:00:00 2001 From: DudaGod Date: Tue, 25 Jun 2019 23:26:27 +0300 Subject: [PATCH 5/5] fix: listen "close" event from socket --- lib/http-proxy/ws/interceptor.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/http-proxy/ws/interceptor.js b/lib/http-proxy/ws/interceptor.js index 6ba640e8d..01b60ddf2 100644 --- a/lib/http-proxy/ws/interceptor.js +++ b/lib/http-proxy/ws/interceptor.js @@ -46,11 +46,16 @@ module.exports = class Interceptor { this._proxyReq = proxyReq; this._proxyRes = proxyRes; this._proxySocket = proxySocket; + this._isSocketOpened = true; this._configure(); } _configure() { + this._proxySocket.on('close', () => { + this._isSocketOpened = false; + }); + const secWsExtensions = this._proxyRes.headers['sec-websocket-extensions']; const extensions = Extensions.parse(secWsExtensions); this._isCompressed = secWsExtensions && secWsExtensions.includes('permessage-deflate'); @@ -79,7 +84,7 @@ module.exports = class Interceptor { receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: false}); receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptClientMsg, dataSender, binary: true}); - receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); + receiver.onclose = (code, msg, {masked: mask}) => this._isSocketOpened && sender.close(code, msg, mask); this._socket.on('data', (data) => receiver.add(data)); } @@ -93,7 +98,7 @@ module.exports = class Interceptor { receiver.ontext = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: false}); receiver.onbinary = getMsgHandler({interceptor: this._options.wsInterceptServerMsg, dataSender, binary: true}); - receiver.onclose = (code, msg, {masked: mask}) => sender.close(code, msg, mask); + receiver.onclose = (code, msg, {masked: mask}) => this._isSocketOpened && sender.close(code, msg, mask); this._proxySocket.on('data', (data) => receiver.add(data)); }