build/faye.js in faye-0.3.1 vs build/faye.js in faye-0.3.2
- old
+ new
@@ -10,11 +10,11 @@
}
return dest;
};
Faye.extend(Faye, {
- VERSION: '0.3.1',
+ VERSION: '0.3.2',
BAYEUX_VERSION: '1.0',
ID_LENGTH: 128,
JSONP_CALLBACK: 'jsonpcallback',
CONNECTION_TYPES: ["long-polling", "callback-polling"],
@@ -141,10 +141,39 @@
this.each(actual, function(key, value) {
result = result && (expected[key] === value);
});
return result;
}
+ },
+
+ // http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/
+ toJSON: function(object) {
+ if (this.stringify)
+ return this.stringify(object, function(key, value) {
+ return (this[key] instanceof Array)
+ ? this[key]
+ : value;
+ });
+
+ return JSON.stringify(object);
+ },
+
+ timestamp: function() {
+ var date = new Date(),
+ year = date.getFullYear(),
+ month = date.getMonth() + 1,
+ day = date.getDate(),
+ hour = date.getHours(),
+ minute = date.getMinutes(),
+ second = date.getSeconds();
+
+ var pad = function(n) {
+ return n < 10 ? '0' + n : String(n);
+ };
+
+ return pad(year) + '-' + pad(month) + '-' + pad(day) + ' ' +
+ pad(hour) + ':' + pad(minute) + ':' + pad(second);
}
});
Faye.Class = function(parent, methods) {
@@ -231,10 +260,76 @@
});
}
};
+Faye.Logging = {
+ LOG_LEVELS: {
+ error: 3,
+ warn: 2,
+ info: 1,
+ debug: 0
+ },
+
+ logLevel: 'error',
+
+ log: function(message, level) {
+ if (!Faye.logger) return;
+
+ var levels = Faye.Logging.LOG_LEVELS;
+ if (levels[Faye.Logging.logLevel] > levels[level]) return;
+
+ var banner = '[' + level.toUpperCase() + '] [Faye',
+ klass = null;
+
+ for (var key in Faye) {
+ if (klass) continue;
+ if (typeof Faye[key] !== 'function') continue;
+ if (this instanceof Faye[key]) klass = key;
+ }
+ if (klass) banner += '.' + klass;
+ banner += '] ';
+
+ Faye.logger(Faye.timestamp() + ' ' + banner + message);
+ },
+
+ error: function(message) {
+ this.log(message, 'error');
+ },
+ warn: function(message) {
+ this.log(message, 'warn');
+ },
+ info: function(message) {
+ this.log(message, 'info');
+ },
+ debug: function(message) {
+ this.log(message, 'debug');
+ }
+};
+
+
+Faye.Timeouts = {
+ addTimeout: function(name, delay, callback, scope) {
+ this._timeouts = this._timeouts || {};
+ if (this._timeouts.hasOwnProperty(name)) return;
+ var self = this;
+ this._timeouts[name] = setTimeout(function() {
+ delete self._timeouts[name];
+ callback.call(scope);
+ }, 1000 * delay);
+ },
+
+ removeTimeout: function(name) {
+ this._timeouts = this._timeouts || {};
+ var timeout = this._timeouts[name];
+ if (!timeout) return;
+ clearTimeout(timeout);
+ delete this._timeouts[name];
+ }
+};
+
+
Faye.Channel = Faye.Class({
initialize: function(name) {
this.__id = this.name = name;
},
@@ -298,10 +393,14 @@
subtree.each(path, block, context);
});
if (this._value !== undefined) block.call(context, prefix, this._value);
},
+ getKeys: function() {
+ return this.map(function(key, value) { return '/' + key.join('/') });
+ },
+
map: function(block, context) {
var result = [];
this.each([], function(path, value) {
result.push(block.call(context, path, value));
});
@@ -387,32 +486,47 @@
});
Faye.Transport = Faye.extend(Faye.Class({
initialize: function(client, endpoint) {
+ this.debug('Created new transport for ' + endpoint);
this._client = client;
this._endpoint = endpoint;
},
send: function(message, callback, scope) {
if (!(message instanceof Array) && !message.id)
message.id = this._client._namespace.generate();
+ this.debug('Client ' + this._client._clientId +
+ ' sending message to ' + this._endpoint + ': ' +
+ Faye.toJSON(message));
+
this.request(message, function(responses) {
+ this.debug('Client ' + this._client._clientId +
+ ' received from ' + this._endpoint + ': ' +
+ Faye.toJSON(responses));
+
if (!callback) return;
+
+ var messages = [], deliverable = true;
Faye.each([].concat(responses), function(response) {
+
+ if (response.id === message.id) {
+ if (callback.call(scope, response) === false)
+ deliverable = false;
+ }
- if (response.id === message.id)
- callback.call(scope, response);
-
if (response.advice)
this._client.handleAdvice(response.advice);
if (response.data && response.channel)
- this._client.sendToSubscribers(response);
+ messages.push(response);
}, this);
+
+ if (deliverable) this._client.deliverMessages(messages);
}, this);
}
}), {
get: function(client, connectionTypes) {
var endpoint = client._endpoint;
@@ -442,27 +556,36 @@
Faye.each(this._transports, function(key, type) { list.push(key) });
return list;
}
});
+Faye.extend(Faye.Transport.prototype, Faye.Logging);
+
Faye.Client = Faye.Class({
- UNCONNECTED: 1,
- CONNECTING: 2,
- CONNECTED: 3,
- DISCONNECTED: 4,
+ UNCONNECTED: 1,
+ CONNECTING: 2,
+ CONNECTED: 3,
+ DISCONNECTED: 4,
- HANDSHAKE: 'handshake',
- RETRY: 'retry',
- NONE: 'none',
+ HANDSHAKE: 'handshake',
+ RETRY: 'retry',
+ NONE: 'none',
- DEFAULT_ENDPOINT: '/bayeux',
- MAX_DELAY: 0.1,
- INTERVAL: 1000.0,
+ CONNECTION_TIMEOUT: 60.0,
- initialize: function(endpoint) {
+ DEFAULT_ENDPOINT: '/bayeux',
+ MAX_DELAY: 0.1,
+ INTERVAL: 1000.0,
+
+ initialize: function(endpoint, options) {
+ this.info('New client created for ' + endpoint);
+
this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
+ this._options = options || {};
+ this._timeout = this._options.timeout || this.CONNECTION_TIMEOUT;
+
this._transport = Faye.Transport.get(this);
this._state = this.UNCONNECTED;
this._namespace = new Faye.Namespace();
this._outbox = [];
this._channels = new Faye.Channel.Tree();
@@ -498,26 +621,30 @@
if (this._state !== this.UNCONNECTED) return;
this._state = this.CONNECTING;
var self = this;
+ this.info('Initiating handshake with ' + this._endpoint);
+
this._transport.send({
channel: Faye.Channel.HANDSHAKE,
version: Faye.BAYEUX_VERSION,
supportedConnectionTypes: Faye.Transport.supportedConnectionTypes()
}, function(response) {
if (!response.successful) {
+ this.info('Handshake unsuccessful');
setTimeout(function() { self.handshake(callback, scope) }, this._advice.interval);
return this._state = this.UNCONNECTED;
}
this._state = this.CONNECTED;
this._clientId = response.clientId;
this._transport = Faye.Transport.get(this, response.supportedConnectionTypes);
+ this.info('Handshake successful: ' + this._clientId);
if (callback) callback.call(scope);
}, this);
},
// Request Response
@@ -531,36 +658,45 @@
// * timestamp
connect: function(callback, scope) {
if (this._advice.reconnect === this.NONE) return;
if (this._state === this.DISCONNECTED) return;
- if (this._advice.reconnect === this.HANDSHAKE || this._state === this.UNCONNECTED)
+ if (this._advice.reconnect === this.HANDSHAKE || this._state === this.UNCONNECTED) {
+ this._beginReconnectTimeout();
return this.handshake(function() { this.connect(callback, scope) }, this);
+ }
if (this._state === this.CONNECTING)
return this.callback(callback, scope);
if (this._state !== this.CONNECTED) return;
+ this.info('Calling deferred actions for ' + this._clientId);
this.setDeferredStatus('succeeded');
this.setDeferredStatus('deferred');
if (callback) callback.call(scope);
if (this._connectionId) return;
this._connectionId = this._namespace.generate();
var self = this;
+ this.info('Initiating connection for ' + this._clientId);
this._transport.send({
channel: Faye.Channel.CONNECT,
clientId: this._clientId,
connectionType: this._transport.connectionType,
id: this._connectionId
- }, function(response) {
+ }, this._verifyClientId(function(response) {
delete this._connectionId;
+ this.removeTimeout('reconnect');
+
+ this.info('Closed connection for ' + this._clientId);
setTimeout(function() { self.connect() }, this._advice.interval);
- }, this);
+ }));
+
+ this._beginReconnectTimeout();
},
// Request Response
// MUST include: * channel MUST include: * channel
// * clientId * successful
@@ -570,15 +706,18 @@
// * id
disconnect: function() {
if (this._state !== this.CONNECTED) return;
this._state = this.DISCONNECTED;
+ this.info('Disconnecting ' + this._clientId);
+
this._transport.send({
channel: Faye.Channel.DISCONNECT,
clientId: this._clientId
});
+ this.info('Clearing channel listeners for ' + this._clientId);
this._channels = new Faye.Channel.Tree();
},
// Request Response
// MUST include: * channel MUST include: * channel
@@ -594,23 +733,29 @@
this.connect(function() {
channels = [].concat(channels);
this._validateChannels(channels);
+ this.info('Client ' + this._clientId + ' attempting to subscribe to [' +
+ channels.join(', ') + ']');
+
this._transport.send({
channel: Faye.Channel.SUBSCRIBE,
clientId: this._clientId,
subscription: channels
- }, function(response) {
- if (!response.successful) return;
+ }, this._verifyClientId(function(response) {
+ if (!response.successful || !callback) return;
+ this.info('Subscription acknowledged for ' + this._clientId + ' to [' +
+ channels.join(', ') + ']');
+
channels = [].concat(response.subscription);
Faye.each(channels, function(channel) {
this._channels.set(channel, [callback, scope]);
}, this);
- }, this);
+ }));
}, this);
},
// Request Response
@@ -627,23 +772,29 @@
this.connect(function() {
channels = [].concat(channels);
this._validateChannels(channels);
+ this.info('Client ' + this._clientId + ' attempting to unsubscribe from [' +
+ channels.join(', ') + ']');
+
this._transport.send({
channel: Faye.Channel.UNSUBSCRIBE,
clientId: this._clientId,
subscription: channels
- }, function(response) {
+ }, this._verifyClientId(function(response) {
if (!response.successful) return;
+ this.info('Unsubscription acknowledged for ' + this._clientId + ' from [' +
+ channels.join(', ') + ']');
+
channels = [].concat(response.subscription);
Faye.each(channels, function(channel) {
this._channels.set(channel, null);
}, this);
- }, this);
+ }));
}, this);
},
// Request Response
@@ -655,40 +806,55 @@
publish: function(channel, data) {
this.connect(function() {
this._validateChannels([channel]);
+ this.info('Client ' + this._clientId + ' queueing published message to ' +
+ channel + ': ' + Faye.toJSON(data));
+
this._enqueue({
channel: channel,
data: data,
clientId: this._clientId
});
- if (this._timeout) return;
- var self = this;
+ this.addTimeout('publish', this.MAX_DELAY, this._flush, this);
- this._timeout = setTimeout(function() {
- delete self._timeout;
- self._flush();
- }, this.MAX_DELAY * 1000);
-
}, this);
},
handleAdvice: function(advice) {
Faye.extend(this._advice, advice);
if (this._advice.reconnect === this.HANDSHAKE) this._clientId = null;
},
- sendToSubscribers: function(message) {
- var channels = this._channels.glob(message.channel);
- Faye.each(channels, function(callback) {
- if (!callback) return;
- callback[0].call(callback[1], message.data);
- });
+ deliverMessages: function(messages) {
+ Faye.each(messages, function(message) {
+ this.info('Client ' + this._clientId + ' calling listeners for ' +
+ message.channel + ' with ' + Faye.toJSON(message.data));
+
+ var channels = this._channels.glob(message.channel);
+ Faye.each(channels, function(callback) {
+ if (!callback) return;
+ callback[0].call(callback[1], message.data);
+ });
+ }, this);
},
+ _beginReconnectTimeout: function() {
+ this.addTimeout('reconnect', this._timeout, function() {
+ delete this._connectionId;
+ delete this._clientId;
+ this._state = this.UNCONNECTED;
+
+ this.info('Server took >' + this._timeout + 's to reply to connection for ' +
+ this._clientId + ': attempting to reconnect');
+
+ this.subscribe(this._channels.getKeys());
+ }, this);
+ },
+
_enqueue: function(message) {
this._outbox.push(message);
},
_flush: function() {
@@ -701,14 +867,25 @@
if (!Faye.Channel.isValid(channel))
throw '"' + channel + '" is not a valid channel name';
if (!Faye.Channel.isSubscribable(channel))
throw 'Clients may not subscribe to channel "' + channel + '"';
});
+ },
+
+ _verifyClientId: function(callback) {
+ var self = this;
+ return function(response) {
+ if (response.clientId !== self._clientId) return false;
+ callback.call(self, response);
+ return true;
+ };
}
});
Faye.extend(Faye.Client.prototype, Faye.Deferrable);
+Faye.extend(Faye.Client.prototype, Faye.Timeouts);
+Faye.extend(Faye.Client.prototype, Faye.Logging);
Faye.Set = Faye.Class({
initialize: function() {
this._index = {};
@@ -755,10 +932,11 @@
});
Faye.Server = Faye.Class({
initialize: function(options) {
+ this.info('New server created');
this._options = options || {};
this._channels = new Faye.Channel.Tree();
this._clients = {};
this._namespace = new Faye.Namespace();
},
@@ -768,19 +946,22 @@
Faye.each(this._clients, function(key, value) { ids.push(key) });
return ids;
},
process: function(messages, local, callback) {
+ this.debug('Processing messages from ' + (local ? 'LOCAL' : 'REMOTE') + ' client');
+
messages = [].concat(messages);
var processed = 0, responses = [];
Faye.each(messages, function(message) {
this._handle(message, local, function(reply) {
responses = responses.concat(reply);
processed += 1;
- if (processed === messages.length) callback(responses);
- });
+ if (processed < messages.length) return;
+ callback(responses);
+ }, this);
}, this);
},
flushConnection: function(messages) {
messages = [].concat(messages);
@@ -801,57 +982,68 @@
client.disconnect();
client.stopObserving('staleClient', this._destroyClient, this);
delete this._clients[client.id];
},
- _handle: function(message, local, callback) {
- var clientId = message.clientId,
- channel = message.channel,
+ _handle: function(message, local, callback, scope) {
+ var channel = message.channel,
response;
message.__id = Faye.random();
- Faye.each(this._channels.glob(channel), function(c) { c.push(message) });
+ Faye.each(this._channels.glob(channel), function(c) {
+ c.push(message);
+ this.info('Publishing message ' + Faye.toJSON(message.data) +
+ ' from client ' + clientId + ' to ' + c.name);
+ }, this);
if (Faye.Channel.isMeta(channel)) {
response = this[Faye.Channel.parse(channel)[1]](message, local);
- clientId = clientId || response.clientId;
+ var clientId = response.clientId;
response.advice = response.advice || {};
Faye.extend(response.advice, {
reconnect: this._clients.hasOwnProperty(clientId) ? 'retry' : 'handshake',
- interval: Faye.Connection.INTERVAL * 1000
+ interval: Math.floor(Faye.Connection.prototype.INTERVAL * 1000)
}, false);
- response.id = message.id;
-
if (response.channel !== Faye.Channel.CONNECT ||
response.successful !== true)
- return callback(response);
+ return callback.call(scope, response);
+ this.info('Accepting connection from ' + response.clientId);
return this._connection(response.clientId).connect(function(events) {
+ this.info('Sending event messages to ' + response.clientId);
+ this.debug('Events for ' + response.clientId + ': ' + Faye.toJSON(events));
Faye.each(events, function(e) { delete e.__id });
- callback([response].concat(events));
- });
+ callback.call(scope, [response].concat(events));
+ }, this);
}
if (!message.clientId || Faye.Channel.isService(channel))
return callback([]);
- callback( { channel: channel,
- successful: true,
- id: message.id } );
+ response = this._makeResponse(message);
+ response.successful = true;
+ callback(response);
},
+ _makeResponse: function(message) {
+ var response = {};
+ Faye.each(['id', 'clientId', 'channel'], function(field) {
+ if (message[field]) response[field] = message[field];
+ });
+ return response;
+ },
+
// MUST contain * version
// * supportedConnectionTypes
// MAY contain * minimumVersion
// * ext
// * id
handshake: function(message, local) {
- var response = { channel: Faye.Channel.HANDSHAKE,
- version: Faye.BAYEUX_VERSION,
- id: message.id };
+ var response = this._makeResponse(message);
+ response.version = Faye.BAYEUX_VERSION;
if (!message.version)
response.error = Faye.Error.parameterMissing('version');
var clientConns = message.supportedConnectionTypes,
@@ -874,66 +1066,66 @@
response.successful = !response.error;
if (!response.successful) return response;
var clientId = this._namespace.generate();
response.clientId = this._connection(clientId).id;
+ this.info('Accepting handshake from client ' + response.clientId);
return response;
},
// MUST contain * clientId
// * connectionType
// MAY contain * ext
// * id
connect: function(message, local) {
- var response = { channel: Faye.Channel.CONNECT,
- id: message.id };
+ var response = this._makeResponse(message);
var clientId = message.clientId,
client = clientId ? this._clients[clientId] : null,
connectionType = message.connectionType;
if (!client) response.error = Faye.Error.clientUnknown(clientId);
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
if (!connectionType) response.error = Faye.Error.parameterMissing('connectionType');
response.successful = !response.error;
+ if (!response.successful) delete response.clientId;
if (!response.successful) return response;
response.clientId = client.id;
return response;
},
// MUST contain * clientId
// MAY contain * ext
// * id
disconnect: function(message, local) {
- var response = { channel: Faye.Channel.DISCONNECT,
- id: message.id };
+ var response = this._makeResponse(message);
var clientId = message.clientId,
client = clientId ? this._clients[clientId] : null;
if (!client) response.error = Faye.Error.clientUnknown(clientId);
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
response.successful = !response.error;
+ if (!response.successful) delete response.clientId;
if (!response.successful) return response;
this._destroyClient(client);
+ this.info('Disconnected client: ' + clientId);
response.clientId = clientId;
return response;
},
// MUST contain * clientId
// * subscription
// MAY contain * ext
// * id
subscribe: function(message, local) {
- var response = { channel: Faye.Channel.SUBSCRIBE,
- clientId: message.clientId,
- id: message.id };
+ var response = this._makeResponse(message);
var clientId = message.clientId,
client = clientId ? this._clients[clientId] : null,
subscription = message.subscription;
@@ -950,10 +1142,12 @@
if (!local && !Faye.Channel.isSubscribable(channel)) response.error = Faye.Error.channelForbidden(channel);
if (!Faye.Channel.isValid(channel)) response.error = Faye.Error.channelInvalid(channel);
if (response.error) return;
channel = this._channels.findOrCreate(channel);
+
+ this.info('Subscribing client ' + clientId + ' to ' + channel.name);
client.subscribe(channel);
}, this);
response.successful = !response.error;
return response;
@@ -962,13 +1156,11 @@
// MUST contain * clientId
// * subscription
// MAY contain * ext
// * id
unsubscribe: function(message, local) {
- var response = { channel: Faye.Channel.UNSUBSCRIBE,
- clientId: message.clientId,
- id: message.id };
+ var response = this._makeResponse(message);
var clientId = message.clientId,
client = clientId ? this._clients[clientId] : null,
subscription = message.subscription;
@@ -983,35 +1175,38 @@
if (!Faye.Channel.isValid(channel))
return response.error = Faye.Error.channelInvalid(channel);
channel = this._channels.get(channel);
- if (channel) client.unsubscribe(channel);
+ if (!channel) return;
+
+ this.info('Unsubscribing client ' + clientId + ' from ' + channel.name);
+ client.unsubscribe(channel);
}, this);
response.successful = !response.error;
return response;
}
});
+Faye.extend(Faye.Server.prototype, Faye.Logging);
+
Faye.Connection = Faye.Class({
MAX_DELAY: 0.1,
INTERVAL: 1.0,
TIMEOUT: 60.0,
initialize: function(id, options) {
this.id = id;
this._options = options;
+ this._timeout = this._options.timeout || this.TIMEOUT;
this._channels = new Faye.Set();
this._inbox = new Faye.Set();
+ this._connected = false
},
- getTimeout: function() {
- return this._options.timeout || this.TIMEOUT;
- },
-
_onMessage: function(event) {
this._inbox.add(event);
this._beginDeliveryTimeout();
},
@@ -1025,21 +1220,17 @@
if (!this._channels.member(channel)) return;
this._channels.remove(channel);
channel.stopObserving('message', this._onMessage, this);
},
- connect: function(callback) {
- this.callback(callback);
+ connect: function(callback, scope) {
+ this.callback(callback, scope);
if (this._connected) return;
this._connected = true;
+ this.removeTimeout('deletion');
- if (this._deletionTimeout) {
- clearTimeout(this._deletionTimeout);
- delete this._deletionTimeout;
- }
-
this._beginDeliveryTimeout();
this._beginConnectionTimeout();
},
flush: function() {
@@ -1057,54 +1248,33 @@
this.unsubscribe('all');
this.flush();
},
_beginDeliveryTimeout: function() {
- if (this._deliveryTimeout || !this._connected || this._inbox.isEmpty())
- return;
-
- var self = this;
- this._deliveryTimeout = setTimeout(function () { self.flush() },
- this.MAX_DELAY * 1000);
+ if (!this._connected || this._inbox.isEmpty()) return;
+ this.addTimeout('delivery', this.MAX_DELAY, this.flush, this);
},
_beginConnectionTimeout: function() {
- if (this._connectionTimeout || !this._connected)
- return;
-
- var self = this;
- this._connectionTimeout = setTimeout(function() { self.flush() },
- this.getTimeout() * 1000);
+ if (!this._connected) return;
+ this.addTimeout('connection', this._timeout, this.flush, this);
},
_releaseConnection: function() {
- if (this._connectionTimeout) {
- clearTimeout(this._connectionTimeout);
- delete this._connectionTimeout;
- }
-
- if (this._deliveryTimeout) {
- clearTimeout(this._deliveryTimeout);
- delete this._deliveryTimeout;
- }
-
+ this.removeTimeout('connection');
+ this.removeTimeout('delivery');
this._connected = false;
- this._scheduleForDeletion();
- },
-
- _scheduleForDeletion: function() {
- if (this._deletionTimeout) return;
- var self = this;
- this._deletionTimeout = setTimeout(function() {
- self.fire('staleClient', self);
- }, 10 * 1000 * this.INTERVAL);
+ this.addTimeout('deletion', 10 * this.INTERVAL, function() {
+ this.fire('staleClient', this);
+ }, this);
}
});
Faye.extend(Faye.Connection.prototype, Faye.Deferrable);
Faye.extend(Faye.Connection.prototype, Faye.Observable);
+Faye.extend(Faye.Connection.prototype, Faye.Timeouts);
Faye.Error = Faye.Class({
initialize: function(code, args, message) {
this.code = code;
@@ -1168,77 +1338,42 @@
return new this(500, arguments, "Internal server error").toString();
};
-Faye.NodeHttpTransport = Faye.Class(Faye.Transport, {
- request: function(message, callback, scope) {
- var params = {message: JSON.stringify(message)},
- request = this.createRequest();
-
- request.write(querystring.stringify(params));
-
- request.addListener('response', function(response) {
- if (!callback) return;
- response.addListener('data', function(chunk) {
- callback.call(scope, JSON.parse(chunk));
- });
- });
- request.close();
- },
-
- createRequest: function() {
- var uri = url.parse(this._endpoint),
- client = http.createClient(uri.port, uri.hostname);
-
- return client.request('POST', uri.pathname, {
- 'Content-Type': 'application/x-www-form-urlencoded'
- });
- }
-});
-
-Faye.NodeHttpTransport.isUsable = function(endpoint) {
- return typeof endpoint === 'string';
-};
-
-Faye.Transport.register('long-polling', Faye.NodeHttpTransport);
-
-Faye.NodeLocalTransport = Faye.Class(Faye.Transport, {
- request: function(message, callback, scope) {
- this._endpoint.process(message, true, function(response) {
- callback.call(scope, response);
- });
- }
-});
-
-Faye.NodeLocalTransport.isUsable = function(endpoint) {
- return endpoint instanceof Faye.Server;
-};
-
-Faye.Transport.register('in-process', Faye.NodeLocalTransport);
-
-
var path = require('path'),
fs = require('fs'),
sys = require('sys'),
url = require('url'),
http = require('http'),
querystring = require('querystring');
+Faye.logger = function(message) {
+ sys.puts(message);
+};
+
+Faye.withDataFor = function(transport, callback, scope) {
+ var data = '';
+ transport.addListener('data', function(chunk) { data += chunk });
+ transport.addListener('end', function() {
+ callback.call(scope, data);
+ });
+};
+
Faye.NodeAdapter = Faye.Class({
DEFAULT_ENDPOINT: '/bayeux',
SCRIPT_PATH: path.dirname(__filename) + '/faye-client-min.js',
- TYPE_JSON: {'Content-Type': 'text/json'},
+ TYPE_JSON: {'Content-Type': 'application/json'},
TYPE_SCRIPT: {'Content-Type': 'text/javascript'},
TYPE_TEXT: {'Content-Type': 'text/plain'},
initialize: function(options) {
- this._options = options || {};
- this._endpoint = this._options.mount || this.DEFAULT_ENDPOINT;
- this._script = this._endpoint + '.js';
- this._server = new Faye.Server(this._options);
+ this._options = options || {};
+ this._endpoint = this._options.mount || this.DEFAULT_ENDPOINT;
+ this._endpointRe = new RegExp('^' + this._endpoint + '(/[^/]+)*(\\.js)?$');
+ this._server = new Faye.Server(this._options);
},
getClient: function() {
return this._client = this._client || new Faye.Client(this._server);
},
@@ -1250,39 +1385,34 @@
}).listen(Number(port));
},
call: function(request, response) {
var requestUrl = url.parse(request.url, true),
- self = this;
+ self = this, data;
- switch (requestUrl.pathname) {
+ if (!this._endpointRe.test(requestUrl.pathname))
+ return false;
+
+ if (/\.js$/.test(requestUrl.pathname)) {
+ fs.readFile(this.SCRIPT_PATH, function(err, content) {
+ response.sendHeader(200, self.TYPE_SCRIPT);
+ response.write(content);
+ response.close();
+ });
- case this._endpoint:
- var isGet = (request.method === 'GET');
-
- if (isGet)
- this._callWithParams(request, response, requestUrl.query);
-
- else
- request.addListener('data', function(chunk) {
- self._callWithParams(request, response, querystring.parse(chunk));
- });
-
- return true;
- break;
+ } else {
+ var isGet = (request.method === 'GET');
- case this._script:
- fs.readFile(this.SCRIPT_PATH, function(err, content) {
- response.sendHeader(200, self.TYPE_SCRIPT);
- response.write(content);
- response.close();
- });
- return true;
- break;
+ if (isGet)
+ this._callWithParams(request, response, requestUrl.query);
- default: return false;
+ else
+ Faye.withDataFor(request, function(data) {
+ self._callWithParams(request, response, {message: data});
+ });
}
+ return true;
},
_callWithParams: function(request, response, params) {
try {
var message = JSON.parse(params.message),
@@ -1306,6 +1436,57 @@
}
}
});
exports.NodeAdapter = Faye.NodeAdapter;
-exports.Client = Faye.Client;
+exports.Client = Faye.Client;
+
+
+Faye.NodeHttpTransport = Faye.Class(Faye.Transport, {
+ request: function(message, callback, scope) {
+ var request = this.createRequestForMessage(message);
+
+ request.addListener('response', function(response) {
+ if (!callback) return;
+ Faye.withDataFor(response, function(data) {
+ callback.call(scope, JSON.parse(data));
+ });
+ });
+ request.close();
+ },
+
+ createRequestForMessage: function(message) {
+ var content = JSON.stringify(message),
+ uri = url.parse(this._endpoint),
+ client = http.createClient(uri.port, uri.hostname);
+
+ if (parseInt(uri.port) === 443) client.setSecure('X509_PEM');
+
+ var request = client.request('POST', uri.pathname, {
+ 'Content-Type': 'application/json',
+ 'host': uri.hostname,
+ 'Content-Length': content.length
+ });
+ request.write(content);
+ return request;
+ }
+});
+
+Faye.NodeHttpTransport.isUsable = function(endpoint) {
+ return typeof endpoint === 'string';
+};
+
+Faye.Transport.register('long-polling', Faye.NodeHttpTransport);
+
+Faye.NodeLocalTransport = Faye.Class(Faye.Transport, {
+ request: function(message, callback, scope) {
+ this._endpoint.process(message, true, function(response) {
+ callback.call(scope, response);
+ });
+ }
+});
+
+Faye.NodeLocalTransport.isUsable = function(endpoint) {
+ return endpoint instanceof Faye.Server;
+};
+
+Faye.Transport.register('in-process', Faye.NodeLocalTransport);
\ No newline at end of file