faye 0.6.8 → 0.7.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 +10 -3
- data/README.rdoc +1 -2
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +89 -32
- data/lib/faye/adapters/rack_adapter.rb +20 -26
- data/lib/faye/engines/base.rb +5 -0
- data/lib/faye/engines/memory.rb +9 -3
- data/lib/faye/engines/redis.rb +26 -11
- data/lib/faye/mixins/publisher.rb +4 -8
- data/lib/faye/protocol/channel.rb +8 -8
- data/lib/faye/protocol/client.rb +45 -4
- data/lib/faye/protocol/publication.rb +5 -0
- data/lib/faye/protocol/server.rb +10 -19
- data/lib/faye/thin_extensions.rb +1 -1
- data/lib/faye/transport/http.rb +17 -8
- data/lib/faye/transport/local.rb +6 -3
- data/lib/faye/transport/transport.rb +23 -9
- data/lib/faye/transport/web_socket.rb +102 -0
- data/lib/faye/util/web_socket.rb +34 -80
- data/lib/faye/util/web_socket/api.rb +103 -0
- data/lib/faye/util/web_socket/client.rb +82 -0
- data/lib/faye/util/web_socket/draft75_parser.rb +3 -5
- data/lib/faye/util/web_socket/draft76_parser.rb +5 -7
- data/lib/faye/util/web_socket/protocol8_parser.rb +111 -46
- data/spec/javascript/client_spec.js +99 -7
- data/spec/javascript/engine_spec.js +116 -3
- data/spec/javascript/node_adapter_spec.js +2 -4
- data/spec/javascript/server/handshake_spec.js +0 -12
- data/spec/javascript/server/integration_spec.js +74 -29
- data/spec/javascript/server_spec.js +0 -11
- data/spec/javascript/web_socket/client_spec.js +121 -0
- data/spec/javascript/web_socket/protocol8parser_spec.js +26 -3
- data/spec/node.js +2 -0
- data/spec/redis.conf +10 -280
- data/spec/ruby/client_spec.rb +101 -8
- data/spec/ruby/engine_spec.rb +106 -0
- data/spec/ruby/server/handshake_spec.rb +0 -12
- data/spec/ruby/server/integration_spec.rb +56 -18
- data/spec/ruby/server_spec.rb +1 -12
- data/spec/ruby/transport_spec.rb +14 -8
- data/spec/ruby/web_socket/client_spec.rb +126 -0
- data/spec/ruby/web_socket/protocol8_parser_spec.rb +28 -3
- metadata +96 -150
data/spec/ruby/client_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe Faye::Client do
|
|
5
5
|
transport = mock("transport")
|
6
6
|
transport.stub(:connection_type).and_return "fake"
|
7
7
|
transport.stub(:send)
|
8
|
+
transport.extend(Faye::Publisher)
|
8
9
|
transport
|
9
10
|
end
|
10
11
|
|
@@ -18,7 +19,7 @@ describe Faye::Client do
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def create_client
|
21
|
-
Faye::Transport.stub(:get).
|
22
|
+
Faye::Transport.stub(:get).and_yield(transport)
|
22
23
|
@client = Faye::Client.new("http://localhost/")
|
23
24
|
end
|
24
25
|
|
@@ -106,7 +107,7 @@ describe Faye::Client do
|
|
106
107
|
stub_response "channel" => "/meta/handshake",
|
107
108
|
"successful" => true,
|
108
109
|
"version" => "1.0",
|
109
|
-
"supportedConnectionTypes" => ["websocket"],
|
110
|
+
"supportedConnectionTypes" => ["long-polling", "websocket"],
|
110
111
|
"clientId" => "fakeid"
|
111
112
|
end
|
112
113
|
|
@@ -119,16 +120,26 @@ describe Faye::Client do
|
|
119
120
|
@client.handshake
|
120
121
|
@client.state.should == :CONNECTED
|
121
122
|
end
|
122
|
-
|
123
|
+
|
124
|
+
it "registers any pre-existing subscriptions" do
|
125
|
+
@client.should_receive(:subscribe).with([], true)
|
126
|
+
@client.handshake
|
127
|
+
end
|
128
|
+
|
123
129
|
it "selects a new transport based on what the server supports" do
|
124
|
-
Faye::Transport.should_receive(:get).with(instance_of(Faye::Client), ["websocket"]).
|
130
|
+
Faye::Transport.should_receive(:get).with(instance_of(Faye::Client), ["long-polling", "websocket"]).
|
125
131
|
and_return(transport)
|
126
132
|
@client.handshake
|
127
133
|
end
|
128
|
-
|
129
|
-
|
130
|
-
@client.
|
131
|
-
|
134
|
+
|
135
|
+
describe "with websocket disabled" do
|
136
|
+
before { @client.disable("websocket") }
|
137
|
+
|
138
|
+
it "selects a new transport, excluding websocket" do
|
139
|
+
Faye::Transport.should_receive(:get).with(instance_of(Faye::Client), ["long-polling"]).
|
140
|
+
and_return(transport)
|
141
|
+
@client.handshake
|
142
|
+
end
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
@@ -257,6 +268,7 @@ describe Faye::Client do
|
|
257
268
|
before { create_connected_client }
|
258
269
|
|
259
270
|
it "sends a disconnect message to the server" do
|
271
|
+
transport.stub(:close)
|
260
272
|
transport.should_receive(:send).with({
|
261
273
|
"channel" => "/meta/disconnect",
|
262
274
|
"clientId" => "fakeid",
|
@@ -266,9 +278,23 @@ describe Faye::Client do
|
|
266
278
|
end
|
267
279
|
|
268
280
|
it "puts the client in the DISCONNECTED state" do
|
281
|
+
transport.stub(:close)
|
269
282
|
@client.disconnect
|
270
283
|
@client.state.should == :DISCONNECTED
|
271
284
|
end
|
285
|
+
|
286
|
+
describe "on successful response" do
|
287
|
+
before do
|
288
|
+
stub_response "channel" => "/meta/disconnect",
|
289
|
+
"successful" => true,
|
290
|
+
"clientId" => "fakeid"
|
291
|
+
end
|
292
|
+
|
293
|
+
it "closes the transport" do
|
294
|
+
transport.should_receive(:close)
|
295
|
+
@client.disconnect
|
296
|
+
end
|
297
|
+
end
|
272
298
|
end
|
273
299
|
|
274
300
|
describe :subscribe do
|
@@ -567,6 +593,29 @@ describe Faye::Client do
|
|
567
593
|
lambda { @client.publish("/messages/*", "hello" => "world") }.should raise_error
|
568
594
|
end
|
569
595
|
|
596
|
+
describe "on publish failure" do
|
597
|
+
before do
|
598
|
+
stub_response "channel" => "/messages/foo",
|
599
|
+
"error" => "407:/messages/foo:Failed to publish",
|
600
|
+
"successful" => false,
|
601
|
+
"clientId" => "fakeid"
|
602
|
+
end
|
603
|
+
|
604
|
+
it "should not be published" do
|
605
|
+
published = false
|
606
|
+
@client.publish("/messages/foo", "text" => "hi").callback { published = true }
|
607
|
+
published.should be_false
|
608
|
+
end
|
609
|
+
|
610
|
+
it "reports the error through an errback" do
|
611
|
+
error = nil
|
612
|
+
@client.publish("/messages/foo", "text" => "hi").errback { |e| error = e }
|
613
|
+
error.code.should == 407
|
614
|
+
error.params.should == ["/messages/foo"]
|
615
|
+
error.message.should == "Failed to publish"
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
570
619
|
describe "with an outgoing extension installed" do
|
571
620
|
before do
|
572
621
|
extension = Class.new do
|
@@ -612,4 +661,48 @@ describe Faye::Client do
|
|
612
661
|
end
|
613
662
|
end
|
614
663
|
end
|
664
|
+
|
665
|
+
describe "network notifications" do
|
666
|
+
before { create_client }
|
667
|
+
|
668
|
+
describe "in the default state" do
|
669
|
+
it "broadcasts a down notification" do
|
670
|
+
@client.should_receive(:trigger).with("transport:down")
|
671
|
+
transport.trigger(:down)
|
672
|
+
end
|
673
|
+
|
674
|
+
it "broadcasts an up notification" do
|
675
|
+
@client.should_receive(:trigger).with("transport:up")
|
676
|
+
transport.trigger(:up)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
describe "when the transport is up" do
|
681
|
+
before { transport.trigger(:up) }
|
682
|
+
|
683
|
+
it "broadcasts a down notification" do
|
684
|
+
@client.should_receive(:trigger).with("transport:down")
|
685
|
+
transport.trigger(:down)
|
686
|
+
end
|
687
|
+
|
688
|
+
it "does not broadcast an up notification" do
|
689
|
+
@client.should_not_receive(:trigger)
|
690
|
+
transport.trigger(:up)
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
describe "when the transport is down" do
|
695
|
+
before { transport.trigger(:down) }
|
696
|
+
|
697
|
+
it "does not broadcast a down notification" do
|
698
|
+
@client.should_not_receive(:trigger)
|
699
|
+
transport.trigger(:down)
|
700
|
+
end
|
701
|
+
|
702
|
+
it "broadcasts an up notification" do
|
703
|
+
@client.should_receive(:trigger).with("transport:up")
|
704
|
+
transport.trigger(:up)
|
705
|
+
end
|
706
|
+
end
|
707
|
+
end
|
615
708
|
end
|
data/spec/ruby/engine_spec.rb
CHANGED
@@ -63,6 +63,12 @@ EngineSteps = EM::RSpec.async_steps do
|
|
63
63
|
EM.add_timer(0.01, &resume)
|
64
64
|
end
|
65
65
|
|
66
|
+
def publish_by(name, message, &resume)
|
67
|
+
message = {"clientId" => @clients[name], "id" => Faye.random}.merge(message)
|
68
|
+
engine.publish(message)
|
69
|
+
EM.add_timer(0.01, &resume)
|
70
|
+
end
|
71
|
+
|
66
72
|
def ping(name, &resume)
|
67
73
|
engine.ping(@clients[name])
|
68
74
|
resume.call
|
@@ -73,6 +79,22 @@ EngineSteps = EM::RSpec.async_steps do
|
|
73
79
|
resume.call
|
74
80
|
end
|
75
81
|
|
82
|
+
def expect_event(name, event, args, &resume)
|
83
|
+
params = [@clients[name]] + args
|
84
|
+
handler = lambda { |*a| }
|
85
|
+
engine.bind(event, &handler)
|
86
|
+
handler.should_receive(:call).with(*params)
|
87
|
+
resume.call
|
88
|
+
end
|
89
|
+
|
90
|
+
def expect_no_event(name, event, args, &resume)
|
91
|
+
params = [@clients[name]] + args
|
92
|
+
handler = lambda { |*a| }
|
93
|
+
engine.bind(event, &handler)
|
94
|
+
handler.should_not_receive(:call).with(*params)
|
95
|
+
resume.call
|
96
|
+
end
|
97
|
+
|
76
98
|
def expect_message(name, messages, &resume)
|
77
99
|
@inboxes[name].should == messages
|
78
100
|
resume.call
|
@@ -83,6 +105,11 @@ EngineSteps = EM::RSpec.async_steps do
|
|
83
105
|
resume.call
|
84
106
|
end
|
85
107
|
|
108
|
+
def check_different_messages(a, b, &resume)
|
109
|
+
@inboxes[a].first.should_not be_equal(@inboxes[b].first)
|
110
|
+
resume.call
|
111
|
+
end
|
112
|
+
|
86
113
|
def clean_redis_db(&resume)
|
87
114
|
engine.disconnect
|
88
115
|
redis = EM::Hiredis::Client.connect
|
@@ -119,6 +146,11 @@ describe "Pub/sub engines" do
|
|
119
146
|
1.upto(7) { |i| create_client "client#{i}" }
|
120
147
|
check_num_clients 10
|
121
148
|
end
|
149
|
+
|
150
|
+
it "publishes an event" do
|
151
|
+
engine.should_receive(:trigger).with(:handshake, match(/^[a-z0-9]+$/)).exactly(4)
|
152
|
+
create_client :dave
|
153
|
+
end
|
122
154
|
end
|
123
155
|
|
124
156
|
describe :client_exists do
|
@@ -153,6 +185,11 @@ describe "Pub/sub engines" do
|
|
153
185
|
check_client_exists :alice, false
|
154
186
|
end
|
155
187
|
|
188
|
+
it "publishes an event" do
|
189
|
+
expect_event :alice, :disconnect, []
|
190
|
+
destroy_client :alice
|
191
|
+
end
|
192
|
+
|
156
193
|
describe "when the client has subscriptions" do
|
157
194
|
before do
|
158
195
|
@message = {"channel" => "/messages/foo", "data" => "ok"}
|
@@ -165,6 +202,45 @@ describe "Pub/sub engines" do
|
|
165
202
|
publish @message
|
166
203
|
expect_no_message :alice
|
167
204
|
end
|
205
|
+
|
206
|
+
it "publishes an event" do
|
207
|
+
expect_event :alice, :disconnect, []
|
208
|
+
destroy_client :alice
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe :subscribe do
|
214
|
+
it "publishes an event" do
|
215
|
+
expect_event :alice, :subscribe, ["/messages/foo"]
|
216
|
+
subscribe :alice, "/messages/foo"
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "when the client is subscribed to the channel" do
|
220
|
+
before { subscribe :alice, "/messages/foo" }
|
221
|
+
|
222
|
+
it "does not publish an event" do
|
223
|
+
expect_no_event :alice, :subscribe, ["/messages/foo"]
|
224
|
+
subscribe :alice, "/messages/foo"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe :unsubscribe do
|
230
|
+
before { subscribe :alice, "/messages/bar" }
|
231
|
+
|
232
|
+
it "does not publish an event" do
|
233
|
+
expect_no_event :alice, :unsubscribe, ["/messages/foo"]
|
234
|
+
unsubscribe :alice, "/messages/foo"
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "when the client is subscribed to the channel" do
|
238
|
+
before { subscribe :alice, "/messages/foo" }
|
239
|
+
|
240
|
+
it "publishes an event" do
|
241
|
+
expect_event :alice, :unsubscribe, ["/messages/foo"]
|
242
|
+
unsubscribe :alice, "/messages/foo"
|
243
|
+
end
|
168
244
|
end
|
169
245
|
end
|
170
246
|
|
@@ -183,6 +259,16 @@ describe "Pub/sub engines" do
|
|
183
259
|
expect_no_message :bob
|
184
260
|
expect_no_message :carol
|
185
261
|
end
|
262
|
+
|
263
|
+
it "publishes a :publish event with a clientId" do
|
264
|
+
expect_event :bob, :publish, ["/messages/foo", "ok"]
|
265
|
+
publish_by :bob, @message
|
266
|
+
end
|
267
|
+
|
268
|
+
it "publishes a :publish event with no clientId" do
|
269
|
+
expect_event nil, :publish, ["/messages/foo", "ok"]
|
270
|
+
publish @message
|
271
|
+
end
|
186
272
|
end
|
187
273
|
|
188
274
|
describe "with a subscriber" do
|
@@ -198,6 +284,11 @@ describe "Pub/sub engines" do
|
|
198
284
|
publish @message
|
199
285
|
expect_message :alice, [@message]
|
200
286
|
end
|
287
|
+
|
288
|
+
it "publishes a :publish event" do
|
289
|
+
expect_event :bob, :publish, ["/messages/foo", "ok"]
|
290
|
+
publish_by :bob, @message
|
291
|
+
end
|
201
292
|
end
|
202
293
|
|
203
294
|
describe "with a subscriber that is removed" do
|
@@ -212,6 +303,11 @@ describe "Pub/sub engines" do
|
|
212
303
|
expect_no_message :bob
|
213
304
|
expect_no_message :carol
|
214
305
|
end
|
306
|
+
|
307
|
+
it "publishes a :publish event" do
|
308
|
+
expect_event :bob, :publish, ["/messages/foo", "ok"]
|
309
|
+
publish_by :bob, @message
|
310
|
+
end
|
215
311
|
end
|
216
312
|
|
217
313
|
describe "with multiple subscribers" do
|
@@ -257,6 +353,11 @@ describe "Pub/sub engines" do
|
|
257
353
|
expect_no_message :bob
|
258
354
|
expect_message :carol, [@message]
|
259
355
|
end
|
356
|
+
|
357
|
+
it "delivers a unique copy of the message to each client" do
|
358
|
+
publish @message
|
359
|
+
check_different_messages :alice, :carol
|
360
|
+
end
|
260
361
|
end
|
261
362
|
|
262
363
|
describe "with multiple matching subscriptions for the same client" do
|
@@ -322,6 +423,11 @@ describe "Pub/sub engines" do
|
|
322
423
|
after { clean_redis_db }
|
323
424
|
it_should_behave_like "faye engine"
|
324
425
|
it_should_behave_like "distributed engine"
|
426
|
+
|
427
|
+
describe "using a Unix socket" do
|
428
|
+
before { engine_opts[:socket] = "/tmp/redis.sock" }
|
429
|
+
it_should_behave_like "faye engine"
|
430
|
+
end
|
325
431
|
end
|
326
432
|
end
|
327
433
|
|
@@ -92,18 +92,6 @@ describe "server handshake" do
|
|
92
92
|
}
|
93
93
|
end
|
94
94
|
end
|
95
|
-
|
96
|
-
it "returns a successful response for local clients" do
|
97
|
-
engine.stub(:create_client).and_yield "clientid"
|
98
|
-
server.handshake(message, true) do |response|
|
99
|
-
response.should == {
|
100
|
-
"channel" => "/meta/handshake",
|
101
|
-
"successful" => true,
|
102
|
-
"version" => "1.0",
|
103
|
-
"clientId" => "clientid"
|
104
|
-
}
|
105
|
-
end
|
106
|
-
end
|
107
95
|
end
|
108
96
|
|
109
97
|
describe "with no matching supportedConnectionTypes" do
|
@@ -6,10 +6,17 @@ require "thin"
|
|
6
6
|
Thin::Logging.silent = true
|
7
7
|
|
8
8
|
IntegrationSteps = EM::RSpec.async_steps do
|
9
|
-
def server(port, &callback)
|
9
|
+
def server(port, ssl, &callback)
|
10
|
+
shared = File.dirname(__FILE__) + '/../../../examples/shared'
|
11
|
+
|
12
|
+
options = ssl ?
|
13
|
+
{ :key => shared + '/server.key', :cert => shared + '/server.crt' } :
|
14
|
+
nil
|
15
|
+
|
10
16
|
@adapter = Faye::RackAdapter.new(:mount => "/bayeux", :timeout => 25)
|
11
|
-
@adapter.listen(port)
|
17
|
+
@adapter.listen(port, options)
|
12
18
|
@port = port
|
19
|
+
@secure = ssl
|
13
20
|
EM.next_tick(&callback)
|
14
21
|
end
|
15
22
|
|
@@ -19,13 +26,14 @@ IntegrationSteps = EM::RSpec.async_steps do
|
|
19
26
|
end
|
20
27
|
|
21
28
|
def client(name, channels, &callback)
|
29
|
+
scheme = @secure ? "https" : "http"
|
22
30
|
@clients ||= {}
|
23
31
|
@inboxes ||= {}
|
24
|
-
@clients[name] = Faye::Client.new("
|
32
|
+
@clients[name] = Faye::Client.new("#{scheme}://0.0.0.0:#{@port}/bayeux")
|
25
33
|
@inboxes[name] = {}
|
26
34
|
|
27
35
|
n = channels.size
|
28
|
-
return callback
|
36
|
+
return @clients[name].connect(&callback) if n.zero?
|
29
37
|
|
30
38
|
channels.each do |channel|
|
31
39
|
subscription = @clients[name].subscribe(channel) do |message|
|
@@ -57,7 +65,7 @@ describe "server integration" do
|
|
57
65
|
|
58
66
|
before do
|
59
67
|
Faye.ensure_reactor_running!
|
60
|
-
server 8000
|
68
|
+
server 8000, server_options[:ssl]
|
61
69
|
client :alice, []
|
62
70
|
client :bob, ["/foo"]
|
63
71
|
sync
|
@@ -65,24 +73,54 @@ describe "server integration" do
|
|
65
73
|
|
66
74
|
after { stop }
|
67
75
|
|
68
|
-
|
69
|
-
|
70
|
-
|
76
|
+
shared_examples_for "message bus" do
|
77
|
+
it "delivers a message between clients" do
|
78
|
+
publish :alice, "/foo", {"hello" => "world"}
|
79
|
+
check_inbox :bob, "/foo", [{"hello" => "world"}]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "does not deliver messages for unsubscribed channels" do
|
83
|
+
publish :alice, "/bar", {"hello" => "world"}
|
84
|
+
check_inbox :bob, "/foo", []
|
85
|
+
end
|
86
|
+
|
87
|
+
it "delivers multiple messages" do
|
88
|
+
publish :alice, "/foo", {"hello" => "world"}
|
89
|
+
publish :alice, "/foo", {"hello" => "world"}
|
90
|
+
check_inbox :bob, "/foo", [{"hello" => "world"}, {"hello" => "world"}]
|
91
|
+
end
|
92
|
+
|
93
|
+
it "delivers multibyte strings" do
|
94
|
+
publish :alice, "/foo", {"hello" => encode("Apple = ")}
|
95
|
+
check_inbox :bob, "/foo", [{"hello" => encode("Apple = ")}]
|
96
|
+
end
|
71
97
|
end
|
72
98
|
|
73
|
-
|
74
|
-
|
75
|
-
|
99
|
+
shared_examples_for "network transports" do
|
100
|
+
describe "with HTTP transport" do
|
101
|
+
before do
|
102
|
+
Faye::Transport::WebSocket.stub(:usable?).and_yield(false)
|
103
|
+
end
|
104
|
+
|
105
|
+
it_should_behave_like "message bus"
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "with WebSocket transport" do
|
109
|
+
before do
|
110
|
+
Faye::Transport::WebSocket.stub(:usable?).and_yield(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
it_should_behave_like "message bus"
|
114
|
+
end
|
76
115
|
end
|
77
116
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
check_inbox :bob, "/foo", [{"hello" => "world"}, {"hello" => "world"}]
|
117
|
+
describe "with HTTP server" do
|
118
|
+
let(:server_options) { {:ssl => false} }
|
119
|
+
it_should_behave_like "network transports"
|
82
120
|
end
|
83
121
|
|
84
|
-
|
85
|
-
|
86
|
-
|
122
|
+
describe "with HTTPS server" do
|
123
|
+
let(:server_options) { {:ssl => true} }
|
124
|
+
it_should_behave_like "network transports"
|
87
125
|
end
|
88
126
|
end
|