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