faye 0.3.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +13 -0
- data/Manifest.txt +16 -33
- data/README.txt +9 -274
- data/Rakefile +4 -4
- data/lib/faye-browser-min.js +1 -0
- data/lib/faye.rb +26 -9
- data/lib/faye/{rack_adapter.rb → adapters/rack_adapter.rb} +38 -25
- data/lib/faye/error.rb +0 -7
- data/lib/faye/{logging.rb → mixins/logging.rb} +0 -0
- data/lib/faye/mixins/publisher.rb +29 -0
- data/lib/faye/{timeouts.rb → mixins/timeouts.rb} +1 -0
- data/lib/faye/{transport.rb → network/transport.rb} +32 -43
- data/lib/faye/{channel.rb → protocol/channel.rb} +23 -3
- data/lib/faye/{client.rb → protocol/client.rb} +124 -90
- data/lib/faye/{connection.rb → protocol/connection.rb} +41 -23
- data/lib/faye/protocol/extensible.rb +47 -0
- data/lib/faye/{grammar.rb → protocol/grammar.rb} +0 -0
- data/lib/faye/{server.rb → protocol/server.rb} +98 -54
- data/lib/faye/protocol/subscription.rb +23 -0
- data/lib/faye/{namespace.rb → util/namespace.rb} +0 -0
- data/lib/faye/util/web_socket.rb +119 -0
- data/lib/thin_extensions.rb +86 -0
- data/test/scenario.rb +68 -14
- data/test/test_clients.rb +215 -2
- data/test/test_server.rb +10 -10
- metadata +102 -71
- data/Jakefile +0 -13
- data/build/faye-client-min.js +0 -1
- data/build/faye.js +0 -1488
- data/examples/README.rdoc +0 -41
- data/examples/node/app.js +0 -26
- data/examples/node/client.js +0 -23
- data/examples/node/faye-client-min.js +0 -1
- data/examples/node/faye.js +0 -1488
- data/examples/rack/app.rb +0 -16
- data/examples/rack/client.rb +0 -25
- data/examples/rack/config.ru +0 -8
- data/examples/shared/public/favicon.ico +0 -0
- data/examples/shared/public/index.html +0 -49
- data/examples/shared/public/jquery.js +0 -19
- data/examples/shared/public/mootools.js +0 -4329
- data/examples/shared/public/prototype.js +0 -4874
- data/examples/shared/public/robots.txt +0 -0
- data/examples/shared/public/soapbox.js +0 -100
- data/examples/shared/public/style.css +0 -43
- data/jake.yml +0 -40
- data/lib/faye-client-min.js +0 -1
- data/test/scenario.js +0 -138
- data/test/test_clients.js +0 -195
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rack'
|
3
3
|
require 'thin'
|
4
|
+
require Faye::ROOT + '/thin_extensions'
|
4
5
|
|
5
6
|
module Faye
|
6
7
|
class RackAdapter
|
@@ -9,7 +10,7 @@ module Faye
|
|
9
10
|
ASYNC_RESPONSE = [-1, {}, []].freeze
|
10
11
|
|
11
12
|
DEFAULT_ENDPOINT = '/bayeux'
|
12
|
-
SCRIPT_PATH = File.join(ROOT, 'faye-
|
13
|
+
SCRIPT_PATH = File.join(ROOT, 'faye-browser-min.js')
|
13
14
|
|
14
15
|
TYPE_JSON = {'Content-Type' => 'application/json'}
|
15
16
|
TYPE_SCRIPT = {'Content-Type' => 'text/javascript'}
|
@@ -20,21 +21,29 @@ module Faye
|
|
20
21
|
@options = [app, options].grep(Hash).first || {}
|
21
22
|
|
22
23
|
@endpoint = @options[:mount] || DEFAULT_ENDPOINT
|
23
|
-
@endpoint_re = Regexp.new('^' + @endpoint + '(/[^/]
|
24
|
+
@endpoint_re = Regexp.new('^' + @endpoint + '(/[^/]*)*(\\.js)?$')
|
24
25
|
@server = Server.new(@options)
|
25
26
|
end
|
26
27
|
|
28
|
+
def add_extension(extension)
|
29
|
+
@server.add_extension(extension)
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_extension(extension)
|
33
|
+
@server.remove_extension(extension)
|
34
|
+
end
|
35
|
+
|
27
36
|
def get_client
|
28
37
|
@client ||= Client.new(@server)
|
29
38
|
end
|
30
39
|
|
31
|
-
def
|
40
|
+
def listen(port)
|
32
41
|
handler = Rack::Handler.get('thin')
|
33
42
|
handler.run(self, :Port => port)
|
34
43
|
end
|
35
44
|
|
36
45
|
def call(env)
|
37
|
-
ensure_reactor_running!
|
46
|
+
Faye.ensure_reactor_running!
|
38
47
|
request = Rack::Request.new(env)
|
39
48
|
|
40
49
|
unless request.path_info =~ @endpoint_re
|
@@ -43,6 +52,10 @@ module Faye
|
|
43
52
|
[404, TYPE_TEXT, ["Sure you're not looking for #{@endpoint} ?"]]
|
44
53
|
end
|
45
54
|
|
55
|
+
if env['HTTP_UPGRADE'] == 'WebSocket'
|
56
|
+
return handle_upgrade(request)
|
57
|
+
end
|
58
|
+
|
46
59
|
if request.path_info =~ /\.js$/
|
47
60
|
return [200, TYPE_SCRIPT, File.new(SCRIPT_PATH)]
|
48
61
|
end
|
@@ -51,14 +64,22 @@ module Faye
|
|
51
64
|
json_msg = request.post? ? request.body.read : request.params['message']
|
52
65
|
message = JSON.parse(json_msg)
|
53
66
|
jsonp = request.params['jsonp'] || JSONP_CALLBACK
|
67
|
+
type = request.get? ? TYPE_SCRIPT : TYPE_JSON
|
68
|
+
callback = env['async.callback']
|
69
|
+
body = DeferredBody.new
|
54
70
|
|
55
71
|
@server.flush_connection(message) if request.get?
|
56
72
|
|
57
|
-
|
73
|
+
callback.call [200, type, body]
|
74
|
+
|
75
|
+
@server.process(message, false) do |replies|
|
58
76
|
response = JSON.unparse(replies)
|
59
77
|
response = "#{ jsonp }(#{ response });" if request.get?
|
60
|
-
response
|
78
|
+
body.succeed(response)
|
61
79
|
end
|
80
|
+
|
81
|
+
ASYNC_RESPONSE
|
82
|
+
|
62
83
|
rescue
|
63
84
|
[400, TYPE_TEXT, 'Bad request']
|
64
85
|
end
|
@@ -66,27 +87,19 @@ module Faye
|
|
66
87
|
|
67
88
|
private
|
68
89
|
|
69
|
-
def
|
70
|
-
|
71
|
-
type = request.get? ? TYPE_SCRIPT : TYPE_JSON
|
72
|
-
callback = env['async.callback']
|
90
|
+
def handle_upgrade(request)
|
91
|
+
socket = Faye::WebSocket.new(request)
|
73
92
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
93
|
+
socket.onmessage = lambda do |message|
|
94
|
+
begin
|
95
|
+
message = JSON.parse(message.data)
|
96
|
+
@server.process(message, socket) do |replies|
|
97
|
+
socket.send(JSON.unparse(replies))
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
end
|
79
101
|
end
|
80
|
-
|
81
|
-
response = nil
|
82
|
-
@server.process(message, false) { |r| response = block.call(r) }
|
83
|
-
sleep(0.1) while response.nil?
|
84
|
-
[200, type, [response]]
|
85
|
-
end
|
86
|
-
|
87
|
-
def ensure_reactor_running!
|
88
|
-
Thread.new { EM.run } unless EM.reactor_running?
|
89
|
-
while not EM.reactor_running?; end
|
102
|
+
ASYNC_RESPONSE
|
90
103
|
end
|
91
104
|
|
92
105
|
class DeferredBody
|
data/lib/faye/error.rb
CHANGED
@@ -1,13 +1,6 @@
|
|
1
1
|
module Faye
|
2
2
|
class Error
|
3
3
|
|
4
|
-
def self.parse(string)
|
5
|
-
return nil unless Grammar::ERROR =~ string
|
6
|
-
parts = string.split(':')
|
7
|
-
args = parts[1].split(',')
|
8
|
-
new(parts[0].to_i, args, parts[2])
|
9
|
-
end
|
10
|
-
|
11
4
|
def self.method_missing(type, *args)
|
12
5
|
code = const_get(type.to_s.upcase)
|
13
6
|
new(code[0], args, code[1]).to_s
|
File without changes
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Faye
|
2
|
+
module Publisher
|
3
|
+
|
4
|
+
def count_subscribers(event_type)
|
5
|
+
return 0 unless @subscribers and @subscribers[event_type]
|
6
|
+
@subscribers[event_type].size
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_subscriber(event_type, listener)
|
10
|
+
@subscribers ||= {}
|
11
|
+
list = @subscribers[event_type] ||= []
|
12
|
+
list << listener
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_subscriber(event_type, listener)
|
16
|
+
return unless @subscribers and @subscribers[event_type]
|
17
|
+
@subscribers[event_type].delete_if(&listener.method(:==))
|
18
|
+
end
|
19
|
+
|
20
|
+
def publish_event(event_type, *args)
|
21
|
+
return unless @subscribers and @subscribers[event_type]
|
22
|
+
@subscribers[event_type].each do |listener|
|
23
|
+
listener.call(*args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -8,49 +8,33 @@ module Faye
|
|
8
8
|
include Logging
|
9
9
|
|
10
10
|
def initialize(client, endpoint)
|
11
|
-
debug('Created new transport for ?', endpoint)
|
12
|
-
@client
|
13
|
-
@endpoint
|
11
|
+
debug('Created new ? transport for ?', connection_type, endpoint)
|
12
|
+
@client = client
|
13
|
+
@endpoint = endpoint
|
14
14
|
end
|
15
15
|
|
16
16
|
def connection_type
|
17
17
|
self.class.connection_type
|
18
18
|
end
|
19
19
|
|
20
|
-
def send(
|
21
|
-
|
22
|
-
|
20
|
+
def send(messages, timeout)
|
21
|
+
messages = [messages].flatten
|
22
|
+
debug('Client ? sending message to ?: ?', @client.client_id, @endpoint, messages)
|
23
|
+
request(messages, timeout)
|
24
|
+
end
|
25
|
+
|
26
|
+
def receive(responses)
|
27
|
+
debug('Client ? received from ?: ?', @client.client_id, @endpoint, responses)
|
28
|
+
responses.each { |response| @client.receive_message(response) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def retry_block(message, timeout)
|
32
|
+
lambda do
|
33
|
+
EventMachine.add_timer(timeout) { request(message, 2 * timeout) }
|
23
34
|
end
|
24
|
-
|
25
|
-
debug('Client ? sending message to ?: ?', @client.client_id, @endpoint, message)
|
26
|
-
|
27
|
-
request(message) { |responses|
|
28
|
-
debug('Client ? received from ?: ?', @client.client_id, @endpoint, responses)
|
29
|
-
|
30
|
-
if block_given?
|
31
|
-
messages, deliverable = [], true
|
32
|
-
[responses].flatten.each do |response|
|
33
|
-
|
34
|
-
if message.is_a?(Hash) and response['id'] == message['id']
|
35
|
-
deliverable = false if block.call(response) == false
|
36
|
-
end
|
37
|
-
|
38
|
-
if response['advice']
|
39
|
-
@client.handle_advice(response['advice'])
|
40
|
-
end
|
41
|
-
|
42
|
-
if response['data'] and response['channel']
|
43
|
-
messages << response
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
@client.deliver_messages(messages) if deliverable
|
49
|
-
end
|
50
|
-
}
|
51
35
|
end
|
52
36
|
|
53
|
-
@transports =
|
37
|
+
@transports = []
|
54
38
|
|
55
39
|
class << self
|
56
40
|
attr_accessor :connection_type
|
@@ -59,7 +43,7 @@ module Faye
|
|
59
43
|
endpoint = client.endpoint
|
60
44
|
connection_types ||= supported_connection_types
|
61
45
|
|
62
|
-
candidate_class = @transports.find do |type, klass|
|
46
|
+
candidate_class = @transports.find do |(type, klass)|
|
63
47
|
connection_types.include?(type) and
|
64
48
|
klass.usable?(endpoint)
|
65
49
|
end
|
@@ -72,12 +56,12 @@ module Faye
|
|
72
56
|
end
|
73
57
|
|
74
58
|
def register(type, klass)
|
75
|
-
@transports[type
|
59
|
+
@transports << [type, klass]
|
76
60
|
klass.connection_type = type
|
77
61
|
end
|
78
62
|
|
79
63
|
def supported_connection_types
|
80
|
-
@transports.
|
64
|
+
@transports.map { |t| t.first }
|
81
65
|
end
|
82
66
|
end
|
83
67
|
end
|
@@ -87,7 +71,9 @@ module Faye
|
|
87
71
|
endpoint.is_a?(String)
|
88
72
|
end
|
89
73
|
|
90
|
-
def request(message,
|
74
|
+
def request(message, timeout)
|
75
|
+
retry_block = retry_block(message, timeout)
|
76
|
+
|
91
77
|
content = JSON.unparse(message)
|
92
78
|
params = {
|
93
79
|
:head => {
|
@@ -100,8 +86,13 @@ module Faye
|
|
100
86
|
}
|
101
87
|
request = EventMachine::HttpRequest.new(@endpoint).post(params)
|
102
88
|
request.callback do
|
103
|
-
|
89
|
+
begin
|
90
|
+
receive(JSON.parse(request.response))
|
91
|
+
rescue
|
92
|
+
retry_block.call
|
93
|
+
end
|
104
94
|
end
|
95
|
+
request.errback { retry_block.call }
|
105
96
|
end
|
106
97
|
end
|
107
98
|
Transport.register 'long-polling', HttpTransport
|
@@ -111,10 +102,8 @@ module Faye
|
|
111
102
|
endpoint.is_a?(Server)
|
112
103
|
end
|
113
104
|
|
114
|
-
def request(message,
|
115
|
-
@endpoint.process(message, true)
|
116
|
-
block.call(response)
|
117
|
-
end
|
105
|
+
def request(message, timeout)
|
106
|
+
@endpoint.process(message, true, &method(:receive))
|
118
107
|
end
|
119
108
|
end
|
120
109
|
Transport.register 'in-process', LocalTransport
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Faye
|
2
2
|
class Channel
|
3
3
|
|
4
|
-
include
|
4
|
+
include Publisher
|
5
5
|
attr_reader :name
|
6
6
|
|
7
7
|
def initialize(name)
|
@@ -9,8 +9,7 @@ module Faye
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def <<(message)
|
12
|
-
|
13
|
-
notify_observers(:message, message)
|
12
|
+
publish_event(:message, message)
|
14
13
|
end
|
15
14
|
|
16
15
|
HANDSHAKE = '/meta/handshake'
|
@@ -120,6 +119,27 @@ module Faye
|
|
120
119
|
list << @children[:**].value if @children[:**]
|
121
120
|
list.flatten
|
122
121
|
end
|
122
|
+
|
123
|
+
def subscribe(names, callback)
|
124
|
+
return unless callback
|
125
|
+
names.each do |name|
|
126
|
+
channel = self[name] ||= Channel.new(name)
|
127
|
+
channel.add_subscriber(:message, callback)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def unsubscribe(name, callback)
|
132
|
+
channel = self[name]
|
133
|
+
return false unless channel
|
134
|
+
channel.remove_subscriber(:message, callback)
|
135
|
+
channel.count_subscribers(:message).zero?
|
136
|
+
end
|
137
|
+
|
138
|
+
def distribute_message(message)
|
139
|
+
glob(message['channel']).each do |channel|
|
140
|
+
channel.publish_event(:message, message['data'])
|
141
|
+
end
|
142
|
+
end
|
123
143
|
end
|
124
144
|
|
125
145
|
end
|
@@ -4,6 +4,7 @@ module Faye
|
|
4
4
|
include EventMachine::Deferrable
|
5
5
|
include Timeouts
|
6
6
|
include Logging
|
7
|
+
include Extensible
|
7
8
|
|
8
9
|
UNCONNECTED = 1
|
9
10
|
CONNECTING = 2
|
@@ -16,22 +17,27 @@ module Faye
|
|
16
17
|
|
17
18
|
CONNECTION_TIMEOUT = 60.0
|
18
19
|
|
19
|
-
attr_reader :endpoint, :client_id
|
20
|
+
attr_reader :endpoint, :client_id
|
20
21
|
|
21
22
|
def initialize(endpoint = nil, options = {})
|
22
23
|
info('New client created for ?', endpoint)
|
23
24
|
|
24
25
|
@endpoint = endpoint || RackAdapter::DEFAULT_ENDPOINT
|
25
26
|
@options = options
|
26
|
-
@timeout = @options[:timeout] || CONNECTION_TIMEOUT
|
27
27
|
|
28
|
-
@transport = Transport.get(self)
|
28
|
+
@transport = Transport.get(self, MANDATORY_CONNECTION_TYPES)
|
29
29
|
@state = UNCONNECTED
|
30
|
-
@namespace = Namespace.new
|
31
30
|
@outbox = []
|
32
31
|
@channels = Channel::Tree.new
|
33
32
|
|
34
|
-
@
|
33
|
+
@namespace = Namespace.new
|
34
|
+
@response_callbacks = {}
|
35
|
+
|
36
|
+
@advice = {
|
37
|
+
'reconnect' => RETRY,
|
38
|
+
'interval' => 1000.0 * (@options[:interval] || Connection::INTERVAL),
|
39
|
+
'timeout' => 1000.0 * (@options[:timeout] || CONNECTION_TIMEOUT)
|
40
|
+
}
|
35
41
|
end
|
36
42
|
|
37
43
|
# Request
|
@@ -61,10 +67,10 @@ module Faye
|
|
61
67
|
|
62
68
|
info('Initiating handshake with ?', @endpoint)
|
63
69
|
|
64
|
-
|
70
|
+
send({
|
65
71
|
'channel' => Channel::HANDSHAKE,
|
66
72
|
'version' => BAYEUX_VERSION,
|
67
|
-
'supportedConnectionTypes' =>
|
73
|
+
'supportedConnectionTypes' => [@transport.connection_type]
|
68
74
|
|
69
75
|
}) do |response|
|
70
76
|
|
@@ -74,6 +80,8 @@ module Faye
|
|
74
80
|
@transport = Transport.get(self, response['supportedConnectionTypes'])
|
75
81
|
|
76
82
|
info('Handshake successful: ?', @client_id)
|
83
|
+
|
84
|
+
subscribe(@channels.keys)
|
77
85
|
block.call if block_given?
|
78
86
|
|
79
87
|
else
|
@@ -97,38 +105,28 @@ module Faye
|
|
97
105
|
return if @advice['reconnect'] == NONE or
|
98
106
|
@state == DISCONNECTED
|
99
107
|
|
100
|
-
|
101
|
-
begin_reconnect_timeout
|
102
|
-
return handshake { connect(&block) }
|
103
|
-
end
|
108
|
+
return handshake { connect(&block) } if @state == UNCONNECTED
|
104
109
|
|
105
|
-
|
110
|
+
callback(&block)
|
106
111
|
return unless @state == CONNECTED
|
107
112
|
|
108
113
|
info('Calling deferred actions for ?', @client_id)
|
109
114
|
set_deferred_status(:succeeded)
|
110
115
|
set_deferred_status(:deferred)
|
111
|
-
block.call if block_given?
|
112
116
|
|
113
|
-
return unless @
|
114
|
-
@
|
117
|
+
return unless @connect_request.nil?
|
118
|
+
@connect_request = true
|
119
|
+
|
115
120
|
info('Initiating connection for ?', @client_id)
|
116
121
|
|
117
|
-
|
122
|
+
send({
|
118
123
|
'channel' => Channel::CONNECT,
|
119
124
|
'clientId' => @client_id,
|
120
|
-
'connectionType' => @transport.connection_type
|
121
|
-
'id' => @connection_id
|
125
|
+
'connectionType' => @transport.connection_type
|
122
126
|
|
123
|
-
}
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
info('Closed connection for ?', @client_id)
|
128
|
-
EventMachine.add_timer(@advice['interval'] / 1000.0) { connect }
|
129
|
-
})
|
130
|
-
|
131
|
-
begin_reconnect_timeout
|
127
|
+
}) do
|
128
|
+
cycle_connection
|
129
|
+
end
|
132
130
|
end
|
133
131
|
|
134
132
|
# Request Response
|
@@ -144,14 +142,13 @@ module Faye
|
|
144
142
|
|
145
143
|
info('Disconnecting ?', @client_id)
|
146
144
|
|
147
|
-
|
145
|
+
send({
|
148
146
|
'channel' => Channel::DISCONNECT,
|
149
147
|
'clientId' => @client_id
|
150
148
|
})
|
151
149
|
|
152
150
|
info('Clearing channel listeners for ?', @client_id)
|
153
151
|
@channels = Channel::Tree.new
|
154
|
-
remove_timeout(:reconnect)
|
155
152
|
end
|
156
153
|
|
157
154
|
# Request Response
|
@@ -165,27 +162,32 @@ module Faye
|
|
165
162
|
# * id
|
166
163
|
# * timestamp
|
167
164
|
def subscribe(channels, &block)
|
165
|
+
if Array === channels
|
166
|
+
return channels.each do |channel|
|
167
|
+
subscribe(channel, &block)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
validate_channel(channels)
|
172
|
+
|
168
173
|
connect {
|
169
|
-
channels = [channels].flatten
|
170
|
-
validate_channels(channels)
|
171
|
-
|
172
174
|
info('Client ? attempting to subscribe to ?', @client_id, channels)
|
173
175
|
|
174
|
-
|
176
|
+
send({
|
175
177
|
'channel' => Channel::SUBSCRIBE,
|
176
178
|
'clientId' => @client_id,
|
177
179
|
'subscription' => channels
|
178
180
|
|
179
|
-
}
|
180
|
-
if response['successful']
|
181
|
-
|
182
|
-
info('Subscription acknowledged for ? to ?', @client_id, channels)
|
181
|
+
}) do |response|
|
182
|
+
if response['successful']
|
183
183
|
|
184
184
|
channels = [response['subscription']].flatten
|
185
|
-
|
185
|
+
info('Subscription acknowledged for ? to ?', @client_id, channels)
|
186
|
+
@channels.subscribe(channels, block)
|
186
187
|
end
|
187
|
-
|
188
|
+
end
|
188
189
|
}
|
190
|
+
Subscription.new(self, channels, block)
|
189
191
|
end
|
190
192
|
|
191
193
|
# Request Response
|
@@ -199,26 +201,32 @@ module Faye
|
|
199
201
|
# * id
|
200
202
|
# * timestamp
|
201
203
|
def unsubscribe(channels, &block)
|
204
|
+
if Array === channels
|
205
|
+
return channels.each do |channel|
|
206
|
+
unsubscribe(channel, &block)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
validate_channel(channels)
|
211
|
+
|
212
|
+
dead = @channels.unsubscribe(channels, block)
|
213
|
+
return unless dead
|
214
|
+
|
202
215
|
connect {
|
203
|
-
channels = [channels].flatten
|
204
|
-
validate_channels(channels)
|
205
|
-
|
206
216
|
info('Client ? attempting to unsubscribe from ?', @client_id, channels)
|
207
217
|
|
208
|
-
|
218
|
+
send({
|
209
219
|
'channel' => Channel::UNSUBSCRIBE,
|
210
220
|
'clientId' => @client_id,
|
211
221
|
'subscription' => channels
|
212
222
|
|
213
|
-
}
|
223
|
+
}) do |response|
|
214
224
|
if response['successful']
|
215
225
|
|
216
|
-
info('Unsubscription acknowledged for ? from ?', @client_id, channels)
|
217
|
-
|
218
226
|
channels = [response['subscription']].flatten
|
219
|
-
|
227
|
+
info('Unsubscription acknowledged for ? from ?', @client_id, channels)
|
220
228
|
end
|
221
|
-
|
229
|
+
end
|
222
230
|
}
|
223
231
|
end
|
224
232
|
|
@@ -229,77 +237,103 @@ module Faye
|
|
229
237
|
# * id * error
|
230
238
|
# * ext * ext
|
231
239
|
def publish(channel, data)
|
240
|
+
validate_channel(channel)
|
241
|
+
|
232
242
|
connect {
|
233
|
-
validate_channels([channel])
|
234
|
-
|
235
243
|
info('Client ? queueing published message to ?: ?', @client_id, channel, data)
|
236
244
|
|
237
|
-
|
245
|
+
send({
|
238
246
|
'channel' => channel,
|
239
247
|
'data' => data,
|
240
248
|
'clientId' => @client_id
|
241
249
|
})
|
242
|
-
|
243
|
-
add_timeout(:publish, Connection::MAX_DELAY) { flush! }
|
244
250
|
}
|
245
251
|
end
|
246
252
|
|
247
|
-
def
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
253
|
+
def receive_message(message)
|
254
|
+
pipe_through_extensions(:incoming, message) do |message|
|
255
|
+
if message
|
256
|
+
handle_advice(message['advice']) if message['advice']
|
257
|
+
|
258
|
+
callback = @response_callbacks[message['id']]
|
259
|
+
if callback
|
260
|
+
@response_callbacks.delete(message['id'])
|
261
|
+
callback.call(message)
|
262
|
+
end
|
263
|
+
|
264
|
+
deliver_message(message)
|
265
|
+
end
|
258
266
|
end
|
259
267
|
end
|
260
268
|
|
261
269
|
private
|
262
270
|
|
263
|
-
def
|
264
|
-
|
265
|
-
|
271
|
+
def handle_advice(advice)
|
272
|
+
@advice.update(advice)
|
273
|
+
|
274
|
+
if @advice['reconnect'] == HANDSHAKE and @state != DISCONNECTED
|
275
|
+
@state = UNCONNECTED
|
266
276
|
@client_id = nil
|
267
|
-
|
268
|
-
|
269
|
-
info('Server took >?s to reply to connection for ?: attempting to reconnect',
|
270
|
-
@timeout, @client_id)
|
271
|
-
|
272
|
-
subscribe(@channels.keys)
|
277
|
+
cycle_connection
|
273
278
|
end
|
274
279
|
end
|
275
280
|
|
276
|
-
def
|
277
|
-
|
281
|
+
def deliver_message(message)
|
282
|
+
return unless message['channel'] and message['data']
|
283
|
+
info('Client ? calling listeners for ? with ?', @client_id, message['channel'], message['data'])
|
284
|
+
@channels.distribute_message(message)
|
278
285
|
end
|
279
286
|
|
280
|
-
def
|
281
|
-
@
|
282
|
-
@
|
287
|
+
def teardown_connection
|
288
|
+
return unless @connect_request
|
289
|
+
@connect_request = nil
|
290
|
+
info('Closed connection for ?', @client_id)
|
283
291
|
end
|
284
292
|
|
285
|
-
def
|
286
|
-
|
287
|
-
|
288
|
-
raise "Clients may not subscribe to channel '#{ channel }'" unless Channel.subscribable?(channel)
|
289
|
-
end
|
293
|
+
def cycle_connection
|
294
|
+
teardown_connection
|
295
|
+
EventMachine.add_timer(@advice['interval'] / 1000.0) { connect }
|
290
296
|
end
|
291
297
|
|
292
|
-
def
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
298
|
+
def send(message, &callback)
|
299
|
+
message['id'] = @namespace.generate
|
300
|
+
@response_callbacks[message['id']] = callback if callback
|
301
|
+
|
302
|
+
pipe_through_extensions(:outgoing, message) do |message|
|
303
|
+
if message
|
304
|
+
if message['channel'] == Channel::HANDSHAKE
|
305
|
+
@transport.send(message, @advice['timeout'] / 1000.0)
|
306
|
+
else
|
307
|
+
@outbox << message
|
308
|
+
|
309
|
+
if message['channel'] == Channel::CONNECT
|
310
|
+
@connect_message = message
|
311
|
+
end
|
312
|
+
|
313
|
+
add_timeout(:publish, Connection::MAX_DELAY) { flush! }
|
314
|
+
end
|
299
315
|
end
|
300
316
|
end
|
301
317
|
end
|
302
318
|
|
319
|
+
def flush!
|
320
|
+
remove_timeout(:publish)
|
321
|
+
|
322
|
+
if @outbox.size > 1 and @connect_message
|
323
|
+
@connect_message['advice'] = {'timeout' => 0}
|
324
|
+
end
|
325
|
+
|
326
|
+
@connect_message = nil
|
327
|
+
|
328
|
+
@transport.send(@outbox, @advice['timeout'] / 1000.0)
|
329
|
+
@outbox = []
|
330
|
+
end
|
331
|
+
|
332
|
+
def validate_channel(channel)
|
333
|
+
raise "'#{ channel }' is not a valid channel name" unless Channel.valid?(channel)
|
334
|
+
raise "Clients may not subscribe to channel '#{ channel }'" unless Channel.subscribable?(channel)
|
335
|
+
end
|
336
|
+
|
303
337
|
end
|
304
338
|
end
|
305
339
|
|