bushido-faye 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/History.txt +247 -0
  2. data/README.rdoc +92 -0
  3. data/lib/faye-browser-min.js +1 -0
  4. data/lib/faye.rb +121 -0
  5. data/lib/faye/adapters/rack_adapter.rb +209 -0
  6. data/lib/faye/engines/connection.rb +60 -0
  7. data/lib/faye/engines/memory.rb +112 -0
  8. data/lib/faye/engines/proxy.rb +111 -0
  9. data/lib/faye/error.rb +49 -0
  10. data/lib/faye/mixins/logging.rb +47 -0
  11. data/lib/faye/mixins/publisher.rb +30 -0
  12. data/lib/faye/mixins/timeouts.rb +22 -0
  13. data/lib/faye/protocol/channel.rb +124 -0
  14. data/lib/faye/protocol/client.rb +378 -0
  15. data/lib/faye/protocol/extensible.rb +43 -0
  16. data/lib/faye/protocol/grammar.rb +58 -0
  17. data/lib/faye/protocol/publication.rb +5 -0
  18. data/lib/faye/protocol/server.rb +282 -0
  19. data/lib/faye/protocol/subscription.rb +24 -0
  20. data/lib/faye/transport/http.rb +76 -0
  21. data/lib/faye/transport/local.rb +22 -0
  22. data/lib/faye/transport/transport.rb +115 -0
  23. data/lib/faye/transport/web_socket.rb +99 -0
  24. data/lib/faye/util/namespace.rb +20 -0
  25. data/spec/browser.html +45 -0
  26. data/spec/encoding_helper.rb +7 -0
  27. data/spec/install.sh +78 -0
  28. data/spec/javascript/channel_spec.js +15 -0
  29. data/spec/javascript/client_spec.js +714 -0
  30. data/spec/javascript/engine/memory_spec.js +7 -0
  31. data/spec/javascript/engine_spec.js +417 -0
  32. data/spec/javascript/faye_spec.js +15 -0
  33. data/spec/javascript/grammar_spec.js +66 -0
  34. data/spec/javascript/node_adapter_spec.js +307 -0
  35. data/spec/javascript/publisher_spec.js +27 -0
  36. data/spec/javascript/server/connect_spec.js +168 -0
  37. data/spec/javascript/server/disconnect_spec.js +121 -0
  38. data/spec/javascript/server/extensions_spec.js +60 -0
  39. data/spec/javascript/server/handshake_spec.js +145 -0
  40. data/spec/javascript/server/integration_spec.js +124 -0
  41. data/spec/javascript/server/publish_spec.js +85 -0
  42. data/spec/javascript/server/subscribe_spec.js +247 -0
  43. data/spec/javascript/server/unsubscribe_spec.js +245 -0
  44. data/spec/javascript/server_spec.js +110 -0
  45. data/spec/javascript/transport_spec.js +130 -0
  46. data/spec/node.js +55 -0
  47. data/spec/phantom.js +17 -0
  48. data/spec/ruby/channel_spec.rb +17 -0
  49. data/spec/ruby/client_spec.rb +724 -0
  50. data/spec/ruby/engine/memory_spec.rb +7 -0
  51. data/spec/ruby/engine_examples.rb +427 -0
  52. data/spec/ruby/faye_spec.rb +14 -0
  53. data/spec/ruby/grammar_spec.rb +68 -0
  54. data/spec/ruby/publisher_spec.rb +27 -0
  55. data/spec/ruby/rack_adapter_spec.rb +236 -0
  56. data/spec/ruby/server/connect_spec.rb +170 -0
  57. data/spec/ruby/server/disconnect_spec.rb +120 -0
  58. data/spec/ruby/server/extensions_spec.rb +68 -0
  59. data/spec/ruby/server/handshake_spec.rb +143 -0
  60. data/spec/ruby/server/integration_spec.rb +126 -0
  61. data/spec/ruby/server/publish_spec.rb +81 -0
  62. data/spec/ruby/server/subscribe_spec.rb +247 -0
  63. data/spec/ruby/server/unsubscribe_spec.rb +247 -0
  64. data/spec/ruby/server_spec.rb +110 -0
  65. data/spec/ruby/transport_spec.rb +134 -0
  66. data/spec/spec_helper.rb +11 -0
  67. data/spec/testswarm +29 -0
  68. data/spec/thin_proxy.rb +37 -0
  69. metadata +302 -0
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+
3
+ describe Faye::Grammar do
4
+ describe :CHANNEL_NAME do
5
+ it "matches valid channel names" do
6
+ Faye::Grammar::CHANNEL_NAME.should =~ "/fo_o/$@()bar"
7
+ end
8
+
9
+ it "does not match channel patterns" do
10
+ Faye::Grammar::CHANNEL_NAME.should_not =~ "/foo/**"
11
+ end
12
+
13
+ it "does not match invalid channel names" do
14
+ Faye::Grammar::CHANNEL_NAME.should_not =~ "foo/$@()bar"
15
+ Faye::Grammar::CHANNEL_NAME.should_not =~ "/foo/$@()bar/"
16
+ Faye::Grammar::CHANNEL_NAME.should_not =~ "/fo o/$@()bar"
17
+ end
18
+ end
19
+
20
+ describe :CHANNEL_PATTERN do
21
+ it "does not match channel names" do
22
+ Faye::Grammar::CHANNEL_PATTERN.should_not =~ "/fo_o/$@()bar"
23
+ end
24
+
25
+ it "matches valid channel patterns" do
26
+ Faye::Grammar::CHANNEL_PATTERN.should =~ "/foo/**"
27
+ Faye::Grammar::CHANNEL_PATTERN.should =~ "/foo/*"
28
+ end
29
+
30
+ it "does not match invalid channel patterns" do
31
+ Faye::Grammar::CHANNEL_PATTERN.should_not =~ "/foo/**/*"
32
+ end
33
+ end
34
+
35
+ describe :ERROR do
36
+ it "matches an error with an argument" do
37
+ Faye::Grammar::ERROR.should =~ "402:xj3sjdsjdsjad:Unknown Client ID"
38
+ end
39
+
40
+ it "matches an error with many arguments" do
41
+ Faye::Grammar::ERROR.should =~ "403:xj3sjdsjdsjad,/foo/bar:Subscription denied"
42
+ end
43
+
44
+ it "matches an error with no arguments" do
45
+ Faye::Grammar::ERROR.should =~ "402::Unknown Client ID"
46
+ end
47
+
48
+ it "does not match an error with no code" do
49
+ Faye::Grammar::ERROR.should_not =~ ":xj3sjdsjdsjad:Unknown Client ID"
50
+ end
51
+
52
+ it "does not match an error with an invalid code" do
53
+ Faye::Grammar::ERROR.should_not =~ "40:xj3sjdsjdsjad:Unknown Client ID"
54
+ end
55
+ end
56
+
57
+ describe :VERSION do
58
+ it "matches a version number" do
59
+ Faye::Grammar::VERSION.should =~ "9"
60
+ Faye::Grammar::VERSION.should =~ "9.0.a-delta1"
61
+ end
62
+
63
+ it "does not match invalid version numbers" do
64
+ Faye::Grammar::VERSION.should_not =~ "9.0.a-delta1."
65
+ Faye::Grammar::VERSION.should_not =~ ""
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ require "spec_helper"
2
+
3
+ describe Faye::Publisher do
4
+ let(:publisher) { Object.new.extend(Faye::Publisher) }
5
+
6
+ describe "with subscribers that remove themselves" do
7
+ before do
8
+ @called_a = false
9
+ @called_b = false
10
+
11
+ handler = lambda do
12
+ @called_a = true
13
+ publisher.unbind(:event, &handler)
14
+ end
15
+
16
+ publisher.bind(:event, &handler)
17
+ publisher.bind(:event) { @called_b = true }
18
+ end
19
+
20
+ it "successfully calls all the callbacks" do
21
+ publisher.trigger(:event)
22
+ @called_a.should == true
23
+ @called_b.should == true
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,236 @@
1
+ require "spec_helper"
2
+ require "thin_proxy"
3
+
4
+ describe Faye::RackAdapter do
5
+ include Rack::Test::Methods
6
+ let(:adapter) { Faye::RackAdapter.new(options) }
7
+ let(:app) { ThinProxy.new(adapter) }
8
+ let(:options) { {:mount => "/bayeux", :timeout => 30} }
9
+ let(:server) { mock "server" }
10
+
11
+ after { app.stop }
12
+
13
+ let(:content_type) { last_response["Content-Type"] }
14
+ let(:content_length) { last_response["Content-Length"] }
15
+ let(:cache_control) { last_response["Cache-Control"] }
16
+ let(:access_control_origin) { last_response["Access-Control-Allow-Origin"] }
17
+ let(:json) { Yajl::Parser.parse(body) }
18
+ let(:body) { last_response.body }
19
+ let(:status) { last_response.status.to_i }
20
+
21
+ before do
22
+ Faye::Server.should_receive(:new).with(options).and_return server
23
+ adapter.stub(:get_client).and_return mock("client")
24
+ end
25
+
26
+ describe "POST requests" do
27
+ describe "with cross-origin access control" do
28
+ shared_examples_for "cross-origin request" do
29
+ before do
30
+ header "Origin", "http://example.com"
31
+ end
32
+
33
+ it "returns a matching cross-origin access control header" do
34
+ server.stub(:process).and_yield []
35
+ post "/bayeux", :message => '[]'
36
+ access_control_origin.should == "http://example.com"
37
+ end
38
+
39
+ it "forwards the message param onto the server" do
40
+ server.should_receive(:process).with({"channel" => "/plain"}, false).and_yield []
41
+ post "/bayeux", "message=%7B%22channel%22%3A%22%2Fplain%22%7D"
42
+ end
43
+
44
+ it "returns the server's response as JSON" do
45
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
46
+ post "/bayeux", "message=%5B%5D"
47
+ status.should == 200
48
+ content_type.should == "application/json"
49
+ content_length.should == "31"
50
+ json.should == ["channel" => "/meta/handshake"]
51
+ end
52
+
53
+ it "returns a 400 response if malformed JSON is given" do
54
+ server.should_not_receive(:process)
55
+ post "/bayeux", "message=%7B%5B"
56
+ status.should == 400
57
+ content_type.should == "text/plain"
58
+ end
59
+
60
+ it "returns a 404 if the path is not matched" do
61
+ server.should_not_receive(:process)
62
+ post "/blaf", 'message=%5B%5D'
63
+ status.should == 404
64
+ content_type.should == "text/plain"
65
+ end
66
+ end
67
+
68
+ describe "with text/plain" do
69
+ before { header "Content-Type", "text/plain" }
70
+ it_should_behave_like "cross-origin request"
71
+ end
72
+
73
+ describe "with application/xml" do
74
+ before { header "Content-Type", "application/xml" }
75
+ it_should_behave_like "cross-origin request"
76
+ end
77
+ end
78
+
79
+ describe "with application/json" do
80
+ before do
81
+ header "Content-Type", "application/json"
82
+ end
83
+
84
+ it "does not return an access control header" do
85
+ server.stub(:process).and_yield []
86
+ post "/bayeux", :message => '[]'
87
+ access_control_origin.should be_nil
88
+ end
89
+
90
+ it "forwards the POST body onto the server" do
91
+ server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
92
+ post "/bayeux", '{"channel":"/foo"}'
93
+ end
94
+
95
+ it "returns the server's response as JSON" do
96
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
97
+ post "/bayeux", '[]'
98
+ status.should == 200
99
+ content_type.should == "application/json"
100
+ content_length.should == "31"
101
+ json.should == ["channel" => "/meta/handshake"]
102
+ end
103
+
104
+ it "returns a 400 response if malformed JSON is given" do
105
+ server.should_not_receive(:process)
106
+ post "/bayeux", "[}"
107
+ status.should == 400
108
+ content_type.should == "text/plain"
109
+ end
110
+
111
+ it "returns a 404 if the path is not matched" do
112
+ server.should_not_receive(:process)
113
+ post "/blaf", "[]"
114
+ status.should == 404
115
+ content_type.should == "text/plain"
116
+ end
117
+ end
118
+
119
+ describe "with no content type" do
120
+ it "forwards the message param onto the server" do
121
+ server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
122
+ post "/bayeux", :message => '{"channel":"/foo"}'
123
+ end
124
+
125
+ it "returns the server's response as JSON" do
126
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
127
+ post "/bayeux", :message => '[]'
128
+ status.should == 200
129
+ content_type.should == "application/json"
130
+ content_length.should == "31"
131
+ json.should == ["channel" => "/meta/handshake"]
132
+ end
133
+
134
+ it "returns a 400 response if malformed JSON is given" do
135
+ server.should_not_receive(:process)
136
+ post "/bayeux", :message => "[}"
137
+ status.should == 400
138
+ content_type.should == "text/plain"
139
+ end
140
+
141
+ it "returns a 404 if the path is not matched" do
142
+ server.should_not_receive(:process)
143
+ post "/blaf", :message => "[]"
144
+ status.should == 404
145
+ content_type.should == "text/plain"
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "GET requests" do
151
+ let(:params) {{:message => '{"channel":"/foo"}', :jsonp => "callback"}}
152
+
153
+ describe "with valid params" do
154
+ before do
155
+ server.should_receive(:flush_connection).with("channel" => "/foo")
156
+ end
157
+
158
+ it "forwards the message param onto the server" do
159
+ server.should_receive(:process).with({"channel" => "/foo"}, false).and_yield []
160
+ get "/bayeux", params
161
+ end
162
+
163
+ it "returns the server's response as JavaScript" do
164
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
165
+ get "/bayeux", params
166
+ status.should == 200
167
+ content_type.should == "text/javascript"
168
+ content_length.should == "42"
169
+ body.should == 'callback([{"channel":"/meta/handshake"}]);'
170
+ end
171
+
172
+ it "does not let the client cache the response" do
173
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
174
+ get "/bayeux", params
175
+ cache_control.should == "no-cache, no-store"
176
+ end
177
+ end
178
+
179
+ describe "with an unknown path" do
180
+ it "returns a 404" do
181
+ server.should_not_receive(:process)
182
+ get "/blah", params
183
+ status.should == 404
184
+ content_type.should == "text/plain"
185
+ end
186
+ end
187
+
188
+ describe "missing jsonp" do
189
+ before do
190
+ params.delete(:jsonp)
191
+ server.should_receive(:flush_connection)
192
+ end
193
+
194
+ it "returns the server's response using the default callback" do
195
+ server.stub(:process).and_yield ["channel" => "/meta/handshake"]
196
+ get "/bayeux", params
197
+ status.should == 200
198
+ content_type.should == "text/javascript"
199
+ content_length.should == "47"
200
+ body.should == 'jsonpcallback([{"channel":"/meta/handshake"}]);'
201
+ end
202
+ end
203
+
204
+ shared_examples_for "bad GET request" do
205
+ it "does not call the server" do
206
+ server.should_not_receive(:process)
207
+ get "/bayeux", params
208
+ end
209
+
210
+ it "returns a 400 response" do
211
+ get "/bayeux", params
212
+ status.should == 400
213
+ content_type.should == "text/plain"
214
+ end
215
+ end
216
+
217
+ describe "with malformed JSON" do
218
+ before { params[:message] = "[}" }
219
+ it_should_behave_like "bad GET request"
220
+ end
221
+
222
+ describe "missing message" do
223
+ before { params.delete(:message) }
224
+ it_should_behave_like "bad GET request"
225
+ end
226
+
227
+ describe "for the client script" do
228
+ it "returns the client script" do
229
+ get "/bayeux.js"
230
+ status.should == 200
231
+ content_type.should == "text/javascript"
232
+ body.should =~ /function\(\)\{/
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,170 @@
1
+ require "spec_helper"
2
+
3
+ describe "server connect" do
4
+ let(:engine) { mock "engine" }
5
+ let(:server) { Faye::Server.new }
6
+
7
+ before do
8
+ Faye::Engine.stub(:get).and_return engine
9
+ end
10
+
11
+ describe :connect do
12
+ let(:client_id) { "fakeclientid" }
13
+ let(:message) {{"channel" => "/meta/connect",
14
+ "clientId" => "fakeclientid",
15
+ "connectionType" => "long-polling"
16
+ }}
17
+
18
+ describe "with valid paramters" do
19
+ before do
20
+ message["advice"] = {"timeout" => 60}
21
+ engine.should_receive(:client_exists).with(client_id).and_yield true
22
+ end
23
+
24
+ it "connects to the engine to wait for new messages" do
25
+ engine.should_receive(:connect).with(client_id, {"timeout" => 60})
26
+ server.connect(message) {}
27
+ end
28
+
29
+ it "returns a successful response and any queued messages" do
30
+ engine.stub(:connect).and_yield([{"channel" => "/x", "data" => "hello"}])
31
+ server.connect(message) do |response|
32
+ response.should == [
33
+ { "channel" => "/meta/connect",
34
+ "successful" => true,
35
+ "clientId" => client_id
36
+ }, {
37
+ "channel" => "/x",
38
+ "data" => "hello"
39
+ }
40
+ ]
41
+ end
42
+ end
43
+
44
+ describe "with a message id" do
45
+ before { message["id"] = "foo" }
46
+
47
+ it "returns the same id" do
48
+ engine.stub(:connect)
49
+ server.connect(message) do |response|
50
+ response.should == {
51
+ "channel" => "/meta/connect",
52
+ "successful" => true,
53
+ "clientId" => client_id,
54
+ "id" => "foo"
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "with an unknown client" do
62
+ before do
63
+ engine.should_receive(:client_exists).with(client_id).and_yield false
64
+ end
65
+
66
+ it "does not connect to the engine" do
67
+ engine.should_not_receive(:connect)
68
+ server.connect(message) {}
69
+ end
70
+
71
+ it "returns an unsuccessful response" do
72
+ server.connect(message) do |response|
73
+ response.should == {
74
+ "channel" => "/meta/connect",
75
+ "successful" => false,
76
+ "error" => "401:fakeclientid:Unknown client"
77
+ }
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "missing clientId" do
83
+ before do
84
+ message.delete("clientId")
85
+ engine.should_receive(:client_exists).with(nil).and_yield false
86
+ end
87
+
88
+ it "does not connect to the engine" do
89
+ engine.should_not_receive(:connect)
90
+ server.connect(message) {}
91
+ end
92
+
93
+ it "returns an unsuccessful response" do
94
+ server.connect(message) do |response|
95
+ response.should == {
96
+ "channel" => "/meta/connect",
97
+ "successful" => false,
98
+ "error" => "402:clientId:Missing required parameter"
99
+ }
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "missing connectionType" do
105
+ before do
106
+ message.delete("connectionType")
107
+ engine.should_receive(:client_exists).with(client_id).and_yield true
108
+ end
109
+
110
+ it "does not connect to the engine" do
111
+ engine.should_not_receive(:connect)
112
+ server.connect(message) {}
113
+ end
114
+
115
+ it "returns an unsuccessful response" do
116
+ server.connect(message) do |response|
117
+ response.should == {
118
+ "channel" => "/meta/connect",
119
+ "successful" => false,
120
+ "error" => "402:connectionType:Missing required parameter"
121
+ }
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "with an unknown connectionType" do
127
+ before do
128
+ message["connectionType"] = "flash"
129
+ engine.should_receive(:client_exists).with(client_id).and_yield true
130
+ end
131
+
132
+ it "does not connect to the engine" do
133
+ engine.should_not_receive(:connect)
134
+ server.connect(message) {}
135
+ end
136
+
137
+ it "returns an unsuccessful response" do
138
+ server.connect(message) do |response|
139
+ response.should == {
140
+ "channel" => "/meta/connect",
141
+ "successful" => false,
142
+ "error" => "301:flash:Connection types not supported"
143
+ }
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "with an error" do
149
+ before do
150
+ message["error"] = "invalid"
151
+ engine.should_receive(:client_exists).with(client_id).and_yield true
152
+ end
153
+
154
+ it "does not connect to the engine" do
155
+ engine.should_not_receive(:connect)
156
+ server.connect(message) {}
157
+ end
158
+
159
+ it "returns an unsuccessful response" do
160
+ server.connect(message) do |response|
161
+ response.should == {
162
+ "channel" => "/meta/connect",
163
+ "successful" => false,
164
+ "error" => "invalid"
165
+ }
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end