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.
		
		
		
		
			
				
					163 lines
				
				4.8 KiB
			
		
		
			
		
	
	
					163 lines
				
				4.8 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Parser   = require('./parser'),
							 | 
						||
| 
								 | 
							
								    Pipeline = require('./pipeline');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Extensions = function() {
							 | 
						||
| 
								 | 
							
								  this._rsv1 = this._rsv2 = this._rsv3 = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._byName   = {};
							 | 
						||
| 
								 | 
							
								  this._inOrder  = [];
							 | 
						||
| 
								 | 
							
								  this._sessions = [];
							 | 
						||
| 
								 | 
							
								  this._index    = {};
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Extensions.MESSAGE_OPCODES = [1, 2];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var instance = {
							 | 
						||
| 
								 | 
							
								  add: function(ext) {
							 | 
						||
| 
								 | 
							
								    if (typeof ext.name !== 'string') throw new TypeError('extension.name must be a string');
							 | 
						||
| 
								 | 
							
								    if (ext.type !== 'permessage') throw new TypeError('extension.type must be "permessage"');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof ext.rsv1 !== 'boolean') throw new TypeError('extension.rsv1 must be true or false');
							 | 
						||
| 
								 | 
							
								    if (typeof ext.rsv2 !== 'boolean') throw new TypeError('extension.rsv2 must be true or false');
							 | 
						||
| 
								 | 
							
								    if (typeof ext.rsv3 !== 'boolean') throw new TypeError('extension.rsv3 must be true or false');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (this._byName.hasOwnProperty(ext.name))
							 | 
						||
| 
								 | 
							
								      throw new TypeError('An extension with name "' + ext.name + '" is already registered');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._byName[ext.name] = ext;
							 | 
						||
| 
								 | 
							
								    this._inOrder.push(ext);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  generateOffer: function() {
							 | 
						||
| 
								 | 
							
								    var sessions = [],
							 | 
						||
| 
								 | 
							
								        offer    = [],
							 | 
						||
| 
								 | 
							
								        index    = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._inOrder.forEach(function(ext) {
							 | 
						||
| 
								 | 
							
								      var session = ext.createClientSession();
							 | 
						||
| 
								 | 
							
								      if (!session) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var record = [ext, session];
							 | 
						||
| 
								 | 
							
								      sessions.push(record);
							 | 
						||
| 
								 | 
							
								      index[ext.name] = record;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var offers = session.generateOffer();
							 | 
						||
| 
								 | 
							
								      offers = offers ? [].concat(offers) : [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      offers.forEach(function(off) {
							 | 
						||
| 
								 | 
							
								        offer.push(Parser.serializeParams(ext.name, off));
							 | 
						||
| 
								 | 
							
								      }, this);
							 | 
						||
| 
								 | 
							
								    }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._sessions = sessions;
							 | 
						||
| 
								 | 
							
								    this._index    = index;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return offer.length > 0 ? offer.join(', ') : null;
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  activate: function(header) {
							 | 
						||
| 
								 | 
							
								    var responses = Parser.parseHeader(header),
							 | 
						||
| 
								 | 
							
								        sessions  = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    responses.eachOffer(function(name, params) {
							 | 
						||
| 
								 | 
							
								      var record = this._index[name];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!record)
							 | 
						||
| 
								 | 
							
								        throw new Error('Server sent an extension response for unknown extension "' + name + '"');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var ext      = record[0],
							 | 
						||
| 
								 | 
							
								          session  = record[1],
							 | 
						||
| 
								 | 
							
								          reserved = this._reserved(ext);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (reserved)
							 | 
						||
| 
								 | 
							
								        throw new Error('Server sent two extension responses that use the RSV' +
							 | 
						||
| 
								 | 
							
								                        reserved[0] + ' bit: "' +
							 | 
						||
| 
								 | 
							
								                        reserved[1] + '" and "' + ext.name + '"');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (session.activate(params) !== true)
							 | 
						||
| 
								 | 
							
								        throw new Error('Server sent unacceptable extension parameters: ' +
							 | 
						||
| 
								 | 
							
								                        Parser.serializeParams(name, params));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      this._reserve(ext);
							 | 
						||
| 
								 | 
							
								      sessions.push(record);
							 | 
						||
| 
								 | 
							
								    }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._sessions = sessions;
							 | 
						||
| 
								 | 
							
								    this._pipeline = new Pipeline(sessions);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  generateResponse: function(header) {
							 | 
						||
| 
								 | 
							
								    var sessions = [],
							 | 
						||
| 
								 | 
							
								        response = [],
							 | 
						||
| 
								 | 
							
								        offers   = Parser.parseHeader(header);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._inOrder.forEach(function(ext) {
							 | 
						||
| 
								 | 
							
								      var offer = offers.byName(ext.name);
							 | 
						||
| 
								 | 
							
								      if (offer.length === 0 || this._reserved(ext)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var session = ext.createServerSession(offer);
							 | 
						||
| 
								 | 
							
								      if (!session) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      this._reserve(ext);
							 | 
						||
| 
								 | 
							
								      sessions.push([ext, session]);
							 | 
						||
| 
								 | 
							
								      response.push(Parser.serializeParams(ext.name, session.generateResponse()));
							 | 
						||
| 
								 | 
							
								    }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._sessions = sessions;
							 | 
						||
| 
								 | 
							
								    this._pipeline = new Pipeline(sessions);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return response.length > 0 ? response.join(', ') : null;
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  validFrameRsv: function(frame) {
							 | 
						||
| 
								 | 
							
								    var allowed = { rsv1: false, rsv2: false, rsv3: false },
							 | 
						||
| 
								 | 
							
								        ext;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Extensions.MESSAGE_OPCODES.indexOf(frame.opcode) >= 0) {
							 | 
						||
| 
								 | 
							
								      for (var i = 0, n = this._sessions.length; i < n; i++) {
							 | 
						||
| 
								 | 
							
								        ext = this._sessions[i][0];
							 | 
						||
| 
								 | 
							
								        allowed.rsv1 = allowed.rsv1 || ext.rsv1;
							 | 
						||
| 
								 | 
							
								        allowed.rsv2 = allowed.rsv2 || ext.rsv2;
							 | 
						||
| 
								 | 
							
								        allowed.rsv3 = allowed.rsv3 || ext.rsv3;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return (allowed.rsv1 || !frame.rsv1) &&
							 | 
						||
| 
								 | 
							
								           (allowed.rsv2 || !frame.rsv2) &&
							 | 
						||
| 
								 | 
							
								           (allowed.rsv3 || !frame.rsv3);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  processIncomingMessage: function(message, callback, context) {
							 | 
						||
| 
								 | 
							
								    this._pipeline.processIncomingMessage(message, callback, context);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  processOutgoingMessage: function(message, callback, context) {
							 | 
						||
| 
								 | 
							
								    this._pipeline.processOutgoingMessage(message, callback, context);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  close: function(callback, context) {
							 | 
						||
| 
								 | 
							
								    if (!this._pipeline) return callback.call(context);
							 | 
						||
| 
								 | 
							
								    this._pipeline.close(callback, context);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _reserve: function(ext) {
							 | 
						||
| 
								 | 
							
								    this._rsv1 = this._rsv1 || (ext.rsv1 && ext.name);
							 | 
						||
| 
								 | 
							
								    this._rsv2 = this._rsv2 || (ext.rsv2 && ext.name);
							 | 
						||
| 
								 | 
							
								    this._rsv3 = this._rsv3 || (ext.rsv3 && ext.name);
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _reserved: function(ext) {
							 | 
						||
| 
								 | 
							
								    if (this._rsv1 && ext.rsv1) return [1, this._rsv1];
							 | 
						||
| 
								 | 
							
								    if (this._rsv2 && ext.rsv2) return [2, this._rsv2];
							 | 
						||
| 
								 | 
							
								    if (this._rsv3 && ext.rsv3) return [3, this._rsv3];
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								for (var key in instance)
							 | 
						||
| 
								 | 
							
								  Extensions.prototype[key] = instance[key];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Extensions;
							 |