rubarb 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/README +77 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/examples/client.rb +32 -0
- data/examples/server.rb +34 -0
- data/lib/dkbrpc.rb +4 -0
- data/lib/rubarb.rb +2 -0
- data/lib/rubarb/connection.rb +170 -0
- data/lib/rubarb/connection_error.rb +11 -0
- data/lib/rubarb/connection_id.rb +11 -0
- data/lib/rubarb/default.rb +15 -0
- data/lib/rubarb/fast_message_protocol.rb +109 -0
- data/lib/rubarb/id.rb +13 -0
- data/lib/rubarb/incoming_connection.rb +25 -0
- data/lib/rubarb/insecure_method_call_error.rb +11 -0
- data/lib/rubarb/outgoing_connection.rb +27 -0
- data/lib/rubarb/remote_call.rb +12 -0
- data/lib/rubarb/responder.rb +18 -0
- data/lib/rubarb/server.rb +185 -0
- data/spec/rubarb/connection_failure_spec.rb +71 -0
- data/spec/rubarb/connection_spec.rb +187 -0
- data/spec/rubarb/id_spec.rb +26 -0
- data/spec/rubarb/incoming_connection_spec.rb +79 -0
- data/spec/rubarb/integration_spec.rb +174 -0
- data/spec/rubarb/outgoing_connection_spec.rb +103 -0
- data/spec/rubarb/remote_call_spec.rb +48 -0
- data/spec/rubarb/responder_spec.rb +35 -0
- data/spec/rubarb/server_failure_spec.rb +261 -0
- data/spec/rubarb/server_spec.rb +201 -0
- data/spec/spec_helper.rb +32 -0
- metadata +146 -0
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'rubarb/server'
|
2
|
+
require 'rubarb/connection'
|
3
|
+
require 'rubarb/insecure_method_call_error'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
7
|
+
|
8
|
+
describe "Server Failures" do
|
9
|
+
|
10
|
+
CONNECTION_ERROR = "Connection Failure"
|
11
|
+
NO_METHOD_ERROR = "received unexpected message :not_a_method"
|
12
|
+
INSECURE_METHOD_ERROR = "Remote client attempts to call method class, but was denied."
|
13
|
+
|
14
|
+
before(:all) do
|
15
|
+
@port = 9441
|
16
|
+
end
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
@server = Rubarb::Server.new("127.0.0.1", @port, mock("server"))
|
20
|
+
@connection1 = Rubarb::Connection.new("127.0.0.1", @port, mock("client"))
|
21
|
+
@connection2 = Rubarb::Connection.new("127.0.0.1", @port, mock("client"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait_for_connections(n, ttl, &block)
|
25
|
+
if ttl <= 0
|
26
|
+
fail("TTL expired")
|
27
|
+
end
|
28
|
+
|
29
|
+
if @cons != n
|
30
|
+
EventMachine.add_periodic_timer(0.1) do
|
31
|
+
wait_for_connections(n, ttl-1, &block)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_messages(size, actual, expected)
|
39
|
+
actual.should have(size).items
|
40
|
+
(0...size).each { |i| actual[i].include?(expected[i]).should be_true }
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_server_failure(blocks)
|
44
|
+
server_block = blocks[:server]
|
45
|
+
client_block = blocks[:client]
|
46
|
+
server_errback = blocks[:server_errback]
|
47
|
+
client_errback = blocks[:client_errback]
|
48
|
+
connected = false
|
49
|
+
errbacked = false
|
50
|
+
thread = start_reactor
|
51
|
+
EM.schedule do
|
52
|
+
@server.errback { |e| server_errback.call(e) if server_errback; errbacked = true }
|
53
|
+
@server.start { |connection| server_block.call(connection) if server_block }
|
54
|
+
@connection1.errback { |e| client_errback.call(e) if client_errback; errbacked = true }
|
55
|
+
@connection1.start { client_block.call(@connection1) if client_block; connected = true }
|
56
|
+
end
|
57
|
+
wait_for{connected}
|
58
|
+
wait_for{errbacked}
|
59
|
+
stop_reactor(thread)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should handle the loss of a client" do
|
63
|
+
EM.run do
|
64
|
+
@cons = 0
|
65
|
+
@server.start do |client|
|
66
|
+
@cons += 1
|
67
|
+
if (@cons == 2)
|
68
|
+
@connection1.stop
|
69
|
+
end
|
70
|
+
client.errback do
|
71
|
+
@cons -= 1
|
72
|
+
if @cons == 1
|
73
|
+
EM.stop
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
@connection1.start
|
79
|
+
@connection2.start
|
80
|
+
|
81
|
+
wait_for_connections(2, 10) do
|
82
|
+
@connection1.stop
|
83
|
+
wait_for_connections(1, 10) do
|
84
|
+
@cons.should == 1
|
85
|
+
EM.stop
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should call errorback when port is already in use" do
|
92
|
+
errback_called = false
|
93
|
+
err_message = ""
|
94
|
+
|
95
|
+
thread = start_reactor
|
96
|
+
EM.run do
|
97
|
+
blocked_server = Rubarb::Server.new("127.0.0.1", @port, mock("server"))
|
98
|
+
|
99
|
+
blocked_server.errback do |e|
|
100
|
+
errback_called = true
|
101
|
+
err_message = e.message
|
102
|
+
end
|
103
|
+
|
104
|
+
@server.start
|
105
|
+
blocked_server.start
|
106
|
+
end
|
107
|
+
|
108
|
+
wait_for{errback_called}
|
109
|
+
stop_reactor(thread)
|
110
|
+
|
111
|
+
errback_called.should be_true
|
112
|
+
err_message.include?("acceptor").should be_true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "handles no method call on server side" do
|
116
|
+
err_messages = []
|
117
|
+
expected_messages = [NO_METHOD_ERROR, CONNECTION_ERROR]
|
118
|
+
client_errback_block = Proc.new { |e| err_messages << e.message }
|
119
|
+
client_block = Proc.new { |connection| connection.not_a_method }
|
120
|
+
run_server_failure({:client => client_block, :client_errback => client_errback_block})
|
121
|
+
check_messages(2, err_messages, expected_messages)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "handles no method call on client side" do
|
125
|
+
err_messages = []
|
126
|
+
expected_messages = [NO_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
|
127
|
+
server_errback_block = Proc.new { |e| err_messages << e.message }
|
128
|
+
server_block = Proc.new { |connection| connection.not_a_method }
|
129
|
+
run_server_failure({:server => server_block, :server_errback => server_errback_block})
|
130
|
+
check_messages(3, err_messages, expected_messages)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "handles insecure method call on server side" do
|
134
|
+
err_messages = []
|
135
|
+
expected_messages = [INSECURE_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
|
136
|
+
|
137
|
+
client_block = Proc.new do |connection|
|
138
|
+
connection.instance_eval("undef class")
|
139
|
+
connection.class do |result|
|
140
|
+
result.should be_a(InsecureMethodCallError)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
client_errback_block = Proc.new { |e| err_messages << e.message }
|
145
|
+
run_server_failure({:server => client_block, :server_errback => client_errback_block})
|
146
|
+
check_messages(3, err_messages, expected_messages)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "handles insecure method call on client side" do
|
150
|
+
err_messages = []
|
151
|
+
expected_messages = [INSECURE_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
|
152
|
+
|
153
|
+
server_block = Proc.new do |connection|
|
154
|
+
connection.instance_eval("undef class")
|
155
|
+
connection.class do |result|
|
156
|
+
result.should be_a(InsecureMethodCallError)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
server_errback_block = Proc.new { |e| err_messages << e.message }
|
161
|
+
run_server_failure({:server => server_block, :server_errback => server_errback_block})
|
162
|
+
check_messages(3, err_messages, expected_messages)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "removes unbinded connection from connections ivar" do
|
166
|
+
thread = start_reactor
|
167
|
+
connected = false
|
168
|
+
EM.schedule do
|
169
|
+
@server.start
|
170
|
+
@connection1.start {connected = true}
|
171
|
+
end
|
172
|
+
wait_for{connected}
|
173
|
+
@server.connections.size.times { @server.connections.first.unbind }
|
174
|
+
@server.connections.should have(0).items
|
175
|
+
stop_reactor(thread)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "removes one unbinded connection from connections ivar of size two" do
|
179
|
+
thread = start_reactor
|
180
|
+
connected = false
|
181
|
+
@server.start
|
182
|
+
@connection1.start
|
183
|
+
@connection2.start {connected = true}
|
184
|
+
wait_for{connected}
|
185
|
+
2.times { @server.connections.first.unbind }
|
186
|
+
@server.connections.should have(2).items
|
187
|
+
stop_reactor(thread)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "does not error out after calling stop twice consecutively" do
|
191
|
+
EventMachine.should_receive(:stop_server).once
|
192
|
+
thread = start_reactor
|
193
|
+
connected = false
|
194
|
+
@server.start
|
195
|
+
@connection1.start {connected = true}
|
196
|
+
wait_for{connected}
|
197
|
+
stopped = []
|
198
|
+
@server.stop do |result|
|
199
|
+
stopped << result
|
200
|
+
end
|
201
|
+
@server.stop do |result|
|
202
|
+
stopped << result
|
203
|
+
end
|
204
|
+
wait_for{stopped.size == 2}
|
205
|
+
stopped.should == [true, false]
|
206
|
+
stop_reactor(thread)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "executes all errback blocks when exception is thrown on client side" do
|
210
|
+
@errback1 = false
|
211
|
+
@errback2 = false
|
212
|
+
@errback3 = false
|
213
|
+
@errback4 = false
|
214
|
+
|
215
|
+
server_block = Proc.new do |connection|
|
216
|
+
connection.errback { |e| @errback1 = true }
|
217
|
+
connection.errback { |e| @errback2 = true }
|
218
|
+
connection.not_a_method
|
219
|
+
end
|
220
|
+
|
221
|
+
client_block = Proc.new do |connection|
|
222
|
+
connection.errback { |e| @errback3 = true }
|
223
|
+
connection.errback { |e| @errback4 = true }
|
224
|
+
end
|
225
|
+
|
226
|
+
run_server_failure({:server => server_block,
|
227
|
+
:client => client_block})
|
228
|
+
|
229
|
+
@errback1.should be_true
|
230
|
+
@errback2.should be_true
|
231
|
+
@errback3.should be_true
|
232
|
+
@errback4.should be_true
|
233
|
+
end
|
234
|
+
|
235
|
+
it "executes all errback blocks when exception is thrown on server side" do
|
236
|
+
@errback1 = false
|
237
|
+
@errback2 = false
|
238
|
+
@errback3 = false
|
239
|
+
@errback4 = false
|
240
|
+
|
241
|
+
server_block = Proc.new do |connection|
|
242
|
+
connection.errback { |e| @errback1 = true }
|
243
|
+
connection.errback { |e| @errback2 = true }
|
244
|
+
end
|
245
|
+
|
246
|
+
client_block = Proc.new do |connection|
|
247
|
+
connection.errback { |e| @errback3 = true }
|
248
|
+
connection.errback { |e| @errback4 = true }
|
249
|
+
connection.not_a_method
|
250
|
+
end
|
251
|
+
|
252
|
+
run_server_failure({:server => server_block,
|
253
|
+
:client => client_block})
|
254
|
+
|
255
|
+
@errback1.should be_true
|
256
|
+
@errback2.should be_true
|
257
|
+
@errback3.should be_true
|
258
|
+
@errback4.should be_true
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'rubarb/server'
|
3
|
+
require "rubarb/remote_call"
|
4
|
+
require 'rubarb/connection'
|
5
|
+
require 'rubarb/default'
|
6
|
+
|
7
|
+
include Rubarb
|
8
|
+
|
9
|
+
describe Listener do
|
10
|
+
include RemoteCall
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
extend(Listener)
|
14
|
+
@sent_data = ""
|
15
|
+
self.stub!(:send_data) do |data|
|
16
|
+
@sent_data = data
|
17
|
+
end
|
18
|
+
post_init
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_id(id)
|
22
|
+
id_generator = mock("id")
|
23
|
+
id_generator.stub!(:next).and_return(id)
|
24
|
+
@conn_id_generator = id_generator
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should generate and send connection ID" do
|
28
|
+
set_id("00000001")
|
29
|
+
receive_data("4")
|
30
|
+
@sent_data.should == "00000001"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should receive another connection" do
|
34
|
+
stub!(:send_message) do |message|
|
35
|
+
@sent_message = message
|
36
|
+
end
|
37
|
+
receive_data("5")
|
38
|
+
receive_data("00000005")
|
39
|
+
@sent_message.should == "00000005"
|
40
|
+
@conn_id.should == "00000005"
|
41
|
+
end
|
42
|
+
|
43
|
+
class OtherProtocol
|
44
|
+
|
45
|
+
attr_reader :buffer, :connection
|
46
|
+
|
47
|
+
def handle_connection(buffer, connection)
|
48
|
+
@buffer = buffer
|
49
|
+
@connection = connection
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should handle other protocols" do
|
55
|
+
@external_protocol = OtherProtocol.new
|
56
|
+
receive_data("1")
|
57
|
+
|
58
|
+
@external_protocol.buffer.should == "1"
|
59
|
+
@external_protocol.connection.should_not be_nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe Rubarb::Server do
|
64
|
+
|
65
|
+
before(:each) do
|
66
|
+
@reactor_thread = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
after(:each) do
|
70
|
+
stop_reactor(@reactor_thread) if @reactor_thread
|
71
|
+
end
|
72
|
+
|
73
|
+
it "has an instance of Rubarb::Id" do
|
74
|
+
server = Server.new("host", "port", "api")
|
75
|
+
server.conn_id_generator.class.should == Rubarb::Id
|
76
|
+
end
|
77
|
+
|
78
|
+
it "has an instance of Rubarb::Id for message ids" do
|
79
|
+
server = Server.new("host", "port", "api")
|
80
|
+
server.msg_id_generator.class.should == Rubarb::Id
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has an instance of insecure_methods" do
|
84
|
+
server = Server.new("host", "port", "api")
|
85
|
+
server.insecure_methods.class.should == Array
|
86
|
+
end
|
87
|
+
|
88
|
+
it "has default insecure_methods" do
|
89
|
+
server = Server.new("host", "port", "api")
|
90
|
+
server.insecure_methods.should == Rubarb::Default::INSECURE_METHODS
|
91
|
+
end
|
92
|
+
|
93
|
+
it "accepts custom insecure methods on initilization" do
|
94
|
+
server = Server.new("host", "port", "api", [:==, :===, :=~])
|
95
|
+
server.insecure_methods.should == [:==, :===, :=~]
|
96
|
+
end
|
97
|
+
|
98
|
+
def connect
|
99
|
+
@reactor_thread = start_reactor
|
100
|
+
connected = false
|
101
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, mock("server"))
|
102
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, mock("client"))
|
103
|
+
EM.schedule do
|
104
|
+
@server.start { |client| @client = client }
|
105
|
+
@connection.start { connected = true }
|
106
|
+
end
|
107
|
+
wait_for { connected }
|
108
|
+
end
|
109
|
+
|
110
|
+
it "sets instance of Rubarb::Id to each connection for connection ids" do
|
111
|
+
connect
|
112
|
+
generator_class = @server.connections.first.conn_id_generator.class
|
113
|
+
generator_class.should == Rubarb::Id
|
114
|
+
end
|
115
|
+
|
116
|
+
it "sets instance of Rubarb::Id to each connection for message ids" do
|
117
|
+
connect
|
118
|
+
generator_class = @server.connections.first.msg_id_generator.class
|
119
|
+
generator_class.should == Rubarb::Id
|
120
|
+
end
|
121
|
+
|
122
|
+
it "sets instance of insecure_methods on each connection" do
|
123
|
+
connect
|
124
|
+
insecure_methods = @server.connections.first.insecure_methods
|
125
|
+
insecure_methods.should == Rubarb::Default::INSECURE_METHODS
|
126
|
+
end
|
127
|
+
|
128
|
+
it "makes sure @conn_id_generator#next is called in handle_incoming" do
|
129
|
+
extend(Listener)
|
130
|
+
id_generator = mock("id")
|
131
|
+
id_generator.stub!(:next).and_return("00000001")
|
132
|
+
@conn_id_generator = id_generator
|
133
|
+
should_receive(:send_data).with("00000001")
|
134
|
+
stub!(:switch_protocol)
|
135
|
+
handle_incoming
|
136
|
+
@conn_id.should == "00000001"
|
137
|
+
end
|
138
|
+
|
139
|
+
it "makes two overlapping calls" do
|
140
|
+
@reactor_thread = start_reactor
|
141
|
+
connected = false
|
142
|
+
|
143
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, TestApi.new)
|
144
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, mock("client"))
|
145
|
+
EM.run do
|
146
|
+
@server.start
|
147
|
+
@connection.start do
|
148
|
+
@connection.get_one do |counter|
|
149
|
+
counter.should == 1
|
150
|
+
end
|
151
|
+
@connection.get_two do |counter|
|
152
|
+
counter.should == 2
|
153
|
+
end
|
154
|
+
connected = true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
wait_for { connected }
|
158
|
+
end
|
159
|
+
|
160
|
+
it "catches exceptions that occur during a remote call to client" do
|
161
|
+
connect
|
162
|
+
error = nil
|
163
|
+
@client.remote_connection.errbacks << proc { |e| error = e }
|
164
|
+
@client.remote_connection.should_receive(:remote_call).and_raise("Blah")
|
165
|
+
|
166
|
+
@client.foo
|
167
|
+
wait_for { error != nil }
|
168
|
+
|
169
|
+
error.to_s.should == "Blah"
|
170
|
+
EM.reactor_running?.should == true
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should catch exceptions in starting server" do
|
174
|
+
EventMachine.stub!(:start_server).and_raise("EMReactor Exception")
|
175
|
+
|
176
|
+
@reactor_thread = start_reactor
|
177
|
+
done = false
|
178
|
+
|
179
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, TestApi.new)
|
180
|
+
@server.errback do |error|
|
181
|
+
error.message.should == "EMReactor Exception"
|
182
|
+
done = true
|
183
|
+
end
|
184
|
+
|
185
|
+
@server.start
|
186
|
+
|
187
|
+
wait_for { done }
|
188
|
+
done.should == true
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class TestApi
|
194
|
+
def get_one(responder)
|
195
|
+
EM.add_timer(0.5) { responder.reply(1) }
|
196
|
+
end
|
197
|
+
|
198
|
+
def get_two(responder)
|
199
|
+
responder.reply(2)
|
200
|
+
end
|
201
|
+
end
|