rubarb 0.2.0 → 0.2.11

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/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", 9441, ClientApi.new(ARGV[0]))
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
@@ -12,7 +12,7 @@ class ServerApi
12
12
  end
13
13
 
14
14
  EM.run do
15
- server = Rubarb::Server.new("127.0.0.1", 9441, ServerApi.new)
15
+ server = Rubarb::Server.new("127.0.0.1", 9447, ServerApi.new, Rubarb::Default::INSECURE_METHODS, 2)
16
16
 
17
17
  connections = {}
18
18
 
@@ -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
- call_errbacks(ConnectionError.new)
40
+ @parent.connection_closed(self)
40
41
  end
41
42
 
42
43
  def call_errbacks(message)
43
- @errbacks.each do |e|
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
- if @incoming_connection
79
- EM.next_tick { @incoming_connection.close_connection }
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
- @errbacks.each do |e|
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
- @incoming_connection = incoming_connection
98
+ incoming_connection.parent = @parent
104
99
  end
105
100
  end
106
101
  end
107
102
  end
108
103
 
109
104
  class Connection
110
- attr_reader :remote_connection
105
+
111
106
  attr_reader :msg_id_generator
112
107
 
113
- def initialize(host, port, api, insecure_methods=Default::INSECURE_METHODS)
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
- @remote_connection = connection
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
- @remote_connection.remote_call(method, args, & block)
187
+ @outgoing_connection.remote_call(method, args, & block)
151
188
  rescue Exception => e
152
- @remote_connection.call_errbacks(e)
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 @remote_connection
196
+ if @outgoing_connection
160
197
  EventMachine::next_tick do
161
- @remote_connection.close_connection
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.close_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.close_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
- @errbacks = [Proc.new { |error| errback_msg = error.message }]
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