Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / http-proxy / lib / node-http-proxy / routing-proxy.js
1 /*
2  * routing-proxy.js: A routing proxy consuming a RoutingTable and multiple HttpProxy instances
3  *
4  * (C) 2011 Nodejitsu Inc.
5  * MIT LICENCE
6  *
7  */
8
9 var events = require('events'),
10     utile = require('utile'),
11     HttpProxy = require('./http-proxy').HttpProxy,
12     ProxyTable = require('./proxy-table').ProxyTable;
13
14 //
15 // ### function RoutingProxy (options)
16 // #### @options {Object} Options for this instance
17 // Constructor function for the RoutingProxy object, a higher level
18 // reverse proxy Object which can proxy to multiple hosts and also interface
19 // easily with a RoutingTable instance.
20 //
21 var RoutingProxy = exports.RoutingProxy = function (options) {
22   events.EventEmitter.call(this);
23
24   var self = this;
25   options = options || {};
26
27   if (options.router) {
28     this.proxyTable = new ProxyTable(options);
29     this.proxyTable.on('routes', function (routes) {
30       self.emit('routes', routes);
31     });
32   }
33
34   //
35   // Create a set of `HttpProxy` objects to be used later on calls
36   // to `.proxyRequest()` and `.proxyWebSocketRequest()`.
37   //
38   this.proxies = {};
39
40   //
41   // Setup default target options (such as `https`).
42   //
43   this.target = {};
44   this.target.https = options.target && options.target.https;
45   this.target.maxSockets = options.target && options.target.maxSockets;
46
47   //
48   // Setup other default options to be used for instances of
49   // `HttpProxy` created by this `RoutingProxy` instance.
50   //
51   this.source  = options.source    || { host: 'localhost', port: 8000 };
52   this.https   = this.source.https || options.https;
53   this.enable  = options.enable;
54   this.forward = options.forward;
55   this.changeOrigin = options.changeOrigin || false;
56
57   //
58   // Listen for 'newListener' events so that we can bind 'proxyError'
59   // listeners to each HttpProxy's 'proxyError' event.
60   //
61   this.on('newListener', function (evt) {
62     if (evt === 'proxyError' || evt === 'webSocketProxyError') {
63       Object.keys(self.proxies).forEach(function (key) {
64         self.proxies[key].on(evt, self.emit.bind(self, evt));
65       });
66     }
67   });
68 };
69
70
71 //
72 // Inherit from `events.EventEmitter`.
73 //
74 utile.inherits(RoutingProxy, events.EventEmitter);
75
76 //
77 // ### function add (options)
78 // #### @options {Object} Options for the `HttpProxy` to add.
79 // Adds a new instance of `HttpProxy` to this `RoutingProxy` instance
80 // for the specified `options.host` and `options.port`.
81 //
82 RoutingProxy.prototype.add = function (options) {
83   var self = this,
84       key = this._getKey(options);
85
86   //
87   // TODO: Consume properties in `options` related to the `ProxyTable`.
88   //
89   options.target            = options.target       || {};
90   options.target.host       = options.target.host  || options.host;
91   options.target.port       = options.target.port  || options.port;
92   options.target.socketPath = options.target.socketPath || options.socketPath;
93   options.target.https      = this.target && this.target.https ||
94                               options.target && options.target.https;
95   options.target.maxSockets = this.target && this.target.maxSockets;
96
97   //
98   // Setup options to pass-thru to the new `HttpProxy` instance
99   // for the specified `options.host` and `options.port` pair.
100   //
101   ['https', 'enable', 'forward', 'changeOrigin'].forEach(function (key) {
102     if (options[key] !== false && self[key]) {
103       options[key] = self[key];
104     }
105   });
106
107   this.proxies[key] = new HttpProxy(options);
108
109   if (this.listeners('proxyError').length > 0) {
110     this.proxies[key].on('proxyError', this.emit.bind(this, 'proxyError'));
111   }
112
113   if (this.listeners('webSocketProxyError').length > 0) {
114     this.proxies[key].on('webSocketProxyError', this.emit.bind(this, 'webSocketProxyError'));
115   }
116
117   [
118     'start',
119     'forward',
120     'end',
121     'proxyResponse',
122     'websocket:start',
123     'websocket:end',
124     'websocket:incoming',
125     'websocket:outgoing'
126   ].forEach(function (event) {
127     this.proxies[key].on(event, this.emit.bind(this, event));
128   }, this);
129 };
130
131 //
132 // ### function remove (options)
133 // #### @options {Object} Options mapping to the `HttpProxy` to remove.
134 // Removes an instance of `HttpProxy` from this `RoutingProxy` instance
135 // for the specified `options.host` and `options.port` (if they exist).
136 //
137 RoutingProxy.prototype.remove = function (options) {
138   var key = this._getKey(options),
139       proxy = this.proxies[key];
140
141   delete this.proxies[key];
142   return proxy;
143 };
144
145 //
146 // ### function close()
147 // Cleans up any state left behind (sockets, timeouts, etc)
148 // associated with this instance.
149 //
150 RoutingProxy.prototype.close = function () {
151   var self = this;
152
153   if (this.proxyTable) {
154     //
155     // Close the `RoutingTable` associated with
156     // this instance (if any).
157     //
158     this.proxyTable.close();
159   }
160
161   //
162   // Close all sockets for all `HttpProxy` object(s)
163   // associated with this instance.
164   //
165   Object.keys(this.proxies).forEach(function (key) {
166     self.proxies[key].close();
167   });
168 };
169
170 //
171 // ### function proxyRequest (req, res, [port, host, paused])
172 // #### @req {ServerRequest} Incoming HTTP Request to proxy.
173 // #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
174 // #### @options {Object} Options for the outgoing proxy request.
175 //
176 //     options.port {number} Port to use on the proxy target host.
177 //     options.host {string} Host of the proxy target.
178 //     options.buffer {Object} Result from `httpProxy.buffer(req)`
179 //     options.https {Object|boolean} Settings for https.
180 //
181 RoutingProxy.prototype.proxyRequest = function (req, res, options) {
182   options = options || {};
183
184   var location;
185
186   //
187   // Check the proxy table for this instance to see if we need
188   // to get the proxy location for the request supplied. We will
189   // always ignore the proxyTable if an explicit `port` and `host`
190   // arguments are supplied to `proxyRequest`.
191   //
192   if (this.proxyTable && !options.host) {
193     location = this.proxyTable.getProxyLocation(req);
194
195     //
196     // If no location is returned from the ProxyTable instance
197     // then respond with `404` since we do not have a valid proxy target.
198     //
199     if (!location) {
200       try {
201         if (!this.emit('notFound', req, res)) {
202           res.writeHead(404);
203           res.end();
204         }
205       }
206       catch (er) {
207         console.error("res.writeHead/res.end error: %s", er.message);
208       }
209
210       return;
211     }
212
213     //
214     // When using the ProxyTable in conjunction with an HttpProxy instance
215     // only the following arguments are valid:
216     //
217     // * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
218     // * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
219     // * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
220     //
221     options.port = location.port;
222     options.host = location.host;
223   }
224
225   var key = this._getKey(options),
226       proxy;
227
228   if ((this.target && this.target.https)
229     || (location && location.protocol === 'https')) {
230     options.target = options.target || {};
231     options.target.https = true;
232   }
233
234   if (!this.proxies[key]) {
235     this.add(utile.clone(options));
236   }
237
238   proxy = this.proxies[key];
239   proxy.proxyRequest(req, res, options.buffer);
240 };
241
242 //
243 // ### function proxyWebSocketRequest (req, socket, head, options)
244 // #### @req {ServerRequest} Websocket request to proxy.
245 // #### @socket {net.Socket} Socket for the underlying HTTP request
246 // #### @head {string} Headers for the Websocket request.
247 // #### @options {Object} Options to use when proxying this request.
248 //
249 //     options.port {number} Port to use on the proxy target host.
250 //     options.host {string} Host of the proxy target.
251 //     options.buffer {Object} Result from `httpProxy.buffer(req)`
252 //     options.https {Object|boolean} Settings for https.
253 //
254 RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
255   options = options || {};
256
257   var location,
258       proxy,
259       key;
260
261   if (this.proxyTable && !options.host) {
262     location = this.proxyTable.getProxyLocation(req);
263
264     if (!location) {
265       return socket.destroy();
266     }
267
268     options.port = location.port;
269     options.host = location.host;
270   }
271
272   key = this._getKey(options);
273
274   if (!this.proxies[key]) {
275     this.add(utile.clone(options));
276   }
277
278   proxy = this.proxies[key];
279   proxy.proxyWebSocketRequest(req, socket, head, options.buffer);
280 };
281
282 //
283 // ### function addHost (host, target)
284 // #### @host {String} Host to add to proxyTable
285 // #### @target {String} Target to add to proxyTable
286 // Adds a host to proxyTable
287 //
288 RoutingProxy.prototype.addHost = function (host, target) {
289   if (this.proxyTable) {
290     this.proxyTable.addRoute(host, target);
291   }
292 };
293
294 //
295 // ### function removeHost (host)
296 // #### @host {String} Host to remove from proxyTable
297 // Removes a host to proxyTable
298 //
299 RoutingProxy.prototype.removeHost = function (host) {
300   if (this.proxyTable) {
301     this.proxyTable.removeRoute(host);
302   }
303 };
304
305 //
306 // ### @private function _getKey (options)
307 // #### @options {Object} Options to extract the key from
308 // Ensures that the appropriate options are present in the `options`
309 // provided and responds with a string key representing the `host`, `port`
310 // combination contained within.
311 //
312 RoutingProxy.prototype._getKey = function (options) {
313   if (!options || ((!options.host || !options.port)
314     && (!options.target || !options.target.host || !options.target.port))) {
315     throw new Error('options.host and options.port or options.target are required.');
316   }
317
318   return [
319     options.host || options.target.host,
320     options.port || options.target.port
321   ].join(':');
322 };