From 4110448046dd945afe3e092968d9382d573a369a Mon Sep 17 00:00:00 2001 From: indexzero Date: Wed, 9 Mar 2011 23:53:41 -0500 Subject: [PATCH] [doc api test] Rename HttpProxy.pause to HttpProxy.resume. Update documentation and tests accordingly --- README.md | 48 +++++++++++++----- bin/node-http-proxy | 5 +- demo.js | 8 +-- docs/node-http-proxy.html | 100 ++++++++++++++++++++++---------------- lib/node-http-proxy.js | 43 ++++++++++------ test/helpers.js | 6 +-- 6 files changed, 132 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index ed35a67..89c7de2 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,14 @@ See the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
   var http = require('http'),
       httpProxy = require('http-proxy');
-
+  //
   // Create your proxy server
+  //
   httpProxy.createServer(9000, 'localhost').listen(8000);
 
+  //
   // Create your target server
+  //
   http.createServer(function (req, res) {
     res.writeHead(200, {'Content-Type': 'text/plain'});
     res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
@@ -63,11 +66,15 @@ See the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
 
   var http = require('http'),
       httpProxy = require('http-proxy');
-
-  // create a proxy server with custom application logic
+      
+  //
+  // Create a proxy server with custom application logic
+  //
   httpProxy.createServer(function (req, res, proxy) {
+    //
     // Put your custom server logic here
-    proxy.proxyRequest(9000, 'localhost');
+    //
+    proxy.proxyRequest(req, res, 9000, 'localhost');
   }).listen(8000);
 
   http.createServer(function (req, res) {
@@ -81,13 +88,23 @@ See the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
 
   var http = require('http'),
       httpProxy = require('http-proxy');
-
-  // create a proxy server with custom application logic
+  
+  //
+  // Create a proxy server with custom application logic
+  //
   httpProxy.createServer(function (req, res, proxy) {
+    //
+    // Buffer the request so that `data` and `end` events
+    // are not lost during async operation(s).
+    //
+    var buffer = proxy.buffer(req);
+    
+    //
     // Wait for two seconds then respond: this simulates
     // performing async actions before proxying a request
+    //
     setTimeout(function () {
-      proxy.proxyRequest(9000, 'localhost');      
+      proxy.proxyRequest(req, res, 9000, 'localhost', buffer);      
     }, 2000);
   }).listen(8000);
 
@@ -102,15 +119,20 @@ See the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
 
   var http = require('http'),
       httpProxy = require('http-proxy');
+      
+  //
+  // Create a new instance of HttProxy to use in your server
+  //
+  var proxy = new httpProxy.HttpProxy();
 
-  // create a regular http server and proxy its handler
+  //
+  // Create a regular http server and proxy its handler
+  //
   http.createServer(function (req, res) {
-    // Create a new instance of HttProxy for this request
-    // each instance is only valid for serving one request
-    var proxy = new httpProxy.HttpProxy(req, res);
-    
+    //
     // Put your custom server logic here, then proxy
-    proxy.proxyRequest(9000, 'localhost', req, res);
+    //
+    proxy.proxyRequest(req, res, 9000, 'localhost');
   }).listen(8001);
 
   http.createServer(function (req, res) {
diff --git a/bin/node-http-proxy b/bin/node-http-proxy
index 29e5c34..ad2b24c 100755
--- a/bin/node-http-proxy
+++ b/bin/node-http-proxy
@@ -9,7 +9,7 @@ var path = require('path'),
 var help = [
     "usage: node-http-proxy [options] ",
     "",
-    "All options should be set with the syntax --option=value",
+    "Starts a node-http-proxy server using the specified command-line options",
     "",
     "options:",
     "  --port   PORT       Port that the proxy server should run on",
@@ -20,8 +20,7 @@ var help = [
 ].join('\n');
 
 if (argv.h || argv.help || Object.keys(argv).length === 2) {
-  util.puts(help);
-  process.exit(0);
+  return util.puts(help);
 }
 
 var location, config = {},
diff --git a/demo.js b/demo.js
index 4e5fbb2..d3c17db 100644
--- a/demo.js
+++ b/demo.js
@@ -59,9 +59,9 @@ util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue +
 // Http Proxy Server with Latency
 //
 httpProxy.createServer(function (req, res, proxy) {
-  var paused = proxy.pause(req);
+  var buffer = proxy.buffer(req);
   setTimeout(function() {
-    proxy.proxyRequest(req, res, 9000, 'localhost', paused);
+    proxy.proxyRequest(req, res, 9000, 'localhost', buffer);
   }, 200)
 }).listen(8002);
 util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with latency'.magenta.underline);
@@ -82,9 +82,9 @@ util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue +
 //
 var standAloneProxy = new httpProxy.HttpProxy();
 http.createServer(function (req, res) {
-  var paused = standAloneProxy.pause(req);
+  var buffer = standAloneProxy.buffer(req);
   setTimeout(function() {
-    proxy.proxyRequest(req, res, 9000, 'localhost', paused);
+    proxy.proxyRequest(req, res, 9000, 'localhost', buffer);
   }, 200);
 }).listen(8004);
 util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8004 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);
diff --git a/docs/node-http-proxy.html b/docs/node-http-proxy.html
index dd9dd97..31f1af1 100644
--- a/docs/node-http-proxy.html
+++ b/docs/node-http-proxy.html
@@ -52,7 +52,23 @@ made by all instances of HttpProxy

allowed on every outgoing request made by all instances of HttpProxy

exports.setMaxSockets = function (value) {
   maxSockets = value;
-};

function createServer ([port, host, options], handler)

exports.createServer = function () {
+};

function createServer ([port, host, options, handler])

+ +

@port {number} Optional Port to use on the proxy target host.

+ +

@host {string} Optional Host of the proxy target.

+ +

@options {Object} Optional Options for the HttpProxy instance used

+ +

@handler {function} Optional Request handler for the server

+ +

Returns a server that manages an instance of HttpProxy. Flexible arguments allow for:

+ +
    +
  • httpProxy.createServer(9000, 'localhost')
  • +
  • `httpProxy.createServer(9000, 'localhost', options)
  • +
  • httpPRoxy.createServer(function (req, res, proxy) { ... })
  • +
exports.createServer = function () {
   var args, callback, port, host, forward, 
       silent, options, proxy, server;
   
@@ -137,7 +153,7 @@ for managing the life-cycle of streaming reverse proxyied HTTP requests.

self.emit('routes', routes); }); } -};

Inherit from events.EventEmitter

util.inherits(HttpProxy, events.EventEmitter);

function pause (obj)

+};

Inherit from events.EventEmitter

util.inherits(HttpProxy, events.EventEmitter);

function buffer (obj)

@obj {Object} Object to pause events from

@@ -147,9 +163,9 @@ Consumers of HttpProxy performing async tasks the async operation has completed, otherwise these events will be lost.

-
 var pause = httpProxy.pause(req);
+
 var buffer = httpProxy.buffer(req);
  fs.readFile(path, function(){
-    httpProxy.proxyRequest(req, res, host, port, paused);
+    httpProxy.proxyRequest(req, res, host, port, buffer);
  });
 
@@ -157,7 +173,7 @@ the async operation has completed, otherwise these Connect. However, this is not a big leap from the implementation in node-http-proxy < 0.4.0. This simply chooses to manage the scope of the events on a new Object literal as opposed to -on the HttpProxy instance.

HttpProxy.prototype.pause = function (obj) {
+on the HttpProxy instance.

HttpProxy.prototype.buffer = function (obj) {
   var onData, onEnd, events = [];
 
   obj.on('data', onData = function (data, encoding) {
@@ -195,7 +211,7 @@ if they exist. 

@host {string} Optional Host of the proxy target.

-

@paused {Object} Optional Result from httpProxy.pause(req)

HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
+

@buffer {Object} Optional Result from httpProxy.buffer(req)

HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
   var self = this, reverseProxy, location, errState = false;
   

Check the proxy table for this instance to see if we need to get the proxy location for the request supplied. We will @@ -211,10 +227,10 @@ then respond with 404 since we do not have a valid proxy target.

    -
  • proxy.proxyRequest(req, res, port, host, paused): This will be skipped
  • -
  • proxy.proxyRequest(req, res, paused): Paused will get updated appropriately
  • +
  • proxy.proxyRequest(req, res, port, host, buffer): This will be skipped
  • +
  • proxy.proxyRequest(req, res, buffer): Buffer will get updated appropriately
  • proxy.proxyRequest(req, res): No effect undefined = undefined
  • -
    paused = port;
+                                         
    buffer = port;
     port = location.port;
     host = location.host;
   }
@@ -242,7 +258,7 @@ specified request to the address provided in this.options.forwardpath: req.url,
     headers: req.headers
   }, function (response) {
-    

Process the reverse_proxy response when it's received.

    if (response.headers.connection) {
+    

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 response headers of the client response

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

Status code = 304 @@ -268,48 +284,50 @@ No 'data' event and no 'end'

if (!errState) { reverseProxy.end(); } - }); - - if (paused && !errState) { - paused.resume(); + });

If we have been passed buffered data, resume it.

  if (buffer && !errState) {
+    buffer.resume();
   }
 };
-  
-HttpProxy.prototype._forwardRequest = function (req) {
+  

@private function _forwardRequest (req)

+ +

@req {ServerRequest} Incoming HTTP Request to proxy.

+ +

Forwards the specified req to the location specified +by this.options.forward ignoring errors and the subsequent response.

HttpProxy.prototype._forwardRequest = function (req) {
   var self = this, port, host, forwardProxy;
 
   port = this.options.forward.port;
   host = this.options.forward.host;
-  

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

  forwardProxy = http.request({
+  

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

  forwardProxy = http.request({
     host: host,
     port: port,
     agent: _getAgent(host, port),
     method: req.method,
     path: req.url,
     headers: req.headers
-  }, function (response) {

Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy. + }, 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.on('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.on('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 (port, server, host, data) {
   var self = this, req = self.req, socket = self.sock, head = self.head, 
-      headers = new _headers(req.headers), CRLF = '\r\n';

Will generate clone of headers + headers = new _headers(req.headers), CRLF = '\r\n';

Will generate clone of headers To not change original

  function _headers(headers) {
     var h = {};
     for (var i in headers) {
       h[i] = headers[i];
     }
     return h;
-  }

WebSocket requests has method = GET

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

This request is not WebSocket request

    return;
-  }

Turn of all bufferings + }

WebSocket requests has method = GET

  if (req.method !== 'GET' || 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, server) {
     socket.setTimeout(0);
@@ -320,20 +338,20 @@ For client set encoding

else { socket.setEncoding('utf8'); } - }

Client socket

  _socket(socket);

If host is undefined + }

Client socket

  _socket(socket);

If host is undefined Get it from headers

  if (!host) {
     host = headers.Host;
   }
-  

Remote host address

  var remote_host = server + (port - 80 === 0 ? '' : ':' + port);

Change headers

  headers.Host = remote_host;
-  headers.Origin = 'http://' + remote_host;

Open request

  var p = manager.getPool(port, server);
+  

Remote host address

  var remote_host = server + (port - 80 === 0 ? '' : ':' + port);

Change headers

  headers.Host = remote_host;
+  headers.Origin = 'http://' + remote_host;

Open request

  var p = manager.getPool(port, server);
 
-  p.getClient(function(client) {

Based on 'pool/main.js'

    var request = client.request('GET', req.url, headers);
+  p.getClient(function(client) {

Based on 'pool/main.js'

    var request = client.request('GET', req.url, headers);
 
     var errorListener = function (error) {
       client.removeListener('error', errorListener);
-      

Remove the client from the pool's available clients since it has errored

      p.clients.splice(p.clients.indexOf(client), 1);
+      

Remove the client from the pool's available clients since it has errored

      p.clients.splice(p.clients.indexOf(client), 1);
       socket.end();
-    }

Not disconnect on update

    client.on('upgrade', function(request, remote_socket, head) {

Prepare socket

      _socket(remote_socket, true);

Emit event

      onUpgrade(remote_socket);
+    }

Not disconnect on update

    client.on('upgrade', function(request, remote_socket, head) {

Prepare socket

      _socket(remote_socket, true);

Emit event

      onUpgrade(remote_socket);
     });
 
     client.on('error', errorListener);
@@ -347,23 +365,23 @@ Get it from headers

client.busy = true; var handshake; - request.socket.on('data', handshake = function(data) {

Handshaking

Ok, kind of harmfull part of code + request.socket.on('data', handshake = function(data) {

Handshaking

Ok, kind of harmfull part of code Socket.IO is sending 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 Printable

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

Get Non-Printable

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

Replace host and origin

      sdata = sdata.replace(remote_host, host)
+(Non-printable will come after double-CRLF)

      var sdata = data.toString();

Get Printable

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

Get Non-Printable

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

Replace host and origin

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

Write printable

        socket.write(sdata);

Write non-printable

        socket.write(data);
+      try {

Write printable

        socket.write(sdata);

Write non-printable

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

Catch socket errors

      socket.on('error', function() {
+      }

Catch socket errors

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

Remove data listener now that the 'handshake' is complete

      request.socket.removeListener('data', handshake);
-    });

Write upgrade-head

    try {
+      });

Remove data listener now that the 'handshake' is complete

      request.socket.removeListener('data', handshake);
+    });

Write upgrade-head

    try {
       request.write(head);
     } 
     catch(e) {
@@ -371,9 +389,9 @@ So we split data to printable data and to non-printable
       socket.end();
     }
     self.unwatch(socket);
-  });

Request

  function onUpgrade(reverse_proxy) {
+  });

Request

  function onUpgrade(reverse_proxy) {
     var listeners = {};
-    

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

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

Pass data to client

      if (socket.writable) {
+    

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

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

Pass data to client

      if (socket.writable) {
         try {
           socket.write(data);
         } 
@@ -384,19 +402,19 @@ So we split data to printable data and to non-printable
       }
     });
 
-    socket.on('data', listeners._data = function(data) {

Pass data from client to server

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

Pass data from client to server

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

Detach event listeners from reverse_proxy

    function detach() {
+    });

Detach event listeners from reverse_proxy

    function detach() {
       reverse_proxy.removeListener('close', listeners._r_close);
       reverse_proxy.removeListener('data', listeners._r_data);
       socket.removeListener('data', listeners._data);
       socket.removeListener('close', listeners._close);
-    }

Hook disconnections

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

Hook disconnections

    reverse_proxy.on('end', listeners._r_close = function() {
       socket.end();
       detach();
     });
diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js
index 5362ab5..1594d48 100644
--- a/lib/node-http-proxy.js
+++ b/lib/node-http-proxy.js
@@ -69,8 +69,16 @@ exports.setMaxSockets = function (value) {
 };
 
 //
-// ### function createServer ([port, host, options], handler)
-// 
+// ### function createServer ([port, host, options, handler])
+// #### @port {number} **Optional** Port to use on the proxy target host.
+// #### @host {string} **Optional** Host of the proxy target.
+// #### @options {Object} **Optional** Options for the HttpProxy instance used
+// #### @handler {function} **Optional** Request handler for the server
+// Returns a server that manages an instance of HttpProxy. Flexible arguments allow for:
+//
+// * `httpProxy.createServer(9000, 'localhost')`
+// * `httpProxy.createServer(9000, 'localhost', options)
+// * `httpPRoxy.createServer(function (req, res, proxy) { ... })`
 //
 exports.createServer = function () {
   var args, callback, port, host, forward, 
@@ -172,7 +180,7 @@ var HttpProxy = exports.HttpProxy = function (options) {
 util.inherits(HttpProxy, events.EventEmitter);
 
 //
-// ### function pause (obj) 
+// ### function buffer (obj) 
 // #### @obj {Object} Object to pause events from
 // Pause `data` and `end` events on the given `obj`.
 // Consumers of HttpProxy performing async tasks 
@@ -180,9 +188,9 @@ util.inherits(HttpProxy, events.EventEmitter);
 // the async operation has completed, otherwise these
 // __events will be lost.__
 //
-//      var pause = httpProxy.pause(req);
+//      var buffer = httpProxy.buffer(req);
 //      fs.readFile(path, function(){
-//         httpProxy.proxyRequest(req, res, host, port, paused);
+//         httpProxy.proxyRequest(req, res, host, port, buffer);
 //      });
 //
 // __Attribution:__ This approach is based heavily on 
@@ -191,7 +199,7 @@ util.inherits(HttpProxy, events.EventEmitter);
 // This simply chooses to manage the scope of  the events on a new Object literal as opposed to
 // [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
 //
-HttpProxy.prototype.pause = function (obj) {
+HttpProxy.prototype.buffer = function (obj) {
   var onData, onEnd, events = [];
 
   obj.on('data', onData = function (data, encoding) {
@@ -231,9 +239,9 @@ HttpProxy.prototype.close = function () {
 // #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
 // #### @port {number} **Optional** Port to use on the proxy target host.
 // #### @host {string} **Optional** Host of the proxy target.
-// #### @paused {Object} **Optional** Result from `httpProxy.pause(req)`
+// #### @buffer {Object} **Optional** Result from `httpProxy.buffer(req)`
 //
-HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
+HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
   var self = this, reverseProxy, location, errState = false;
   
   //
@@ -258,11 +266,11 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
     // When using the ProxyTable in conjunction with an HttpProxy instance
     // only the following arguments are valid:
     // 
-    // * `proxy.proxyRequest(req, res, port, host, paused)`: This will be skipped
-    // * `proxy.proxyRequest(req, res, paused)`: Paused will get updated appropriately
+    // * `proxy.proxyRequest(req, res, port, host, buffer)`: This will be skipped
+    // * `proxy.proxyRequest(req, res, buffer)`: Buffer will get updated appropriately
     // * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined` 
     //
-    paused = port;
+    buffer = port;
     port = location.port;
     host = location.host;
   }
@@ -297,7 +305,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
     headers: req.headers
   }, function (response) {
     
-    // Process the reverse_proxy response when it's received.
+    // 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';
@@ -345,11 +353,18 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
     }
   });
 
-  if (paused && !errState) {
-    paused.resume();
+  // If we have been passed buffered data, resume it.
+  if (buffer && !errState) {
+    buffer.resume();
   }
 };
   
+//
+// ### @private function _forwardRequest (req)
+// #### @req {ServerRequest} Incoming HTTP Request to proxy.
+// Forwards the specified `req` to the location specified
+// by `this.options.forward` ignoring errors and the subsequent response.
+//
 HttpProxy.prototype._forwardRequest = function (req) {
   var self = this, port, host, forwardProxy;
 
diff --git a/test/helpers.js b/test/helpers.js
index 87a1e95..85782e7 100644
--- a/test/helpers.js
+++ b/test/helpers.js
@@ -100,7 +100,7 @@ TestRunner.prototype.startProxyServer = function (port, targetPort, host, callba
 TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) {
   // Initialize the nodeProxy and start proxying the request
   var that = this, proxyServer = httpProxy.createServer(function (req, res, proxy) {
-    var data = proxy.pause(req);
+    var data = proxy.buffer(req);
     
     setTimeout(function () {
       proxy.proxyRequest(req, res, targetPort, host, data);
@@ -133,9 +133,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
   // Initialize the nodeProxy and start proxying the request
   var proxyServer, that = this, proxy = new httpProxy.HttpProxy(options);
   proxyServer = http.createServer(function (req, res) {
-    var paused = proxy.pause(req);
+    var buffer = proxy.buffer(req);
     setTimeout(function () {
-      proxy.proxyRequest(req, res, paused);
+      proxy.proxyRequest(req, res, buffer);
     }, latency);
   });