Compare commits

...

1 Commits

Author SHA1 Message Date
James Coglan e6dea40c8c [WIP] Begin implementing permessage-deflate. 2014-05-11 22:32:15 +01:00
7 changed files with 82 additions and 11 deletions
+3 -1
View File
@@ -1,8 +1,10 @@
var net = require('net'),
websocket = require('../lib/websocket/driver');
websocket = require('../lib/websocket/driver'),
deflate = require('../lib/websocket/extensions/per_message_deflate');
var server = net.createServer(function(connection) {
var driver = websocket.server();
driver.addExtension('permessage-deflate', deflate.create());
driver.on('connect', function() {
if (websocket.isWebSocket(driver)) driver.start();
+15 -7
View File
@@ -6,13 +6,14 @@ var Emitter = require('events').EventEmitter,
var Base = function(request, url, options) {
Emitter.call(this);
this._request = request;
this._options = options || {};
this._maxLength = this._options.maxLength || this.MAX_LENGTH;
this.__headers = new Headers();
this.__queue = [];
this.readyState = 0;
this.url = url;
this._request = request;
this._options = options || {};
this._extensions = {};
this._maxLength = this._options.maxLength || this.MAX_LENGTH;
this.__headers = new Headers();
this.__queue = [];
this.readyState = 0;
this.url = url;
this.io = new streams.IO(this);
this.messages = new streams.Messages(this);
@@ -51,6 +52,10 @@ var instance = {
});
},
addExtension: function(name, extension) {
this._extensions[name] = extension;
},
getState: function() {
return this.STATES[this.readyState] || null;
},
@@ -108,6 +113,9 @@ var instance = {
for (var key in instance)
Base.prototype[key] = instance[key];
Base.parseHeader = function(header) {
return Headers.parseHeader(header);
};
Base.ConnectEvent = function() {};
+16
View File
@@ -3,6 +3,22 @@ var Headers = function() {
this._lines = [];
};
Headers.parseHeader = function(header) {
if (!header) return [];
return header.split(/\s*,\s*/).map(function(value) {
var parts = value.split(/\s*;\s*/),
name = parts.shift(),
params = {};
parts.forEach(function(part) {
var pair = part.split(/\s*=\s*/);
params[pair[0]] = pair[1] || true;
});
return {name: name, params: params};
});
};
Headers.prototype.ALLOWED_DUPLICATES = ['set-cookie', 'set-cookie2', 'warning', 'www-authenticate']
Headers.prototype.set = function(name, value) {
+10 -3
View File
@@ -19,7 +19,7 @@ var HttpParser = function(type) {
};
this._parser.onHeaderValue = function(b, start, length) {
self.headers[current] = b.toString('utf8', start, start + length);
self.set(current, b.toString('utf8', start, start + length));
};
this._parser.onHeadersComplete = this._parser[HTTPParser.kOnHeadersComplete] = function(info) {
@@ -27,11 +27,11 @@ var HttpParser = function(type) {
self.statusCode = info.statusCode;
self.url = info.url;
var headers = info.headers;
var headers = info.headers, name, values;
if (!headers) return;
for (var i = 0, n = headers.length; i < n; i += 2)
self.headers[headers[i].toLowerCase()] = headers[i+1];
self.set(headers[i].toLowerCase(), headers[i+1]);
};
this._parser.onMessageComplete = this._parser[HTTPParser.kOnMessageComplete] = function() {
@@ -63,6 +63,13 @@ HttpParser.METHODS = {
24: 'PATCH'
};
HttpParser.prototype.set = function(name, value) {
var values = [];
if (this.headers.hasOwnProperty(name)) values.push(this.headers[name]);
values.push(value);
this.headers[name] = values.join(', ');
};
HttpParser.prototype.isComplete = function() {
return this._complete;
};
+15
View File
@@ -175,6 +175,7 @@ var instance = {
if (!secKey) return '';
var accept = Hybi.generateAccept(secKey),
offers = Base.parseHeader(this._request.headers['sec-websocket-extensions']),
protos = this._request.headers['sec-websocket-protocol'],
supported = this._protocols,
proto,
@@ -195,6 +196,20 @@ var instance = {
}
}
// TODO: implement a selection procedure for extensions
// see http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-17#section-5
var extensions = offers.map(function(offer) {
var ext = this._extensions[offer.name];
return ext && ext.createSession(offer.params);
}, this).filter(function(k) {
return k;
});
if (extensions.length > 0) {
extensions = extensions.map(function(e) { return e.responseHeader() });
headers.push('Sec-WebSocket-Extensions: ' + extensions.join(', '));
}
return new Buffer(headers.concat(this.__headers.toString(), '').join('\r\n'), 'utf8');
},
+1
View File
@@ -34,6 +34,7 @@ var instance = {
this._delegate = Server.http(this, this._options);
this._delegate.messages = this.messages;
this._delegate.io = this.io;
this._delegate._extensions = this._extensions;
this._delegate.on('open', function() { self._open() });
this.EVENTS.forEach(function(event) {
@@ -0,0 +1,22 @@
var PerMessageDeflate = function() {
this.name = 'permessage-deflate';
};
PerMessageDeflate.create = function() {
return new this();
};
PerMessageDeflate.prototype.createSession = function(params) {
return new Session(this, params);
};
var Session = function(extension, params) {
this._ext = extension;
this._params = params;
};
Session.prototype.responseHeader = function() {
return this._ext.name;
};
module.exports = PerMessageDeflate;