diff --git a/.gitmodules b/.gitmodules
index 4d88b912..851d5c96 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "lib/vendor/js-oo"]
- path = lib/vendor/js-oo
- url = git://github.com/visionmedia/js-oo.git
[submodule "example/client"]
path = example/client
url = git://github.com/LearnBoost/Socket.IO.git
diff --git a/example/server.js b/example/server.js
index b0cd83d9..1a8b5bf6 100644
--- a/example/server.js
+++ b/example/server.js
@@ -58,9 +58,7 @@ io.listen(server, {
onClientMessage: function(message, client){
var msg = { message: [client.sessionId, message] };
buffer.push(msg);
- if (buffer.length > 15) {
- buffer.shift();
- }
+ if (buffer.length > 15) buffer.shift();
client.broadcast(json(msg));
}
diff --git a/lib/socket.io/client.js b/lib/socket.io/client.js
index c6d92541..b635ee0e 100644
--- a/lib/socket.io/client.js
+++ b/lib/socket.io/client.js
@@ -1,159 +1,150 @@
-var options = require('./util/options').options,
- urlparse = require('url').parse;
+var urlparse = require('url').parse,
-module.exports = Class({
-
- include: [options],
-
- options: {
+Client = module.exports = function(listener, req, res, options, head){
+ this.listener = listener;
+ this.options({
timeout: 12000,
closeTimeout: 0
- },
+ }, options);
+ this.connections = 0;
+ this.connected = false;
+ this._heartbeats = 0;
+ this.upgradeHead = head;
+ this._onConnect(req, res);
+};
- init: function(listener, req, res, options, head){
- this.listener = listener;
- this.setOptions(options);
- this.connections = 0;
- this.connected = false;
- this._heartbeats = 0;
- this.upgradeHead = head;
- this._onConnect(req, res);
- },
+sys.inherits(Client, require('./utils').options);
- send: function(message){
- if (!this.connected || !(this.connection.readyState === 'open' ||
- this.connection.readyState === 'writeOnly')){
- return this._queue(message);
- }
- this._write(this._encode(message));
- return this;
- },
-
- broadcast: function(message){
- if (!('sessionId' in this)) {
- return this;
- }
- this.listener.broadcast(message, this.sessionId);
- return this;
- },
-
- _onMessage: function(data){
- var messages = this._decode(data);
- if (messages === false) return this.listener.options.log('Bad message received from client ' + this.sessionId);
- for (var i = 0, l = messages.length; i < l; i++){
- if (messages[i].substr(0, 3) == '\ufffdh\ufffd'){
- return this._onHeartbeat(data.substr(3));
- }
- this.listener._onClientMessage(messages[i], this);
- }
- },
-
- _onConnect: function(req, res){
- var self = this;
- this.request = req;
- this.response = res;
- this.connection = this.request.connection;
- if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
- },
-
- _encode: function(messages){
- var ret = '',
- messages = messages instanceof Array ? messages : [];
- for (var i = 0, l = messages.length; i < l; i++){
- ret += messages[i].length + message;
- }
- return '\ufffdm\ufffd' + ret;
- },
-
- _decode: function(data){
- if (data.substr(0, 3) !== '\ufffdm\ufffd') return false;
- var messages = [];
- do(){
- for (var i = 0, n, number = '';; i++;){
- var n = data.substr(i, 1);
- if (Number(n) != n){
- number = Number(number);
- break;
- }
- number += n;
- }
- messages.push(data.substr(i, i + number)); // here
- data = data.substr(i + number);
- } while(data !== '');
- return messages;
- },
-
- _payload: function(){
- var payload = [];
-
- this.connections++;
- this.connected = true;
-
- if (!this.handshaked){
- this._generateSessionId();
- payload.push(this.sessionId);
- this.handshaked = true;
- }
-
- payload = payload.concat(this._writeQueue || []);
- this._writeQueue = [];
-
- if (payload.length) this._write(this._encode(payload));
- if (this.connections === 1) this.listener._onClientConnect(this);
-
- if (this.listener.options.timeout) this._heartbeat();
- },
-
- _heartbeat: function(){
- var self = this;
- this.send('\ufffdh\ufffd' + ++this._heartbeats);
- this._heartbitTimeout = setTimeout(function(){
- self.close();
- }, this.listener.options.timeout);
- },
-
- _onHeartbeat: function(h){
- if (h === this._heartbeats) clearTimeout(this._heartbitTimeout);
- },
-
- _onClose: function(){
- var self = this;
- clearTimeout(this._heartbeatTimeout);
- this.connected = false;
- this._disconnectTimeout = setTimeout(function(){
- self._onDisconnect();
- }, this.options.closeTimeout);
- },
-
- _onDisconnect: function(){
- if (!this.finalized){
- this._writeQueue = [];
- this.connected = false;
- this.finalized = true;
- if (this.handshaked) this.listener._onClientDisconnect(this);
- }
- },
-
- _queue: function(message){
- if (!('_writeQueue' in this)){
- this._writeQueue = [];
- }
- this._writeQueue.push(message);
- return this;
- },
-
- _generateSessionId: function(){
- if (this.sessionId) return this.listener.options.log('This client already has a session id');
- this.sessionId = Math.random().toString().substr(2);
- return this;
- },
-
- _verifyOrigin: function(origin){
- var parts = urlparse(origin), origins = this.listener.options.origins;
- return origins.indexOf('*:*') !== -1 ||
- origins.indexOf(parts.host + ':' + parts.port) !== -1 ||
- origins.indexOf(parts.host + ':*') !== -1 ||
- origins.indexOf('*:' + parts.port) !== -1;
+Client.prototype.send = function(message){
+ if (!this.connected || !(this.connection.readyState === 'open' ||
+ this.connection.readyState === 'writeOnly')){
+ return this._queue(message);
}
+ this._write(this._encode(message));
+ return this;
+};
-});
\ No newline at end of file
+Client.prototype.broadcast = function(message){
+ if (!('sessionId' in this)) return this;
+ this.listener.broadcast(message, this.sessionId);
+ return this;
+};
+
+Client.prototype._onMessage = function(data){
+ var messages = this._decode(data);
+ if (messages === false) return this.listener.options.log('Bad message received from client ' + this.sessionId);
+ for (var i = 0, l = messages.length; i < l; i++){
+ if (messages[i].substr(0, 3) == '\ufffdh\ufffd'){
+ return this._onHeartbeat(data.substr(3));
+ }
+ this.listener._onClientMessage(messages[i], this);
+ }
+};
+
+Client.prototype._onConnect = function(req, res){
+ var self = this;
+ this.request = req;
+ this.response = res;
+ this.connection = this.request.connection;
+ if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
+};
+
+Client.prototype._encode = function(messages){
+ var ret = '',
+ messages = messages instanceof Array ? messages : [];
+ for (var i = 0, l = messages.length; i < l; i++){
+ ret += messages[i].length + message;
+ }
+ return '\ufffdm\ufffd' + ret;
+};
+
+Client.prototype._decode = function(data){
+ if (data.substr(0, 3) !== '\ufffdm\ufffd') return false;
+ var messages = [];
+ do {
+ for (var i = 0, n, number = '';; i++){
+ var n = data.substr(i, 1);
+ if (Number(n) != n){
+ number = Number(number);
+ break;
+ }
+ number += n;
+ }
+ messages.push(data.substr(i, i + number)); // here
+ data = data.substr(i + number);
+ } while(data !== '');
+ return messages;
+};
+
+Client.prototype._payload = function(){
+ var payload = [];
+
+ this.connections++;
+ this.connected = true;
+
+ if (!this.handshaked){
+ this._generateSessionId();
+ payload.push(this.sessionId);
+ this.handshaked = true;
+ }
+
+ payload = payload.concat(this._writeQueue || []);
+ this._writeQueue = [];
+
+ if (payload.length) this._write(this._encode(payload));
+ if (this.connections === 1) this.listener._onClientConnect(this);
+
+ if (this.listener.options.timeout) this._heartbeat();
+};
+
+Client.prototype._heartbeat = function(){
+ var self = this;
+ this.send('\ufffdh\ufffd' + ++this._heartbeats);
+ this._heartbitTimeout = setTimeout(function(){
+ self.close();
+ }, this.listener.options.timeout);
+};
+
+Client.prototype._onHeartbeat = function(h){
+ if (h === this._heartbeats) clearTimeout(this._heartbitTimeout);
+};
+
+Client.prototype._onClose = function(){
+ var self = this;
+ clearTimeout(this._heartbeatTimeout);
+ this.connected = false;
+ this._disconnectTimeout = setTimeout(function(){
+ self._onDisconnect();
+ }, this.options.closeTimeout);
+};
+
+Client.prototype._onDisconnect = function(){
+ if (!this.finalized){
+ this._writeQueue = [];
+ this.connected = false;
+ this.finalized = true;
+ if (this.handshaked) this.listener._onClientDisconnect(this);
+ }
+};
+
+Client.prototype._queue = function(message){
+ if (!('_writeQueue' in this)){
+ this._writeQueue = [];
+ }
+ this._writeQueue.push(message);
+ return this;
+};
+
+Client.prototype._generateSessionId = function(){
+ if (this.sessionId) return this.listener.options.log('This client already has a session id');
+ this.sessionId = Math.random().toString().substr(2);
+ return this;
+};
+
+Client.prototype._verifyOrigin = function(origin){
+ var parts = urlparse(origin), origins = this.listener.options.origins;
+ return origins.indexOf('*:*') !== -1 ||
+ origins.indexOf(parts.host + ':' + parts.port) !== -1 ||
+ origins.indexOf(parts.host + ':*') !== -1 ||
+ origins.indexOf('*:' + parts.port) !== -1;
+};
\ No newline at end of file
diff --git a/lib/socket.io/listener.js b/lib/socket.io/listener.js
index 0bd0d067..f6b34f4e 100644
--- a/lib/socket.io/listener.js
+++ b/lib/socket.io/listener.js
@@ -1,119 +1,120 @@
var url = require('url'),
- options = require('./util/options').options,
- Client = require('./client'),
- transports = {};
+ Client = require('./client'),
+ transports = {
+ 'flashsocket': require('./transports/flashsocket'),
+ 'htmlfile': require('./transports/htmlfile'),
+ 'websocket': require('./transports/websocket'),
+ 'xhr-multipart': require('./transports/xhr-multipart'),
+ 'xhr-polling': require('./transports/xhr-polling')
+ },
-module.exports = Class({
-
- include: [process.EventEmitter.prototype, options],
-
- options: {
+Listener = module.exports = function(server, options){
+ var self = this;
+ process.EventEmitter.call(this);
+ this.server = server;
+ this.options({
origins: '*:*',
resource: 'socket.io',
transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling'],
- transportOptions: {},
+ transportOptions: {
+ 'xhr-polling': {
+ closeTimeout: 5000,
+ duration: 20000
+ }
+ },
log: function(message){
require('sys').log(message);
}
- },
-
- init: function(server, options){
- var self = this;
- process.EventEmitter.call(this);
- this.server = server;
- this.setOptions(options);
- this.clients = [];
- this.clientsIndex = {};
-
- var listeners = this.server.listeners('request');
- this.server.removeAllListeners('request');
-
- this.server.addListener('request', function(req, res){
- if (self.check(req, res)) return;
- for (var i = 0; i < listeners.length; i++) {
- listeners[i].call(this, req, res);
- }
- });
-
- this.server.addListener('upgrade', function(req, socket, head){
- if (!self.check(req, socket, true, head)){
- socket.destroy();
- }
- });
-
- this.options.transports.forEach(function(t){
- if (!(t in transports)){
- transports[t] = require('./transports/' + t)[t];
- if (transports[t].init) transports[t].init(this);
- }
- }, this);
-
- this.options.log('socket.io ready - accepting connections');
- },
-
- broadcast: function(message, except){
- for (var i = 0, l = this.clients.length; i < l; i++){
- if (this.clients[i] && (!except || [].concat(except).indexOf(this.clients[i].sessionId) == -1)){
- this.clients[i].send(message);
- }
+ }, options);
+ this.clients = [];
+ this.clientsIndex = {};
+
+ var listeners = this.server.listeners('request');
+ this.server.removeAllListeners('request');
+
+ this.server.addListener('request', function(req, res){
+ if (self.check(req, res)) return;
+ for (var i = 0; i < listeners.length; i++){
+ listeners[i].call(this, req, res);
}
- return this;
- },
-
- check: function(req, res, httpUpgrade, head){
- var path = url.parse(req.url).pathname, parts, cn;
- if (path.indexOf('/' + this.options.resource) === 0){
- parts = path.substr(1).split('/');
- if (parts[2]){
- cn = this._lookupClient(parts[2]);
- if (cn){
- cn._onConnect(req, res);
- } else {
- req.connection.end();
- this.options.log('Couldnt find client with session id "' + parts[2] + '"');
- }
- } else {
- this._onConnection(parts[1], req, res, httpUpgrade, head);
- }
- return true;
+ });
+
+ this.server.addListener('upgrade', function(req, socket, head){
+ if (!self.check(req, socket, true, head)){
+ socket.destroy();
}
- return false;
- },
+ });
- _lookupClient: function(sid){
- return this.clientsIndex[sid];
- },
-
- _onClientConnect: function(client){
- if (!(client instanceof Client) || !client.sessionId){
- return this.options.log('Invalid client');
- }
- client.i = this.clients.length;
- this.clients.push(client);
- this.clientsIndex[client.sessionId] = client;
- this.options.log('Client '+ client.sessionId +' connected');
- this.emit('clientConnect', client);
- },
-
- _onClientMessage: function(data, client){
- this.emit('clientMessage', data, client);
- },
-
- _onClientDisconnect: function(client){
- this.clientsIndex[client.sessionId] = null;
- this.clients[client.i] = null;
- this.options.log('Client '+ client.sessionId +' disconnected');
- this.emit('clientDisconnect', client);
- },
-
- // new connections (no session id)
- _onConnection: function(transport, req, res, httpUpgrade, head){
- if (this.options.transports.indexOf(transport) === -1 || (httpUpgrade && !transports[transport].httpUpgrade)){
- httpUpgrade ? res.destroy() : req.connection.destroy();
- return this.options.log('Illegal transport "'+ transport +'"');
- }
- this.options.log('Initializing client with transport "'+ transport +'"');
- new transports[transport](this, req, res, this.options.transportOptions[transport], head);
+ for (var i in transports){
+ if ('init' in transports[i]) transports[t].init(this);
}
-
-});
+
+ this.options.log('socket.io ready - accepting connections');
+};
+
+sys.inherits(Listener, process.EventEmitter);
+sys.inherits(Listener, require('./utils').options);
+
+Listener.prototype.broadcast = function(message, except){
+ for (var i = 0, l = this.clients.length; i < l; i++){
+ if (this.clients[i] && (!except || [].concat(except).indexOf(this.clients[i].sessionId) == -1)){
+ this.clients[i].send(message);
+ }
+ }
+ return this;
+};
+
+Listener.prototype.check = function(req, res, httpUpgrade, head){
+ var path = url.parse(req.url).pathname, parts, cn;
+ if (path.indexOf('/' + this.options.resource) === 0){
+ parts = path.substr(1).split('/');
+ if (parts[2]){
+ cn = this._lookupClient(parts[2]);
+ if (cn){
+ cn._onConnect(req, res);
+ } else {
+ req.connection.end();
+ this.options.log('Couldnt find client with session id "' + parts[2] + '"');
+ }
+ } else {
+ this._onConnection(parts[1], req, res, httpUpgrade, head);
+ }
+ return true;
+ }
+ return false;
+};
+
+Listener.prototype._lookupClient = function(sid){
+ return this.clientsIndex[sid];
+};
+
+Listener.prototype._onClientConnect = function(client){
+ if (!(client instanceof Client) || !client.sessionId){
+ return this.options.log('Invalid client');
+ }
+ client.i = this.clients.length;
+ this.clients.push(client);
+ this.clientsIndex[client.sessionId] = client;
+ this.options.log('Client '+ client.sessionId +' connected');
+ this.emit('clientConnect', client);
+};
+
+Listener.prototype._onClientMessage = function(data, client){
+ this.emit('clientMessage', data, client);
+};
+
+Listener.prototype._onClientDisconnect = function(client){
+ this.clientsIndex[client.sessionId] = null;
+ this.clients[client.i] = null;
+ this.options.log('Client '+ client.sessionId +' disconnected');
+ this.emit('clientDisconnect', client);
+};
+
+Listener.prototype._onConnection = function(transport, req, res, httpUpgrade, head){
+ if (this.options.transports.indexOf(transport) === -1 || (httpUpgrade && !transports[transport].httpUpgrade)){
+ httpUpgrade ? res.destroy() : req.connection.destroy();
+ return this.options.log('Illegal transport "'+ transport +'"');
+ }
+ this.options.log('Initializing client with transport "'+ transport +'"');
+ new transports[transport](this, req, res, this.options.transportOptions[transport], head);
+};
\ No newline at end of file
diff --git a/lib/socket.io/transports/flashsocket.js b/lib/socket.io/transports/flashsocket.js
index 9533087a..6e458c20 100644
--- a/lib/socket.io/transports/flashsocket.js
+++ b/lib/socket.io/transports/flashsocket.js
@@ -1,25 +1,31 @@
-var websocket = require('./websocket').websocket,
- net = require('net'),
- listeners = [];
+var Websocket = require('./websocket'),
+ net = require('net'),
+ listeners = [],
-exports.flashsocket = websocket.extend({});
-exports.flashsocket.httpUpgrade = true;
-exports.flashsocket.init = function(listener){
+Flashsocket = module.exports = function(){};
+
+sys.inherits(HTMLFile, Websocket);
+
+Flashsocket.httpUpgrade = true;
+
+Flashsocket.init = function(listener){
listeners.push(listener);
};
-net.createServer(function(socket){
- socket.write('\n');
- socket.write('\n');
- socket.write('\n');
+try {
+ net.createServer(function(socket){
+ socket.write('\n');
+ socket.write('\n');
+ socket.write('\n');
- listeners.forEach(function(l){
- [].concat(l.options.origins).forEach(function(origin){
- var parts = origin.split(':');
- socket.write('\n');
+ listeners.forEach(function(l){
+ [].concat(l.options.origins).forEach(function(origin){
+ var parts = origin.split(':');
+ socket.write('\n');
+ });
});
- });
- socket.write('\n');
- socket.end();
-}).listen(843);
+ socket.write('\n');
+ socket.end();
+ }).listen(843);
+} catch(e){}
\ No newline at end of file
diff --git a/lib/socket.io/transports/htmlfile.js b/lib/socket.io/transports/htmlfile.js
index ae68ebe7..882051f5 100644
--- a/lib/socket.io/transports/htmlfile.js
+++ b/lib/socket.io/transports/htmlfile.js
@@ -1,45 +1,45 @@
-var Client = require('../client').Client,
- qs = require('querystring');
+var Client = require('../client'),
+ qs = require('querystring'),
-exports.htmlfile = Client.extend({
+HTMLFile = module.exports = function(){};
+
+sys.inherits(HTMLFile, Client);
- _onConnect: function(req, res){
- var self = this, body = '';
- switch (req.method){
- case 'GET':
- this.__super__(req, res);
- this.request.connection.addListener('close', function(){ self._onClose(); });
- this.response.useChunkedEncodingByDefault = true;
- this.response.shouldKeepAlive = true;
- this.response.writeHead(200, {
- 'Content-Type': 'text/html',
- 'Connection': 'keep-alive',
- 'Transfer-Encoding': 'chunked'
- });
- if ('flush' in this.response) this.response.flush();
- this._payload();
- break;
-
- case 'POST':
- req.addListener('data', function(message){
- body += message;
- });
- req.addListener('end', function(){
- try {
- var msg = qs.parse(body);
- self._onMessage(msg.data);
- } catch(e){}
- res.writeHead(200);
- res.write('ok');
- res.end();
- });
- break;
- }
- },
-
- _write: function(message){
- this.response.write('');
- if ('flush' in this.response) this.response.flush();
+HTMLFile.prototype._onConnect = function(req, res){
+ var self = this, body = '';
+ switch (req.method){
+ case 'GET':
+ Client.prototype._onConnect.apply(this, [req, res]);
+ this.request.connection.addListener('close', function(){ self._onClose(); });
+ this.response.useChunkedEncodingByDefault = true;
+ this.response.shouldKeepAlive = true;
+ this.response.writeHead(200, {
+ 'Content-Type': 'text/html',
+ 'Connection': 'keep-alive',
+ 'Transfer-Encoding': 'chunked'
+ });
+ if ('flush' in this.response) this.response.flush();
+ this._payload();
+ break;
+
+ case 'POST':
+ req.addListener('data', function(message){
+ body += message;
+ });
+ req.addListener('end', function(){
+ try {
+ var msg = qs.parse(body);
+ self._onMessage(msg.data);
+ } catch(e){}
+ res.writeHead(200);
+ res.write('ok');
+ res.end();
+ });
+ break;
}
+};
-});
\ No newline at end of file
+HTMLFile.prototype._write = function(message){
+ this.response.write('');
+ if ('flush' in this.response) this.response.flush();
+};
\ No newline at end of file
diff --git a/lib/socket.io/transports/websocket.js b/lib/socket.io/transports/websocket.js
index 26f8bb7c..6b6087b8 100644
--- a/lib/socket.io/transports/websocket.js
+++ b/lib/socket.io/transports/websocket.js
@@ -1,134 +1,137 @@
-var Client = require('../client').Client,
+var Client = require('../client'),
url = require('url'),
Buffer = require('buffer').Buffer,
- crypto = require('crypto');
+ crypto = require('crypto'),
-exports.websocket = Client.extend({
+WebSocket = exports.websocket = function(){};
- _onConnect: function(req, socket){
- var self = this, headers = [];
- this.request = req;
- this.connection = socket;
- this.data = '';
+sys.inherits(WebSocket, Client);
- if (this.request.headers.upgrade !== 'WebSocket' || !this._verifyOrigin(this.request.headers.origin)){
- this.listener.options.log('WebSocket connection invalid');
- this.connection.end();
- }
+WebSocket.prototype._onConnect = function(req, socket){
+ var self = this, headers = [];
+ this.request = req;
+ this.connection = socket;
+ this.data = '';
- this.connection.setTimeout(0);
- this.connection.setEncoding('utf8');
- this.connection.setNoDelay(true);
+ if (this.request.headers.upgrade !== 'WebSocket' || !this._verifyOrigin(this.request.headers.origin)){
+ this.listener.options.log('WebSocket connection invalid');
+ this.connection.end();
+ }
- if ('sec-websocket-key1' in this.request.headers) {
- this.draft = 76;
- }
+ this.connection.setTimeout(0);
+ this.connection.setEncoding('utf8');
+ this.connection.setNoDelay(true);
- if (this.draft == 76) {
+ if ('sec-websocket-key1' in this.request.headers){
+ this.draft = 76;
+ }
- var origin = this.request.headers.origin;
+ if (this.draft == 76){
+ var origin = this.request.headers.origin;
- headers = [
- 'HTTP/1.1 101 WebSocket Protocol Handshake',
- 'Upgrade: WebSocket',
- 'Connection: Upgrade',
- 'Sec-WebSocket-Origin: ' + (origin || 'null'),
- 'Sec-WebSocket-Location: ws://' + this.request.headers.host + this.request.url
- ];
-
- if ('sec-websocket-protocol' in this.request.headers) {
- headers.push('Sec-WebSocket-Protocol: ' + this.request.headers['sec-websocket-protocol']);
- }
- }
- else {
-
- headers = [
- 'HTTP/1.1 101 Web Socket Protocol Handshake',
- 'Upgrade: WebSocket',
- 'Connection: Upgrade',
- 'WebSocket-Origin: ' + this.request.headers.origin,
- 'WebSocket-Location: ws://' + this.request.headers.host + this.request.url
- ];
-
- try {
- this.connection.write(headers.concat('', '').join('\r\n'));
- } catch(e){
- this._onClose();
- }
- }
+ headers = [
+ 'HTTP/1.1 101 WebSocket Protocol Handshake',
+ 'Upgrade: WebSocket',
+ 'Connection: Upgrade',
+ 'Sec-WebSocket-Origin: ' + (origin || 'null'),
+ 'Sec-WebSocket-Location: ws://' + this.request.headers.host + this.request.url
+ ];
- this.connection.addListener('end', function(){self._onClose();});
- this.connection.addListener('data', function(data){self._handle(data);});
-
- if (this._proveReception(headers)) this._payload();
- },
-
- _handle: function(data){
- var chunk, chunks, chunk_count;
- this.data += data;
- chunks = this.data.split('\ufffd');
- chunk_count = chunks.length - 1;
- for (var i = 0; i < chunk_count; i++) {
- chunk = chunks[i];
- if (chunk[0] !== '\u0000') {
- this.listener.options.log('Data incorrectly framed by UA. Dropping connection');
- this.connection.destroy();
- return false;
- }
- this._onMessage(chunk.slice(1));
+ if ('sec-websocket-protocol' in this.request.headers){
+ headers.push('Sec-WebSocket-Protocol: ' + this.request.headers['sec-websocket-protocol']);
}
- this.data = chunks[chunks.length - 1];
- },
-
- // http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
- _proveReception: function(headers){
- var k1 = this.request.headers['sec-websocket-key1'],
- k2 = this.request.headers['sec-websocket-key2'];
+ } else {
- if (k1 && k2) {
- var md5 = crypto.createHash('md5');
-
- [k1, k2].forEach(function(k) {
- var n = parseInt(k.replace(/[^\d]/g, '')),
- spaces = k.replace(/[^ ]/g, '').length;
-
- if (spaces === 0 || n % spaces !== 0) {
- this.listener.options.log('Invalid WebSocket key: "' + k + '". Dropping connection');
- this.connection.destroy();
- return false;
- }
-
- n /= spaces;
-
- md5.update(String.fromCharCode(
- n >> 24 & 0xFF,
- n >> 16 & 0xFF,
- n >> 8 & 0xFF,
- n & 0xFF));
- });
-
- md5.update(this.upgradeHead.toString('binary'));
-
- try {
- this.connection.write(headers.concat('', '').join('\r\n') + md5.digest('binary'), 'binary');
- } catch(e){
- this._onClose();
- }
- }
+ headers = [
+ 'HTTP/1.1 101 Web Socket Protocol Handshake',
+ 'Upgrade: WebSocket',
+ 'Connection: Upgrade',
+ 'WebSocket-Origin: ' + this.request.headers.origin,
+ 'WebSocket-Location: ws://' + this.request.headers.host + this.request.url
+ ];
- return true;
- },
-
- _write: function(message){
try {
- this.connection.write('\u0000', 'binary');
- this.connection.write(message, 'utf8');
- this.connection.write('\uffff', 'binary');
+ this.connection.write(headers.concat('', '').join('\r\n'));
} catch(e){
this._onClose();
}
}
+
+ this.connection.addListener('end', function(){
+ self._onClose();
+ });
+
+ this.connection.addListener('data', function(data){
+ self._handle(data);
+ });
-});
+ if (this._proveReception(headers)) this._payload();
+};
-exports.websocket.httpUpgrade = true;
\ No newline at end of file
+WebSocket.prototype._handle = function(data){
+ var chunk, chunks, chunk_count;
+ this.data += data;
+ chunks = this.data.split('\ufffd');
+ chunk_count = chunks.length - 1;
+ for (var i = 0; i < chunk_count; i++){
+ chunk = chunks[i];
+ if (chunk[0] !== '\u0000'){
+ this.listener.options.log('Data incorrectly framed by UA. Dropping connection');
+ this.connection.destroy();
+ return false;
+ }
+ this._onMessage(chunk.slice(1));
+ }
+ this.data = chunks[chunks.length - 1];
+};
+
+ // http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
+WebSocket.prototype._proveReception = function(headers){
+ var k1 = this.request.headers['sec-websocket-key1'],
+ k2 = this.request.headers['sec-websocket-key2'];
+
+ if (k1 && k2){
+ var md5 = crypto.createHash('md5');
+
+ [k1, k2].forEach(function(k){
+ var n = parseInt(k.replace(/[^\d]/g, '')),
+ spaces = k.replace(/[^ ]/g, '').length;
+
+ if (spaces === 0 || n % spaces !== 0){
+ this.listener.options.log('Invalid WebSocket key: "' + k + '". Dropping connection');
+ this.connection.destroy();
+ return false;
+ }
+
+ n /= spaces;
+
+ md5.update(String.fromCharCode(
+ n >> 24 & 0xFF,
+ n >> 16 & 0xFF,
+ n >> 8 & 0xFF,
+ n & 0xFF));
+ });
+
+ md5.update(this.upgradeHead.toString('binary'));
+
+ try {
+ this.connection.write(headers.concat('', '').join('\r\n') + md5.digest('binary'), 'binary');
+ } catch(e){
+ this._onClose();
+ }
+ }
+
+ return true;
+};
+
+WebSocket.prototype._write = function(message){
+ try {
+ this.connection.write('\u0000', 'binary');
+ this.connection.write(message, 'utf8');
+ this.connection.write('\uffff', 'binary');
+ } catch(e){
+ this._onClose();
+ }
+};
+
+WebSocket.httpUpgrade = true;
\ No newline at end of file
diff --git a/lib/socket.io/transports/xhr-multipart.js b/lib/socket.io/transports/xhr-multipart.js
index 24a9499e..9f614ead 100644
--- a/lib/socket.io/transports/xhr-multipart.js
+++ b/lib/socket.io/transports/xhr-multipart.js
@@ -1,66 +1,60 @@
-var Client = require('../client').Client,
- qs = require('querystring');
+var Client = require('../client'),
+ qs = require('querystring'),
-exports['xhr-multipart'] = Client.extend({
+Multipart = module.exports = function(){};
- options: {
- pingInterval: 7000
- },
-
- _pingInterval: null,
+sys.inherits(HTMLFile, Client);
- _onConnect: function(req, res){
- var self = this, body = '', headers = {};
- // https://developer.mozilla.org/En/HTTP_Access_Control
- if (req.headers.origin && this._verifyOrigin(req.headers.origin)) {
- headers['Access-Control-Allow-Origin'] = req.headers.origin;
- headers['Access-Control-Allow-Credentials'] = 'true';
- }
- if (typeof req.headers['access-control-request-method'] !== 'undefined') {
- // CORS preflight message
- headers['Access-Control-Allow-Methods'] = req.headers['access-control-request-method'];
- res.writeHead(200, headers);
- res.write('ok');
- res.end();
- return;
- }
- switch (req.method){
- case 'GET':
- this.__super__(req, res);
- headers['Content-Type'] = 'multipart/x-mixed-replace;boundary="socketio"';
- headers['Connection'] = 'keep-alive';
- this.request.connection.addListener('end', function(){ self._onClose(); });
- this.response.useChunkedEncodingByDefault = false;
- this.response.shouldKeepAlive = true;
- this.response.writeHead(200, headers);
- this.response.write("--socketio\n");
- if ('flush' in this.response) this.response.flush();
- this._payload();
- break;
-
- case 'POST':
- req.addListener('data', function(message){
- body += message.toString();
- });
- req.addListener('end', function(){
- try {
- var msg = qs.parse(body);
- self._onMessage(msg.data);
- } catch(e){}
- res.writeHead(200, headers);
- res.write('ok');
- res.end();
- body = '';
- });
- break;
- }
- },
-
- _write: function(message){
- this.response.write("Content-Type: text/plain" + (message.length === 1 && message.charCodeAt(0) === 6 ? "; charset=us-ascii" : "") + "\n\n");
- this.response.write(message + "\n");
- this.response.write("--socketio\n");
- if ('flush' in this.response) this.response.flush();
+Multipart.prototype._onConnect = function(req, res){
+ var self = this, body = '', headers = {};
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ if (req.headers.origin && this._verifyOrigin(req.headers.origin)){
+ headers['Access-Control-Allow-Origin'] = req.headers.origin;
+ headers['Access-Control-Allow-Credentials'] = 'true';
}
+ if (typeof req.headers['access-control-request-method'] !== 'undefined'){
+ // CORS preflight message
+ headers['Access-Control-Allow-Methods'] = req.headers['access-control-request-method'];
+ res.writeHead(200, headers);
+ res.write('ok');
+ res.end();
+ return;
+ }
+ switch (req.method){
+ case 'GET':
+ Client.prototype._onConnect.apply(this, [req, res]);
+ headers['Content-Type'] = 'multipart/x-mixed-replace;boundary="socketio"';
+ headers['Connection'] = 'keep-alive';
+ this.request.connection.addListener('end', function(){ self._onClose(); });
+ this.response.useChunkedEncodingByDefault = false;
+ this.response.shouldKeepAlive = true;
+ this.response.writeHead(200, headers);
+ this.response.write("--socketio\n");
+ if ('flush' in this.response) this.response.flush();
+ this._payload();
+ break;
+
+ case 'POST':
+ req.addListener('data', function(message){
+ body += message.toString();
+ });
+ req.addListener('end', function(){
+ try {
+ var msg = qs.parse(body);
+ self._onMessage(msg.data);
+ } catch(e){}
+ res.writeHead(200, headers);
+ res.write('ok');
+ res.end();
+ body = '';
+ });
+ break;
+ }
+};
-});
\ No newline at end of file
+Multipart.prototype._write = function(message){
+ this.response.write("Content-Type: text/plain" + (message.length === 1 && message.charCodeAt(0) === 6 ? "; charset=us-ascii" : "") + "\n\n");
+ this.response.write(message + "\n");
+ this.response.write("--socketio\n");
+ if ('flush' in this.response) this.response.flush();
+};
\ No newline at end of file
diff --git a/lib/socket.io/transports/xhr-polling.js b/lib/socket.io/transports/xhr-polling.js
index f1626df4..1e93ee46 100644
--- a/lib/socket.io/transports/xhr-polling.js
+++ b/lib/socket.io/transports/xhr-polling.js
@@ -1,57 +1,48 @@
-var Client = require('../client').Client,
- qs = require('querystring');
+var Client = require('../client'),
+ qs = require('querystring'),
-exports['xhr-polling'] = Client.extend({
-
- options: {
- closeTimeout: 5000,
- duration: 20000
- },
-
- _onConnect: function(req, res){
- var self = this, body = '';
- switch (req.method){
- case 'GET':
- this.__super__(req, res);
- this._closeTimeout = setTimeout(function(){
- self._write('');
- }, this.options.duration);
- this._payload();
- break;
-
- case 'POST':
- req.addListener('data', function(message){
- body += message;
- });
- req.addListener('end', function(){
- try {
- var msg = qs.parse(body);
- self._onMessage(msg.data);
- } catch(e){}
- res.writeHead(200);
- res.write('ok');
- res.end();
- });
- break;
- }
- },
-
- _write: function(message){
- if (this._closeTimeout) {
- clearTimeout(this._closeTimeout);
- }
- var headers = {'Content-Type': 'text/plain', 'Content-Length': message.length};
- // https://developer.mozilla.org/En/HTTP_Access_Control
- if (this.request.headers.origin && this._verifyOrigin(this.request.headers.origin)) {
- headers['Access-Control-Allow-Origin'] = this.request.headersorigin;
- if (this.request.headers.cookie) {
- headers['Access-Control-Allow-Credentials'] = 'true';
- }
- }
- this.response.writeHead(200, headers);
- this.response.write(message);
- this.response.end();
- this._onClose();
+Polling = module.exports = function(){};
+
+sys.inherits(Polling, Client);
+
+Polling.prototype._onConnect = function(req, res){
+ var self = this, body = '';
+ switch (req.method){
+ case 'GET':
+ this.__super__(req, res);
+ this._closeTimeout = setTimeout(function(){
+ self._write('');
+ }, this.options.duration);
+ this._payload();
+ break;
+
+ case 'POST':
+ req.addListener('data', function(message){
+ body += message;
+ });
+ req.addListener('end', function(){
+ try {
+ var msg = qs.parse(body);
+ self._onMessage(msg.data);
+ } catch(e){}
+ res.writeHead(200);
+ res.write('ok');
+ res.end();
+ });
+ break;
}
+};
-});
\ No newline at end of file
+Polling.prototype._write = function(message){
+ if (this._closeTimeout) clearTimeout(this._closeTimeout);
+ var headers = {'Content-Type': 'text/plain', 'Content-Length': message.length};
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ if (this.request.headers.origin && this._verifyOrigin(this.request.headers.origin)){
+ headers['Access-Control-Allow-Origin'] = this.request.headersorigin;
+ if (this.request.headers.cookie) headers['Access-Control-Allow-Credentials'] = 'true';
+ }
+ this.response.writeHead(200, headers);
+ this.response.write(message);
+ this.response.end();
+ this._onClose();
+};
\ No newline at end of file
diff --git a/lib/socket.io/util/array.js b/lib/socket.io/util/array.js
deleted file mode 100644
index 7e0de59f..00000000
--- a/lib/socket.io/util/array.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Based on Mixin.js from MooTools (MIT)
-// Copyright (c) 2006-2009 Valerio Proietti,
-
-exports.flatten = function(arr){
- var array = [];
- for (var i = 0, l = arr.length; i < l; i++){
- var item = arr[i];
- if (item !== null) {
- array = array.concat(item instanceof Array ? array.flatten(item) : item);
- }
- }
- return array;
-};
-
-exports.include = function(arr, item){
- if (arr.indexOf(item) === -1) {
- arr.push(item);
- }
- return arr;
-};
\ No newline at end of file
diff --git a/lib/socket.io/util/object.js b/lib/socket.io/util/object.js
deleted file mode 100644
index 4603aee0..00000000
--- a/lib/socket.io/util/object.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Based on Mixin.js from MooTools (MIT)
-// Copyright (c) 2006-2009 Valerio Proietti,
-
-exports.clone = function(item) {
- var cloned;
- if (item instanceof Array){
- cloned = [];
- for (var i = 0; i < item.length; i++) {
- cloned[i] = exports.clone(item[i]);
- }
- return cloned;
- }
-
- if (typeof item === 'object') {
- cloned = {};
- for (var key in item) {
- cloned[key] = exports.clone(item[key]);
- }
- return cloned;
- }
-
- return item;
-};
-
-var mergeOne = function(source, key, current){
- if (current instanceof Array){
- source[key] = exports.clone(current);
- } else if (typeof current === 'object'){
- if (typeof source[key] === 'object') {
- exports.merge(source[key], current);
- } else {
- source[key] = exports.clone(current);
- }
- } else {
- source[key] = current;
- }
- return source;
-};
-
-exports.merge = function(source, k, v){
- if (typeof k === 'string') {
- return mergeOne(source, k, v);
- }
- for (var i = 1, l = arguments.length; i < l; i++){
- var object = arguments[i];
- for (var key in object) {
- mergeOne(source, key, object[key]);
- }
- }
- return source;
-};
diff --git a/lib/socket.io/util/options.js b/lib/socket.io/util/options.js
deleted file mode 100644
index 7d08b937..00000000
--- a/lib/socket.io/util/options.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Based on Mixin.js from MooTools (MIT)
-// Copyright (c) 2006-2009 Valerio Proietti,
-var object = require('./object'), sys = require('sys');
-
-exports.options = {
-
- options: {},
-
- setOption: function(key, value){
- object.merge(this.options, key, value);
- return this;
- },
-
- setOptions: function(options){
- for (var key in options) {
- this.setOption(key, options[key]);
- }
- if (this.addListener){
- var first_lower = function(full, first){
- return first.toLowerCase();
- };
-
- // Automagically register callbacks if the varname starts with on
- for (var i in this.options){
- if (!(/^on[A-Z]/).test(i) || typeof this.options[i] !== 'function') {
- continue;
- }
- this.addListener(i.replace(/^on([A-Z])/, first_lower), this.options[i]);
- this.options[i] = null;
- }
- }
- return this;
- }
-
-};
\ No newline at end of file
diff --git a/lib/socket.io/utils.js b/lib/socket.io/utils.js
new file mode 100644
index 00000000..9dd819a6
--- /dev/null
+++ b/lib/socket.io/utils.js
@@ -0,0 +1,12 @@
+var Options = exports.options = function(){};
+
+Options.prototype = {
+ options: function(options, merge){
+ this.options = exports.merge(options || {}, merge || {});
+ }
+};
+
+exports.merge = function(source, merge){
+ for (var i in merge) source[i] = merge[i];
+ return source;
+};
\ No newline at end of file
diff --git a/lib/vendor/js-oo b/lib/vendor/js-oo
deleted file mode 160000
index 1f94bd89..00000000
--- a/lib/vendor/js-oo
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1f94bd897994a952114cb1f280c1949d5e6a0648