Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / http-proxy / lib / node-http-proxy / proxy-table.js
1 /*
2   node-http-proxy.js: Lookup table for proxy targets in node.js
3
4   Copyright (c) 2010 Charlie Robbins
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 util = require('util'),
28     events = require('events'),
29     fs = require('fs'),
30     url = require('url');
31
32 //
33 // ### function ProxyTable (router, silent)
34 // #### @router {Object} Object containing the host based routes
35 // #### @silent {Boolean} Value indicating whether we should suppress logs
36 // #### @hostnameOnly {Boolean} Value indicating if we should route based on __hostname string only__
37 // #### @pathnameOnly {Boolean} Value indicating if we should route based on only the pathname.  __This causes hostnames to be ignored.__.  Using this along with hostnameOnly wont work at all.
38 // Constructor function for the ProxyTable responsible for getting
39 // locations of proxy targets based on ServerRequest headers; specifically
40 // the HTTP host header.
41 //
42 var ProxyTable = exports.ProxyTable = function (options) {
43   events.EventEmitter.call(this);
44
45   this.silent       = options.silent || options.silent !== true;
46   this.target       = options.target || {};
47   this.pathnameOnly = options.pathnameOnly === true;
48   this.hostnameOnly = options.hostnameOnly === true;
49
50   if (typeof options.router === 'object') {
51     //
52     // If we are passed an object literal setup
53     // the routes with RegExps from the router
54     //
55     this.setRoutes(options.router);
56   }
57   else if (typeof options.router === 'string') {
58     //
59     // If we are passed a string then assume it is a
60     // file path, parse that file and watch it for changes
61     //
62     var self = this;
63     this.routeFile = options.router;
64     this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router);
65
66     fs.watchFile(this.routeFile, function () {
67       fs.readFile(self.routeFile, function (err, data) {
68         if (err) {
69           self.emit('error', err);
70         }
71
72         self.setRoutes(JSON.parse(data).router);
73         self.emit('routes', self.hostnameOnly === false ? self.routes : self.router);
74       });
75     });
76   }
77   else {
78     throw new Error('Cannot parse router with unknown type: ' + typeof router);
79   }
80 };
81
82 //
83 // Inherit from `events.EventEmitter`
84 //
85 util.inherits(ProxyTable, events.EventEmitter);
86
87 //
88 // ### function addRoute (route, target)
89 // #### @route {String} String containing route coming in
90 // #### @target {String} String containing the target
91 // Adds a host-based route to this instance.
92 //
93 ProxyTable.prototype.addRoute = function (route, target) {
94   if (!this.router) {
95     throw new Error('Cannot update ProxyTable routes without router.');
96   }
97
98   this.router[route] = target;
99   this.setRoutes(this.router);
100 };
101
102 //
103 // ### function removeRoute (route)
104 // #### @route {String} String containing route to remove
105 // Removes a host-based route from this instance.
106 //
107 ProxyTable.prototype.removeRoute = function (route) {
108   if (!this.router) {
109     throw new Error('Cannot update ProxyTable routes without router.');
110   }
111
112   delete this.router[route];
113   this.setRoutes(this.router);
114 };
115
116 //
117 // ### function setRoutes (router)
118 // #### @router {Object} Object containing the host based routes
119 // Sets the host-based routes to be used by this instance.
120 //
121 ProxyTable.prototype.setRoutes = function (router) {
122   if (!router) {
123     throw new Error('Cannot update ProxyTable routes without router.');
124   }
125
126   var self = this;
127   this.router = router;
128
129   if (this.hostnameOnly === false) {
130     this.routes = [];
131
132     Object.keys(router).forEach(function (path) {
133       if (!/http[s]?/.test(router[path])) {
134         router[path] = (self.target.https ? 'https://' : 'http://')
135           + router[path];
136       }
137
138       var target = url.parse(router[path]),
139           defaultPort = self.target.https ? 443 : 80;
140
141       //
142       // Setup a robust lookup table for the route:
143       //
144       //    {
145       //      source: {
146       //        regexp: /^foo.com/i,
147       //        sref: 'foo.com',
148       //        url: {
149       //          protocol: 'http:',
150       //          slashes: true,
151       //          host: 'foo.com',
152       //          hostname: 'foo.com',
153       //          href: 'http://foo.com/',
154       //          pathname: '/',
155       //          path: '/'
156       //        }
157       //    },
158       //    {
159       //      target: {
160       //        sref: '127.0.0.1:8000/',
161       //        url: {
162       //          protocol: 'http:',
163       //          slashes: true,
164       //          host: '127.0.0.1:8000',
165       //          hostname: '127.0.0.1',
166       //          href: 'http://127.0.0.1:8000/',
167       //          pathname: '/',
168       //          path: '/'
169       //        }
170       //    },
171       //
172       self.routes.push({
173         source: {
174           regexp: new RegExp('^' + path, 'i'),
175           sref: path,
176           url: url.parse('http://' + path)
177         },
178         target: {
179           sref: target.hostname + ':' + (target.port || defaultPort) + target.path,
180           url: target
181         }
182       });
183     });
184   }
185 };
186
187 //
188 // ### function getProxyLocation (req)
189 // #### @req {ServerRequest} The incoming server request to get proxy information about.
190 // Returns the proxy location based on the HTTP Headers in the  ServerRequest `req`
191 // available to this instance.
192 //
193 ProxyTable.prototype.getProxyLocation = function (req) {
194   if (!req || !req.headers || !req.headers.host) {
195     return null;
196   }
197
198   var targetHost = req.headers.host.split(':')[0];
199   if (this.hostnameOnly === true) {
200     var target = targetHost;
201     if (this.router.hasOwnProperty(target)) {
202       var location = this.router[target].split(':'),
203           host = location[0],
204           port = location.length === 1 ? 80 : location[1];
205
206       return {
207         port: port,
208         host: host
209       };
210     }
211   }
212   else if (this.pathnameOnly === true) {
213     var target = req.url;
214     for (var i in this.routes) {
215       var route = this.routes[i];
216       //
217       // If we are matching pathname only, we remove the matched pattern.
218       //
219       // IE /wiki/heartbeat
220       // is redirected to
221       // /heartbeat
222       //
223       // for the route "/wiki" : "127.0.0.1:8020"
224       //
225       if (target.match(route.source.regexp)) {
226         req.url = url.format(target.replace(route.source.regexp, ''));
227         return {
228           protocol: route.target.url.protocol.replace(':', ''),
229           host: route.target.url.hostname,
230           port: route.target.url.port
231             || (this.target.https ? 443 : 80)
232         };
233       }
234     }
235
236   }
237   else {
238     var target = targetHost + req.url;
239     for (var i in this.routes) {
240       var route = this.routes[i];
241       if (target.match(route.source.regexp)) {
242         //
243         // Attempt to perform any path replacement for differences
244         // between the source path and the target path. This replaces the
245         // path's part of the URL to the target's part of the URL.
246         //
247         // 1. Parse the request URL
248         // 2. Replace any portions of the source path with the target path
249         // 3. Set the request URL to the formatted URL with replacements.
250         //
251         var parsed = url.parse(req.url);
252
253         parsed.pathname = parsed.pathname.replace(
254           route.source.url.pathname,
255           route.target.url.pathname
256         );
257
258         req.url = url.format(parsed);
259
260         return {
261           protocol: route.target.url.protocol.replace(':', ''),
262           host: route.target.url.hostname,
263           port: route.target.url.port
264             || (this.target.https ? 443 : 80)
265         };
266       }
267     }
268   }
269
270   return null;
271 };
272
273 //
274 // ### close function ()
275 // Cleans up the event listeneners maintained
276 // by this instance.
277 //
278 ProxyTable.prototype.close = function () {
279   if (typeof this.routeFile === 'string') {
280     fs.unwatchFile(this.routeFile);
281   }
282 };