123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- '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;
|