server.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * Module dependencies.
  3. */
  4. var debug = require('debug');
  5. /**
  6. * Expose `Server`.
  7. */
  8. module.exports = Server;
  9. /**
  10. * Initialize a server with the given `sock`.
  11. *
  12. * @param {Socket} sock
  13. * @api public
  14. */
  15. function Server(sock) {
  16. if (typeof sock.format === 'function') sock.format('json');
  17. this.sock = sock;
  18. this.methods = {};
  19. this.sock.on('message', this.onmessage.bind(this));
  20. }
  21. /**
  22. * Return method descriptions with:
  23. *
  24. * `.name` string
  25. * `.params` array
  26. *
  27. * @return {Object}
  28. * @api private
  29. */
  30. Server.prototype.methodDescriptions = function(){
  31. var obj = {};
  32. var fn;
  33. for (var name in this.methods) {
  34. fn = this.methods[name];
  35. obj[name] = {
  36. name: name,
  37. params: params(fn)
  38. };
  39. }
  40. return obj;
  41. };
  42. /**
  43. * Response with the method descriptions.
  44. *
  45. * @param {Function} fn
  46. * @api private
  47. */
  48. Server.prototype.respondWithMethods = function(reply){
  49. reply({ methods: this.methodDescriptions() });
  50. };
  51. /**
  52. * Handle `msg`.
  53. *
  54. * @param {Object} msg
  55. * @param {Object} fn
  56. * @api private
  57. */
  58. Server.prototype.onmessage = function(msg, reply){
  59. if ('methods' == msg.type) return this.respondWithMethods(reply);
  60. if (!reply) {
  61. console.error('reply false');
  62. return false;
  63. }
  64. // .method
  65. var meth = msg.method;
  66. if (!meth) return reply({ error: '.method required' });
  67. // ensure .method is exposed
  68. var fn = this.methods[meth];
  69. if (!fn) return reply({ error: 'method "' + meth + '" does not exist' });
  70. // .args
  71. var args = msg.args;
  72. if (!args) return reply({ error: '.args required' });
  73. // invoke
  74. args.push(function(err){
  75. if (err) {
  76. if (err instanceof Error)
  77. return reply({ error: err.message, stack: err.stack });
  78. else
  79. return reply({error : err});
  80. }
  81. var args = [].slice.call(arguments, 1);
  82. reply({ args: args });
  83. });
  84. fn.apply(null, args);
  85. };
  86. /**
  87. * Expose many or a single method.
  88. *
  89. * @param {String|Object} name
  90. * @param {String|Object} fn
  91. * @api public
  92. */
  93. Server.prototype.expose = function(name, fn){
  94. if (1 == arguments.length) {
  95. for (var key in name) {
  96. this.expose(key, name[key]);
  97. }
  98. } else {
  99. debug('expose "%s"', name);
  100. this.methods[name] = fn;
  101. }
  102. };
  103. /**
  104. * Parse params.
  105. *
  106. * @param {Function} fn
  107. * @return {Array}
  108. * @api private
  109. */
  110. function params(fn) {
  111. // remove space to make it work on node 10.x.x too
  112. var ret = fn.toString().replace(/\s/g, '').match(/^function *(\w*)\((.*?)\)/)[2];
  113. if (ret) return ret.split(/ *, */);
  114. return [];
  115. }