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,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'rubarb/id'
|
3
|
+
|
4
|
+
include Rubarb
|
5
|
+
|
6
|
+
describe Id do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@id = Id.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "starts generator at 00000001" do
|
13
|
+
@id.next.should == "00000001"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "increments counter by 1 when next is called" do
|
17
|
+
@id.next
|
18
|
+
@id.next.should == "00000002"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "resets counter to one if id is 99999999" do
|
22
|
+
@id.instance_eval{ @id = 99999999 }
|
23
|
+
@id.next
|
24
|
+
@id.next.should == "00000001"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require "rubarb/incoming_connection"
|
4
|
+
require "rubarb/remote_call"
|
5
|
+
require "rubarb/default"
|
6
|
+
|
7
|
+
describe Rubarb::IncomingConnection do
|
8
|
+
include Rubarb::RemoteCall
|
9
|
+
include Rubarb::IncomingConnection
|
10
|
+
attr_reader :api
|
11
|
+
|
12
|
+
class TestApi
|
13
|
+
attr_accessor :dodo
|
14
|
+
def amethod(responder, dodo)
|
15
|
+
@dodo = dodo
|
16
|
+
end
|
17
|
+
def ==(rhs)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
@api = TestApi.new
|
23
|
+
@insecure_methods = Rubarb::Default::INSECURE_METHODS
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should receive message" do
|
27
|
+
receive_message(marshal_call("00000001", :amethod, "goo"))
|
28
|
+
@api.dodo.should == "goo"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should receive reply with exception when a non-existent method is called" do
|
32
|
+
should_receive(:reply).with do |id, exception|
|
33
|
+
id.should == "0"
|
34
|
+
exception.message.include?("undefined method").should == true
|
35
|
+
end
|
36
|
+
receive_message(marshal_call("00000001", :not_a_method, "foo"))
|
37
|
+
@api.should_not_receive(:send)
|
38
|
+
end
|
39
|
+
|
40
|
+
def blocks_method_in_receive_message(method)
|
41
|
+
@method = method
|
42
|
+
should_receive(:reply).with do |id, exception|
|
43
|
+
id.should == "0"
|
44
|
+
exception.message.should == "Remote client attempts to call method #{@method}, but was denied."
|
45
|
+
end
|
46
|
+
receive_message(marshal_call("00000001", method, "foo"))
|
47
|
+
@api.should_not_receive(:send)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "blocks :== in receive_message" do
|
51
|
+
blocks_method_in_receive_message(:==)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "blocks any insecure method in receive_message" do
|
55
|
+
Rubarb::Default::INSECURE_METHODS.each do |method|
|
56
|
+
blocks_method_in_receive_message(method)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "receives message with id, method, and *args" do
|
61
|
+
message = marshal_call("00000001", :amethod, "foo")
|
62
|
+
receive_message(message)
|
63
|
+
@api.dodo.should == "foo"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "replies message with id and args" do
|
67
|
+
message = marshal_call("00000001", :amethod, "foo")
|
68
|
+
receive_message(message)
|
69
|
+
self.should_receive(:send_message).with(marshal_call(["00000001", "result"]))
|
70
|
+
reply("00000001", "result")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "replies message with id 00000002 and args" do
|
74
|
+
message = marshal_call("00000002", :amethod, "foo")
|
75
|
+
receive_message(message)
|
76
|
+
self.should_receive(:send_message).with(marshal_call(["00000002", "result"]))
|
77
|
+
reply("00000002", "result")
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'rubarb/server'
|
4
|
+
require 'rubarb/connection'
|
5
|
+
|
6
|
+
describe "Server to Client communication and response" do
|
7
|
+
|
8
|
+
class TestClientApi
|
9
|
+
def name(responder)
|
10
|
+
responder.reply("Doug")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
@reactor = start_reactor
|
16
|
+
@server_api = mock("server")
|
17
|
+
@client_api = TestClientApi.new
|
18
|
+
|
19
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, @server_api)
|
20
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, @client_api)
|
21
|
+
end
|
22
|
+
|
23
|
+
after(:each) do
|
24
|
+
stop_reactor(@reactor)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should communicate with the client" do
|
28
|
+
@callback_called = false
|
29
|
+
|
30
|
+
@server.start do |new_client|
|
31
|
+
new_client.name do|result|
|
32
|
+
result.should == "Doug"
|
33
|
+
@callback_called = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@connection.start
|
38
|
+
|
39
|
+
wait_for {@callback_called}
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "Client to Server communication and response" do
|
46
|
+
class TestReply
|
47
|
+
attr_reader :message
|
48
|
+
|
49
|
+
def initialize(name)
|
50
|
+
@message = "How are you #{name}, my friend?"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class TestServerApi
|
55
|
+
attr_reader :hi_called
|
56
|
+
def hi(responder)
|
57
|
+
responder.reply("how are you?")
|
58
|
+
@hi_called = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def hello(responder, name)
|
62
|
+
responder.reply("how are you #{name}?")
|
63
|
+
end
|
64
|
+
|
65
|
+
def hello_friend(responder, name)
|
66
|
+
responder.reply(TestReply.new(name))
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :conn_id
|
70
|
+
def save_id(responder)
|
71
|
+
@conn_id = responder.conn_id
|
72
|
+
responder.reply(nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
@reactor = start_reactor
|
78
|
+
@server_api = TestServerApi.new
|
79
|
+
@client_api = mock("client")
|
80
|
+
|
81
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, @server_api)
|
82
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, @client_api)
|
83
|
+
end
|
84
|
+
|
85
|
+
after(:each) do
|
86
|
+
stop_reactor(@reactor)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "without a callback" do
|
90
|
+
@server.start
|
91
|
+
|
92
|
+
@connection.start do
|
93
|
+
@connection.hi
|
94
|
+
end
|
95
|
+
|
96
|
+
wait_for {@server_api.hi_called}
|
97
|
+
end
|
98
|
+
|
99
|
+
it "without parameters" do
|
100
|
+
@callback_called = false
|
101
|
+
@server.start
|
102
|
+
|
103
|
+
@connection.start do
|
104
|
+
@connection.hi do |response|
|
105
|
+
response.should == "how are you?"
|
106
|
+
@callback_called = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
wait_for {@callback_called}
|
111
|
+
end
|
112
|
+
|
113
|
+
it "with parameters" do
|
114
|
+
@callback_called = false
|
115
|
+
@server.start
|
116
|
+
@connection.start do
|
117
|
+
@connection.hello("Doug") do |response|
|
118
|
+
@callback_called = true
|
119
|
+
response.should == "how are you Doug?"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
wait_for {@callback_called}
|
123
|
+
end
|
124
|
+
|
125
|
+
it "with complex response" do
|
126
|
+
@callback_called = false
|
127
|
+
@server.start
|
128
|
+
@connection.start do
|
129
|
+
@connection.hello_friend("Doug") do |response|
|
130
|
+
@callback_called = true
|
131
|
+
response.message.should == "How are you Doug, my friend?"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
wait_for {@callback_called}
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can get connection id on connection" do
|
139
|
+
@callback_called = false
|
140
|
+
@new_connection_id = nil
|
141
|
+
@server.start do |new_connection|
|
142
|
+
@new_connection_id = new_connection.conn_id
|
143
|
+
end
|
144
|
+
|
145
|
+
@connection.start do
|
146
|
+
@connection.save_id do |response|
|
147
|
+
@callback_called = true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
wait_for {@callback_called}
|
152
|
+
@server_api.conn_id.should == @new_connection_id
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should close from server side" do
|
156
|
+
@server_side_client_proxy = nil
|
157
|
+
@server.start do |new_connection|
|
158
|
+
@server_side_client_proxy = new_connection
|
159
|
+
end
|
160
|
+
|
161
|
+
@connection.errback do
|
162
|
+
@client_side_closed = true
|
163
|
+
end
|
164
|
+
@connection.start
|
165
|
+
|
166
|
+
wait_for {!@server_side_client_proxy.nil?}
|
167
|
+
@server_side_client_proxy.stop
|
168
|
+
|
169
|
+
wait_for {@client_side_closed}
|
170
|
+
@client_side_closed.should == true
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require "rubarb/outgoing_connection"
|
4
|
+
require "rubarb/remote_call"
|
5
|
+
|
6
|
+
describe Rubarb::OutgoingConnection do
|
7
|
+
include Rubarb::RemoteCall
|
8
|
+
include Rubarb::OutgoingConnection
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@callback = {}
|
12
|
+
set_id("00000001")
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_id(id)
|
16
|
+
id_generator = mock("id")
|
17
|
+
id_generator.stub!(:next).and_return(id)
|
18
|
+
@msg_id_generator = id_generator
|
19
|
+
end
|
20
|
+
|
21
|
+
it "sets callback" do
|
22
|
+
block = Proc.new { puts "Hello" }
|
23
|
+
self.should_receive(:send_message)
|
24
|
+
remote_call(:amethod, "asdf", &block)
|
25
|
+
@callback["00000001"].should == block
|
26
|
+
end
|
27
|
+
|
28
|
+
it "executes callback block when receive_message is called" do
|
29
|
+
block = Proc.new do |v|
|
30
|
+
@value = v
|
31
|
+
end
|
32
|
+
should_receive(:send_message)
|
33
|
+
remote_call(:amethod, "asdf", &block)
|
34
|
+
receive_message(marshal_call("00000001", "value"))
|
35
|
+
@value.should == "value"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not execute callback block when receive_message is called with an exception" do
|
39
|
+
block = Proc.new { puts "Hello" }
|
40
|
+
should_receive(:send_message)
|
41
|
+
remote_call(:amethod, "asdf", &block)
|
42
|
+
should_receive(:call_errbacks)
|
43
|
+
@callback.should_not_receive(:call)
|
44
|
+
receive_message(marshal_call("00000001", Exception.new))
|
45
|
+
end
|
46
|
+
|
47
|
+
it "saves error message when receive_message is called with an exception" do
|
48
|
+
exception = Exception.new("Hello")
|
49
|
+
error = nil
|
50
|
+
@errbacks = [Proc.new { |e| error = e }]
|
51
|
+
block = Proc.new { puts "Hello" }
|
52
|
+
|
53
|
+
should_receive(:send_message)
|
54
|
+
remote_call(:amethod, "asdf", &block)
|
55
|
+
stub!(:call_errbacks).and_return(@errbacks.first.call(exception))
|
56
|
+
@callback.stub!(:call)
|
57
|
+
|
58
|
+
receive_message(marshal_call("00000001", exception))
|
59
|
+
|
60
|
+
error.class.should == Exception
|
61
|
+
error.message.should == (exception.message)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "has a message id argument for marshal_call" do
|
65
|
+
block = Proc.new { puts "Hello" }
|
66
|
+
should_receive(:marshal_call).with("00000001", :amethod, "asdf")
|
67
|
+
self.should_receive(:send_message)
|
68
|
+
remote_call(:amethod, "asdf", &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "generates a message id" do
|
72
|
+
set_id("00000002")
|
73
|
+
block = Proc.new { puts "Hello" }
|
74
|
+
should_receive(:marshal_call).with("00000002", :amethod, "asdf")
|
75
|
+
should_receive(:send_message)
|
76
|
+
remote_call(:amethod, "asdf", &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "pushes callback block to callback hash with id as key" do
|
80
|
+
@callback = {}
|
81
|
+
set_id("00000001")
|
82
|
+
block = Proc.new { puts "Hello" }
|
83
|
+
stub!(:send_message)
|
84
|
+
remote_call(:amethod, "asdf", &block)
|
85
|
+
@callback["00000001"].should == block
|
86
|
+
end
|
87
|
+
|
88
|
+
it "calls the corresponding block with given id" do
|
89
|
+
block = mock("block")
|
90
|
+
block.should_receive(:call).with("asdf")
|
91
|
+
@callback = { "00000002" => block }
|
92
|
+
receive_message(marshal_call("00000002", "asdf"))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "removes callback from hash after call" do
|
96
|
+
block = mock("block")
|
97
|
+
block.should_receive(:call).with("asdf")
|
98
|
+
@callback = { "00000002" => block }
|
99
|
+
receive_message(marshal_call("00000002", "asdf"))
|
100
|
+
@callback.should have(0).items
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'rubarb/remote_call'
|
4
|
+
describe Rubarb::RemoteCall do
|
5
|
+
include Rubarb::RemoteCall
|
6
|
+
|
7
|
+
it "should marshal a call" do
|
8
|
+
identity_test(:foo, "barr", "none")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should marshal with no args" do
|
12
|
+
identity_test(:food)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should work with multiple return values" do
|
16
|
+
serialized = marshal_call(:eat, "potatos", "carrots")
|
17
|
+
recovered_method, arg1, arg2 = unmarshal_call(serialized)
|
18
|
+
recovered_method.should == :eat
|
19
|
+
arg1.should == "potatos"
|
20
|
+
arg2.should == "carrots"
|
21
|
+
end
|
22
|
+
|
23
|
+
class Wine
|
24
|
+
attr_accessor :age, :content
|
25
|
+
def initialize(age, content)
|
26
|
+
@age = age
|
27
|
+
@content = content
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
return false unless self.age == other.age
|
32
|
+
return false unless self.content == other.content
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should handle complexy objects" do
|
38
|
+
identity_test(:make_grapes, Wine.new(15, 0.15), "grapes")
|
39
|
+
end
|
40
|
+
|
41
|
+
def identity_test(method, *args)
|
42
|
+
serialized = marshal_call(method, *args)
|
43
|
+
recovered_method, *recovered_args = unmarshal_call(serialized)
|
44
|
+
recovered_method.should == method
|
45
|
+
recovered_args.should == args
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require "rubarb/responder"
|
3
|
+
require "rubarb/incoming_connection"
|
4
|
+
require "rubarb/server"
|
5
|
+
|
6
|
+
include Rubarb
|
7
|
+
|
8
|
+
class MockHandler
|
9
|
+
include Listener
|
10
|
+
include IncomingConnection
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Responder do
|
14
|
+
before(:each) do
|
15
|
+
@responder = Responder.new(MockHandler.new, "00000001")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "sets id on initialize" do
|
19
|
+
@responder.message_id.should == "00000001"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets handler on initialize" do
|
23
|
+
@responder.handler.class.should == MockHandler
|
24
|
+
end
|
25
|
+
|
26
|
+
it "calls handler#reply with stored message_id" do
|
27
|
+
@responder.handler.should_receive(:reply).with("00000001", "Hello Doug")
|
28
|
+
@responder.reply("Hello Doug")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "calls handler's conn_id" do
|
32
|
+
@responder.handler.should_receive(:conn_id)
|
33
|
+
@responder.conn_id
|
34
|
+
end
|
35
|
+
end
|