websocket_extensions.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. 'use strict';
  2. var Parser = require('./parser'),
  3. Pipeline = require('./pipeline');
  4. var Extensions = function() {
  5. this._rsv1 = this._rsv2 = this._rsv3 = null;
  6. this._byName = {};
  7. this._inOrder = [];
  8. this._sessions = [];
  9. this._index = {};
  10. };
  11. Extensions.MESSAGE_OPCODES = [1, 2];
  12. var instance = {
  13. add: function(ext) {
  14. if (typeof ext.name !== 'string') throw new TypeError('extension.name must be a string');
  15. if (ext.type !== 'permessage') throw new TypeError('extension.type must be "permessage"');
  16. if (typeof ext.rsv1 !== 'boolean') throw new TypeError('extension.rsv1 must be true or false');
  17. if (typeof ext.rsv2 !== 'boolean') throw new TypeError('extension.rsv2 must be true or false');
  18. if (typeof ext.rsv3 !== 'boolean') throw new TypeError('extension.rsv3 must be true or false');
  19. if (this._byName.hasOwnProperty(ext.name))
  20. throw new TypeError('An extension with name "' + ext.name + '" is already registered');
  21. this._byName[ext.name] = ext;
  22. this._inOrder.push(ext);
  23. },
  24. generateOffer: function() {
  25. var sessions = [],
  26. offer = [],
  27. index = {};
  28. this._inOrder.forEach(function(ext) {
  29. var session = ext.createClientSession();
  30. if (!session) return;
  31. var record = [ext, session];
  32. sessions.push(record);
  33. index[ext.name] = record;
  34. var offers = session.generateOffer();
  35. offers = offers ? [].concat(offers) : [];
  36. offers.forEach(function(off) {
  37. offer.push(Parser.serializeParams(ext.name, off));
  38. }, this);
  39. }, this);
  40. this._sessions = sessions;
  41. this._index = index;
  42. return offer.length > 0 ? offer.join(', ') : null;
  43. },
  44. activate: function(header) {
  45. var responses = Parser.parseHeader(header),
  46. sessions = [];
  47. responses.eachOffer(function(name, params) {
  48. var record = this._index[name];
  49. if (!record)
  50. throw new Error('Server sent an extension response for unknown extension "' + name + '"');
  51. var ext = record[0],
  52. session = record[1],
  53. reserved = this._reserved(ext);
  54. if (reserved)
  55. throw new Error('Server sent two extension responses that use the RSV' +
  56. reserved[0] + ' bit: "' +
  57. reserved[1] + '" and "' + ext.name + '"');
  58. if (session.activate(params) !== true)
  59. throw new Error('Server sent unacceptable extension parameters: ' +
  60. Parser.serializeParams(name, params));
  61. this._reserve(ext);
  62. sessions.push(record);
  63. }, this);
  64. this._sessions = sessions;
  65. this._pipeline = new Pipeline(sessions);
  66. },
  67. generateResponse: function(header) {
  68. var sessions = [],
  69. response = [],
  70. offers = Parser.parseHeader(header);
  71. this._inOrder.forEach(function(ext) {
  72. var offer = offers.byName(ext.name);
  73. if (offer.length === 0 || this._reserved(ext)) return;
  74. var session = ext.createServerSession(offer);
  75. if (!session) return;
  76. this._reserve(ext);
  77. sessions.push([ext, session]);
  78. response.push(Parser.serializeParams(ext.name, session.generateResponse()));
  79. }, this);
  80. this._sessions = sessions;
  81. this._pipeline = new Pipeline(sessions);
  82. return response.length > 0 ? response.join(', ') : null;
  83. },
  84. validFrameRsv: function(frame) {
  85. var allowed = { rsv1: false, rsv2: false, rsv3: false },
  86. ext;
  87. if (Extensions.MESSAGE_OPCODES.indexOf(frame.opcode) >= 0) {
  88. for (var i = 0, n = this._sessions.length; i < n; i++) {
  89. ext = this._sessions[i][0];
  90. allowed.rsv1 = allowed.rsv1 || ext.rsv1;
  91. allowed.rsv2 = allowed.rsv2 || ext.rsv2;
  92. allowed.rsv3 = allowed.rsv3 || ext.rsv3;
  93. }
  94. }
  95. return (allowed.rsv1 || !frame.rsv1) &&
  96. (allowed.rsv2 || !frame.rsv2) &&
  97. (allowed.rsv3 || !frame.rsv3);
  98. },
  99. processIncomingMessage: function(message, callback, context) {
  100. this._pipeline.processIncomingMessage(message, callback, context);
  101. },
  102. processOutgoingMessage: function(message, callback, context) {
  103. this._pipeline.processOutgoingMessage(message, callback, context);
  104. },
  105. close: function(callback, context) {
  106. if (!this._pipeline) return callback.call(context);
  107. this._pipeline.close(callback, context);
  108. },
  109. _reserve: function(ext) {
  110. this._rsv1 = this._rsv1 || (ext.rsv1 && ext.name);
  111. this._rsv2 = this._rsv2 || (ext.rsv2 && ext.name);
  112. this._rsv3 = this._rsv3 || (ext.rsv3 && ext.name);
  113. },
  114. _reserved: function(ext) {
  115. if (this._rsv1 && ext.rsv1) return [1, this._rsv1];
  116. if (this._rsv2 && ext.rsv2) return [2, this._rsv2];
  117. if (this._rsv3 && ext.rsv3) return [3, this._rsv3];
  118. return false;
  119. }
  120. };
  121. for (var key in instance)
  122. Extensions.prototype[key] = instance[key];
  123. module.exports = Extensions;