faye 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +1 -0
- data/Jakefile +2 -0
- data/Manifest.txt +33 -0
- data/README.txt +111 -0
- data/Rakefile +13 -0
- data/client/channel.js +135 -0
- data/client/client.js +265 -0
- data/client/faye.js +95 -0
- data/client/transport.js +91 -0
- data/client/util/class.js +20 -0
- data/client/util/event.js +44 -0
- data/client/util/json.js +479 -0
- data/client/util/uri.js +69 -0
- data/client/util/xhr.js +99 -0
- data/examples/soapbox/README +22 -0
- data/examples/soapbox/app.rb +8 -0
- data/examples/soapbox/config.ru +8 -0
- data/examples/soapbox/public/jquery.js +19 -0
- data/examples/soapbox/public/soapbox.js +100 -0
- data/examples/soapbox/public/style.css +43 -0
- data/examples/soapbox/views/index.erb +50 -0
- data/jake.yml +19 -0
- data/lib/faye-min.js +1 -0
- data/lib/faye.rb +30 -0
- data/lib/faye/channel.rb +122 -0
- data/lib/faye/connection.rb +110 -0
- data/lib/faye/error.rb +44 -0
- data/lib/faye/grammar.rb +58 -0
- data/lib/faye/rack_adapter.rb +53 -0
- data/lib/faye/server.rb +231 -0
- data/test/test_channel.rb +34 -0
- data/test/test_grammar.rb +86 -0
- data/test/test_server.rb +420 -0
- metadata +130 -0
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/Jakefile
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
jake.yml
|
6
|
+
Jakefile
|
7
|
+
client/util/class.js
|
8
|
+
client/util/event.js
|
9
|
+
client/util/json.js
|
10
|
+
client/util/uri.js
|
11
|
+
client/util/xhr.js
|
12
|
+
client/faye.js
|
13
|
+
client/transport.js
|
14
|
+
client/client.js
|
15
|
+
client/channel.js
|
16
|
+
lib/faye-min.js
|
17
|
+
lib/faye.rb
|
18
|
+
lib/faye/channel.rb
|
19
|
+
lib/faye/connection.rb
|
20
|
+
lib/faye/error.rb
|
21
|
+
lib/faye/grammar.rb
|
22
|
+
lib/faye/rack_adapter.rb
|
23
|
+
lib/faye/server.rb
|
24
|
+
examples/soapbox/README
|
25
|
+
examples/soapbox/app.rb
|
26
|
+
examples/soapbox/config.ru
|
27
|
+
examples/soapbox/public/jquery.js
|
28
|
+
examples/soapbox/public/soapbox.js
|
29
|
+
examples/soapbox/public/style.css
|
30
|
+
examples/soapbox/views/index.erb
|
31
|
+
test/test_channel.rb
|
32
|
+
test/test_grammar.rb
|
33
|
+
test/test_server.rb
|
data/README.txt
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
= Faye
|
2
|
+
|
3
|
+
* http://github.com/jcoglan/faye
|
4
|
+
|
5
|
+
Faye is an implementation of the Bayeux protocol in Ruby. It includes a
|
6
|
+
server backend and an adapter for Rack, and a self-contained JavaScript
|
7
|
+
client. Bayeux is a protocol designed to facilitate Comet, also known as
|
8
|
+
'server push' or 'Ajax push', a technique for pushing messages from the
|
9
|
+
server to the client to minimize latency.
|
10
|
+
|
11
|
+
NOTE this is not currently intended for large-scale use. It is being
|
12
|
+
developed mostly as a tool for scripting multiple browsers and automating
|
13
|
+
JavaScript testing. However, use of a well-known protocol should allow
|
14
|
+
it to be repurposed fairly easily.
|
15
|
+
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
Faye can be installed as middleware in front of any Rack application. For
|
20
|
+
example, here's a <tt>config.ru</tt> for running it with Sinatra:
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'faye'
|
24
|
+
require 'sinatra'
|
25
|
+
require 'path/to/sinatra/app'
|
26
|
+
|
27
|
+
use Faye::RackAdapter, :mount => '/comet',
|
28
|
+
:timeout => 30
|
29
|
+
|
30
|
+
run Sinatra::Application
|
31
|
+
|
32
|
+
This starts a Comet endpoint at <tt>http://localhost:9292/comet</tt> with
|
33
|
+
the client script at <tt>http://localhost:9292/comet.js</tt>. The +timeout+
|
34
|
+
option sets how long (in seconds) a long-polling request will wait before
|
35
|
+
timing out; this must be less than the timeout set on your frontend web server
|
36
|
+
so that the Comet server can send a response before Apache (for example)
|
37
|
+
closes the connection.
|
38
|
+
|
39
|
+
In your front-end code, set up the client as follows:
|
40
|
+
|
41
|
+
<script type="text/javascript" src="/comet.js"></script>
|
42
|
+
|
43
|
+
<script type="text/javascript">
|
44
|
+
CometClient = new Faye.Client('/comet');
|
45
|
+
CometClient.connect();
|
46
|
+
</script>
|
47
|
+
|
48
|
+
This client object can then be used to publish and subscribe to named
|
49
|
+
channels as follows:
|
50
|
+
|
51
|
+
CometClient.subscribe('/path/to/channel', function(message) {
|
52
|
+
// process received message
|
53
|
+
});
|
54
|
+
|
55
|
+
CometClient.publish('/some/other/channel', {foo: 'bar'});
|
56
|
+
|
57
|
+
You can publish arbitrary JavaScript objects to a channel, and the object will
|
58
|
+
be transmitted as the +message+ parameter to any subscribers to that channel.
|
59
|
+
Channel names must be formatted as absolute path names as shown. Channels
|
60
|
+
beginning with <tt>/meta/</tt> are reserved for use by the messaging protocol
|
61
|
+
and may not be subscribed to.
|
62
|
+
|
63
|
+
|
64
|
+
== Examples
|
65
|
+
|
66
|
+
See demo apps in +examples+. If you've checked this out from the repo rather than
|
67
|
+
installing the gem you will need to build the JavaScript client from source, this
|
68
|
+
is done as follows:
|
69
|
+
|
70
|
+
sudo gem install jake
|
71
|
+
jake -f
|
72
|
+
|
73
|
+
|
74
|
+
== To-do
|
75
|
+
|
76
|
+
* Provide support for user-defined <tt>/service/*</tt> channels
|
77
|
+
* Support Thin's async response
|
78
|
+
* Allow server to scale to multiple nodes
|
79
|
+
* Provide a server-side client
|
80
|
+
* Console for scripting browsers from the command line
|
81
|
+
|
82
|
+
|
83
|
+
== Installation
|
84
|
+
|
85
|
+
sudo gem install hoe faye
|
86
|
+
|
87
|
+
|
88
|
+
== License
|
89
|
+
|
90
|
+
(The MIT License)
|
91
|
+
|
92
|
+
Copyright (c) 2009 James Coglan
|
93
|
+
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
95
|
+
a copy of this software and associated documentation files (the
|
96
|
+
'Software'), to deal in the Software without restriction, including
|
97
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
98
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
99
|
+
permit persons to whom the Software is furnished to do so, subject to
|
100
|
+
the following conditions:
|
101
|
+
|
102
|
+
The above copyright notice and this permission notice shall be
|
103
|
+
included in all copies or substantial portions of the Software.
|
104
|
+
|
105
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
106
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
107
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
108
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
109
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
110
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
111
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/faye.rb'
|
6
|
+
|
7
|
+
Hoe.spec('faye') do |p|
|
8
|
+
# p.rubyforge_name = 'fayex' # if different than lowercase project name
|
9
|
+
p.developer('James Coglan', 'jcoglan@googlemail.com')
|
10
|
+
p.extra_deps = %w[eventmachine rack json]
|
11
|
+
end
|
12
|
+
|
13
|
+
# vim: syntax=Ruby
|
data/client/channel.js
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
Faye.Channel = {
|
2
|
+
HANDSHAKE: '<%= Faye::Channel::HANDSHAKE %>',
|
3
|
+
CONNECT: '<%= Faye::Channel::CONNECT %>',
|
4
|
+
SUBSCRIBE: '<%= Faye::Channel::SUBSCRIBE %>',
|
5
|
+
UNSUBSCRIBE: '<%= Faye::Channel::UNSUBSCRIBE %>',
|
6
|
+
DISCONNECT: '<%= Faye::Channel::DISCONNECT %>',
|
7
|
+
|
8
|
+
META: '<%= Faye::Channel::META %>',
|
9
|
+
SERVICE: '<%= Faye::Channel::SERVICE %>',
|
10
|
+
|
11
|
+
isValid: function(name) {
|
12
|
+
return Faye.Grammar.CHANNEL_NAME.test(name) ||
|
13
|
+
Faye.Grammar.CHANNEL_PATTERN.test(name);
|
14
|
+
},
|
15
|
+
|
16
|
+
parse: function(name) {
|
17
|
+
if (!this.isValid(name)) return null;
|
18
|
+
return name.split('/').slice(1);
|
19
|
+
},
|
20
|
+
|
21
|
+
isMeta: function(name) {
|
22
|
+
var segments = this.parse(name);
|
23
|
+
return segments ? (segments[0] === this.META) : null;
|
24
|
+
},
|
25
|
+
|
26
|
+
isService: function(name) {
|
27
|
+
var segments = this.parse(name);
|
28
|
+
return segments ? (segments[0] === this.SERVICE) : null;
|
29
|
+
},
|
30
|
+
|
31
|
+
isSubscribable: function(name) {
|
32
|
+
if (!this.isValid(name)) return null;
|
33
|
+
return !this.isMeta(name) && !this.isService(name);
|
34
|
+
},
|
35
|
+
|
36
|
+
Tree: Faye.Class({
|
37
|
+
initialize: function(value) {
|
38
|
+
this._value = value;
|
39
|
+
this._children = {};
|
40
|
+
},
|
41
|
+
|
42
|
+
eachChild: function(block, context) {
|
43
|
+
Faye.each(this._children, function(key, subtree) {
|
44
|
+
block.call(context, key, subtree);
|
45
|
+
});
|
46
|
+
},
|
47
|
+
|
48
|
+
each: function(prefix, block, context) {
|
49
|
+
this.eachChild(function(path, subtree) {
|
50
|
+
path = prefix.concat(path);
|
51
|
+
subtree.each(path, block, context);
|
52
|
+
});
|
53
|
+
if (this._value !== undefined) block.call(context, prefix, this._value);
|
54
|
+
},
|
55
|
+
|
56
|
+
map: function(block, context) {
|
57
|
+
var result = [];
|
58
|
+
this.each([], function(path, value) {
|
59
|
+
result.push(block.call(context, path, value));
|
60
|
+
});
|
61
|
+
return result;
|
62
|
+
},
|
63
|
+
|
64
|
+
get: function(name) {
|
65
|
+
var tree = this.traverse(name);
|
66
|
+
return tree ? tree._value : null;
|
67
|
+
},
|
68
|
+
|
69
|
+
set: function(name, value) {
|
70
|
+
var subtree = this.traverse(name, true);
|
71
|
+
if (subtree) subtree._value = value;
|
72
|
+
},
|
73
|
+
|
74
|
+
traverse: function(path, createIfAbsent) {
|
75
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
76
|
+
|
77
|
+
if (path === null) return null;
|
78
|
+
if (path.length === 0) return this;
|
79
|
+
|
80
|
+
var subtree = this._children[path[0]];
|
81
|
+
if (!subtree && !createIfAbsent) return null;
|
82
|
+
if (!subtree) subtree = this._children[path[0]] = new Faye.Channel.Tree();
|
83
|
+
|
84
|
+
return subtree.traverse(path.slice(1), createIfAbsent);
|
85
|
+
},
|
86
|
+
|
87
|
+
glob: function(path) {
|
88
|
+
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
89
|
+
|
90
|
+
if (path === null) return [];
|
91
|
+
if (path.length === 0) return (this._value === undefined) ? [] : [this._value];
|
92
|
+
|
93
|
+
var list = [];
|
94
|
+
|
95
|
+
if (Faye.enumEqual(path, ['*'])) {
|
96
|
+
Faye.each(this._children, function(key, subtree) {
|
97
|
+
if (subtree._value !== undefined) list.push(subtree._value);
|
98
|
+
});
|
99
|
+
return list;
|
100
|
+
}
|
101
|
+
|
102
|
+
if (Faye.enumEqual(path, ['**'])) {
|
103
|
+
list = this.map(function(key, value) { return value });
|
104
|
+
list.pop();
|
105
|
+
return list;
|
106
|
+
}
|
107
|
+
|
108
|
+
Faye.each(this._children, function(key, subtree) {
|
109
|
+
if (key !== path[0] && key !== '*') return;
|
110
|
+
var sublist = subtree.glob(path.slice(1));
|
111
|
+
Faye.each(sublist, function(channel) { list.push(channel) });
|
112
|
+
});
|
113
|
+
|
114
|
+
if (this._children['**']) list.push(this._children['**']._value);
|
115
|
+
return list;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
Tests
|
120
|
+
|
121
|
+
glob = new Faye.Channel.Tree();
|
122
|
+
list = '/foo/bar /foo/boo /foo /foobar /foo/bar/boo /foobar/boo /foo/* /foo/**'.split(' ');
|
123
|
+
|
124
|
+
Faye.each(list, function(c, i) {
|
125
|
+
glob.set(c, i + 1);
|
126
|
+
});
|
127
|
+
|
128
|
+
console.log(glob.glob('/foo/*').sort()); // 1,2,7,8
|
129
|
+
console.log(glob.glob('/foo/bar').sort()); // 1,7,8
|
130
|
+
console.log(glob.glob('/foo/**').sort()); // 1,2,5,7,8
|
131
|
+
console.log(glob.glob('/foo/bar/boo').sort()); // 5,8
|
132
|
+
**/
|
133
|
+
})
|
134
|
+
};
|
135
|
+
|
data/client/client.js
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
Faye.Client = Faye.Class({
|
2
|
+
_UNCONNECTED: {},
|
3
|
+
_CONNECTING: {},
|
4
|
+
_CONNECTED: {},
|
5
|
+
_DISCONNECTED: {},
|
6
|
+
|
7
|
+
_HANDSHAKE: 'handshake',
|
8
|
+
_RETRY: 'retry',
|
9
|
+
_NONE: 'none',
|
10
|
+
|
11
|
+
DEFAULT_ENDPOINT: '<%= Faye::RackAdapter::DEFAULT_ENDPOINT %>',
|
12
|
+
MAX_DELAY: <%= Faye::Connection::MAX_DELAY %>,
|
13
|
+
INTERVAL: <%= Faye::Connection::INTERVAL * 1000 %>,
|
14
|
+
|
15
|
+
initialize: function(endpoint) {
|
16
|
+
this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
|
17
|
+
this._transport = Faye.Transport.get(this);
|
18
|
+
this._state = this._UNCONNECTED;
|
19
|
+
this._outbox = [];
|
20
|
+
this._channels = new Faye.Channel.Tree();
|
21
|
+
|
22
|
+
this._advice = {reconnect: this._RETRY, interval: this.INTERVAL};
|
23
|
+
|
24
|
+
Faye.Event.on(Faye.ENV, 'beforeunload', this.disconnect, this);
|
25
|
+
},
|
26
|
+
|
27
|
+
// Request
|
28
|
+
// MUST include: * channel
|
29
|
+
// * version
|
30
|
+
// * supportedConnectionTypes
|
31
|
+
// MAY include: * minimumVersion
|
32
|
+
// * ext
|
33
|
+
// * id
|
34
|
+
//
|
35
|
+
// Success Response Failed Response
|
36
|
+
// MUST include: * channel MUST include: * channel
|
37
|
+
// * version * successful
|
38
|
+
// * supportedConnectionTypes * error
|
39
|
+
// * clientId MAY include: * supportedConnectionTypes
|
40
|
+
// * successful * advice
|
41
|
+
// MAY include: * minimumVersion * version
|
42
|
+
// * advice * minimumVersion
|
43
|
+
// * ext * ext
|
44
|
+
// * id * id
|
45
|
+
// * authSuccessful
|
46
|
+
handshake: function(callback, scope) {
|
47
|
+
if (this._advice.reconnect === this._NONE) return;
|
48
|
+
if (this._state !== this._UNCONNECTED) return;
|
49
|
+
|
50
|
+
this._state = this._CONNECTING;
|
51
|
+
var self = this, id = this.generateId();
|
52
|
+
|
53
|
+
this._transport.send({
|
54
|
+
channel: Faye.Channel.HANDSHAKE,
|
55
|
+
version: Faye.BAYEUX_VERSION,
|
56
|
+
supportedConnectionTypes: Faye.Transport.supportedConnectionTypes(),
|
57
|
+
id: id
|
58
|
+
|
59
|
+
}, function(response) {
|
60
|
+
if (response.id !== id) return;
|
61
|
+
|
62
|
+
if (!response.successful) {
|
63
|
+
setTimeout(function() { self.handshake(callback, scope) }, this._advice.interval);
|
64
|
+
return this._state = this._UNCONNECTED;
|
65
|
+
}
|
66
|
+
|
67
|
+
this._state = this._CONNECTED;
|
68
|
+
this._clientId = response.clientId;
|
69
|
+
this._transport = Faye.Transport.get(this, response.supportedConnectionTypes);
|
70
|
+
|
71
|
+
if (callback) callback.call(scope);
|
72
|
+
}, this);
|
73
|
+
},
|
74
|
+
|
75
|
+
// Request Response
|
76
|
+
// MUST include: * channel MUST include: * channel
|
77
|
+
// * clientId * successful
|
78
|
+
// * connectionType * clientId
|
79
|
+
// MAY include: * ext MAY include: * error
|
80
|
+
// * id * advice
|
81
|
+
// * ext
|
82
|
+
// * id
|
83
|
+
// * timestamp
|
84
|
+
connect: function(callback, scope) {
|
85
|
+
if (this._advice.reconnect === this._NONE) return;
|
86
|
+
|
87
|
+
if (this._advice.reconnect === this._HANDSHAKE || this._state === this._UNCONNECTED)
|
88
|
+
return this.handshake(function() { this.connect(callback, scope) }, this);
|
89
|
+
|
90
|
+
if (this._state !== this._CONNECTED) return;
|
91
|
+
|
92
|
+
if (this._connectionId) return;
|
93
|
+
this._connectionId = this.generateId();
|
94
|
+
var self = this;
|
95
|
+
|
96
|
+
this._transport.send({
|
97
|
+
channel: Faye.Channel.CONNECT,
|
98
|
+
clientId: this._clientId,
|
99
|
+
connectionType: this._transport.connectionType,
|
100
|
+
id: this._connectionId
|
101
|
+
|
102
|
+
}, function(response) {
|
103
|
+
if (response.id !== this._connectionId) return;
|
104
|
+
delete this._connectionId;
|
105
|
+
|
106
|
+
if (response.successful)
|
107
|
+
this.connect();
|
108
|
+
else
|
109
|
+
setTimeout(function() { self.connect() }, this._advice.interval);
|
110
|
+
}, this);
|
111
|
+
|
112
|
+
if (callback) callback.call(scope);
|
113
|
+
},
|
114
|
+
|
115
|
+
// Request Response
|
116
|
+
// MUST include: * channel MUST include: * channel
|
117
|
+
// * clientId * successful
|
118
|
+
// MAY include: * ext * clientId
|
119
|
+
// * id MAY include: * error
|
120
|
+
// * ext
|
121
|
+
// * id
|
122
|
+
disconnect: function() {
|
123
|
+
if (this._state !== this._CONNECTED) return;
|
124
|
+
this._state = this._DISCONNECTED;
|
125
|
+
|
126
|
+
this._transport.send({
|
127
|
+
channel: Faye.Channel.DISCONNECT,
|
128
|
+
clientId: this._clientId
|
129
|
+
});
|
130
|
+
|
131
|
+
this._channels = new Faye.Channel.Tree();
|
132
|
+
},
|
133
|
+
|
134
|
+
// Request Response
|
135
|
+
// MUST include: * channel MUST include: * channel
|
136
|
+
// * clientId * successful
|
137
|
+
// * subscription * clientId
|
138
|
+
// MAY include: * ext * subscription
|
139
|
+
// * id MAY include: * error
|
140
|
+
// * advice
|
141
|
+
// * ext
|
142
|
+
// * id
|
143
|
+
// * timestamp
|
144
|
+
subscribe: function(channels, callback, scope) {
|
145
|
+
if (this._state !== this._CONNECTED) return;
|
146
|
+
|
147
|
+
channels = [].concat(channels);
|
148
|
+
this._validateChannels(channels);
|
149
|
+
|
150
|
+
var id = this.generateId();
|
151
|
+
|
152
|
+
this._transport.send({
|
153
|
+
channel: Faye.Channel.SUBSCRIBE,
|
154
|
+
clientId: this._clientId,
|
155
|
+
subscription: channels,
|
156
|
+
id: id
|
157
|
+
|
158
|
+
}, function(response) {
|
159
|
+
if (response.id !== id) return;
|
160
|
+
if (!response.successful) return;
|
161
|
+
|
162
|
+
channels = [].concat(response.subscription);
|
163
|
+
Faye.each(channels, function(channel) {
|
164
|
+
this._channels.set(channel, [callback, scope]);
|
165
|
+
}, this);
|
166
|
+
}, this);
|
167
|
+
},
|
168
|
+
|
169
|
+
// Request Response
|
170
|
+
// MUST include: * channel MUST include: * channel
|
171
|
+
// * clientId * successful
|
172
|
+
// * subscription * clientId
|
173
|
+
// MAY include: * ext * subscription
|
174
|
+
// * id MAY include: * error
|
175
|
+
// * advice
|
176
|
+
// * ext
|
177
|
+
// * id
|
178
|
+
// * timestamp
|
179
|
+
unsubscribe: function(channels, callback, scope) {
|
180
|
+
if (this._state !== this._CONNECTED) return;
|
181
|
+
|
182
|
+
channels = [].concat(channels);
|
183
|
+
this._validateChannels(channels);
|
184
|
+
|
185
|
+
var id = this.generateId();
|
186
|
+
|
187
|
+
this._transport.send({
|
188
|
+
channel: Faye.Channel.UNSUBSCRIBE,
|
189
|
+
clientId: this._clientId,
|
190
|
+
subscription: channels,
|
191
|
+
id: id
|
192
|
+
|
193
|
+
}, function(response) {
|
194
|
+
if (response.id !== id) return;
|
195
|
+
if (!response.successful) return;
|
196
|
+
|
197
|
+
channels = [].concat(response.subscription);
|
198
|
+
Faye.each(channels, function(channel) {
|
199
|
+
this._channels.set(channel, null);
|
200
|
+
}, this);
|
201
|
+
}, this);
|
202
|
+
},
|
203
|
+
|
204
|
+
// Request Response
|
205
|
+
// MUST include: * channel MUST include: * channel
|
206
|
+
// * data * successful
|
207
|
+
// MAY include: * clientId MAY include: * id
|
208
|
+
// * id * error
|
209
|
+
// * ext * ext
|
210
|
+
publish: function(channel, data) {
|
211
|
+
if (this._state !== this._CONNECTED) return;
|
212
|
+
this._validateChannels([channel]);
|
213
|
+
|
214
|
+
this.enqueue({
|
215
|
+
channel: channel,
|
216
|
+
data: data,
|
217
|
+
clientId: this._clientId
|
218
|
+
});
|
219
|
+
|
220
|
+
if (this._timeout) return;
|
221
|
+
var self = this;
|
222
|
+
|
223
|
+
this._timeout = setTimeout(function() {
|
224
|
+
delete self._timeout;
|
225
|
+
self.flush();
|
226
|
+
}, this.MAX_DELAY * 1000);
|
227
|
+
},
|
228
|
+
|
229
|
+
generateId: function(bitdepth) {
|
230
|
+
bitdepth = bitdepth || 32;
|
231
|
+
return Math.floor(Math.pow(2,bitdepth) * Math.random()).toString(16);
|
232
|
+
},
|
233
|
+
|
234
|
+
enqueue: function(message) {
|
235
|
+
this._outbox.push(message);
|
236
|
+
},
|
237
|
+
|
238
|
+
flush: function() {
|
239
|
+
this._transport.send(this._outbox);
|
240
|
+
this._outbox = [];
|
241
|
+
},
|
242
|
+
|
243
|
+
_validateChannels: function(channels) {
|
244
|
+
Faye.each(channels, function(channel) {
|
245
|
+
if (!Faye.Channel.isValid(channel))
|
246
|
+
throw '"' + channel + '" is not a valid channel name';
|
247
|
+
if (!Faye.Channel.isSubscribable(channel))
|
248
|
+
throw 'Clients may not subscribe to channel "' + channel + '"';
|
249
|
+
});
|
250
|
+
},
|
251
|
+
|
252
|
+
_handleAdvice: function(advice) {
|
253
|
+
Faye.extend(this._advice, advice);
|
254
|
+
if (this._advice.reconnect === this._HANDSHAKE) this._clientId = null;
|
255
|
+
},
|
256
|
+
|
257
|
+
_sendToSubscribers: function(message) {
|
258
|
+
var channels = this._channels.glob(message.channel);
|
259
|
+
Faye.each(channels, function(callback) {
|
260
|
+
if (!callback) return;
|
261
|
+
callback[0].call(callback[1], message.data);
|
262
|
+
});
|
263
|
+
}
|
264
|
+
});
|
265
|
+
|