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.

PerMessageDeflate.js 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. var zlib = require('zlib');
  2. var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
  3. var DEFAULT_WINDOW_BITS = 15;
  4. var DEFAULT_MEM_LEVEL = 8;
  5. PerMessageDeflate.extensionName = 'permessage-deflate';
  6. /**
  7. * Per-message Compression Extensions implementation
  8. */
  9. function PerMessageDeflate(options, isServer) {
  10. this._options = options || {};
  11. this._isServer = !!isServer;
  12. this._inflate = null;
  13. this._deflate = null;
  14. this.params = null;
  15. }
  16. /**
  17. * Create extension parameters offer
  18. *
  19. * @api public
  20. */
  21. PerMessageDeflate.prototype.offer = function() {
  22. var params = {};
  23. if (this._options.serverNoContextTakeover) {
  24. params.server_no_context_takeover = true;
  25. }
  26. if (this._options.clientNoContextTakeover) {
  27. params.client_no_context_takeover = true;
  28. }
  29. if (this._options.serverMaxWindowBits) {
  30. params.server_max_window_bits = this._options.serverMaxWindowBits;
  31. }
  32. if (this._options.clientMaxWindowBits) {
  33. params.client_max_window_bits = this._options.clientMaxWindowBits;
  34. } else if (this._options.clientMaxWindowBits == null) {
  35. params.client_max_window_bits = true;
  36. }
  37. return params;
  38. };
  39. /**
  40. * Accept extension offer
  41. *
  42. * @api public
  43. */
  44. PerMessageDeflate.prototype.accept = function(paramsList) {
  45. paramsList = this.normalizeParams(paramsList);
  46. var params;
  47. if (this._isServer) {
  48. params = this.acceptAsServer(paramsList);
  49. } else {
  50. params = this.acceptAsClient(paramsList);
  51. }
  52. this.params = params;
  53. return params;
  54. };
  55. /**
  56. * Accept extension offer from client
  57. *
  58. * @api private
  59. */
  60. PerMessageDeflate.prototype.acceptAsServer = function(paramsList) {
  61. var accepted = {};
  62. var result = paramsList.some(function(params) {
  63. accepted = {};
  64. if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) {
  65. return;
  66. }
  67. if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) {
  68. return;
  69. }
  70. if (typeof this._options.serverMaxWindowBits === 'number' &&
  71. typeof params.server_max_window_bits === 'number' &&
  72. this._options.serverMaxWindowBits > params.server_max_window_bits) {
  73. return;
  74. }
  75. if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) {
  76. return;
  77. }
  78. if (this._options.serverNoContextTakeover || params.server_no_context_takeover) {
  79. accepted.server_no_context_takeover = true;
  80. }
  81. if (this._options.clientNoContextTakeover) {
  82. accepted.client_no_context_takeover = true;
  83. }
  84. if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) {
  85. accepted.client_no_context_takeover = true;
  86. }
  87. if (typeof this._options.serverMaxWindowBits === 'number') {
  88. accepted.server_max_window_bits = this._options.serverMaxWindowBits;
  89. } else if (typeof params.server_max_window_bits === 'number') {
  90. accepted.server_max_window_bits = params.server_max_window_bits;
  91. }
  92. if (typeof this._options.clientMaxWindowBits === 'number') {
  93. accepted.client_max_window_bits = this._options.clientMaxWindowBits;
  94. } else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') {
  95. accepted.client_max_window_bits = params.client_max_window_bits;
  96. }
  97. return true;
  98. }, this);
  99. if (!result) {
  100. throw new Error('Doesn\'t support the offered configuration');
  101. }
  102. return accepted;
  103. };
  104. /**
  105. * Accept extension response from server
  106. *
  107. * @api privaye
  108. */
  109. PerMessageDeflate.prototype.acceptAsClient = function(paramsList) {
  110. var params = paramsList[0];
  111. if (this._options.clientNoContextTakeover != null) {
  112. if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
  113. throw new Error('Invalid value for "client_no_context_takeover"');
  114. }
  115. }
  116. if (this._options.clientMaxWindowBits != null) {
  117. if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) {
  118. throw new Error('Invalid value for "client_max_window_bits"');
  119. }
  120. if (typeof this._options.clientMaxWindowBits === 'number' &&
  121. (!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) {
  122. throw new Error('Invalid value for "client_max_window_bits"');
  123. }
  124. }
  125. return params;
  126. };
  127. /**
  128. * Normalize extensions parameters
  129. *
  130. * @api private
  131. */
  132. PerMessageDeflate.prototype.normalizeParams = function(paramsList) {
  133. return paramsList.map(function(params) {
  134. Object.keys(params).forEach(function(key) {
  135. var value = params[key];
  136. if (value.length > 1) {
  137. throw new Error('Multiple extension parameters for ' + key);
  138. }
  139. value = value[0];
  140. switch (key) {
  141. case 'server_no_context_takeover':
  142. case 'client_no_context_takeover':
  143. if (value !== true) {
  144. throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
  145. }
  146. params[key] = true;
  147. break;
  148. case 'server_max_window_bits':
  149. case 'client_max_window_bits':
  150. if (typeof value === 'string') {
  151. value = parseInt(value, 10);
  152. if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
  153. throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
  154. }
  155. }
  156. if (!this._isServer && value === true) {
  157. throw new Error('Missing extension parameter value for ' + key);
  158. }
  159. params[key] = value;
  160. break;
  161. default:
  162. throw new Error('Not defined extension parameter (' + key + ')');
  163. }
  164. }, this);
  165. return params;
  166. }, this);
  167. };
  168. /**
  169. * Decompress message
  170. *
  171. * @api public
  172. */
  173. PerMessageDeflate.prototype.decompress = function (data, fin, callback) {
  174. var endpoint = this._isServer ? 'client' : 'server';
  175. if (!this._inflate) {
  176. var maxWindowBits = this.params[endpoint + '_max_window_bits'];
  177. this._inflate = zlib.createInflateRaw({
  178. windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS
  179. });
  180. }
  181. var self = this;
  182. var buffers = [];
  183. this._inflate.on('error', onError).on('data', onData);
  184. this._inflate.write(data);
  185. if (fin) {
  186. this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
  187. }
  188. this._inflate.flush(function() {
  189. cleanup();
  190. callback(null, Buffer.concat(buffers));
  191. });
  192. function onError(err) {
  193. cleanup();
  194. callback(err);
  195. }
  196. function onData(data) {
  197. buffers.push(data);
  198. }
  199. function cleanup() {
  200. self._inflate.removeListener('error', onError);
  201. self._inflate.removeListener('data', onData);
  202. if (fin && self.params[endpoint + '_no_context_takeover']) {
  203. self._inflate = null;
  204. }
  205. }
  206. };
  207. /**
  208. * Compress message
  209. *
  210. * @api public
  211. */
  212. PerMessageDeflate.prototype.compress = function (data, fin, callback) {
  213. var endpoint = this._isServer ? 'server' : 'client';
  214. if (!this._deflate) {
  215. var maxWindowBits = this.params[endpoint + '_max_window_bits'];
  216. this._deflate = zlib.createDeflateRaw({
  217. flush: zlib.Z_SYNC_FLUSH,
  218. windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS,
  219. memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
  220. });
  221. }
  222. var self = this;
  223. var buffers = [];
  224. this._deflate.on('error', onError).on('data', onData);
  225. this._deflate.write(data);
  226. this._deflate.flush(function() {
  227. cleanup();
  228. var data = Buffer.concat(buffers);
  229. if (fin) {
  230. data = data.slice(0, data.length - 4);
  231. }
  232. callback(null, data);
  233. });
  234. function onError(err) {
  235. cleanup();
  236. callback(err);
  237. }
  238. function onData(data) {
  239. buffers.push(data);
  240. }
  241. function cleanup() {
  242. self._deflate.removeListener('error', onError);
  243. self._deflate.removeListener('data', onData);
  244. if (fin && self.params[endpoint + '_no_context_takeover']) {
  245. self._deflate = null;
  246. }
  247. }
  248. };
  249. module.exports = PerMessageDeflate;