Skip to content

Commit

Permalink
support compression
Browse files Browse the repository at this point in the history
  • Loading branch information
nkzawa committed Dec 22, 2014
1 parent 1e36bfe commit a16629c
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 59 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ to a single process.
to (`['polling', 'websocket']`)
- `allowUpgrades` (`Boolean`): whether to allow transport upgrades
(`true`)
- `perMessageDeflate` (`Object|Boolean`): paramters of the WebSocket permessage-deflate extension
(see [ws module](https://github.com/einaros/ws) api docs). Set to `false` to disable. (`true`)
- `httpCompression` (`Object|Boolean`): paramters of the http compression for the polling transports
(see [zlib](http://nodejs.org/api/zlib.html#zlib_options) api docs). Set to `false` to disable. (`true`)
- `cookie` (`String|Boolean`): name of the HTTP cookie that
contains the client sid to send as part of handshake response
headers. Set to `false` to not send one. (`io`)
Expand Down Expand Up @@ -297,7 +301,10 @@ A representation of a client. _Inherits from EventEmitter_.
sending binary data, which is sent as is.
- **Parameters**
- `String``Buffer` | `ArrayBuffer` | `ArrayBufferView`: a string or any object implementing `toString()`, with outgoing data, or a Buffer or ArrayBuffer with binary data. Also any ArrayBufferView can be sent as is.
- `Object`: optional, options object
- `Function`: optional, a callback executed when the message gets flushed out by the transport
- **Options**
- `compress` (`Boolean`): whether to compress sending data. This option might be ignored and forced to be `true` when using polling. (`true`)
- **Returns** `Socket` for chaining
- `close`
- Disconnects the client
Expand Down
9 changes: 8 additions & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,16 @@ function Server(opts){
this.allowUpgrades = false !== opts.allowUpgrades;
this.allowRequest = opts.allowRequest;
this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false;
this.perMessageDeflate = opts.perMessageDeflate;
this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || true) : false;

// initialize websocket server
if (~this.transports.indexOf('websocket')) {
this.ws = new WebSocketServer({ noServer: true, clientTracking: false });
this.ws = new WebSocketServer({
noServer: true,
clientTracking: false,
perMessageDeflate: this.perMessageDeflate
});
}
}

Expand Down Expand Up @@ -228,6 +234,7 @@ Server.prototype.handshake = function(transport, req){
var transport = new transports[transport](req);
if ('polling' == transportName) {
transport.maxHttpBufferSize = this.maxHttpBufferSize;
transport.httpCompression = true !== this.httpCompression ? this.httpCompression : {};
}

if (req._query && req._query.b64) {
Expand Down
14 changes: 11 additions & 3 deletions lib/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,14 +280,15 @@ Socket.prototype.setupSendCallback = function () {
* Sends a message packet.
*
* @param {String} message
* @param {Object} options
* @param {Function} callback
* @return {Socket} for chaining
* @api public
*/

Socket.prototype.send =
Socket.prototype.write = function(data, callback){
this.sendPacket('message', data, callback);
Socket.prototype.write = function(data, options, callback){
this.sendPacket('message', data, options, callback);
return this;
};

Expand All @@ -296,15 +297,22 @@ Socket.prototype.write = function(data, callback){
*
* @param {String} packet type
* @param {String} optional, data
* @param {Object} options
* @api private
*/

Socket.prototype.sendPacket = function (type, data, callback) {
Socket.prototype.sendPacket = function (type, data, options, callback) {
if ('function' == typeof options) {
callback = options;
options = null;
}

if ('closing' != this.readyState) {
debug('sending packet "%s" (%s)', type, data);

var packet = { type: type };
if (data) packet.data = data;
if (options) packet.options = options;

// exports packetCreate event
this.emit('packetCreate', packet);
Expand Down
18 changes: 2 additions & 16 deletions lib/transports/polling-jsonp.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ JSONP.prototype.onData = function (data) {
* @api private
*/

JSONP.prototype.doWrite = function (data) {
JSONP.prototype.doWrite = function (data, options, callback) {
// we must output valid javascript, not valid json
// see: http://timelessrepo.com/json-isnt-a-javascript-subset
var js = JSON.stringify(data)
Expand All @@ -70,21 +70,7 @@ JSONP.prototype.doWrite = function (data) {
// prepare response
data = this.head + js + this.foot;

// explicit UTF-8 is required for pages not served under utf
var headers = {
'Content-Type': 'text/javascript; charset=UTF-8',
'Content-Length': Buffer.byteLength(data)
};

// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = this.req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}

this.res.writeHead(200, this.headers(this.req, headers));
this.res.end(data);
Polling.prototype.doWrite.call(this, data, options, callback);
};

/**
Expand Down
30 changes: 0 additions & 30 deletions lib/transports/polling-xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,6 @@ XHR.prototype.onRequest = function (req) {
}
};

/**
* Frames data prior to write.
*
* @api private
*/

XHR.prototype.doWrite = function(data){
// explicit UTF-8 is required for pages not served under utf
var isString = typeof data == 'string';
var contentType = isString
? 'text/plain; charset=UTF-8'
: 'application/octet-stream';
var contentLength = '' + (isString ? Buffer.byteLength(data) : data.length);

var headers = {
'Content-Type': contentType,
'Content-Length': contentLength
};

// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = this.req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}

this.res.writeHead(200, this.headers(this.req, headers));
this.res.end(data);
};

/**
* Returns headers for a response.
*
Expand Down
106 changes: 101 additions & 5 deletions lib/transports/polling.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@

var Transport = require('../transport')
, parser = require('engine.io-parser')
, zlib = require('zlib')
, accepts = require('accepts')
, debug = require('debug')('engine:polling');

var compressionMethods = {
gzip: zlib.createGzip,
deflate: zlib.createDeflate
};

/**
* Exports the constructor.
*/
Expand Down Expand Up @@ -228,6 +235,8 @@ Polling.prototype.onClose = function () {
*/

Polling.prototype.send = function (packets) {
this.writable = false;

if (this.shouldClose) {
debug('appending close packet to payload');
packets.push({ type: 'close' });
Expand All @@ -237,22 +246,109 @@ Polling.prototype.send = function (packets) {

var self = this;
parser.encodePayload(packets, this.supportsBinary, function(data) {
self.write(data);
var compress = packets.some(function(packet) {
var options = packet.options || {};
return options.compress !== false;
});
self.write(data, { compress: compress });
});
};

/**
* Writes data as response to poll request.
*
* @param {String} data
* @param {Object} options
* @api private
*/

Polling.prototype.write = function (data) {
Polling.prototype.write = function (data, options) {
debug('writing "%s"', data);
this.doWrite(data);
this.req.cleanup();
this.writable = false;
var self = this;
this.doWrite(data, options, function() {
self.req.cleanup();
});
};

/**
* Performs the write.
*
* @api private
*/

Polling.prototype.doWrite = function (data, options, callback) {
var self = this;

// explicit UTF-8 is required for pages not served under utf
var isString = typeof data == 'string';
var contentType = isString
? 'text/plain; charset=UTF-8'
: 'application/octet-stream';

var headers = {
'Content-Type': contentType
};

// prevent XSS warnings on IE
// https://github.com/LearnBoost/socket.io/pull/1333
var ua = this.req.headers['user-agent'];
if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
headers['X-XSS-Protection'] = '0';
}

if (!this.httpCompression || !options.compress) {
respond(data);
return;
}

var encoding = accepts(this.req).encodings(['gzip', 'deflate']);
if (!encoding) {
respond(data);
return;
}

this.compress(data, encoding, function(err, data) {
if (err) {
self.res.writeHead(500);
self.res.end();
callback(err);
return;
}

headers['Content-Encoding'] = encoding;
respond(data);
});

function respond(data) {
headers['Content-Length'] = 'string' == typeof data ? Buffer.byteLength(data) : data.length;
self.res.writeHead(200, self.headers(self.req, headers));
self.res.end(data);
callback();
}
};

/**
* Comparesses data.
*
* @api private
*/

Polling.prototype.compress = function (data, encoding, callback) {
debug('compressing');

var buffers = [];
var nread = 0;

compressionMethods[encoding](this.httpCompression)
.on('error', callback)
.on('data', function(chunk) {
buffers.push(chunk);
nread += chunk.length;
})
.on('end', function() {
callback(null, Buffer.concat(buffers, nread));
})
.end(data);
};

/**
Expand Down
5 changes: 3 additions & 2 deletions lib/transports/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ WebSocket.prototype.onData = function (data) {
WebSocket.prototype.send = function (packets) {
var self = this;
for (var i = 0, l = packets.length; i < l; i++) {
parser.encodePacket(packets[i], this.supportsBinary, function(data) {
var packet = packets[i];
parser.encodePacket(packet, this.supportsBinary, function(data) {
debug('writing "%s"', data);
self.writable = false;
self.socket.send(data, function (err){
self.socket.send(data, packet.options, function (err){
if (err) return self.onError('write error', err.stack);
self.writable = true;
self.emit('drain');
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
],
"dependencies": {
"debug": "1.0.3",
"ws": "0.5.0",
"ws": "0.6.3",
"engine.io-parser": "1.1.0",
"base64id": "0.1.0"
"base64id": "0.1.0",
"accepts": "1.1.4"
},
"devDependencies": {
"mocha": "1.12.0",
Expand Down
Loading

0 comments on commit a16629c

Please sign in to comment.