rubarb 0.2.0 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +89 -0
- data/examples/client.rb +3 -1
- data/examples/server.rb +1 -1
- data/lib/rubarb/connection.rb +61 -24
- data/lib/rubarb/incoming_connection.rb +9 -1
- data/lib/rubarb/outgoing_connection.rb +15 -0
- data/lib/rubarb/server.rb +21 -4
- data/spec/rubarb/connection_failure_spec.rb +0 -8
- data/spec/rubarb/connection_spec.rb +15 -10
- data/spec/rubarb/incoming_connection_spec.rb +6 -0
- data/spec/rubarb/integration_spec.rb +67 -8
- data/spec/rubarb/outgoing_connection_spec.rb +4 -0
- data/spec/rubarb/server_failure_spec.rb +124 -122
- data/spec/rubarb/server_spec.rb +4 -10
- data/spec/spec_helper.rb +28 -7
- metadata +79 -102
- data/.gitignore +0 -7
- data/README +0 -77
- data/Rakefile +0 -51
- data/VERSION +0 -1
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# rubaRb
|
2
|
+
### A Bidirectional Event Machine Based Remote Procedure Call Library for Ruby
|
3
|
+
|
4
|
+
This library uses two socket connections between a client and a server.
|
5
|
+
One is used for request / replies from the client to the server.
|
6
|
+
The other is used for remote calls made from the server to the client.
|
7
|
+
|
8
|
+
Each end publishes a single object on which methods can be called by the remote end.
|
9
|
+
All calls to the remote objects are asyncronous. Do not make any blocking calls in
|
10
|
+
the published object. Responses are return by calling the "reply" method on the responder object.
|
11
|
+
|
12
|
+
Server and Connection object may be created and started outside of EM::run,
|
13
|
+
but the Eventmachine reactor must be started somewhere in your application
|
14
|
+
|
15
|
+
|
16
|
+
## Compilation
|
17
|
+
|
18
|
+
rake build
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
gem install rubarb
|
23
|
+
|
24
|
+
## Server Example
|
25
|
+
|
26
|
+
class ServerApi
|
27
|
+
def time(responder)
|
28
|
+
puts "Server received time request"
|
29
|
+
responder.reply(Time.now)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
EM.run do
|
34
|
+
server = Rubarb::Server.new("127.0.0.1", 9441, ServerApi.new)
|
35
|
+
|
36
|
+
connections = {}
|
37
|
+
|
38
|
+
server.start do |client|
|
39
|
+
puts "Connection Made: #{client}"
|
40
|
+
client.name do |name|
|
41
|
+
connections[name] = client
|
42
|
+
client.errback do
|
43
|
+
puts "Connection Lost: #{name}"
|
44
|
+
connections.delete(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
EventMachine.add_periodic_timer(1) { puts "Connections: #{connections.keys.inspect}" }
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
## Client Example
|
56
|
+
|
57
|
+
class ClientApi
|
58
|
+
def initialize(name)
|
59
|
+
@name = name
|
60
|
+
end
|
61
|
+
def name(responder)
|
62
|
+
responder.reply(@name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
EM::run do
|
67
|
+
connection = Rubarb::Connection.new("127.0.0.1", 9441, ClientApi.new(ARGV[0]))
|
68
|
+
connection.errback do |error|
|
69
|
+
puts ("Connection Error: #{error}")
|
70
|
+
end
|
71
|
+
|
72
|
+
connection.start do
|
73
|
+
connection.time do |response|
|
74
|
+
puts "Server Said it is: #{response.strftime("%D")}"
|
75
|
+
end
|
76
|
+
|
77
|
+
EventMachine.add_timer(20) do
|
78
|
+
puts "stopping"
|
79
|
+
connection.stop
|
80
|
+
EM::stop
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
## Building
|
87
|
+
gem install jeweller
|
88
|
+
rake build
|
89
|
+
|
data/examples/client.rb
CHANGED
@@ -12,7 +12,7 @@ class ClientApi
|
|
12
12
|
end
|
13
13
|
|
14
14
|
EM::run do
|
15
|
-
connection = Rubarb::Connection.new("127.0.0.1",
|
15
|
+
connection = Rubarb::Connection.new("127.0.0.1", 9447, ClientApi.new(ARGV[0]))
|
16
16
|
connection.errback do |error|
|
17
17
|
puts ("Connection Error: #{error}")
|
18
18
|
end
|
@@ -22,6 +22,8 @@ EM::run do
|
|
22
22
|
puts "Server Said it is: #{response.strftime("%D")}"
|
23
23
|
end
|
24
24
|
|
25
|
+
connection.keep_alive_time=2
|
26
|
+
|
25
27
|
EventMachine.add_timer(20) do
|
26
28
|
puts "stopping"
|
27
29
|
connection.stop
|
data/examples/server.rb
CHANGED
data/lib/rubarb/connection.rb
CHANGED
@@ -13,8 +13,8 @@ module Rubarb
|
|
13
13
|
attr_accessor :id
|
14
14
|
attr_accessor :on_connection
|
15
15
|
attr_accessor :api
|
16
|
-
attr_accessor :errbacks
|
17
16
|
attr_accessor :insecure_methods
|
17
|
+
attr_accessor :parent
|
18
18
|
|
19
19
|
def post_init
|
20
20
|
@buffer = ""
|
@@ -24,6 +24,7 @@ module Rubarb
|
|
24
24
|
Rubarb::FastMessageProtocol.install(self)
|
25
25
|
send_data("5")
|
26
26
|
send_data(@id)
|
27
|
+
@parent.connection_completed(self) if @parent
|
27
28
|
end
|
28
29
|
|
29
30
|
def receive_message message
|
@@ -36,13 +37,11 @@ module Rubarb
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def unbind
|
39
|
-
|
40
|
+
@parent.connection_closed(self)
|
40
41
|
end
|
41
42
|
|
42
43
|
def call_errbacks(message)
|
43
|
-
@
|
44
|
-
e.call(message)
|
45
|
-
end
|
44
|
+
@parent.call_errbacks(message)
|
46
45
|
end
|
47
46
|
|
48
47
|
end
|
@@ -54,10 +53,11 @@ module Rubarb
|
|
54
53
|
attr_accessor :port
|
55
54
|
attr_accessor :on_connection
|
56
55
|
attr_accessor :api
|
57
|
-
attr_accessor :errbacks
|
58
56
|
attr_accessor :callback
|
59
57
|
attr_accessor :msg_id_generator
|
60
58
|
attr_accessor :insecure_methods
|
59
|
+
attr_accessor :parent
|
60
|
+
attr_accessor :keep_alive_time
|
61
61
|
|
62
62
|
def post_init
|
63
63
|
@buffer = ""
|
@@ -65,6 +65,7 @@ module Rubarb
|
|
65
65
|
|
66
66
|
def connection_completed
|
67
67
|
send_data("4")
|
68
|
+
@parent.connection_completed(self) if @parent
|
68
69
|
end
|
69
70
|
|
70
71
|
def receive_data data
|
@@ -75,17 +76,12 @@ module Rubarb
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def unbind
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
call_errbacks(ConnectionError.new)
|
82
|
-
end
|
79
|
+
cancel_keep_alive
|
80
|
+
@parent.connection_closed(self)
|
83
81
|
end
|
84
82
|
|
85
83
|
def call_errbacks(message)
|
86
|
-
@
|
87
|
-
e.call(message)
|
88
|
-
end
|
84
|
+
@parent.call_errbacks(message)
|
89
85
|
end
|
90
86
|
|
91
87
|
private
|
@@ -98,25 +94,65 @@ module Rubarb
|
|
98
94
|
incoming_connection.id = @id
|
99
95
|
incoming_connection.on_connection = @on_connection
|
100
96
|
incoming_connection.api = @api
|
101
|
-
incoming_connection.errbacks = @errbacks
|
102
97
|
incoming_connection.insecure_methods = @insecure_methods
|
103
|
-
|
98
|
+
incoming_connection.parent = @parent
|
104
99
|
end
|
105
100
|
end
|
106
101
|
end
|
107
102
|
end
|
108
103
|
|
109
104
|
class Connection
|
110
|
-
|
105
|
+
|
111
106
|
attr_reader :msg_id_generator
|
112
107
|
|
113
|
-
def
|
108
|
+
def remote_connection
|
109
|
+
@outgoing_connection
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize(host, port, api, insecure_methods=Default::INSECURE_METHODS, keep_alive_time = 0)
|
114
113
|
@host = host
|
115
114
|
@port = port
|
116
115
|
@api = api
|
117
116
|
@msg_id_generator = Id.new
|
118
117
|
@errbacks = []
|
119
118
|
@insecure_methods = insecure_methods
|
119
|
+
@connections = []
|
120
|
+
@keep_alive_time = keep_alive_time
|
121
|
+
end
|
122
|
+
|
123
|
+
def keep_alive_time=(keep_alive_time_seconds)
|
124
|
+
@keep_alive_time = keep_alive_time_seconds
|
125
|
+
if @outgoing_connection
|
126
|
+
EventMachine::schedule do
|
127
|
+
@outgoing_connection.keep_alive_time = @keep_alive_time
|
128
|
+
@outgoing_connection.reset_keep_alive
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def close_connections
|
134
|
+
EM.next_tick do
|
135
|
+
@connections.each { |conn| conn.close_connection_after_writing }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def connection_closed(connection)
|
140
|
+
@connections.delete(connection)
|
141
|
+
if !@connections.empty?
|
142
|
+
close_connections
|
143
|
+
else
|
144
|
+
call_errbacks(ConnectionError.new)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def connection_completed(connection)
|
149
|
+
@connections << connection
|
150
|
+
end
|
151
|
+
|
152
|
+
def call_errbacks(message)
|
153
|
+
@errbacks.each do |e|
|
154
|
+
e.call(message)
|
155
|
+
end
|
120
156
|
end
|
121
157
|
|
122
158
|
def errback & block
|
@@ -131,10 +167,11 @@ module Rubarb
|
|
131
167
|
connection.port = @port
|
132
168
|
connection.on_connection = block
|
133
169
|
connection.api = @api
|
134
|
-
connection.errbacks = @errbacks
|
135
170
|
connection.msg_id_generator = @msg_id_generator
|
136
171
|
connection.insecure_methods = @insecure_methods
|
137
|
-
|
172
|
+
connection.parent = self
|
173
|
+
connection.keep_alive_time = @keep_alive_time
|
174
|
+
@outgoing_connection = connection
|
138
175
|
end
|
139
176
|
rescue Exception => e
|
140
177
|
@errbacks.each do |errback|
|
@@ -147,18 +184,18 @@ module Rubarb
|
|
147
184
|
def method_missing(method, * args, & block)
|
148
185
|
EventMachine::schedule do
|
149
186
|
begin
|
150
|
-
@
|
187
|
+
@outgoing_connection.remote_call(method, args, & block)
|
151
188
|
rescue Exception => e
|
152
|
-
|
189
|
+
call_errbacks(e)
|
153
190
|
end
|
154
191
|
end
|
155
192
|
end
|
156
193
|
|
157
194
|
def stop(& callback)
|
158
195
|
EventMachine::schedule do
|
159
|
-
if @
|
196
|
+
if @outgoing_connection
|
160
197
|
EventMachine::next_tick do
|
161
|
-
@
|
198
|
+
@outgoing_connection.close_connection_after_writing
|
162
199
|
callback.call(true) if callback
|
163
200
|
end
|
164
201
|
else
|
@@ -9,10 +9,11 @@ module Rubarb
|
|
9
9
|
|
10
10
|
def receive_message(message)
|
11
11
|
id, method, args = unmarshal_call(message)
|
12
|
+
return unless method
|
12
13
|
responder = Responder.new(self, id)
|
13
14
|
begin
|
14
15
|
raise Rubarb::InsecureMethodCallError.new(method) if @insecure_methods.include?(method)
|
15
|
-
api.send(method, *[responder, *args])
|
16
|
+
api.send(method, *[responder, *args])
|
16
17
|
rescue Exception => e
|
17
18
|
reply("0", e)
|
18
19
|
end
|
@@ -20,6 +21,13 @@ module Rubarb
|
|
20
21
|
|
21
22
|
def reply(id, *args)
|
22
23
|
send_message(marshal_call(args.unshift(id)))
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset_keep_alive
|
27
|
+
end
|
28
|
+
|
29
|
+
def cancel_keep_alive
|
30
|
+
|
23
31
|
end
|
24
32
|
end
|
25
33
|
end
|
@@ -17,11 +17,26 @@ module Rubarb
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def remote_call(method, *args, &block)
|
20
|
+
reset_keep_alive
|
20
21
|
id = @msg_id_generator.next
|
21
22
|
@callback ||= {}
|
22
23
|
@callback[id] = block
|
23
24
|
send_message(marshal_call(id, method, *args))
|
24
25
|
end
|
25
26
|
|
27
|
+
def cancel_keep_alive
|
28
|
+
EventMachine::cancel_timer(@keep_alive_timer) if @keep_alive_timer
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset_keep_alive
|
32
|
+
cancel_keep_alive
|
33
|
+
return if @keep_alive_time.to_i == 0
|
34
|
+
|
35
|
+
@keep_alive_timer = EventMachine::add_timer(@keep_alive_time) do
|
36
|
+
send_message(marshal_call(""))
|
37
|
+
reset_keep_alive
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
26
41
|
end
|
27
42
|
end
|
data/lib/rubarb/server.rb
CHANGED
@@ -34,8 +34,8 @@ module Rubarb
|
|
34
34
|
|
35
35
|
def stop
|
36
36
|
EventMachine::next_tick do
|
37
|
-
@remote_connection.
|
38
|
-
end
|
37
|
+
@remote_connection.close_connection_after_writing
|
38
|
+
end if @remote_connection
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -47,7 +47,7 @@ module Rubarb
|
|
47
47
|
attr_reader :insecure_methods
|
48
48
|
attr_accessor :external_protocol
|
49
49
|
|
50
|
-
def initialize(host, port, api, insecure_methods=Default::INSECURE_METHODS)
|
50
|
+
def initialize(host, port, api, insecure_methods=Default::INSECURE_METHODS, keep_alive_time = 0)
|
51
51
|
@host = host
|
52
52
|
@port = port
|
53
53
|
@api = api
|
@@ -58,6 +58,16 @@ module Rubarb
|
|
58
58
|
@conn_id_generator = Id.new
|
59
59
|
@msg_id_generator = Id.new
|
60
60
|
@insecure_methods = insecure_methods
|
61
|
+
@keep_alive_time = keep_alive_time
|
62
|
+
end
|
63
|
+
|
64
|
+
def keep_alive_time=(keep_alive_seconds)
|
65
|
+
EventMachine::schedule do
|
66
|
+
@connections.each do |c|
|
67
|
+
c.keep_alive_time = @keep_alive_time = keep_alive_seconds
|
68
|
+
c.reset_keep_alive
|
69
|
+
end
|
70
|
+
end
|
61
71
|
end
|
62
72
|
|
63
73
|
def start(& callback)
|
@@ -72,6 +82,7 @@ module Rubarb
|
|
72
82
|
connection.unbindback = @unbind_block
|
73
83
|
connection.insecure_methods = @insecure_methods
|
74
84
|
connection.external_protocol = @external_protocol
|
85
|
+
connection.keep_alive_time = @keep_alive_time
|
75
86
|
@connections << connection
|
76
87
|
end
|
77
88
|
rescue Exception => e
|
@@ -97,7 +108,7 @@ module Rubarb
|
|
97
108
|
private #################################################################################
|
98
109
|
def close_all_connections
|
99
110
|
@connections.each do |connection|
|
100
|
-
connection.
|
111
|
+
connection.close_connection_after_writing
|
101
112
|
end
|
102
113
|
end
|
103
114
|
|
@@ -126,6 +137,7 @@ module Rubarb
|
|
126
137
|
attr_accessor :unbindback
|
127
138
|
attr_accessor :insecure_methods
|
128
139
|
attr_accessor :external_protocol
|
140
|
+
attr_accessor :keep_alive_time
|
129
141
|
|
130
142
|
include ConnectionId
|
131
143
|
|
@@ -138,7 +150,12 @@ module Rubarb
|
|
138
150
|
handshake(@buffer) if @conn_id.nil?
|
139
151
|
end
|
140
152
|
|
153
|
+
def cancel_keep_alive
|
154
|
+
#will be replaced by later included modules
|
155
|
+
end
|
156
|
+
|
141
157
|
def unbind
|
158
|
+
cancel_keep_alive
|
142
159
|
call_errbacks(ConnectionError.new)
|
143
160
|
@unbindback.call(self) if @unbindback
|
144
161
|
end
|
@@ -5,14 +5,6 @@ require 'rubarb/connection'
|
|
5
5
|
|
6
6
|
describe "Connection Failures" do
|
7
7
|
|
8
|
-
before(:each) do
|
9
|
-
@reactor = start_reactor
|
10
|
-
end
|
11
|
-
|
12
|
-
after(:each) do
|
13
|
-
stop_reactor(@reactor)
|
14
|
-
end
|
15
|
-
|
16
8
|
it "should fail to connect" do
|
17
9
|
@connection = Rubarb::Connection.new("127.0.0.1", 9441, mock("client api"))
|
18
10
|
|
@@ -8,14 +8,6 @@ require "rubarb/default"
|
|
8
8
|
describe Rubarb::Connection do
|
9
9
|
CUSTOM_INSECURE_METHODS = [:==, :===, :=~]
|
10
10
|
|
11
|
-
before(:all) do
|
12
|
-
@reactor = start_reactor
|
13
|
-
end
|
14
|
-
|
15
|
-
after(:all) do
|
16
|
-
stop_reactor(@reactor)
|
17
|
-
end
|
18
|
-
|
19
11
|
it "has an instance of Rubarb::Id" do
|
20
12
|
@connection = Rubarb::Connection.new("host", "port", "api")
|
21
13
|
@connection.msg_id_generator.class.should == Rubarb::Id
|
@@ -49,6 +41,10 @@ describe Rubarb::Connection do
|
|
49
41
|
@connection = connect(Rubarb::Connection.new("127.0.0.1", 9441, mock("client")))
|
50
42
|
end
|
51
43
|
|
44
|
+
after(:each) do
|
45
|
+
sync_stop(@server)
|
46
|
+
end
|
47
|
+
|
52
48
|
it "sets an instance of Rubarb::Id to remote_connection" do
|
53
49
|
@connection.remote_connection.msg_id_generator.class.should == Rubarb::Id
|
54
50
|
end
|
@@ -144,6 +140,10 @@ describe Rubarb::OutgoingHandler do
|
|
144
140
|
remote_call(:foo, "bary")
|
145
141
|
@sent_msg.should == marshal_call("00000001", :foo, "bary")
|
146
142
|
end
|
143
|
+
|
144
|
+
it "should receive empty message" do
|
145
|
+
proc {receive_data("")}.should_not raise_error
|
146
|
+
end
|
147
147
|
end
|
148
148
|
|
149
149
|
describe Rubarb::IncomingHandler do
|
@@ -177,11 +177,16 @@ describe Rubarb::IncomingHandler do
|
|
177
177
|
|
178
178
|
it "should errback if ids do not match" do
|
179
179
|
errback_msg = false
|
180
|
-
@
|
180
|
+
@parent = mock("parent")
|
181
|
+
@parent.should_receive(:call_errbacks) do |error|
|
182
|
+
error.message.should == "Handshake Failure"
|
183
|
+
end
|
184
|
+
|
185
|
+
@parent.should_receive(:connection_completed).with(self)
|
186
|
+
|
181
187
|
@id = "00000001"
|
182
188
|
|
183
189
|
connection_completed
|
184
190
|
receive_message("00000004")
|
185
|
-
errback_msg.should == "Handshake Failure"
|
186
191
|
end
|
187
192
|
end
|