All files / lib/http-proxy/ws interceptor.js

93.55% Statements 58/62
72.22% Branches 13/18
100% Functions 16/16
93.1% Lines 54/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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    1x 1x 1x 1x   1x 8x 8x   8x       8x 8x   8x     1x 20x 7x 7x 7x                       1x   5x       5x 5x 5x 5x 5x 5x   5x       5x 4x     5x 5x 5x     5x 5x       10x 7x 7x   7x         5x 5x     5x 5x   5x 5x 5x   7x       5x 5x   5x 5x   5x 5x 5x   5x       5x 5x      
'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];
 
    Iif (!extension) {
        return {};
    }
 
    const perMessageDeflate = new PerMessageDeflate({}, isServer);
    perMessageDeflate.accept(extension);
 
    return {[extensionName]: perMessageDeflate};
};
 
const getMsgHandler = ({interceptor, dataSender, binary}) => {
    return (data, flags) => {
        Eif (typeof interceptor !== 'function') {
            dataSender({data});
            return;
        }
 
        const modifiedData = interceptor(data, flags);
 
        // if interceptor does not return data then nothing will be sended to the server
        if (modifiedData) {
            dataSender({data: modifiedData, binary});
        }
    }
};
 
module.exports = class Interceptor {
    static create(opts = {}) {
        return new this(opts);
    }
 
    constructor({socket, options, proxyReq, proxyRes, proxySocket}) {
        this._socket = socket;
        this._options = options;
        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');
 
        // 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, event, options}) {
        return ({data, binary = false}) => {
            const opts = Object.assign({fin: true, compress: this._isCompressed, binary}, options);
            sender.send(data, opts);
 
            this._proxyReq.emit(event, {data, binary});
        };
    }
 
    _interceptClientMessages() {
        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};
        const dataSender = this._getDataSender({sender, event: 'wsClientMsg', options});
 
        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}) => this._isSocketOpened && 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);
 
        const options = {mask: false};
        const dataSender = this._getDataSender({sender, event: 'wsServerMsg', options});
 
        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}) => this._isSocketOpened && sender.close(code, msg, mask);
 
        this._proxySocket.on('data', (data) => receiver.add(data));
    }
 
    startDataTransfer() {
        this._interceptClientMessages();
        this._interceptServerMessages();
    }
};