summaryrefslogtreecommitdiffstats
path: root/vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js
diff options
context:
space:
mode:
authorseshukm <seshu.kumar.m@huawei.com>2017-04-10 15:52:12 +0530
committerseshukm <seshu.kumar.m@huawei.com>2017-04-10 15:52:12 +0530
commit84b3a1c939dc2baf22f9f5e30029b263c1512ade (patch)
tree131f2c17ad61078f95425eab1e349b96d0245d36 /vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js
parentef9cd2561e325c82d4d1fcb03fef4582a53d7839 (diff)
Integration of VNFMarket to Main GUI
IssueId : CLIENT-189 Change-Id: I37f49f6447ba2d5fb57779754117549eb315f0b5 Signed-off-by: seshukm <seshu.kumar.m@huawei.com>
Diffstat (limited to 'vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js')
-rw-r--r--vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js983
1 files changed, 983 insertions, 0 deletions
diff --git a/vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js b/vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js
new file mode 100644
index 00000000..92541bae
--- /dev/null
+++ b/vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js
@@ -0,0 +1,983 @@
+/*
+ node-http-proxy.js: http proxy for node.js
+
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+var events = require('events'),
+ http = require('http'),
+ util = require('util'),
+ url = require('url'),
+ httpProxy = require('../node-http-proxy');
+
+//
+// @private {RegExp} extractPort
+// Reusable regular expression for getting the
+// port from a host string.
+//
+var extractPort = /:(\d+)$/;
+
+//
+// ### function HttpProxy (options)
+// #### @options {Object} Options for this instance.
+// Constructor function for new instances of HttpProxy responsible
+// for managing the life-cycle of streaming reverse proxyied HTTP requests.
+//
+// Example options:
+//
+// {
+// target: {
+// host: 'localhost',
+// port: 9000
+// },
+// forward: {
+// host: 'localhost',
+// port: 9001
+// }
+// }
+//
+var HttpProxy = exports.HttpProxy = function (options) {
+ if (!options || !options.target) {
+ throw new Error('Both `options` and `options.target` are required.');
+ }
+
+ events.EventEmitter.call(this);
+
+ var self = this;
+
+ //
+ // Setup basic proxying options:
+ //
+ // * forward {Object} Options for a forward-proxy (if-any)
+ // * target {Object} Options for the **sole** proxy target of this instance
+ //
+ this.forward = options.forward;
+ this.target = options.target;
+ this.timeout = options.timeout;
+
+ //
+ // Setup the necessary instances instance variables for
+ // the `target` and `forward` `host:port` combinations
+ // used by this instance.
+ //
+ // * agent {http[s].Agent} Agent to be used by this instance.
+ // * protocol {http|https} Core node.js module to make requests with.
+ // * base {Object} Base object to create when proxying containing any https settings.
+ //
+ function setupProxy (key) {
+ self[key].agent = httpProxy._getAgent(self[key]);
+ self[key].protocol = httpProxy._getProtocol(self[key]);
+ self[key].base = httpProxy._getBase(self[key]);
+ }
+
+ setupProxy('target');
+ if (this.forward) {
+ setupProxy('forward');
+ }
+
+ //
+ // Setup opt-in features
+ //
+ this.enable = options.enable || {};
+ this.enable.xforward = typeof this.enable.xforward === 'boolean'
+ ? this.enable.xforward
+ : true;
+
+ // if event listener is set then use it else unlimited.
+ this.eventListenerCount = typeof options.eventListenerCount === 'number'? options.eventListenerCount : 0 ;
+
+ //
+ // Setup additional options for WebSocket proxying. When forcing
+ // the WebSocket handshake to change the `sec-websocket-location`
+ // and `sec-websocket-origin` headers `options.source` **MUST**
+ // be provided or the operation will fail with an `origin mismatch`
+ // by definition.
+ //
+ this.source = options.source || { host: 'localhost', port: 80 };
+ this.source.https = this.source.https || options.https;
+ this.changeOrigin = options.changeOrigin || false;
+};
+
+// Inherit from events.EventEmitter
+util.inherits(HttpProxy, events.EventEmitter);
+
+//
+// ### function proxyRequest (req, res, buffer)
+// #### @req {ServerRequest} Incoming HTTP Request to proxy.
+// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
+// #### @buffer {Object} Result from `httpProxy.buffer(req)`
+//
+HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
+ var self = this,
+ errState = false,
+ outgoing = new(this.target.base),
+ reverseProxy,
+ location;
+
+ // If this is a DELETE request then set the "content-length"
+ // header (if it is not already set)
+ if (req.method === 'DELETE') {
+ req.headers['content-length'] = req.headers['content-length'] || '0';
+ }
+
+ //
+ // Add common proxy headers to the request so that they can
+ // be availible to the proxy target server. If the proxy is
+ // part of proxy chain it will append the address:
+ //
+ // * `x-forwarded-for`: IP Address of the original request
+ // * `x-forwarded-proto`: Protocol of the original request
+ // * `x-forwarded-port`: Port of the original request.
+ //
+ if (this.enable.xforward && req.connection && req.socket) {
+ if (req.headers['x-forwarded-for']) {
+ var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress;
+ req.headers['x-forwarded-for'] += addressToAppend;
+ }
+ else {
+ req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress;
+ }
+
+ if (req.headers['x-forwarded-port']) {
+ var portToAppend = "," + getPortFromHostHeader(req);
+ req.headers['x-forwarded-port'] += portToAppend;
+ }
+ else {
+ req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
+ }
+
+ if (req.headers['x-forwarded-proto']) {
+ var protoToAppend = "," + getProto(req);
+ req.headers['x-forwarded-proto'] += protoToAppend;
+ }
+ else {
+ req.headers['x-forwarded-proto'] = getProto(req);
+ }
+ }
+
+ if (this.timeout) {
+ req.socket.setTimeout(this.timeout);
+ }
+
+ //
+ // Emit the `start` event indicating that we have begun the proxy operation.
+ //
+ this.emit('start', req, res, this.target);
+
+ //
+ // If forwarding is enabled for this instance, foward proxy the
+ // specified request to the address provided in `this.forward`
+ //
+ if (this.forward) {
+ this.emit('forward', req, res, this.forward);
+ this._forwardRequest(req);
+ }
+
+ //
+ // #### function proxyError (err)
+ // #### @err {Error} Error contacting the proxy target
+ // 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') {
+ //
+ // 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));
+ }
+ }
+
+ try { res.end() }
+ catch (ex) { console.error("res.end error: %s", ex.message) }
+ }
+
+ //
+ // Setup outgoing proxy with relevant properties.
+ //
+ outgoing.host = this.target.host;
+ outgoing.hostname = this.target.hostname;
+ outgoing.port = this.target.port;
+ outgoing.socketPath = this.target.socketPath;
+ outgoing.agent = this.target.agent;
+ outgoing.method = req.method;
+ outgoing.path = url.parse(req.url).path;
+ outgoing.headers = req.headers;
+
+ //
+ // If the changeOrigin option is specified, change the
+ // origin of the host header to the target URL! Please
+ // don't revert this without documenting it!
+ //
+ if (this.changeOrigin) {
+ outgoing.headers.host = this.target.host;
+ // Only add port information to the header if not default port
+ // for this protocol.
+ // See https://github.com/nodejitsu/node-http-proxy/issues/458
+ if (this.target.port !== 443 && this.target.https ||
+ this.target.port !== 80 && !this.target.https) {
+ outgoing.headers.host += ':' + this.target.port;
+ }
+ }
+
+ //
+ // Open new HTTP request to internal resource with will act
+ // as a reverse proxy pass
+ //
+ reverseProxy = this.target.protocol.request(outgoing, function (response) {
+ //
+ // Process the `reverseProxy` `response` when it's received.
+ //
+ if (req.httpVersion === '1.0') {
+ if (req.headers.connection) {
+ response.headers.connection = req.headers.connection
+ } else {
+ response.headers.connection = 'close'
+ }
+ } else if (!response.headers.connection) {
+ if (req.headers.connection) { response.headers.connection = req.headers.connection }
+ else {
+ response.headers.connection = 'keep-alive'
+ }
+ }
+
+ // Remove `Transfer-Encoding` header if client's protocol is HTTP/1.0
+ // or if this is a DELETE request with no content-length header.
+ // See: https://github.com/nodejitsu/node-http-proxy/pull/373
+ if (req.httpVersion === '1.0' || (req.method === 'DELETE'
+ && !req.headers['content-length'])) {
+ delete response.headers['transfer-encoding'];
+ }
+
+ if ((response.statusCode === 301 || response.statusCode === 302)
+ && typeof response.headers.location !== 'undefined') {
+ location = url.parse(response.headers.location);
+ if (location.host === req.headers.host) {
+ if (self.source.https && !self.target.https) {
+ response.headers.location = response.headers.location.replace(/^http\:/, 'https:');
+ }
+ if (self.target.https && !self.source.https) {
+ response.headers.location = response.headers.location.replace(/^https\:/, 'http:');
+ }
+ }
+ }
+
+ //
+ // 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
+ // removed.
+ //
+ var ended = false;
+ response.on('close', function () {
+ if (!ended) { response.emit('end') }
+ });
+
+ //
+ // After reading a chunked response, the underlying socket
+ // will hit EOF and emit a 'end' event, which will abort
+ // the request. If the socket was paused at that time,
+ // pending data gets discarded, truncating the response.
+ // This code makes sure that we flush pending data.
+ //
+ response.connection.on('end', function () {
+ if (response.readable && response.resume) {
+ response.resume();
+ }
+ });
+
+ response.on('end', function () {
+ ended = true;
+ if (!errState) {
+ try { res.end() }
+ catch (ex) { console.error("res.end error: %s", ex.message) }
+
+ // Emit the `end` event now that we have completed proxying
+ self.emit('end', req, res, response);
+ }
+ });
+
+ // Allow observer to modify headers or abort response
+ try { self.emit('proxyResponse', req, res, response) }
+ catch (ex) {
+ errState = true;
+ return;
+ }
+
+ // Set the headers of the client response
+ if (res.sentHeaders !== true) {
+ Object.keys(response.headers).forEach(function (key) {
+ res.setHeader(key, response.headers[key]);
+ });
+ res.writeHead(response.statusCode);
+ }
+
+ function ondata(chunk) {
+ if (res.writable) {
+ // Only pause if the underlying buffers are full,
+ // *and* the connection is not in 'closing' state.
+ // Otherwise, the pause will cause pending data to
+ // be discarded and silently lost.
+ if (false === res.write(chunk) && response.pause
+ && response.connection.readable) {
+ response.pause();
+ }
+ }
+ }
+
+ response.on('data', ondata);
+
+ function ondrain() {
+ if (response.readable && response.resume) {
+ response.resume();
+ }
+ }
+
+ res.on('drain', ondrain);
+ });
+
+ // allow unlimited listeners ...
+ reverseProxy.setMaxListeners(this.eventListenerCount);
+
+ //
+ // Handle 'error' events from the `reverseProxy`. Setup timeout override if needed
+ //
+ reverseProxy.once('error', proxyError);
+
+ // Set a timeout on the socket if `this.timeout` is specified.
+ reverseProxy.once('socket', function (socket) {
+ if (self.timeout) {
+ socket.setTimeout(self.timeout);
+ }
+ });
+
+ //
+ // Handle 'error' events from the `req` (e.g. `Parse Error`).
+ //
+ req.on('error', proxyError);
+
+ //
+ // If `req` is aborted, we abort our `reverseProxy` request as well.
+ //
+ req.on('aborted', function () {
+ reverseProxy.abort();
+ });
+
+ //
+ // For each data `chunk` received from the incoming
+ // `req` write it to the `reverseProxy` request.
+ //
+ req.on('data', function (chunk) {
+ if (!errState) {
+ var flushed = reverseProxy.write(chunk);
+ if (!flushed) {
+ req.pause();
+ reverseProxy.once('drain', function () {
+ try { req.resume() }
+ catch (er) { console.error("req.resume error: %s", er.message) }
+ });
+
+ //
+ // Force the `drain` event in 100ms if it hasn't
+ // happened on its own.
+ //
+ setTimeout(function () {
+ reverseProxy.emit('drain');
+ }, 100);
+ }
+ }
+ });
+
+ //
+ // 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();
+ }
+ });
+
+ //Aborts reverseProxy if client aborts the connection.
+ req.on('close', function () {
+ if (!errState) {
+ reverseProxy.abort();
+ }
+ });
+
+ //
+ // If we have been passed buffered data, resume it.
+ //
+ if (buffer) {
+ return !errState
+ ? buffer.resume()
+ : buffer.destroy();
+ }
+};
+
+//
+// ### function proxyWebSocketRequest (req, socket, head, buffer)
+// #### @req {ServerRequest} Websocket request to proxy.
+// #### @socket {net.Socket} Socket for the underlying HTTP request
+// #### @head {string} Headers for the Websocket request.
+// #### @buffer {Object} Result from `httpProxy.buffer(req)`
+// Performs a WebSocket proxy operation to the location specified by
+// `this.target`.
+//
+HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, upgradeHead, buffer) {
+ var self = this,
+ outgoing = new(this.target.base),
+ listeners = {},
+ errState = false,
+ CRLF = '\r\n',
+ //copy upgradeHead to avoid retention of large slab buffers used in node core
+ head = new Buffer(upgradeHead.length);
+ upgradeHead.copy(head);
+
+ //
+ // WebSocket requests must have the `GET` method and
+ // the `upgrade:websocket` header
+ //
+ if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
+ //
+ // This request is not WebSocket request
+ //
+ return socket.destroy();
+ }
+
+ //
+ // Add common proxy headers to the request so that they can
+ // be availible to the proxy target server. If the proxy is
+ // part of proxy chain it will append the address:
+ //
+ // * `x-forwarded-for`: IP Address of the original request
+ // * `x-forwarded-proto`: Protocol of the original request
+ // * `x-forwarded-port`: Port of the original request.
+ //
+ if (this.enable.xforward && req.connection) {
+ if (req.headers['x-forwarded-for']) {
+ var addressToAppend = "," + req.connection.remoteAddress || socket.remoteAddress;
+ req.headers['x-forwarded-for'] += addressToAppend;
+ }
+ else {
+ req.headers['x-forwarded-for'] = req.connection.remoteAddress || socket.remoteAddress;
+ }
+
+ if (req.headers['x-forwarded-port']) {
+ var portToAppend = "," + getPortFromHostHeader(req);
+ req.headers['x-forwarded-port'] += portToAppend;
+ }
+ else {
+ req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
+ }
+
+ if (req.headers['x-forwarded-proto']) {
+ var protoToAppend = "," + (req.connection.pair ? 'wss' : 'ws');
+ req.headers['x-forwarded-proto'] += protoToAppend;
+ }
+ else {
+ req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws';
+ }
+ }
+
+ self.emit('websocket:start', req, socket, head, this.target);
+
+ //
+ // Helper function for setting appropriate socket values:
+ // 1. Turn of all bufferings
+ // 2. For server set KeepAlive
+ //
+ function _socket(socket, keepAlive) {
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+
+ if (keepAlive) {
+ if (socket.setKeepAlive) {
+ socket.setKeepAlive(true, 0);
+ }
+ else if (socket.pair.cleartext.socket.setKeepAlive) {
+ socket.pair.cleartext.socket.setKeepAlive(true, 0);
+ }
+ }
+ }
+
+ //
+ // Setup the incoming client socket.
+ //
+ _socket(socket, true);
+
+ //
+ // On `upgrade` from the Agent socket, listen to
+ // the appropriate events.
+ //
+ function onUpgrade (reverseProxy, proxySocket) {
+ if (!reverseProxy) {
+ proxySocket.end();
+ socket.end();
+ return;
+ }
+
+ //
+ // Any incoming data on this WebSocket to the proxy target
+ // will be written to the `reverseProxy` socket.
+ //
+ proxySocket.on('data', listeners.onIncoming = function (data) {
+ if (reverseProxy.incoming.socket.writable) {
+ try {
+ self.emit('websocket:outgoing', req, socket, head, data);
+ var flushed = reverseProxy.incoming.socket.write(data);
+ if (!flushed) {
+ proxySocket.pause();
+ reverseProxy.incoming.socket.once('drain', function () {
+ try { proxySocket.resume() }
+ catch (er) { console.error("proxySocket.resume error: %s", er.message) }
+ });
+
+ //
+ // Force the `drain` event in 100ms if it hasn't
+ // happened on its own.
+ //
+ setTimeout(function () {
+ reverseProxy.incoming.socket.emit('drain');
+ }, 100);
+ }
+ }
+ catch (ex) {
+ detach();
+ }
+ }
+ });
+
+ //
+ // Any outgoing data on this Websocket from the proxy target
+ // will be written to the `proxySocket` socket.
+ //
+ reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function (data) {
+ try {
+ self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
+ var flushed = proxySocket.write(data);
+ if (!flushed) {
+ reverseProxy.incoming.socket.pause();
+ proxySocket.once('drain', function () {
+ try { reverseProxy.incoming.socket.resume() }
+ catch (er) { console.error("reverseProxy.incoming.socket.resume error: %s", er.message) }
+ });
+
+ //
+ // Force the `drain` event in 100ms if it hasn't
+ // happened on its own.
+ //
+ setTimeout(function () {
+ proxySocket.emit('drain');
+ }, 100);
+ }
+ }
+ catch (ex) {
+ detach();
+ }
+ });
+
+ //
+ // Helper function to detach all event listeners
+ // from `reverseProxy` and `proxySocket`.
+ //
+ function detach() {
+ proxySocket.destroySoon();
+ proxySocket.removeListener('end', listeners.onIncomingClose);
+ proxySocket.removeListener('data', listeners.onIncoming);
+ reverseProxy.incoming.socket.destroySoon();
+ reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
+ reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
+ }
+
+ //
+ // If the incoming `proxySocket` socket closes, then
+ // detach all event listeners.
+ //
+ listeners.onIncomingClose = function () {
+ reverseProxy.incoming.socket.destroy();
+ detach();
+
+ // Emit the `end` event now that we have completed proxying
+ self.emit('websocket:end', req, socket, head);
+ }
+
+ //
+ // If the `reverseProxy` socket closes, then detach all
+ // event listeners.
+ //
+ listeners.onOutgoingClose = function () {
+ proxySocket.destroy();
+ detach();
+ }
+
+ proxySocket.on('end', listeners.onIncomingClose);
+ proxySocket.on('close', listeners.onIncomingClose);
+ reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose);
+ reverseProxy.incoming.socket.on('close', listeners.onOutgoingClose);
+ }
+
+ function getPort (port) {
+ port = port || 80;
+ return port - 80 === 0 ? '' : ':' + port;
+ }
+
+ //
+ // Get the protocol, and host for this request and create an instance
+ // of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
+ //
+ var agent = this.target.agent,
+ protocolName = this.target.https ? 'https' : 'http',
+ portUri = getPort(this.source.port),
+ remoteHost = this.target.host + portUri;
+
+ //
+ // Change headers (if requested).
+ //
+ if (this.changeOrigin) {
+ req.headers.host = remoteHost;
+ req.headers.origin = protocolName + '://' + remoteHost;
+ }
+
+ //
+ // Make the outgoing WebSocket request
+ //
+ outgoing.host = this.target.host;
+ outgoing.port = this.target.port;
+ outgoing.agent = agent;
+ outgoing.method = 'GET';
+ outgoing.path = req.url;
+ outgoing.headers = req.headers;
+ outgoing.agent = agent;
+
+ var reverseProxy = this.target.protocol.request(outgoing);
+
+ //
+ // On any errors from the `reverseProxy` emit the
+ // `webSocketProxyError` and close the appropriate
+ // connections.
+ //
+ function proxyError (err) {
+ reverseProxy.destroy();
+
+ process.nextTick(function () {
+ //
+ // Destroy the incoming socket in the next tick, in case the error handler
+ // wants to write to it.
+ //
+ socket.destroy();
+ });
+
+ self.emit('webSocketProxyError', err, req, socket, head);
+ }
+
+ //
+ // 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.incoming = {
+ request: req,
+ socket: socket,
+ head: head
+ };
+
+ //
+ // Here we set the handshake `headers` and `statusCode` data to the outgoing
+ // request so that we can reuse this data later.
+ //
+ reverseProxy.handshake = {
+ headers: {},
+ statusCode: null,
+ }
+
+ //
+ // 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 socket to the request bound to it.
+ //
+ reverseProxy.on('upgrade', function (res, remoteSocket, head) {
+ //
+ // Prepare handshake response 'headers' and 'statusCode'.
+ //
+ reverseProxy.handshake = {
+ headers: res.headers,
+ statusCode: res.statusCode,
+ }
+
+ //
+ // 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,
+ // then execute the WebSocket handshake.
+ //
+ reverseProxy.once('socket', function (revSocket) {
+ revSocket.on('data', function handshake (data) {
+ // Set empty headers
+ var headers = '';
+
+ //
+ // If the handshake statusCode 101, concat headers.
+ //
+ if (reverseProxy.handshake.statusCode && reverseProxy.handshake.statusCode == 101) {
+ headers = [
+ 'HTTP/1.1 101 Switching Protocols',
+ 'Upgrade: websocket',
+ 'Connection: Upgrade',
+ 'Sec-WebSocket-Accept: ' + reverseProxy.handshake.headers['sec-websocket-accept']
+ ];
+
+ headers = headers.concat('', '').join('\r\n');
+ }
+
+ //
+ // 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);
+
+ if (self.source.https && !self.target.https) {
+ //
+ // If the proxy server is running HTTPS but the client is running
+ // HTTP then replace `ws` with `wss` in the data sent back to the client.
+ //
+ sdata = sdata.replace('ws:', 'wss:');
+ }
+
+ try {
+ //
+ // Write the printable and non-printable data to the socket
+ // from the original incoming request.
+ //
+ self.emit('websocket:handshake', req, socket, head, sdata, data);
+ // add headers to the socket
+ socket.write(headers + sdata);
+ var flushed = socket.write(data);
+ if (!flushed) {
+ revSocket.pause();
+ socket.once('drain', function () {
+ try { revSocket.resume() }
+ catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) }
+ });
+
+ //
+ // Force the `drain` event in 100ms if it hasn't
+ // happened on its own.
+ //
+ setTimeout(function () {
+ socket.emit('drain');
+ }, 100);
+ }
+ }
+ catch (ex) {
+ //
+ // Remove data listener on socket error because the
+ // 'handshake' has failed.
+ //
+ revSocket.removeListener('data', handshake);
+ return proxyError(ex);
+ }
+
+ //
+ // Remove data listener now that the 'handshake' is complete
+ //
+ revSocket.removeListener('data', handshake);
+ });
+ });
+
+ //
+ // Handle 'error' events from the `reverseProxy`.
+ //
+ reverseProxy.on('error', proxyError);
+
+ //
+ // Handle 'error' events from the `req` (e.g. `Parse Error`).
+ //
+ req.on('error', proxyError);
+
+ try {
+ //
+ // Attempt to write the upgrade-head to the reverseProxy
+ // request. This is small, and there's only ever one of
+ // it; no need for pause/resume.
+ //
+ // XXX This is very wrong and should be fixed in node's core
+ //
+ reverseProxy.write(head);
+ if (head && head.length === 0) {
+ reverseProxy._send('');
+ }
+ }
+ catch (ex) {
+ return proxyError(ex);
+ }
+
+ //
+ // If we have been passed buffered data, resume it.
+ //
+ if (buffer) {
+ return !errState
+ ? buffer.resume()
+ : buffer.destroy();
+ }
+};
+
+//
+// ### function close()
+// Closes all sockets associated with the Agents
+// belonging to this instance.
+//
+HttpProxy.prototype.close = function () {
+ [this.forward, this.target].forEach(function (proxy) {
+ if (proxy && proxy.agent) {
+ for (var host in proxy.agent.sockets) {
+ proxy.agent.sockets[host].forEach(function (socket) {
+ socket.end();
+ });
+ }
+ }
+ });
+};
+
+//
+// ### @private function _forwardRequest (req)
+// #### @req {ServerRequest} Incoming HTTP Request to proxy.
+// Forwards the specified `req` to the location specified
+// by `this.forward` ignoring errors and the subsequent response.
+//
+HttpProxy.prototype._forwardRequest = function (req) {
+ var self = this,
+ outgoing = new(this.forward.base),
+ forwardProxy;
+
+ //
+ // Setup outgoing proxy with relevant properties.
+ //
+ outgoing.host = this.forward.host;
+ outgoing.port = this.forward.port,
+ outgoing.agent = this.forward.agent;
+ outgoing.method = req.method;
+ outgoing.path = req.url;
+ outgoing.headers = req.headers;
+
+ //
+ // Open new HTTP request to internal resource with will
+ // act as a reverse proxy pass.
+ //
+ forwardProxy = this.forward.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.
+ //
+ // 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) {
+ var flushed = forwardProxy.write(chunk);
+ if (!flushed) {
+ req.pause();
+ forwardProxy.once('drain', function () {
+ try { req.resume() }
+ catch (er) { console.error("req.resume error: %s", er.message) }
+ });
+
+ //
+ // Force the `drain` event in 100ms if it hasn't
+ // happened on its own.
+ //
+ setTimeout(function () {
+ forwardProxy.emit('drain');
+ }, 100);
+ }
+ });
+
+ //
+ // At the end of the client request, we are going to
+ // stop the proxied request
+ //
+ req.on('end', function () {
+ forwardProxy.end();
+ });
+};
+
+function getPortFromHostHeader(req) {
+ var match;
+ if ((match = extractPort.exec(req.headers.host))) {
+ return parseInt(match[1]);
+ }
+
+ return getProto(req) === 'https' ? 443 : 80;
+}
+
+function getProto(req) {
+ return req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http');
+}