diff options
author | 2017-04-10 15:52:12 +0530 | |
---|---|---|
committer | 2017-04-10 15:52:12 +0530 | |
commit | 84b3a1c939dc2baf22f9f5e30029b263c1512ade (patch) | |
tree | 131f2c17ad61078f95425eab1e349b96d0245d36 /vnfmarket/src/main/webapp/vnfmarket/node_modules/http-proxy/lib/node-http-proxy/http-proxy.js | |
parent | ef9cd2561e325c82d4d1fcb03fef4582a53d7839 (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.js | 983 |
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'); +} |