Initial commit
[yaffs-website] / node_modules / websocket-extensions / lib / websocket_extensions.js
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 offers   = Parser.parseHeader(header),
93         sessions = [],
94         response = [];
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;