# All files / lib/http-proxy/passes web-incoming.js

13.24% Statements 9/68
0% Branches 0/67
7.69% Functions 1/13
15% Lines 9/60

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
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    1931x
    1x
    1x
    1x
    1x

    1x
    5x

    1x

    1x

     var httpNative   = require('http'),
        httpsNative  = require('https'),
        web_o  = require('./web-outgoing'),
        common = require('../common'),
        followRedirects = require('follow-redirects');

    web_o = Object.keys(web_o).map(function(pass) {
      return web_o[pass];
    });

    var nativeAgents = { http: httpNative, https: httpsNative };

    /*!
     * Array of passes.
     *
     * A `pass` is just a function that is executed on `req, res, options`
     * so that you can easily add new checks while still keeping the base
     * flexible.
     */

    module.exports = {

      /**
       * Sets `content-length` to '0' if request is of DELETE type.
       *
       * @param {ClientRequest} Req Request object
       * @param {IncomingMessage} Res Response object
       * @param {Object} Options Config object passed to the proxy
       *
       * @api private
       */

      deleteLength: function deleteLength(req, res, options) {
        if((req.method === 'DELETE' || req.method === 'OPTIONS')
           && !req.headers['content-length']) {
          req.headers['content-length'] = '0';
          delete req.headers['transfer-encoding'];
        }
      },

      /**
       * Sets timeout in request socket if it was specified in options.
       *
       * @param {ClientRequest} Req Request object
       * @param {IncomingMessage} Res Response object
       * @param {Object} Options Config object passed to the proxy
       *
       * @api private
       */

      timeout: function timeout(req, res, options) {
        if(options.timeout) {
          req.socket.setTimeout(options.timeout);
        }
      },

      /**
       * Sets `x-forwarded-*` headers if specified in config.
       *
       * @param {ClientRequest} Req Request object
       * @param {IncomingMessage} Res Response object
       * @param {Object} Options Config object passed to the proxy
       *
       * @api private
       */

      XHeaders: function XHeaders(req, res, options) {
        if(!options.xfwd) return;

        var encrypted = req.isSpdy || common.hasEncryptedConnection(req);
        var values = {
          for  : req.connection.remoteAddress || req.socket.remoteAddress,
          port : common.getPort(req),
          proto: encrypted ? 'https' : 'http'
        };

        ['for', 'port', 'proto'].forEach(function(header) {
          req.headers['x-forwarded-' + header] =
            (req.headers['x-forwarded-' + header] || '') +
            (req.headers['x-forwarded-' + header] ? ',' : '') +
            values[header];
        });

        req.headers['x-forwarded-host'] = req.headers['host'] || '';
      },

      /**
       * Does the actual proxying. If `forward` is enabled fires up
       * a ForwardStream, same happens for ProxyStream. The request
       * just dies otherwise.
       *
       * @param {ClientRequest} Req Request object
       * @param {IncomingMessage} Res Response object
       * @param {Object} Options Config object passed to the proxy
       *
       * @api private
       */

      stream: function stream(req, res, options, _, server, clb) {

        // And we begin!
        server.emit('start', req, res, options.target || options.forward);

        var agents = options.followRedirects ? followRedirects : nativeAgents;
        var http = agents.http;
        var https = agents.https;

        if(options.forward) {
          // If forward enable, so just pipe the request
          var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
            common.setupOutgoing(options.ssl || {}, options, req, 'forward')
          );

          // error handler (e.g. ECONNRESET, ECONNREFUSED)
          // Handle errors on incoming request as well as it makes sense to
          var forwardError = createErrorHandler(forwardReq, options.forward);
          req.on('error', forwardError);
          forwardReq.on('error', forwardError);

          (options.buffer || req).pipe(forwardReq);
          if(!options.target) { return res.end(); }
        }

        // Request initalization
        var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
          common.setupOutgoing(options.ssl || {}, options, req)
        );

        // Enable developers to modify the proxyReq before headers are sent
        proxyReq.on('socket', function(socket) {
          if(server) { server.emit('proxyReq', proxyReq, req, res, options); }
        });

        // allow outgoing socket to timeout so that we could
        // show an error page at the initial request
        if(options.proxyTimeout) {
          proxyReq.setTimeout(options.proxyTimeout, function() {
             proxyReq.abort();
          });
        }

        // Ensure we abort proxy if request is aborted
        req.on('aborted', function () {
          proxyReq.abort();
        });

        // handle errors in proxy and incoming request, just like for forward proxy
        var proxyError = createErrorHandler(proxyReq, options.target);
        req.on('error', proxyError);
        proxyReq.on('error', proxyError);

        function createErrorHandler(proxyReq, url) {
          return function proxyError(err) {
            if (req.socket.destroyed && err.code === 'ECONNRESET') {
              server.emit('econnreset', err, req, res, url);
              return proxyReq.abort();
            }

            if (clb) {
              clb(err, req, res, url);
            } else {
              server.emit('error', err, req, res, url);
            }
          }
        }

        (options.buffer || req).pipe(proxyReq);

        proxyReq.on('response', function(proxyRes) {
          if(server) { server.emit('proxyRes', proxyRes, req, res); }

          if(!res.headersSent && !options.selfHandleResponse) {
            for(var i=0; i < web_o.length; i++) {
              if(web_o[i](req, res, proxyRes, options)) { break; }
            }
          }

          if (!res.finished) {
            // Allow us to listen when the proxy has completed
            proxyRes.on('end', function () {
              if (server) server.emit('end', req, res, proxyRes);
            });
            // We pipe to the response unless its expected to be handled by the user
            if (!options.selfHandleResponse) proxyRes.pipe(res);
          } else {
            if (server) server.emit('end', req, res, proxyRes);
          }
        });
      }

    };