Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / http-proxy / lib / node-http-proxy / http-proxy.js
1 /*
2   node-http-proxy.js: http proxy for node.js
3
4   Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
5
6   Permission is hereby granted, free of charge, to any person obtaining
7   a copy of this software and associated documentation files (the
8   "Software"), to deal in the Software without restriction, including
9   without limitation the rights to use, copy, modify, merge, publish,
10   distribute, sublicense, and/or sell copies of the Software, and to
11   permit persons to whom the Software is furnished to do so, subject to
12   the following conditions:
13
14   The above copyright notice and this permission notice shall be
15   included in all copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 */
26
27 var events = require('events'),
28     http = require('http'),
29     util = require('util'),
30     url = require('url'),
31     httpProxy = require('../node-http-proxy');
32
33 //
34 // @private {RegExp} extractPort
35 // Reusable regular expression for getting the
36 // port from a host string.
37 //
38 var extractPort = /:(\d+)$/;
39
40 //
41 // ### function HttpProxy (options)
42 // #### @options {Object} Options for this instance.
43 // Constructor function for new instances of HttpProxy responsible
44 // for managing the life-cycle of streaming reverse proxyied HTTP requests.
45 //
46 // Example options:
47 //
48 //      {
49 //        target: {
50 //          host: 'localhost',
51 //          port: 9000
52 //        },
53 //        forward: {
54 //          host: 'localhost',
55 //          port: 9001
56 //        }
57 //      }
58 //
59 var HttpProxy = exports.HttpProxy = function (options) {
60   if (!options || !options.target) {
61     throw new Error('Both `options` and `options.target` are required.');
62   }
63
64   events.EventEmitter.call(this);
65
66   var self  = this;
67
68   //
69   // Setup basic proxying options:
70   //
71   // * forward {Object} Options for a forward-proxy (if-any)
72   // * target {Object} Options for the **sole** proxy target of this instance
73   //
74   this.forward  = options.forward;
75   this.target   = options.target;
76   this.timeout = options.timeout;
77
78   //
79   // Setup the necessary instances instance variables for
80   // the `target` and `forward` `host:port` combinations
81   // used by this instance.
82   //
83   // * agent {http[s].Agent} Agent to be used by this instance.
84   // * protocol {http|https} Core node.js module to make requests with.
85   // * base {Object} Base object to create when proxying containing any https settings.
86   //
87   function setupProxy (key) {
88     self[key].agent    = httpProxy._getAgent(self[key]);
89     self[key].protocol = httpProxy._getProtocol(self[key]);
90     self[key].base     = httpProxy._getBase(self[key]);
91   }
92
93   setupProxy('target');
94   if (this.forward) {
95     setupProxy('forward');
96   }
97
98   //
99   // Setup opt-in features
100   //
101   this.enable          = options.enable || {};
102   this.enable.xforward = typeof this.enable.xforward === 'boolean'
103     ? this.enable.xforward
104     : true;
105
106   // if event listener is set then  use it else unlimited.
107   this.eventListenerCount = typeof options.eventListenerCount === 'number'? options.eventListenerCount : 0 ; 
108
109   //
110   // Setup additional options for WebSocket proxying. When forcing
111   // the WebSocket handshake to change the `sec-websocket-location`
112   // and `sec-websocket-origin` headers `options.source` **MUST**
113   // be provided or the operation will fail with an `origin mismatch`
114   // by definition.
115   //
116   this.source       = options.source       || { host: 'localhost', port: 80 };
117   this.source.https = this.source.https    || options.https;
118   this.changeOrigin = options.changeOrigin || false;
119 };
120
121 // Inherit from events.EventEmitter
122 util.inherits(HttpProxy, events.EventEmitter);
123
124 //
125 // ### function proxyRequest (req, res, buffer)
126 // #### @req {ServerRequest} Incoming HTTP Request to proxy.
127 // #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
128 // #### @buffer {Object} Result from `httpProxy.buffer(req)`
129 //
130 HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
131   var self = this,
132       errState = false,
133       outgoing = new(this.target.base),
134       reverseProxy,
135       location;
136
137   // If this is a DELETE request then set the "content-length"
138   // header (if it is not already set)
139   if (req.method === 'DELETE') {
140     req.headers['content-length'] = req.headers['content-length'] || '0';
141   }
142
143   //
144   // Add common proxy headers to the request so that they can
145   // be availible to the proxy target server. If the proxy is
146   // part of proxy chain it will append the address:
147   //
148   // * `x-forwarded-for`: IP Address of the original request
149   // * `x-forwarded-proto`: Protocol of the original request
150   // * `x-forwarded-port`: Port of the original request.
151   //
152   if (this.enable.xforward && req.connection && req.socket) {
153     if (req.headers['x-forwarded-for']) {
154       var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress;
155       req.headers['x-forwarded-for'] += addressToAppend;
156     }
157     else {
158       req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress;
159     }
160
161     if (req.headers['x-forwarded-port']) {
162       var portToAppend = "," + getPortFromHostHeader(req);
163       req.headers['x-forwarded-port'] += portToAppend;
164     }
165     else {
166       req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
167     }
168
169     if (req.headers['x-forwarded-proto']) {
170       var protoToAppend = "," + getProto(req);
171       req.headers['x-forwarded-proto'] += protoToAppend;
172     }
173     else {
174       req.headers['x-forwarded-proto'] = getProto(req);
175     }
176   }
177
178   if (this.timeout) {
179     req.socket.setTimeout(this.timeout);
180   }
181
182   //
183   // Emit the `start` event indicating that we have begun the proxy operation.
184   //
185   this.emit('start', req, res, this.target);
186
187   //
188   // If forwarding is enabled for this instance, foward proxy the
189   // specified request to the address provided in `this.forward`
190   //
191   if (this.forward) {
192     this.emit('forward', req, res, this.forward);
193     this._forwardRequest(req);
194   }
195
196   //
197   // #### function proxyError (err)
198   // #### @err {Error} Error contacting the proxy target
199   // Short-circuits `res` in the event of any error when
200   // contacting the proxy target at `host` / `port`.
201   //
202   function proxyError(err) {
203     errState = true;
204
205     //
206     // Emit an `error` event, allowing the application to use custom
207     // error handling. The error handler should end the response.
208     //
209     if (self.emit('proxyError', err, req, res)) {
210       return;
211     }
212
213     res.writeHead(500, { 'Content-Type': 'text/plain' });
214
215     if (req.method !== 'HEAD') {
216       //
217       // This NODE_ENV=production behavior is mimics Express and
218       // Connect.
219       //
220       if (process.env.NODE_ENV === 'production') {
221         res.write('Internal Server Error');
222       }
223       else {
224         res.write('An error has occurred: ' + JSON.stringify(err));
225       }
226     }
227
228     try { res.end() }
229     catch (ex) { console.error("res.end error: %s", ex.message) }
230   }
231
232   //
233   // Setup outgoing proxy with relevant properties.
234   //
235   outgoing.host       = this.target.host;
236   outgoing.hostname   = this.target.hostname;
237   outgoing.port       = this.target.port;
238   outgoing.socketPath = this.target.socketPath;
239   outgoing.agent      = this.target.agent;
240   outgoing.method     = req.method;
241   outgoing.path       = url.parse(req.url).path;
242   outgoing.headers    = req.headers;
243
244   //
245   // If the changeOrigin option is specified, change the
246   // origin of the host header to the target URL! Please
247   // don't revert this without documenting it!
248   //
249   if (this.changeOrigin) {
250     outgoing.headers.host = this.target.host;
251     // Only add port information to the header if not default port
252     // for this protocol.
253     // See https://github.com/nodejitsu/node-http-proxy/issues/458
254     if (this.target.port !== 443 && this.target.https ||
255         this.target.port !== 80 && !this.target.https) {
256       outgoing.headers.host += ':' + this.target.port;
257     }
258   }
259
260   //
261   // Open new HTTP request to internal resource with will act
262   // as a reverse proxy pass
263   //
264   reverseProxy = this.target.protocol.request(outgoing, function (response) {
265     //
266     // Process the `reverseProxy` `response` when it's received.
267     //
268     if (req.httpVersion === '1.0') {
269       if (req.headers.connection) {
270         response.headers.connection = req.headers.connection
271       } else {
272         response.headers.connection = 'close'
273       }
274     } else if (!response.headers.connection) {
275       if (req.headers.connection) { response.headers.connection = req.headers.connection }
276       else {
277         response.headers.connection = 'keep-alive'
278       }
279     }
280
281     // Remove `Transfer-Encoding` header if client's protocol is HTTP/1.0
282     // or if this is a DELETE request with no content-length header.
283     // See: https://github.com/nodejitsu/node-http-proxy/pull/373
284     if (req.httpVersion === '1.0' || (req.method === 'DELETE'
285       && !req.headers['content-length'])) {
286       delete response.headers['transfer-encoding'];
287     }
288
289     if ((response.statusCode === 301 || response.statusCode === 302)
290       && typeof response.headers.location !== 'undefined') {
291       location = url.parse(response.headers.location);
292       if (location.host === req.headers.host) {
293         if (self.source.https && !self.target.https) {
294           response.headers.location = response.headers.location.replace(/^http\:/, 'https:');
295         }
296         if (self.target.https && !self.source.https) {
297           response.headers.location = response.headers.location.replace(/^https\:/, 'http:');
298         }
299       }
300     }
301
302     //
303     // When the `reverseProxy` `response` ends, end the
304     // corresponding outgoing `res` unless we have entered
305     // an error state. In which case, assume `res.end()` has
306     // already been called and the 'error' event listener
307     // removed.
308     //
309     var ended = false;
310     response.on('close', function () {
311       if (!ended) { response.emit('end') }
312     });
313
314     //
315     // After reading a chunked response, the underlying socket
316     // will hit EOF and emit a 'end' event, which will abort
317     // the request. If the socket was paused at that time,
318     // pending data gets discarded, truncating the response.
319     // This code makes sure that we flush pending data.
320     //
321     response.connection.on('end', function () {
322       if (response.readable && response.resume) {
323         response.resume();
324       }
325     });
326
327     response.on('end', function () {
328       ended = true;
329       if (!errState) {
330         try { res.end() }
331         catch (ex) { console.error("res.end error: %s", ex.message) }
332
333         // Emit the `end` event now that we have completed proxying
334         self.emit('end', req, res, response);
335       }
336     });
337
338     // Allow observer to modify headers or abort response
339     try { self.emit('proxyResponse', req, res, response) }
340     catch (ex) {
341       errState = true;
342       return;
343     }
344
345     // Set the headers of the client response
346     if (res.sentHeaders !== true) {
347       Object.keys(response.headers).forEach(function (key) {
348         res.setHeader(key, response.headers[key]);
349       });
350       res.writeHead(response.statusCode);
351     }
352
353     function ondata(chunk) {
354       if (res.writable) {
355         // Only pause if the underlying buffers are full,
356         // *and* the connection is not in 'closing' state.
357         // Otherwise, the pause will cause pending data to
358         // be discarded and silently lost.
359         if (false === res.write(chunk) && response.pause
360             && response.connection.readable) {
361           response.pause();
362         }
363       }
364     }
365
366     response.on('data', ondata);
367
368     function ondrain() {
369       if (response.readable && response.resume) {
370         response.resume();
371       }
372     }
373
374     res.on('drain', ondrain);
375   });
376
377   // allow unlimited listeners ... 
378   reverseProxy.setMaxListeners(this.eventListenerCount);
379
380   //
381   // Handle 'error' events from the `reverseProxy`. Setup timeout override if needed
382   //
383   reverseProxy.once('error', proxyError);
384
385   // Set a timeout on the socket if `this.timeout` is specified.
386   reverseProxy.once('socket', function (socket) {
387     if (self.timeout) {
388       socket.setTimeout(self.timeout);
389     }
390   });
391
392   //
393   // Handle 'error' events from the `req` (e.g. `Parse Error`).
394   //
395   req.on('error', proxyError);
396
397   //
398   // If `req` is aborted, we abort our `reverseProxy` request as well.
399   //
400   req.on('aborted', function () {
401     reverseProxy.abort();
402   });
403
404   //
405   // For each data `chunk` received from the incoming
406   // `req` write it to the `reverseProxy` request.
407   //
408   req.on('data', function (chunk) {
409     if (!errState) {
410       var flushed = reverseProxy.write(chunk);
411       if (!flushed) {
412         req.pause();
413         reverseProxy.once('drain', function () {
414           try { req.resume() }
415           catch (er) { console.error("req.resume error: %s", er.message) }
416         });
417
418         //
419         // Force the `drain` event in 100ms if it hasn't
420         // happened on its own.
421         //
422         setTimeout(function () {
423           reverseProxy.emit('drain');
424         }, 100);
425       }
426     }
427   });
428
429   //
430   // When the incoming `req` ends, end the corresponding `reverseProxy`
431   // request unless we have entered an error state.
432   //
433   req.on('end', function () {
434     if (!errState) {
435       reverseProxy.end();
436     }
437   });
438
439   //Aborts reverseProxy if client aborts the connection.
440   req.on('close', function () {
441     if (!errState) {
442       reverseProxy.abort();
443     }
444   });
445
446   //
447   // If we have been passed buffered data, resume it.
448   //
449   if (buffer) {
450     return !errState
451       ? buffer.resume()
452       : buffer.destroy();
453   }
454 };
455
456 //
457 // ### function proxyWebSocketRequest (req, socket, head, buffer)
458 // #### @req {ServerRequest} Websocket request to proxy.
459 // #### @socket {net.Socket} Socket for the underlying HTTP request
460 // #### @head {string} Headers for the Websocket request.
461 // #### @buffer {Object} Result from `httpProxy.buffer(req)`
462 // Performs a WebSocket proxy operation to the location specified by
463 // `this.target`.
464 //
465 HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, upgradeHead, buffer) {
466   var self      = this,
467       outgoing  = new(this.target.base),
468       listeners = {},
469       errState  = false,
470       CRLF      = '\r\n',
471       //copy upgradeHead to avoid retention of large slab buffers used in node core
472       head = new Buffer(upgradeHead.length);
473       upgradeHead.copy(head);
474
475   //
476   // WebSocket requests must have the `GET` method and
477   // the `upgrade:websocket` header
478   //
479   if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
480     //
481     // This request is not WebSocket request
482     //
483     return socket.destroy();
484   }
485
486   //
487   // Add common proxy headers to the request so that they can
488   // be availible to the proxy target server. If the proxy is
489   // part of proxy chain it will append the address:
490   //
491   // * `x-forwarded-for`: IP Address of the original request
492   // * `x-forwarded-proto`: Protocol of the original request
493   // * `x-forwarded-port`: Port of the original request.
494   //
495   if (this.enable.xforward && req.connection) {
496     if (req.headers['x-forwarded-for']) {
497       var addressToAppend = "," + req.connection.remoteAddress || socket.remoteAddress;
498       req.headers['x-forwarded-for'] += addressToAppend;
499     }
500     else {
501       req.headers['x-forwarded-for'] = req.connection.remoteAddress || socket.remoteAddress;
502     }
503
504     if (req.headers['x-forwarded-port']) {
505       var portToAppend = "," + getPortFromHostHeader(req);
506       req.headers['x-forwarded-port'] += portToAppend;
507     }
508     else {
509       req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
510     }
511
512     if (req.headers['x-forwarded-proto']) {
513       var protoToAppend = "," + (req.connection.pair ? 'wss' : 'ws');
514       req.headers['x-forwarded-proto'] += protoToAppend;
515     }
516     else {
517       req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws';
518     }
519   }
520
521   self.emit('websocket:start', req, socket, head, this.target);
522
523   //
524   // Helper function for setting appropriate socket values:
525   // 1. Turn of all bufferings
526   // 2. For server set KeepAlive
527   //
528   function _socket(socket, keepAlive) {
529     socket.setTimeout(0);
530     socket.setNoDelay(true);
531
532     if (keepAlive) {
533       if (socket.setKeepAlive) {
534         socket.setKeepAlive(true, 0);
535       }
536       else if (socket.pair.cleartext.socket.setKeepAlive) {
537         socket.pair.cleartext.socket.setKeepAlive(true, 0);
538       }
539     }
540   }
541
542   //
543   // Setup the incoming client socket.
544   //
545   _socket(socket, true);
546
547   //
548   // On `upgrade` from the Agent socket, listen to
549   // the appropriate events.
550   //
551   function onUpgrade (reverseProxy, proxySocket) {
552     if (!reverseProxy) {
553       proxySocket.end();
554       socket.end();
555       return;
556     }
557
558     //
559     // Any incoming data on this WebSocket to the proxy target
560     // will be written to the `reverseProxy` socket.
561     //
562     proxySocket.on('data', listeners.onIncoming = function (data) {
563       if (reverseProxy.incoming.socket.writable) {
564         try {
565           self.emit('websocket:outgoing', req, socket, head, data);
566           var flushed = reverseProxy.incoming.socket.write(data);
567           if (!flushed) {
568             proxySocket.pause();
569             reverseProxy.incoming.socket.once('drain', function () {
570               try { proxySocket.resume() }
571               catch (er) { console.error("proxySocket.resume error: %s", er.message) }
572             });
573
574             //
575             // Force the `drain` event in 100ms if it hasn't
576             // happened on its own.
577             //
578             setTimeout(function () {
579               reverseProxy.incoming.socket.emit('drain');
580             }, 100);
581           }
582         }
583         catch (ex) {
584           detach();
585         }
586       }
587     });
588
589     //
590     // Any outgoing data on this Websocket from the proxy target
591     // will be written to the `proxySocket` socket.
592     //
593     reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function (data) {
594       try {
595         self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
596         var flushed = proxySocket.write(data);
597         if (!flushed) {
598           reverseProxy.incoming.socket.pause();
599           proxySocket.once('drain', function () {
600             try { reverseProxy.incoming.socket.resume() }
601             catch (er) { console.error("reverseProxy.incoming.socket.resume error: %s", er.message) }
602           });
603
604           //
605           // Force the `drain` event in 100ms if it hasn't
606           // happened on its own.
607           //
608           setTimeout(function () {
609             proxySocket.emit('drain');
610           }, 100);
611         }
612       }
613       catch (ex) {
614         detach();
615       }
616     });
617
618     //
619     // Helper function to detach all event listeners
620     // from `reverseProxy` and `proxySocket`.
621     //
622     function detach() {
623       proxySocket.destroySoon();
624       proxySocket.removeListener('end', listeners.onIncomingClose);
625       proxySocket.removeListener('data', listeners.onIncoming);
626       reverseProxy.incoming.socket.destroySoon();
627       reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
628       reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
629     }
630
631    //
632     // If the incoming `proxySocket` socket closes, then
633     // detach all event listeners.
634     //
635     listeners.onIncomingClose = function () {
636       reverseProxy.incoming.socket.destroy();
637       detach();
638
639       // Emit the `end` event now that we have completed proxying
640       self.emit('websocket:end', req, socket, head);
641     }
642
643     //
644     // If the `reverseProxy` socket closes, then detach all
645     // event listeners.
646     //
647     listeners.onOutgoingClose = function () {
648       proxySocket.destroy();
649       detach();
650     }
651
652     proxySocket.on('end', listeners.onIncomingClose);
653     proxySocket.on('close', listeners.onIncomingClose);
654     reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose);
655     reverseProxy.incoming.socket.on('close', listeners.onOutgoingClose);
656   }
657
658   function getPort (port) {
659     port = port || 80;
660     return port - 80 === 0 ? '' : ':' + port;
661   }
662
663   //
664   // Get the protocol, and host for this request and create an instance
665   // of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
666   //
667   var agent        = this.target.agent,
668       protocolName = this.target.https ? 'https' : 'http',
669       portUri      = getPort(this.source.port),
670       remoteHost   = this.target.host + portUri;
671
672   //
673   // Change headers (if requested).
674   //
675   if (this.changeOrigin) {
676     req.headers.host   = remoteHost;
677     req.headers.origin = protocolName + '://' + remoteHost;
678   }
679
680   //
681   // Make the outgoing WebSocket request
682   //
683   outgoing.host    = this.target.host;
684   outgoing.port    = this.target.port;
685   outgoing.agent   = agent;
686   outgoing.method  = 'GET';
687   outgoing.path    = req.url;
688   outgoing.headers = req.headers;
689   outgoing.agent   = agent;
690
691   var reverseProxy = this.target.protocol.request(outgoing);
692
693   //
694   // On any errors from the `reverseProxy` emit the
695   // `webSocketProxyError` and close the appropriate
696   // connections.
697   //
698   function proxyError (err) {
699     reverseProxy.destroy();
700
701     process.nextTick(function () {
702       //
703       // Destroy the incoming socket in the next tick, in case the error handler
704       // wants to write to it.
705       //
706       socket.destroy();
707     });
708
709     self.emit('webSocketProxyError', err, req, socket, head);
710   }
711
712   //
713   // Here we set the incoming `req`, `socket` and `head` data to the outgoing
714   // request so that we can reuse this data later on in the closure scope
715   // available to the `upgrade` event. This bookkeeping is not tracked anywhere
716   // in nodejs core and is **very** specific to proxying WebSockets.
717   //
718   reverseProxy.incoming = {
719     request: req,
720     socket: socket,
721     head: head
722   };
723
724   //
725   // Here we set the handshake `headers` and `statusCode` data to the outgoing
726   // request so that we can reuse this data later.
727   //
728   reverseProxy.handshake = {
729     headers: {},
730     statusCode: null,
731   }
732
733   //
734   // If the agent for this particular `host` and `port` combination
735   // is not already listening for the `upgrade` event, then do so once.
736   // This will force us not to disconnect.
737   //
738   // In addition, it's important to note the closure scope here. Since
739   // there is no mapping of the socket to the request bound to it.
740   //
741   reverseProxy.on('upgrade', function (res, remoteSocket, head) {
742     //
743     // Prepare handshake response 'headers' and 'statusCode'.
744     //
745     reverseProxy.handshake = {
746       headers: res.headers,
747       statusCode: res.statusCode,
748     }
749
750     //
751     // Prepare the socket for the reverseProxy request and begin to
752     // stream data between the two sockets. Here it is important to
753     // note that `remoteSocket._httpMessage === reverseProxy`.
754     //
755     _socket(remoteSocket, true);
756     onUpgrade(remoteSocket._httpMessage, remoteSocket);
757   });
758
759   //
760   // If the reverseProxy connection has an underlying socket,
761   // then execute the WebSocket handshake.
762   //
763   reverseProxy.once('socket', function (revSocket) {
764     revSocket.on('data', function handshake (data) {
765       // Set empty headers
766       var headers = '';
767
768       //
769       // If the handshake statusCode 101, concat headers.
770       //
771       if (reverseProxy.handshake.statusCode && reverseProxy.handshake.statusCode == 101) {
772         headers = [
773           'HTTP/1.1 101 Switching Protocols',
774           'Upgrade: websocket',
775           'Connection: Upgrade',
776           'Sec-WebSocket-Accept: ' + reverseProxy.handshake.headers['sec-websocket-accept']
777         ];
778
779         headers = headers.concat('', '').join('\r\n');
780       }
781
782       //
783       // Ok, kind of harmfull part of code. Socket.IO sends a hash
784       // at the end of handshake if protocol === 76, but we need
785       // to replace 'host' and 'origin' in response so we split
786       // data to printable data and to non-printable. (Non-printable
787       // will come after double-CRLF).
788       //
789       var sdata = data.toString();
790
791       // Get the Printable data
792       sdata = sdata.substr(0, sdata.search(CRLF + CRLF));
793
794       // Get the Non-Printable data
795       data = data.slice(Buffer.byteLength(sdata), data.length);
796
797       if (self.source.https && !self.target.https) {
798         //
799         // If the proxy server is running HTTPS but the client is running
800         // HTTP then replace `ws` with `wss` in the data sent back to the client.
801         //
802         sdata = sdata.replace('ws:', 'wss:');
803       }
804
805       try {
806         //
807         // Write the printable and non-printable data to the socket
808         // from the original incoming request.
809         //
810         self.emit('websocket:handshake', req, socket, head, sdata, data);
811         // add headers to the socket
812         socket.write(headers + sdata);
813         var flushed = socket.write(data);
814         if (!flushed) {
815           revSocket.pause();
816           socket.once('drain', function () {
817             try { revSocket.resume() }
818             catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) }
819           });
820
821           //
822           // Force the `drain` event in 100ms if it hasn't
823           // happened on its own.
824           //
825           setTimeout(function () {
826             socket.emit('drain');
827           }, 100);
828         }
829       }
830       catch (ex) {
831         //
832         // Remove data listener on socket error because the
833         // 'handshake' has failed.
834         //
835         revSocket.removeListener('data', handshake);
836         return proxyError(ex);
837       }
838
839       //
840       // Remove data listener now that the 'handshake' is complete
841       //
842       revSocket.removeListener('data', handshake);
843     });
844   });
845
846   //
847   // Handle 'error' events from the `reverseProxy`.
848   //
849   reverseProxy.on('error', proxyError);
850
851   //
852   // Handle 'error' events from the `req` (e.g. `Parse Error`).
853   //
854   req.on('error', proxyError);
855
856   try {
857     //
858     // Attempt to write the upgrade-head to the reverseProxy
859     // request. This is small, and there's only ever one of
860     // it; no need for pause/resume.
861     //
862     // XXX This is very wrong and should be fixed in node's core
863     //
864     reverseProxy.write(head);
865     if (head && head.length === 0) {
866       reverseProxy._send('');
867     }
868   }
869   catch (ex) {
870     return proxyError(ex);
871   }
872
873   //
874   // If we have been passed buffered data, resume it.
875   //
876   if (buffer) {
877     return !errState
878       ? buffer.resume()
879       : buffer.destroy();
880   }
881 };
882
883 //
884 // ### function close()
885 // Closes all sockets associated with the Agents
886 // belonging to this instance.
887 //
888 HttpProxy.prototype.close = function () {
889   [this.forward, this.target].forEach(function (proxy) {
890     if (proxy && proxy.agent) {
891       for (var host in proxy.agent.sockets) {
892         proxy.agent.sockets[host].forEach(function (socket) {
893           socket.end();
894         });
895       }
896     }
897   });
898 };
899
900 //
901 // ### @private function _forwardRequest (req)
902 // #### @req {ServerRequest} Incoming HTTP Request to proxy.
903 // Forwards the specified `req` to the location specified
904 // by `this.forward` ignoring errors and the subsequent response.
905 //
906 HttpProxy.prototype._forwardRequest = function (req) {
907   var self = this,
908       outgoing = new(this.forward.base),
909       forwardProxy;
910
911   //
912   // Setup outgoing proxy with relevant properties.
913   //
914   outgoing.host    = this.forward.host;
915   outgoing.port    = this.forward.port,
916   outgoing.agent   = this.forward.agent;
917   outgoing.method  = req.method;
918   outgoing.path    = req.url;
919   outgoing.headers = req.headers;
920
921   //
922   // Open new HTTP request to internal resource with will
923   // act as a reverse proxy pass.
924   //
925   forwardProxy = this.forward.protocol.request(outgoing, function (response) {
926     //
927     // Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
928     // Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
929     //
930   });
931
932   //
933   // Add a listener for the connection timeout event.
934   //
935   // Remark: Ignoring this error in the event
936   //         forward target doesn't exist.
937   //
938   forwardProxy.once('error', function (err) { });
939
940   //
941   // Chunk the client request body as chunks from
942   // the proxied request come in
943   //
944   req.on('data', function (chunk) {
945     var flushed = forwardProxy.write(chunk);
946     if (!flushed) {
947       req.pause();
948       forwardProxy.once('drain', function () {
949         try { req.resume() }
950         catch (er) { console.error("req.resume error: %s", er.message) }
951       });
952
953       //
954       // Force the `drain` event in 100ms if it hasn't
955       // happened on its own.
956       //
957       setTimeout(function () {
958         forwardProxy.emit('drain');
959       }, 100);
960     }
961   });
962
963   //
964   // At the end of the client request, we are going to
965   // stop the proxied request
966   //
967   req.on('end', function () {
968     forwardProxy.end();
969   });
970 };
971
972 function getPortFromHostHeader(req) {
973   var match;
974   if ((match = extractPort.exec(req.headers.host))) {
975     return parseInt(match[1]);
976   }
977
978   return getProto(req) === 'https' ? 443 : 80;
979 }
980
981 function getProto(req) {
982   return req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http');
983 }