Ein Projekt das es ermöglicht Beerpong über das Internet von zwei unabhängigen positionen aus zu spielen. Entstehung im Rahmen einer Praktikumsaufgabe im Fach Interaktion.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /**
  2. * Module dependencies.
  3. */
  4. var Route = require('./route');
  5. var Layer = require('./layer');
  6. var methods = require('methods');
  7. var mixin = require('utils-merge');
  8. var debug = require('debug')('express:router');
  9. var parseUrl = require('parseurl');
  10. var utils = require('../utils');
  11. /**
  12. * Module variables.
  13. */
  14. var objectRegExp = /^\[object (\S+)\]$/;
  15. var slice = Array.prototype.slice;
  16. var toString = Object.prototype.toString;
  17. /**
  18. * Initialize a new `Router` with the given `options`.
  19. *
  20. * @param {Object} options
  21. * @return {Router} which is an callable function
  22. * @api public
  23. */
  24. var proto = module.exports = function(options) {
  25. options = options || {};
  26. function router(req, res, next) {
  27. router.handle(req, res, next);
  28. }
  29. // mixin Router class functions
  30. router.__proto__ = proto;
  31. router.params = {};
  32. router._params = [];
  33. router.caseSensitive = options.caseSensitive;
  34. router.mergeParams = options.mergeParams;
  35. router.strict = options.strict;
  36. router.stack = [];
  37. return router;
  38. };
  39. /**
  40. * Map the given param placeholder `name`(s) to the given callback.
  41. *
  42. * Parameter mapping is used to provide pre-conditions to routes
  43. * which use normalized placeholders. For example a _:user_id_ parameter
  44. * could automatically load a user's information from the database without
  45. * any additional code,
  46. *
  47. * The callback uses the same signature as middleware, the only difference
  48. * being that the value of the placeholder is passed, in this case the _id_
  49. * of the user. Once the `next()` function is invoked, just like middleware
  50. * it will continue on to execute the route, or subsequent parameter functions.
  51. *
  52. * Just like in middleware, you must either respond to the request or call next
  53. * to avoid stalling the request.
  54. *
  55. * app.param('user_id', function(req, res, next, id){
  56. * User.find(id, function(err, user){
  57. * if (err) {
  58. * return next(err);
  59. * } else if (!user) {
  60. * return next(new Error('failed to load user'));
  61. * }
  62. * req.user = user;
  63. * next();
  64. * });
  65. * });
  66. *
  67. * @param {String} name
  68. * @param {Function} fn
  69. * @return {app} for chaining
  70. * @api public
  71. */
  72. proto.param = function(name, fn){
  73. // param logic
  74. if ('function' == typeof name) {
  75. this._params.push(name);
  76. return;
  77. }
  78. // apply param functions
  79. var params = this._params;
  80. var len = params.length;
  81. var ret;
  82. if (name[0] === ':') {
  83. name = name.substr(1);
  84. }
  85. for (var i = 0; i < len; ++i) {
  86. if (ret = params[i](name, fn)) {
  87. fn = ret;
  88. }
  89. }
  90. // ensure we end up with a
  91. // middleware function
  92. if ('function' != typeof fn) {
  93. throw new Error('invalid param() call for ' + name + ', got ' + fn);
  94. }
  95. (this.params[name] = this.params[name] || []).push(fn);
  96. return this;
  97. };
  98. /**
  99. * Dispatch a req, res into the router.
  100. *
  101. * @api private
  102. */
  103. proto.handle = function(req, res, done) {
  104. var self = this;
  105. debug('dispatching %s %s', req.method, req.url);
  106. var search = 1 + req.url.indexOf('?');
  107. var pathlength = search ? search - 1 : req.url.length;
  108. var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
  109. var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
  110. var idx = 0;
  111. var removed = '';
  112. var slashAdded = false;
  113. var paramcalled = {};
  114. // store options for OPTIONS request
  115. // only used if OPTIONS request
  116. var options = [];
  117. // middleware and routes
  118. var stack = self.stack;
  119. // manage inter-router variables
  120. var parentParams = req.params;
  121. var parentUrl = req.baseUrl || '';
  122. done = restore(done, req, 'baseUrl', 'next', 'params');
  123. // setup next layer
  124. req.next = next;
  125. // for options requests, respond with a default if nothing else responds
  126. if (req.method === 'OPTIONS') {
  127. done = wrap(done, function(old, err) {
  128. if (err || options.length === 0) return old(err);
  129. var body = options.join(',');
  130. return res.set('Allow', body).send(body);
  131. });
  132. }
  133. // setup basic req values
  134. req.baseUrl = parentUrl;
  135. req.originalUrl = req.originalUrl || req.url;
  136. next();
  137. function next(err) {
  138. var layerError = err === 'route'
  139. ? null
  140. : err;
  141. var layer = stack[idx++];
  142. if (slashAdded) {
  143. req.url = req.url.substr(1);
  144. slashAdded = false;
  145. }
  146. if (removed.length !== 0) {
  147. req.baseUrl = parentUrl;
  148. req.url = protohost + removed + req.url.substr(protohost.length);
  149. removed = '';
  150. }
  151. if (!layer) {
  152. return done(layerError);
  153. }
  154. self.match_layer(layer, req, res, function (err, path) {
  155. if (err || path === undefined) {
  156. return next(layerError || err);
  157. }
  158. // route object and not middleware
  159. var route = layer.route;
  160. // if final route, then we support options
  161. if (route) {
  162. // we don't run any routes with error first
  163. if (layerError) {
  164. return next(layerError);
  165. }
  166. var method = req.method;
  167. var has_method = route._handles_method(method);
  168. // build up automatic options response
  169. if (!has_method && method === 'OPTIONS') {
  170. options.push.apply(options, route._options());
  171. }
  172. // don't even bother
  173. if (!has_method && method !== 'HEAD') {
  174. return next();
  175. }
  176. // we can now dispatch to the route
  177. req.route = route;
  178. }
  179. // Capture one-time layer values
  180. req.params = self.mergeParams
  181. ? mergeParams(layer.params, parentParams)
  182. : layer.params;
  183. var layerPath = layer.path;
  184. // this should be done for the layer
  185. self.process_params(layer, paramcalled, req, res, function (err) {
  186. if (err) {
  187. return next(layerError || err);
  188. }
  189. if (route) {
  190. return layer.handle_request(req, res, next);
  191. }
  192. trim_prefix(layer, layerError, layerPath, path);
  193. });
  194. });
  195. }
  196. function trim_prefix(layer, layerError, layerPath, path) {
  197. var c = path[layerPath.length];
  198. if (c && '/' !== c && '.' !== c) return next(layerError);
  199. // Trim off the part of the url that matches the route
  200. // middleware (.use stuff) needs to have the path stripped
  201. if (layerPath.length !== 0) {
  202. debug('trim prefix (%s) from url %s', layerPath, req.url);
  203. removed = layerPath;
  204. req.url = protohost + req.url.substr(protohost.length + removed.length);
  205. // Ensure leading slash
  206. if (!fqdn && req.url[0] !== '/') {
  207. req.url = '/' + req.url;
  208. slashAdded = true;
  209. }
  210. // Setup base URL (no trailing slash)
  211. req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
  212. ? removed.substring(0, removed.length - 1)
  213. : removed);
  214. }
  215. debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
  216. if (layerError) {
  217. layer.handle_error(layerError, req, res, next);
  218. } else {
  219. layer.handle_request(req, res, next);
  220. }
  221. }
  222. };
  223. /**
  224. * Match request to a layer.
  225. *
  226. * @api private
  227. */
  228. proto.match_layer = function match_layer(layer, req, res, done) {
  229. var error = null;
  230. var path;
  231. try {
  232. path = parseUrl(req).pathname;
  233. if (!layer.match(path)) {
  234. path = undefined;
  235. }
  236. } catch (err) {
  237. error = err;
  238. }
  239. done(error, path);
  240. };
  241. /**
  242. * Process any parameters for the layer.
  243. *
  244. * @api private
  245. */
  246. proto.process_params = function(layer, called, req, res, done) {
  247. var params = this.params;
  248. // captured parameters from the layer, keys and values
  249. var keys = layer.keys;
  250. // fast track
  251. if (!keys || keys.length === 0) {
  252. return done();
  253. }
  254. var i = 0;
  255. var name;
  256. var paramIndex = 0;
  257. var key;
  258. var paramVal;
  259. var paramCallbacks;
  260. var paramCalled;
  261. // process params in order
  262. // param callbacks can be async
  263. function param(err) {
  264. if (err) {
  265. return done(err);
  266. }
  267. if (i >= keys.length ) {
  268. return done();
  269. }
  270. paramIndex = 0;
  271. key = keys[i++];
  272. if (!key) {
  273. return done();
  274. }
  275. name = key.name;
  276. paramVal = req.params[name];
  277. paramCallbacks = params[name];
  278. paramCalled = called[name];
  279. if (paramVal === undefined || !paramCallbacks) {
  280. return param();
  281. }
  282. // param previously called with same value or error occurred
  283. if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
  284. // restore value
  285. req.params[name] = paramCalled.value;
  286. // next param
  287. return param(paramCalled.error);
  288. }
  289. called[name] = paramCalled = {
  290. error: null,
  291. match: paramVal,
  292. value: paramVal
  293. };
  294. paramCallback();
  295. }
  296. // single param callbacks
  297. function paramCallback(err) {
  298. var fn = paramCallbacks[paramIndex++];
  299. // store updated value
  300. paramCalled.value = req.params[key.name];
  301. if (err) {
  302. // store error
  303. paramCalled.error = err;
  304. param(err);
  305. return;
  306. }
  307. if (!fn) return param();
  308. try {
  309. fn(req, res, paramCallback, paramVal, key.name);
  310. } catch (e) {
  311. paramCallback(e);
  312. }
  313. }
  314. param();
  315. };
  316. /**
  317. * Use the given middleware function, with optional path, defaulting to "/".
  318. *
  319. * Use (like `.all`) will run for any http METHOD, but it will not add
  320. * handlers for those methods so OPTIONS requests will not consider `.use`
  321. * functions even if they could respond.
  322. *
  323. * The other difference is that _route_ path is stripped and not visible
  324. * to the handler function. The main effect of this feature is that mounted
  325. * handlers can operate without any code changes regardless of the "prefix"
  326. * pathname.
  327. *
  328. * @api public
  329. */
  330. proto.use = function use(fn) {
  331. var offset = 0;
  332. var path = '/';
  333. var self = this;
  334. // default path to '/'
  335. // disambiguate router.use([fn])
  336. if (typeof fn !== 'function') {
  337. var arg = fn;
  338. while (Array.isArray(arg) && arg.length !== 0) {
  339. arg = arg[0];
  340. }
  341. // first arg is the path
  342. if (typeof arg !== 'function') {
  343. offset = 1;
  344. path = fn;
  345. }
  346. }
  347. var callbacks = utils.flatten(slice.call(arguments, offset));
  348. if (callbacks.length === 0) {
  349. throw new TypeError('Router.use() requires middleware functions');
  350. }
  351. callbacks.forEach(function (fn) {
  352. if (typeof fn !== 'function') {
  353. throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
  354. }
  355. // add the middleware
  356. debug('use %s %s', path, fn.name || '<anonymous>');
  357. var layer = new Layer(path, {
  358. sensitive: self.caseSensitive,
  359. strict: false,
  360. end: false
  361. }, fn);
  362. layer.route = undefined;
  363. self.stack.push(layer);
  364. });
  365. return this;
  366. };
  367. /**
  368. * Create a new Route for the given path.
  369. *
  370. * Each route contains a separate middleware stack and VERB handlers.
  371. *
  372. * See the Route api documentation for details on adding handlers
  373. * and middleware to routes.
  374. *
  375. * @param {String} path
  376. * @return {Route}
  377. * @api public
  378. */
  379. proto.route = function(path){
  380. var route = new Route(path);
  381. var layer = new Layer(path, {
  382. sensitive: this.caseSensitive,
  383. strict: this.strict,
  384. end: true
  385. }, route.dispatch.bind(route));
  386. layer.route = route;
  387. this.stack.push(layer);
  388. return route;
  389. };
  390. // create Router#VERB functions
  391. methods.concat('all').forEach(function(method){
  392. proto[method] = function(path){
  393. var route = this.route(path)
  394. route[method].apply(route, slice.call(arguments, 1));
  395. return this;
  396. };
  397. });
  398. // get type for error message
  399. function gettype(obj) {
  400. var type = typeof obj;
  401. if (type !== 'object') {
  402. return type;
  403. }
  404. // inspect [[Class]] for objects
  405. return toString.call(obj)
  406. .replace(objectRegExp, '$1');
  407. }
  408. // merge params with parent params
  409. function mergeParams(params, parent) {
  410. if (typeof parent !== 'object' || !parent) {
  411. return params;
  412. }
  413. // make copy of parent for base
  414. var obj = mixin({}, parent);
  415. // simple non-numeric merging
  416. if (!(0 in params) || !(0 in parent)) {
  417. return mixin(obj, params);
  418. }
  419. var i = 0;
  420. var o = 0;
  421. // determine numeric gaps
  422. while (i === o || o in parent) {
  423. if (i in params) i++;
  424. if (o in parent) o++;
  425. }
  426. // offset numeric indices in params before merge
  427. for (i--; i >= 0; i--) {
  428. params[i + o] = params[i];
  429. // create holes for the merge when necessary
  430. if (i < o) {
  431. delete params[i];
  432. }
  433. }
  434. return mixin(parent, params);
  435. }
  436. // restore obj props after function
  437. function restore(fn, obj) {
  438. var props = new Array(arguments.length - 2);
  439. var vals = new Array(arguments.length - 2);
  440. for (var i = 0; i < props.length; i++) {
  441. props[i] = arguments[i + 2];
  442. vals[i] = obj[props[i]];
  443. }
  444. return function(err){
  445. // restore vals
  446. for (var i = 0; i < props.length; i++) {
  447. obj[props[i]] = vals[i];
  448. }
  449. return fn.apply(this, arguments);
  450. };
  451. }
  452. // wrap a function
  453. function wrap(old, fn) {
  454. return function proxy() {
  455. var args = new Array(arguments.length + 1);
  456. args[0] = old;
  457. for (var i = 0, len = arguments.length; i < len; i++) {
  458. args[i + 1] = arguments[i];
  459. }
  460. fn.apply(this, args);
  461. };
  462. }