rubarb 0.2.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.
- 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
|