Implement the client-side framework for extensions.
This commit is contained in:
@@ -59,10 +59,18 @@ var instance = {
|
||||
if (!this._http.isComplete()) return;
|
||||
|
||||
this._validateHandshake();
|
||||
if (this.readyState === 3) return;
|
||||
|
||||
this._extensions.activate(this.headers['sec-websocket-extensions']);
|
||||
this._open();
|
||||
this.parse(this._http.body);
|
||||
},
|
||||
|
||||
_handshakeRequest: function() {
|
||||
var extensions = this._extensions.generateOffer();
|
||||
if (extensions)
|
||||
this._headers.set('Sec-WebSocket-Extensions', extensions);
|
||||
|
||||
var start = 'GET ' + this._pathname + ' HTTP/1.1',
|
||||
headers = [start, this._headers.toString(), ''];
|
||||
|
||||
@@ -110,8 +118,6 @@ var instance = {
|
||||
else
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
this._open();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -227,11 +227,11 @@ var instance = {
|
||||
}
|
||||
|
||||
var length = frame.length,
|
||||
masked = frame.masked,
|
||||
header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10),
|
||||
offset = header + (masked ? 4 : 0),
|
||||
offset = header + (frame.masked ? 4 : 0),
|
||||
buffer = new Buffer(offset + length),
|
||||
BYTE = this.BYTE;
|
||||
BYTE = this.BYTE,
|
||||
masked = frame.masked ? this.MASK : 0;
|
||||
|
||||
buffer[0] = (frame.final ? this.FIN : 0) |
|
||||
(frame.rsv1 ? this.RSV1 : 0) |
|
||||
@@ -268,7 +268,7 @@ var instance = {
|
||||
},
|
||||
|
||||
_handshakeResponse: function() {
|
||||
var extensions = this._extensions.activate(this._request.headers['sec-websocket-extensions']);
|
||||
var extensions = this._extensions.generateResponse(this._request.headers['sec-websocket-extensions']);
|
||||
if (extensions) this._headers.set('Sec-WebSocket-Extensions', extensions);
|
||||
|
||||
var start = 'HTTP/1.1 101 Switching Protocols',
|
||||
|
||||
@@ -13,6 +13,8 @@ var Extensions = function(driver) {
|
||||
this._byName = {};
|
||||
this._inOrder = [];
|
||||
this._sessions = [];
|
||||
|
||||
this._rsv1 = this._rsv2 = this._rsv3 = false;
|
||||
};
|
||||
|
||||
var instance = {
|
||||
@@ -30,28 +32,68 @@ var instance = {
|
||||
this._inOrder.push(extension);
|
||||
},
|
||||
|
||||
activate: function(header) {
|
||||
if (header === undefined) return;
|
||||
generateOffer: function() {
|
||||
var sessions = [],
|
||||
offer = [],
|
||||
index = {};
|
||||
|
||||
this._inOrder.forEach(function(ext) {
|
||||
var session = ext.createClientSession(this);
|
||||
if (!session) return;
|
||||
|
||||
sessions.push(session);
|
||||
index[ext.name] = {ext: ext, session: session};
|
||||
|
||||
var offers = session.generateOffers();
|
||||
offers = offers ? [].concat(offers) : [];
|
||||
|
||||
offers.forEach(function(off) {
|
||||
offer.push(this._serializeParams(ext.name, off));
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
this._sessions = sessions;
|
||||
this._index = index;
|
||||
|
||||
return offer.length > 0 ? offer.join(', ') : null;
|
||||
},
|
||||
|
||||
activate: function(header) {
|
||||
var responses = this._parseHeader(header),
|
||||
active = [];
|
||||
|
||||
responses.forEach(function(response) {
|
||||
var record = this._index[response.name],
|
||||
ext = record.ext,
|
||||
session = record.session;
|
||||
|
||||
if (this._reserved(ext)) return;
|
||||
this._reserve(ext);
|
||||
|
||||
session.activate(response.params);
|
||||
active.push(session);
|
||||
}, this);
|
||||
|
||||
this._sessions = this._sessions.filter(function(session) {
|
||||
return active.indexOf(session) >= 0;
|
||||
});
|
||||
},
|
||||
|
||||
generateResponse: function(header) {
|
||||
var offers = this._parseHeader(header),
|
||||
sessions = [],
|
||||
response = [];
|
||||
|
||||
this._rsv1 = this._rsv2 = this._rsv3 = false;
|
||||
|
||||
offers.forEach(function(offer) {
|
||||
var ext = this._byName[offer.name];
|
||||
if (!ext || (this._rsv1 && ext.rsv1) || (this._rsv2 && ext.rsv2) || (this._rsv3 && ext.rsv3)) return;
|
||||
if (!ext || this._reserved(ext)) return;
|
||||
|
||||
var session = ext && ext.createServerSession(this, offer.params);
|
||||
if (!session) return;
|
||||
|
||||
this._rsv1 = this._rsv1 || ext.rsv1;
|
||||
this._rsv2 = this._rsv2 || ext.rsv2;
|
||||
this._rsv3 = this._rsv3 || ext.rsv3;
|
||||
|
||||
this._reserve(ext);
|
||||
sessions.push(session);
|
||||
response.push(this._serializeParams(offer.name, session.getResponseParams()));
|
||||
response.push(this._serializeParams(offer.name, session.generateResponse()));
|
||||
}, this);
|
||||
|
||||
this._sessions = sessions;
|
||||
@@ -62,7 +104,7 @@ var instance = {
|
||||
var sessions = this._sessions.slice();
|
||||
|
||||
var pipe = function(msg) {
|
||||
var session = sessions.shift();
|
||||
var session = sessions.pop();
|
||||
if (!session) return callback.call(context, msg);
|
||||
session.processIncomingMessage(msg, pipe);
|
||||
};
|
||||
@@ -73,7 +115,7 @@ var instance = {
|
||||
var sessions = this._sessions.slice();
|
||||
|
||||
var pipe = function(msg) {
|
||||
var session = sessions.pop();
|
||||
var session = sessions.shift();
|
||||
if (!session) return callback.call(context, msg);
|
||||
session.processOutgoingMessage(msg, pipe);
|
||||
};
|
||||
@@ -88,7 +130,19 @@ var instance = {
|
||||
this._driver._fail(type, message);
|
||||
},
|
||||
|
||||
_reserve: function(ext) {
|
||||
this._rsv1 = this._rsv1 || ext.rsv1;
|
||||
this._rsv2 = this._rsv2 || ext.rsv2;
|
||||
this._rsv3 = this._rsv3 || ext.rsv3;
|
||||
},
|
||||
|
||||
_reserved: function(ext) {
|
||||
return (this._rsv1 && ext.rsv1) || (this._rsv2 && ext.rsv2) || (this._rsv3 && ext.rsv3);
|
||||
},
|
||||
|
||||
_parseHeader: function(header) {
|
||||
if (header === undefined) return [];
|
||||
|
||||
if (!EXT_LIST.test(header))
|
||||
throw new Error('Not a valid Sec-WebSocket-Extensions header: ' + header);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user