Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes to event encoding and acknowledgement #242

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Manager.prototype.handleClient = function (data, req) {
if (count == 1) {
// initialize the socket for all namespaces
for (var i in self.namespaces) {
self.namespaces[i].socket(data.id, true);
self.namespaces[i].handlePacket(data.id, {type: 'connect'});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way this works changed recently. Please see up-to-date version.

}

// handle packets for the client (all namespaces)
Expand Down Expand Up @@ -418,7 +418,7 @@ Manager.prototype.handleHandshake = function (data, req, res) {
function writeErr (status, message) {
if (data.query.jsonp) {
res.writeHead(200);
res.end('io.j[' + data.query.jsonp + '](new Error(' + message + '));');
res.end('io.j[' + data.query.jsonp + '](new Error(' + JSON.stringify(message) + '));');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll rather see "' + message + '" here instead of a JSON.stringify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if message is verbatim 'I like to collaborate to "3rd-Eden"'? ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message.replace(/\"/g, '\\"') ;D? I rather prevent as much JSON.stringify's as possible as they are blocking actions.. There is no streaming JSON parser / stringifier yet ;9..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we stringify a short message once for a faulty connection. It's not used in normal message traffic. But I admit that, if we are sure we won't use dquotes in our error messages, your initial proposal saves a couple of ticks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going with 3rd-Eden's first approach here.

} else {
res.writeHead(status);
res.end(message);
Expand Down Expand Up @@ -454,7 +454,7 @@ Manager.prototype.handleHandshake = function (data, req, res) {
if (data.query.jsonp)
hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');';

res.writeHead(200);
res.writeHead(200, {'content-type': 'application/javascript'});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Content-Type is preferred here
  • You're sending application/json for all handshakes, not just JSONP.

res.end(hs);
});
} else {
Expand Down
25 changes: 19 additions & 6 deletions lib/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ SocketNamespace.prototype.except = function (id) {
return this;
};

/**
* Defines a filtering function which should select session ids to
* relay messages to (flag)
*
* @api public
*/

SocketNamespace.prototype.filter = function (fn) {
this.flags.filter = fn;
return this;
};

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool and needed, but I want tests for this one. Do it for any transport, xhr-polling might be easiest since we have similar tests (look for broadcast flag or similar tests).

* Sets the default flags.
*
Expand All @@ -107,6 +119,7 @@ SocketNamespace.prototype.setFlags = function () {
this.flags = {
endpoint: this.name
, exceptions: []
, filter: null
};
return this;
};
Expand All @@ -125,7 +138,9 @@ SocketNamespace.prototype.packet = function (packet) {
, packet = parser.encodePacket(packet);

store.clients(this.flags.endpoint, function (clients) {
clients.forEach(function (id) {
(this.flags.filter ? clients.filter(this.flags.filter) : clients)
.forEach(function (id) {
// N.B. this should be implemented as filtering function
if (~exceptions.indexOf(id)) {
log.debug('ignoring packet to ', id);
return;
Expand Down Expand Up @@ -185,9 +200,6 @@ SocketNamespace.prototype.emit = function (name) {
SocketNamespace.prototype.socket = function (sid, readable) {
if (!this.sockets[sid]) {
this.sockets[sid] = new Socket(this.manager, sid, this, readable);
if (this.name === '') {
this.emit('connection', this.sockets[sid]);
}
}

return this.sockets[sid];
Expand All @@ -200,7 +212,7 @@ SocketNamespace.prototype.socket = function (sid, readable) {
*/

SocketNamespace.prototype.handlePacket = function (sessid, packet) {
var socket = this.socket(sessid)
var socket = this.socket(sessid, true) // readable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the packet is handled, the socket should have already been initialized in a readable state. What's the need for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the call to SocketNamespace#socket is moved to manager and done with explicit true, no need remains.

, dataAck = packet.ack == 'data'
, self = this;

Expand All @@ -216,13 +228,14 @@ SocketNamespace.prototype.handlePacket = function (sessid, packet) {
switch (packet.type) {
case 'connect':
this.store.join(sessid, this.name, function () {
self.emit('connection', self.sockets[sessid]);
self.emit('connection', socket);
});
break;

case 'ack':
if (socket.acks[packet.ackId]) {
socket.acks[packet.ackId].apply(socket, packet.args);
//delete socket.acks[packet.ackId];
} else {
this.log.info('unknown ack packet');
}
Expand Down
4 changes: 2 additions & 2 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ exports.encodePacket = function (packet) {
case 'event':
var params = packet.args && packet.args.length
? JSON.stringify(packet.args) : '';
data = packet.name + (params !== '' ? ('\ufffd' + params) : '');
data = packet.name + (params !== '' ? ('\ufffc' + params) : '');
break;

case 'json':
Expand Down Expand Up @@ -173,7 +173,7 @@ exports.decodePacket = function (data) {
break;

case 'event':
var pieces = data.match(/([^\ufffd]+)(\ufffd)?(.*)/);
var pieces = data.match(/([^\ufffc]+)(\ufffc)?(.*)/);
packet.name = pieces[1] || '';
packet.args = [];

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec changed around this. See 1db340a
Thanks!

Expand Down
2 changes: 1 addition & 1 deletion lib/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ Socket.prototype.$emit = EventEmitter.prototype.emit;

Socket.prototype.emit = function (ev) {
if (events[ev]) {
return EventEmitter.prototype.emit.apply(this, arguments);
return this.$emit.apply(this, arguments);
}

var args = util.toArray(arguments).slice(1)
Expand Down
3 changes: 1 addition & 2 deletions lib/stores/memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,12 @@ Client.prototype.count = function (fn) {
*/

Client.prototype.consume = function (fn) {
this.consumer = fn;
this.paused = false;

if (this.buffer.length) {
fn(this.buffer, null);
this.buffer = [];
} else {
this.consumer = fn;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was probably creating problems in certain circumstances. However, it never came up with our test suite. Any chance you can create a test to avoid a regression for this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, still can't run tests -- they just timeout, because of ECONNREFUSED, because of client should connect to server only when server is bound for real. Mac has magical kernel, it listen()s synchronously, PC's doesn't ;)

update:
Client#consume is called only once. If it happens that client is paused at that moment, the value of consumer function is lost forever. This occurs (to me) if i sent back a packet during connection event, say.
I can't formalize it so far to constitute a test, but since the logic of this.consumer assignment is conditional, it surely will fail (and fails) under some conditions.


return this;
Expand Down
16 changes: 8 additions & 8 deletions lib/transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,12 @@ Transport.prototype.setHeartbeatTimeout = function () {
var self = this;

this.heartbeatTimeout = setTimeout(function () {
self.log.debug('fired heartbeat timeout for client', self.id);
//DVV self.log.debug('fired heartbeat timeout for client', self.id);
self.heartbeatTimeout = null;
self.end(false, 'heartbeat timeout');
}, this.manager.get('heartbeat timeout') * 1000);

this.log.debug('set heartbeat timeout for client', this.id);
//DVV this.log.debug('set heartbeat timeout for client', this.id);
}
};

Expand All @@ -238,7 +238,7 @@ Transport.prototype.clearHeartbeatTimeout = function () {
if (this.heartbeatTimeout) {
clearTimeout(this.heartbeatTimeout);
this.heartbeatTimeout = null;
this.log.debug('cleared heartbeat timeout for client', this.id);
//DVV this.log.debug('cleared heartbeat timeout for client', this.id);
}
};

Expand All @@ -257,7 +257,7 @@ Transport.prototype.setHeartbeatInterval = function () {
self.heartbeat();
}, this.manager.get('heartbeat interval') * 1000);

this.log.debug('set heartbeat interval for client', this.id);
//DVV this.log.debug('set heartbeat interval for client', this.id);
}
};

Expand All @@ -281,7 +281,7 @@ Transport.prototype.clearTimeouts = function () {

Transport.prototype.heartbeat = function () {
if (this.open) {
this.log.debug('emitting heartbeat for client', this.id);
//DVV this.log.debug('emitting heartbeat for client', this.id);
this.packet({ type: 'heartbeat' });
this.setHeartbeatTimeout();
}
Expand All @@ -298,10 +298,10 @@ Transport.prototype.heartbeat = function () {

Transport.prototype.onMessage = function (packet) {
if ('heartbeat' == packet.type) {
this.log.debug('got heartbeat packet');
//DVV this.log.debug('got heartbeat packet');
this.store.heartbeat(this.id);
} else if ('disconnect' == packet.type && packet.endpoint == '') {
this.log.debug('got disconnection packet');
//DVV this.log.debug('got disconnection packet');
this.store.disconnect(this.id, true);
} else {
this.log.debug('got packet');
Expand Down Expand Up @@ -329,7 +329,7 @@ Transport.prototype.clearHeartbeatInterval = function () {
if (this.heartbeatInterval) {
clearTimeout(this.heartbeatInterval);
this.heartbeatInterval = null;
this.log.debug('cleared heartbeat interval for client', this.id);
//DVV this.log.debug('cleared heartbeat interval for client', this.id);
}
};

Expand Down
6 changes: 4 additions & 2 deletions lib/transports/htmlfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ HTMLFile.prototype.handleRequest = function (req) {

req.res.write(
'<html><body>'
+ '<script>var _ = function (msg) { parent.s._(msg, document); };</script>'
+ '<script>var _ = function (msg) { parent.s.transport._(msg, document); };</script>'
+ new Array(174).join(' ')
);
}
Expand All @@ -65,7 +65,9 @@ HTMLFile.prototype.handleRequest = function (req) {
*/

HTMLFile.prototype.write = function (data) {
data = '<script>_(' + JSON.stringify(data) + ');</script>';
//DVV: very bad to overwrite global _, it's usually for underscore.js :|
// data = '<script>_(' + JSON.stringify(data) + ');</script>';
data = '<script>parent.s.transport._(' + JSON.stringify(data) + ', document);</script>';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be io.JSON.stringify

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • _ is not a global (it's executed in an iframe we create, in an ActiveXObject virtual document scope).
  • JSON here is server side, so no io.JSON.
  • .s is the Transport object
  • we want to minimize framing, so parent.s.transport._ and , document are undesirable as part of every message frame.


this.drain = false;
this.response.write(data);
Expand Down