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