API Docs for: 0.8.0
Show:

File: include/http/router.js

  1. 'use strict';
  2.  
  3. //dependencies
  4. var url = require('url');
  5. var util = require('util');
  6. var domain = require('domain');
  7.  
  8. module.exports = function (pb) {
  9.  
  10. //pb dependencies
  11. var RequestHandler = pb.RequestHandler;
  12.  
  13. /**
  14. * Responsible for routing a request through the registered middleware to serve up a response
  15. * @class Router
  16. * @constructor
  17. * @param {Request} req
  18. * @param {Response} res
  19. */
  20. class Router {
  21. constructor(req, res) {
  22.  
  23. /**
  24. * Represents the current position of the middleware that is currently executing
  25. * @property index
  26. * @type {number}
  27. */
  28. this.index = 0;
  29.  
  30. /**
  31. * @property req
  32. * @type {Request}
  33. */
  34. this.req = req;
  35.  
  36. /**
  37. * @property res
  38. * @type {Response}
  39. */
  40. this.res = res;
  41. }
  42.  
  43. /**
  44. * Starts the execution of the middleware pipeline against the specified request/response pair
  45. * @method handle
  46. */
  47. handle() {
  48.  
  49. //set reference to the handler
  50. this.req.handler = new RequestHandler(null, this.req, this.res);
  51. this.req.router = this;
  52.  
  53. return this._handle(this.req, this.res);
  54. }
  55.  
  56. /**
  57. * Handles the incoming request by executing each of the middleware in the pipeline
  58. * @private
  59. * @method _handle
  60. * @param {Request} req
  61. * @param {Response} res
  62. */
  63. _handle (req, res) {
  64. var resolve, reject;
  65. var promise = new Promise(function(reso, rej) { resolve = reso; reject = rej; });
  66.  
  67. // initialize completion function
  68. var self = this;
  69. var done = function (err) {
  70. if (!util.isError(err)) {
  71. return resolve();
  72. }
  73.  
  74. req.handler.serveError(err, { handler: function(data) {
  75. req.controllerResult = data;
  76. self.continueAfter('render')
  77. .then(resolve, reject);
  78. }});
  79. };
  80.  
  81. //create execution loop
  82. var execute = function () {
  83. if (self.index >= Router.middleware.length) {
  84. return done();
  85. }
  86.  
  87. //execute the next task
  88. var sync = true;
  89. var action = Router.middleware[self.index].action;
  90. action(req, res, function (err) {
  91. if (err) {
  92. return done(err);
  93. }
  94.  
  95. // delay by a tick when reaching here synchronously otherwise just proceed
  96. self.index++;
  97. if (sync) {
  98. process.nextTick(function() {
  99. execute();
  100. });
  101. }
  102. else {
  103. execute();
  104. }
  105. });
  106. sync = false;
  107. };
  108.  
  109. domain.create()
  110. .once('error', function(err) {
  111. pb.log.error('Router: An unhandled error occurred after calling middleware "%s": %s', Router.middleware[self.index].name, err.stack);
  112. reject(err);
  113. })
  114. .run(function() {
  115. process.nextTick(execute);
  116. });
  117. return promise;
  118. }
  119.  
  120. /**
  121. * Instructs the router to continue pipeline execution after the specified middleware.
  122. * @method continueAfter
  123. * @param {string} middlewareName
  124. */
  125. continueAfter (middlewareName) {
  126. var index = Router.indexOfMiddleware(middlewareName);
  127. return this.continueAt(index + 1);
  128. }
  129.  
  130. /**
  131. * Instructs the router to continue processing at the specified position in the set of middleware being executed
  132. * @method continueAt
  133. * @param {number} index
  134. */
  135. continueAt (index) {
  136. this.index = index;
  137. return this._handle(this.req, this.res);
  138. }
  139.  
  140. /**
  141. * Causes a redirect result to be created and set off of the Request object as the controllerResult.
  142. * The pipeline is then instructed to continue after the "render" middleware
  143. * @static
  144. * @method redirect
  145. * @param {string} location The location to redirect to
  146. * @param {number} httpStatusCode The integer that represents the status code to be returned
  147. */
  148. redirect (location, httpStatusCode) {
  149. this.req.controllerResult = {
  150. redirect: location,
  151. code: httpStatusCode
  152. };
  153. return this.continueAfter('render');
  154. }
  155.  
  156. /**
  157. * Removes the specified middleware from the pipeline
  158. * @static
  159. * @method removeMiddleware
  160. * @param {string} name
  161. * @returns {boolean}
  162. */
  163. static removeMiddleware(name) {
  164. var index = Router.indexOfMiddleware(name);
  165. if (index >= 0) {
  166. Router.middleware.splice(index, 1);
  167. return true;
  168. }
  169. return false;
  170. }
  171.  
  172. /**
  173. * Replaces the middleware with the specified name at its current position in the middleware pipeline
  174. * @static
  175. * @method replaceMiddleware
  176. * @param {string} name
  177. * @param {object} middleware
  178. * @param {string} middleware.name
  179. * @param {function} middleware.action
  180. * @returns {boolean}
  181. */
  182. static replaceMiddleware(name, middleware) {
  183. var index = Router.indexOfMiddleware(name);
  184. if (index >= 0) {
  185. Router.middleware.splice(index, 1, middleware);
  186. return true;
  187. }
  188. return false;
  189. }
  190.  
  191. /**
  192. * Adds middleware after the middleware with the specified name
  193. * @static
  194. * @method addMiddlewareAfter
  195. * @param {string} name
  196. * @param {object} middleware
  197. * @param {string} middleware.name
  198. * @param {function} middleware.action
  199. * @returns {boolean}
  200. */
  201. static addMiddlewareAfter(name, middleware) {
  202. var index = Router.indexOfMiddleware(name);
  203. if (index >= 0) {
  204. return Router.addMiddlewareAt(index + 1, middleware);
  205. }
  206. return false;
  207. }
  208.  
  209. /**
  210. * Adds middleware after all other registered middleware
  211. * @static
  212. * @method addMiddlewareAfterAll
  213. * @param {object} middleware
  214. * @param {string} middleware.name
  215. * @param {function} middleware.action
  216. * @returns {boolean}
  217. */
  218. static addMiddlewareAfterAll(middleware) {
  219. return Router.addMiddlewareAt(Router.middleware.length, middleware);
  220. }
  221.  
  222. /**
  223. * Adds middleware before the middleware with the specified name
  224. * @static
  225. * @method addMiddlewareBefore
  226. * @param {string} name
  227. * @param {object} middleware
  228. * @param {string} middleware.name
  229. * @param {function} middleware.action
  230. * @returns {boolean}
  231. */
  232. static addMiddlewareBefore(name, middleware) {
  233. var index = Router.indexOfMiddleware(name);
  234. if (index >= 0) {
  235. return Router.addMiddlewareAt(index, middleware);
  236. }
  237. return false;
  238. }
  239.  
  240. /**
  241. * Adds middleware before all other registered middleware
  242. * @static
  243. * @method addMiddlewareBeforeAll
  244. * @param {object} middleware
  245. * @param {string} middleware.name
  246. * @param {function} middleware.action
  247. * @returns {boolean}
  248. */
  249. static addMiddlewareBeforeAll(middleware) {
  250. return Router.addMiddlewareAt(0, middleware);
  251. }
  252.  
  253. /**
  254. * Adds middleware at the specified index
  255. * @static
  256. * @method addMiddlewareAt
  257. * @param {number} index
  258. * @param {object} middleware
  259. * @param {string} middleware.name
  260. * @param {function} middleware.action
  261. * @returns {boolean} TRUE if added, FALSE if the middleware already exists in the pipeline
  262. */
  263. static addMiddlewareAt(index, middleware) {//TODO add check to ensure you can't add middleware with the same name, valid name, valid action
  264. Router.middleware.splice(index, 0, middleware);
  265. return true;
  266. }
  267.  
  268. /**
  269. * Determines the position in the middleware pipeline where the middleware executes.
  270. * @static
  271. * @method indexOfMiddleware
  272. * @param {string} name
  273. * @returns {number} The position of the middleware or -1 when not found
  274. */
  275. static indexOfMiddleware(name) {
  276. for (var i = 0; i < Router.middleware.length; i++) {
  277. if (Router.middleware[i].name === name) {
  278. return i;
  279. }
  280. }
  281. return -1;
  282. }
  283. }
  284.  
  285. /**
  286. * @static
  287. * @property middleware
  288. * @type {Array}
  289. */
  290. Router.middleware = [];
  291.  
  292. return Router;
  293. };
  294.