diff --git a/docs/node-http-proxy.html b/docs/node-http-proxy.html index c70aebb..ddfc783 100644 --- a/docs/node-http-proxy.html +++ b/docs/node-http-proxy.html @@ -29,7 +29,7 @@ https = require('https'), events = require('events'), ProxyTable = require('./proxy-table').ProxyTable, - maxSockets = 100;

Version 0.5.3 // 5/17/2011

exports.version = [0, 5, 3];

function _getAgent (host, port, secure)

+ maxSockets = 100;

Version 0.5.7 // 5/19/2011

exports.version = [0, 5, 7];

function _getAgent (host, port, secure)

@host {string} Host of the agent to get

@@ -279,7 +279,16 @@ only the following arguments are valid:

    options.port = location.port;
     options.host = location.host;
   }
-  

Add x-forwarded-for header to availible client IP to apps behind proxy

  req.headers['x-forwarded-for'] = req.connection.remoteAddress;
+  

Add common proxy headers to the request so that they can +be availible to the proxy target server:

+ +
  req.headers['x-forwarded-for']   = req.connection.remoteAddress || req.connection.socket.remoteAddress;
+  req.headers['x-forwarded-port']  = req.connection.remotePort || req.connection.socket.remotePort;
+  req.headers['x-forwarded-proto'] = res.connection.pair ? 'https' : 'http';
   

Emit the start event indicating that we have begun the proxy operation.

  this.emit('start', req, res, options);
   

If forwarding is enabled for this instance, foward proxy the specified request to the address provided in this.forward

  if (this.forward) {
@@ -293,10 +302,20 @@ specified request to the address provided in this.forward

Short-circuits res in the event of any error when contacting the proxy target at host / port.

  function proxyError(err) {
     errState = true;
+    

Emit an error event, allowing the application to use custom +error handling. The error handler should end the response.

    if (self.emit('proxyError', err, req, res)) {
+      return;
+    }
+
     res.writeHead(500, { 'Content-Type': 'text/plain' });
 
-    if (req.method !== 'HEAD') {
-      res.write('An error has occurred: ' + JSON.stringify(err));
+    if (req.method !== 'HEAD') {

This NODE_ENV=production behavior is mimics Express and +Connect.

      if (process.env.NODE_ENV === 'production') {
+        res.write('Internal Server Error');
+      }
+      else {
+        res.write('An error has occurred: ' + JSON.stringify(err));
+      }
     }
   
     res.end();
@@ -310,22 +329,22 @@ contacting the proxy target at host / port.

path: req.url, headers: req.headers }; -

Force the connection header to be 'close' until +

Force the connection header to be 'close' until node.js core re-implements 'keep-alive'.

  outgoing.headers['connection'] = 'close';
   
   protocol = _getProtocol(options.https || this.target.https, outgoing);
-  

Open new HTTP request to internal resource with will act as a reverse proxy pass

  reverseProxy = protocol.request(outgoing, function (response) {
-    

Process the reverseProxy response when it's received.

    if (response.headers.connection) {
+  

Open new HTTP request to internal resource with will act as a reverse proxy pass

  reverseProxy = protocol.request(outgoing, function (response) {
+    

Process the reverseProxy response when it's received.

    if (response.headers.connection) {
       if (req.headers.connection) response.headers.connection = req.headers.connection;
       else response.headers.connection = 'close';
-    }

Set the headers of the client response

    res.writeHead(response.statusCode, response.headers);

response.statusCode === 304: No 'data' event and no 'end'

    if (response.statusCode === 304) {
+    }

Set the headers of the client response

    res.writeHead(response.statusCode, response.headers);

response.statusCode === 304: No 'data' event and no 'end'

    if (response.statusCode === 304) {
       return res.end();
-    }

For each data chunk received from the reverseProxy + }

For each data chunk received from the reverseProxy response write it to the outgoing res.

    response.on('data', function (chunk) {
       if (req.method !== 'HEAD') {
         res.write(chunk);
       }
-    });

When the reverseProxy response ends, end the + });

When the reverseProxy response ends, end the corresponding outgoing res unless we have entered an error state. In which case, assume res.end() has already been called and the 'error' event listener @@ -333,26 +352,26 @@ removed.

if (!errState) { reverseProxy.removeListener('error', proxyError); res.end(); -

Emit the end event now that we have completed proxying

        self.emit('end', req, res);
+        

Emit the end event now that we have completed proxying

        self.emit('end', req, res);
       }
     });
   });
-  

Handle 'error' events from the reverseProxy.

  reverseProxy.once('error', proxyError);
-  

For each data chunk received from the incoming +

Handle 'error' events from the reverseProxy.

  reverseProxy.once('error', proxyError);
+  

