All files / lib/http-proxy/passes ws-incoming.js

80% Statements 44/55
52.78% Branches 19/36
81.82% Functions 9/11
82.35% Lines 42/51

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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167    1x 1x 1x 1x                               1x                       7x         7x                                 7x                                                       7x 6x 21x   21x 20x 20x     1x 2x   1x         7x   7x     7x         7x     7x 7x   1x 1x 1x       7x 5x     5x 4x           5x       5x   5x           5x   5x 5x   5x 5x     7x     1x     1x   1x        
'use strict';
 
const http = require('http');
const https = require('https');
const common = require('../common');
const WsInterceptor = require('../ws/interceptor');
 
/*!
 * Array of passes.
 *
 * A `pass` is just a function that is executed on `req, socket, options`
 * so that you can easily add new checks while still keeping the base
 * flexible.
 */
 
/*
 * Websockets Passes
 *
 */
 
 
module.exports = {
  /**
   * WebSocket requests must have the `GET` method and
   * the `upgrade:websocket` header
   *
   * @param {ClientRequest} Req Request object
   * @param {Socket} Websocket
   *
   * @api private
   */
 
  checkMethodAndHeader : function checkMethodAndHeader(req, socket) {
    Iif (req.method !== 'GET' || !req.headers.upgrade) {
      socket.destroy();
      return true;
    }
 
    Iif (req.headers.upgrade.toLowerCase() !== 'websocket') {
      socket.destroy();
      return true;
    }
  },
 
  /**
   * Sets `x-forwarded-*` headers if specified in config.
   *
   * @param {ClientRequest} Req Request object
   * @param {Socket} Websocket
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */
 
  XHeaders : function XHeaders(req, socket, options) {
    Eif(!options.xfwd) return;
 
    var values = {
      for  : req.connection.remoteAddress || req.socket.remoteAddress,
      port : common.getPort(req),
      proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws'
    };
 
    ['for', 'port', 'proto'].forEach(function(header) {
      req.headers['x-forwarded-' + header] =
        (req.headers['x-forwarded-' + header] || '') +
        (req.headers['x-forwarded-' + header] ? ',' : '') +
        values[header];
    });
  },
 
  /**
   * Does the actual proxying. Make the request and upgrade it
   * send the Switching Protocols request and pipe the sockets.
   *
   * @param {ClientRequest} Req Request object
   * @param {Socket} Websocket
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */
  stream : function stream(req, socket, options, head, server, clb) {
 
    var createHttpHeader = function(line, headers) {
      return Object.keys(headers).reduce(function (head, key) {
        var value = headers[key];
 
        if (!Array.isArray(value)) {
          head.push(key + ': ' + value);
          return head;
        }
 
        for (var i = 0; i < value.length; i++) {
          head.push(key + ': ' + value[i]);
        }
        return head;
      }, [line])
      .join('\r\n') + '\r\n\r\n';
    }
 
    common.setupSocket(socket);
 
    Iif (head && head.length) socket.unshift(head);
 
 
    var proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request(
      common.setupOutgoing(options.ssl || {}, options, req)
    );
 
    // Enable developers to modify the proxyReq before headers are sent
    Eif (server) { server.emit('proxyReqWs', proxyReq, req, socket, options, head); }
 
    // Error Handler
    proxyReq.on('error', onOutgoingError);
    proxyReq.on('response', function (res) {
      // if upgrade event isn't going to happen, close the socket
      Eif (!res.upgrade) {
        socket.write(createHttpHeader('HTTP/' + res.httpVersion + ' ' + res.statusCode + ' ' + res.statusMessage, res.headers));
        res.pipe(socket);
      }
    });
 
    proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
      proxySocket.on('error', onOutgoingError);
 
      // Allow us to listen when the websocket has completed
      proxySocket.on('end', function () {
        server.emit('close', proxyRes, proxySocket, proxyHead);
      });
 
      // The pipe below will end proxySocket if socket closes cleanly, but not
      // if it errors (eg, vanishes from the net and starts returning
      // EHOSTUNREACH). We need to do that explicitly.
      socket.on('error', function () {
        proxySocket.end();
      });
 
      common.setupSocket(proxySocket);
 
      Iif (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead);
 
      //
      // Remark: Handle writing the headers to the socket when switching protocols
      // Also handles when a header is an array
      //
      socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers));
 
      const wsInterceptor = WsInterceptor.create({socket, options, proxyReq, proxyRes, proxySocket});
      wsInterceptor.startDataTransfer();
 
      server.emit('open', proxySocket);
      server.emit('proxySocket', proxySocket);  //DEPRECATED.
    });
 
    return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
 
    function onOutgoingError(err) {
      Iif (clb) {
        clb(err, req, socket);
      } else {
        server.emit('error', err, req, socket);
      }
      socket.end();
    }
  }
};