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;
exports.version = [0, 5, 3];exports.version = [0, 5, 7]; 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:
+ +x-forwarded-for: IP Address of the original requestx-forwarded-proto: Protocol of the original requestx-forwarded-port: Port of the original request. 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.
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();
}
};
- 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 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 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 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 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();
}
};