For each data chunk received from the incoming req write it to the reverseProxy request.

  req.on('data', function (chunk) {
     if (!errState) {
       reverseProxy.write(chunk);
     }
-  });

When the incoming req ends, end the corresponding reverseProxy + });

When the incoming req ends, end the corresponding reverseProxy request unless we have entered an error state.

  req.on('end', function () {
     if (!errState) {
       reverseProxy.end();
     }
-  });

If we have been passed buffered data, resume it.

  if (options.buffer && !errState) {
+  });

If we have been passed buffered data, resume it.

  if (options.buffer && !errState) {
     options.buffer.resume();
   }
 };
-  

@private function _forwardRequest (req)

+

@private function _forwardRequest (req)

@req {ServerRequest} Incoming HTTP Request to proxy.

@@ -371,25 +390,26 @@ by this.forward ignoring errors and the subsequent response.

path: req.url, headers: req.headers }; -

Force the connection header to be 'close' until +

Force the connection header to be 'close' until node.js core re-implements 'keep-alive'.

  outgoing.headers['connection'] = 'close';
   
   protocol = _getProtocol(this.forward.https, outgoing);
-  

Open new HTTP request to internal resource with will act as a reverse proxy pass

  forwardProxy = protocol.request(outgoing, function (response) {

Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy. +

Open new HTTP request to internal resource with will act as a reverse proxy pass

  forwardProxy = protocol.request(outgoing, function (response) {

Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy. Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.

  });
-  

Add a listener for the connection timeout event.

+

Add a listener for the connection timeout event.

Remark: Ignoring this error in the event - forward target doesn't exist.

  forwardProxy.once('error', function (err) { });

Chunk the client request body as chunks from the proxied request come in

  req.on('data', function (chunk) {
+        forward target doesn't exist.

  forwardProxy.once('error', function (err) { });

Chunk the client request body as chunks from the proxied request come in

  req.on('data', function (chunk) {
     forwardProxy.write(chunk);
-  })

At the end of the client request, we are going to stop the proxied request

  req.on('end', function () {
+  })

At the end of the client request, we are going to stop the proxied request

  req.on('end', function () {
     forwardProxy.end();
   });
 };
 
 HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
-  var self = this, outgoing, errState = false, CRLF = '\r\n';

WebSocket requests has method = GET

  if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {

This request is not WebSocket request

    return;
-  }

Turn of all bufferings + var self = this, outgoing, errState = false, CRLF = '\r\n';

WebSocket requests has method = GET

  if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {

This request is not WebSocket request

    return;
+  }
+  

Turn of all bufferings For server set KeepAlive For client set encoding

  function _socket(socket, keepAlive) {
     socket.setTimeout(0);
@@ -415,7 +435,7 @@ For client set encoding

} var listeners = {}; -

We're now connected to the server, so lets change server socket

    proxySocket.on('data', listeners._r_data = function(data) {

Pass data to client

      if (reverseProxy.incoming.socket.writable) {
+    

We're now connected to the server, so lets change server socket

    proxySocket.on('data', listeners._r_data = function(data) {

Pass data to client

      if (reverseProxy.incoming.socket.writable) {
         try {
           reverseProxy.incoming.socket.write(data);
         } 
@@ -426,19 +446,19 @@ For client set encoding

} }); - reverseProxy.incoming.socket.on('data', listeners._data = function(data) {

Pass data from client to server

      try {
+    reverseProxy.incoming.socket.on('data', listeners._data = function(data) {

Pass data from client to server

      try {
         proxySocket.write(data);
       } 
       catch (e) {
         proxySocket.end();
         socket.end();
       }
-    });

Detach event listeners from reverseProxy

    function detach() {
+    });

Detach event listeners from reverseProxy

    function detach() {
       proxySocket.removeListener('end', listeners._r_close);
       proxySocket.removeListener('data', listeners._r_data);
       reverseProxy.incoming.socket.removeListener('data', listeners._data);
       reverseProxy.incoming.socket.removeListener('end', listeners._close);
-    }

Hook disconnections

    proxySocket.on('end', listeners._r_close = function() {
+    }

Hook disconnections

    proxySocket.on('end', listeners._r_close = function() {
       reverseProxy.incoming.socket.end();
       detach();
     });
@@ -447,10 +467,10 @@ For client set encoding

proxySocket.end(); detach(); }); - };

Client socket

  _socket(socket);
-  

Remote host address

  var protocolName = options.https || this.target.https ? 'https' : 'http',
+  };

Client socket

  _socket(socket);
+  

Remote host address

  var protocolName = options.https || this.target.https ? 'https' : 'http',
       agent        = _getAgent(options.host, options.port, options.https || this.target.https),
-      remoteHost   = options.host + (options.port - 80 === 0 ? '' : ':' + options.port);

Change headers

  if (this.changeOrigin) {
+      remoteHost   = options.host + (options.port - 80 === 0 ? '' : ':' + options.port);

Change headers

  if (this.changeOrigin) {
     req.headers.host   = remoteHost;
     req.headers.origin = protocolName + '://' + remoteHost;
   }
@@ -461,7 +481,16 @@ For client set encoding

method: 'GET', path: req.url, headers: req.headers, - };

Make the outgoing WebSocket request

  var reverseProxy = agent.appendMessage(outgoing);

Here we set the incoming req, socket and head data to the outgoing + };

Make the outgoing WebSocket request

  var reverseProxy = agent.appendMessage(outgoing);
+
+  function proxyError (err) {
+    reverseProxy.end();
+    if (self.emit('webSocketProxyError', req, socket, head)) {
+      return;
+    }
+    
+    socket.end();
+  }

Here we set the incoming req, socket and head data to the outgoing request so that we can reuse this data later on in the closure scope available to the upgrade event. This bookkeeping is not tracked anywhere in nodejs core and is very specific to proxying WebSockets.

  reverseProxy.agent = agent;
@@ -470,52 +499,45 @@ in nodejs core and is very specific to proxying WebSockets.

socket: socket, head: head }; -

If the agent for this particular host and port combination +

If the agent for this particular host and port combination is not already listening for the upgrade event, then do so once. This will force us not to disconnect.

In addition, it's important to note the closure scope here. Since there is no mapping of the

  if (!agent._events || agent._events['upgrade'].length === 0) {
-    agent.on('upgrade', function (_, remoteSocket, head) {

Prepare the socket for the reverseProxy request and begin to -stream data between the two sockets

      _socket(remoteSocket, true);
+    agent.on('upgrade', function (_, remoteSocket, head) {

Prepare the socket for the reverseProxy request and begin to +stream data between the two sockets. Here it is important to +note that remoteSocket._httpMessage === reverseProxy.

      _socket(remoteSocket, true);
       onUpgrade(remoteSocket._httpMessage, remoteSocket);
     });
   }
-  

If the reverseProxy connection has an underlying socket, +

If the reverseProxy connection has an underlying socket, then behing the handshake.

  if (typeof reverseProxy.socket !== 'undefined') {
-    reverseProxy.socket.on('data', function handshake (data) {

Ok, kind of harmfull part of code. Socket.IO sends a hash + reverseProxy.socket.on('data', function handshake (data) {

Ok, kind of harmfull part of code. Socket.IO sends a hash at the end of handshake if protocol === 76, but we need to replace 'host' and 'origin' in response so we split data to printable data and to non-printable. (Non-printable -will come after double-CRLF).

      var sdata = data.toString();

Get the Printable data

      sdata = sdata.substr(0, sdata.search(CRLF + CRLF));

Get the Non-Printable data

      data = data.slice(Buffer.byteLength(sdata), data.length);

Replace the host and origin headers in the Printable data

      sdata = sdata.replace(remoteHost, options.host)
+will come after double-CRLF).

      var sdata = data.toString();

Get the Printable data

      sdata = sdata.substr(0, sdata.search(CRLF + CRLF));

Get the Non-Printable data

      data = data.slice(Buffer.byteLength(sdata), data.length);

Replace the host and origin headers in the Printable data

      sdata = sdata.replace(remoteHost, options.host)
                    .replace(remoteHost, options.host);
 
-      try {

Write the printable and non-printable data to the socket + try {

Write the printable and non-printable data to the socket from the original incoming request.

        socket.write(sdata);
         socket.write(data);
       } 
-      catch (e) {
-        reverseProxy.end();
-        socket.end();
-      }

Catch socket errors

      socket.on('error', function() {
-        reverseProxy.end();
-        socket.end();
-      });

Remove data listener now that the 'handshake' is complete

      reverseProxy.socket.removeListener('data', handshake);
+      catch (ex) {
+        proxyError(ex)
+      }

Catch socket errors

      socket.on('error', proxyError);

Remove data listener now that the 'handshake' is complete

      reverseProxy.socket.removeListener('data', handshake);
     });
   }
   
-  reverseProxy.on('error', function (err) {
-    reverseProxy.end();
-    socket.end();
-  });
+  reverseProxy.on('error', proxyError);
 
-  try {

Attempt to write the upgrade-head to the reverseProxy request.

    reverseProxy.write(head);
+  try {

Attempt to write the upgrade-head to the reverseProxy request.

    reverseProxy.write(head);
   } 
   catch (ex) {
-    reverseProxy.end();
-    socket.end();
+    proxyError(ex);
   }
-  

If we have been passed buffered data, resume it.

  if (options.buffer && !errState) {
+  

If we have been passed buffered data, resume it.

  if (options.buffer && !errState) {
     options.buffer.resume();
   }
 };