pomelo-citrus-rpc 0.0.2

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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -0
  3. data/Rakefile +0 -0
  4. data/citrus-rpc.gemspec +32 -0
  5. data/example/client.rb +43 -0
  6. data/example/remote/test/service.rb +9 -0
  7. data/example/server.rb +20 -0
  8. data/lib/citrus-rpc.rb +16 -0
  9. data/lib/citrus-rpc/rpc-client/client.rb +328 -0
  10. data/lib/citrus-rpc/rpc-client/mailboxes/ws_mailbox.rb +164 -0
  11. data/lib/citrus-rpc/rpc-client/mailstation.rb +363 -0
  12. data/lib/citrus-rpc/rpc-client/proxy.rb +37 -0
  13. data/lib/citrus-rpc/rpc-client/router.rb +63 -0
  14. data/lib/citrus-rpc/rpc-server/acceptors/ws_acceptor.rb +143 -0
  15. data/lib/citrus-rpc/rpc-server/dispatcher.rb +36 -0
  16. data/lib/citrus-rpc/rpc-server/gateway.rb +58 -0
  17. data/lib/citrus-rpc/rpc-server/server.rb +92 -0
  18. data/lib/citrus-rpc/util/constants.rb +20 -0
  19. data/lib/citrus-rpc/util/utils.rb +42 -0
  20. data/lib/citrus-rpc/version.rb +7 -0
  21. data/spec/mock-remote/area/add_one_remote.rb +13 -0
  22. data/spec/mock-remote/area/add_three_remote.rb +9 -0
  23. data/spec/mock-remote/area/who_am_i_remote.rb +9 -0
  24. data/spec/mock-remote/connector/who_am_i_remote.rb +9 -0
  25. data/spec/rpc-client/client_spec.rb +166 -0
  26. data/spec/rpc-client/mailstaion_spec.rb +235 -0
  27. data/spec/rpc-client/proxy_spec.rb +8 -0
  28. data/spec/rpc-client/router_spec.rb +8 -0
  29. data/spec/rpc-client/ws_mailbox_spec.rb +144 -0
  30. data/spec/rpc-server/client/mock-tcp-client.rb +6 -0
  31. data/spec/rpc-server/client/mock-ws-client.rb +48 -0
  32. data/spec/rpc-server/dispatcher_spec.rb +88 -0
  33. data/spec/rpc-server/gateway_spec.rb +206 -0
  34. data/spec/rpc-server/server_spec.rb +79 -0
  35. data/spec/rpc-server/ws_acceptor_spec.rb +138 -0
  36. data/spec/spec_helper.rb +25 -0
  37. metadata +179 -0
@@ -0,0 +1,8 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+
7
+ describe Proxy do
8
+ end
@@ -0,0 +1,8 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+
7
+ describe Router do
8
+ end
@@ -0,0 +1,144 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 7 July 2014
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+
7
+ describe WsMailBox do
8
+
9
+ dirname = File.expand_path File.dirname(__FILE__)
10
+ records = [
11
+ { :namespace => 'user', :server_type => 'area', :path => dirname + '/../mock-remote/area' },
12
+ { :namespace => 'sys', :server_type => 'connector', :path => dirname + '/../mock-remote/connector' }
13
+ ]
14
+
15
+ port = 3333
16
+
17
+ server = { :server_id => 'area-server-1', :host => '127.0.0.1', :port => port }
18
+
19
+ value = 1
20
+ msg = {
21
+ 'namespace' => 'user',
22
+ 'server_type' => 'area',
23
+ 'service' => 'addOneRemote',
24
+ 'method' => 'do',
25
+ 'args' => [value]
26
+ };
27
+
28
+ describe '#create' do
29
+ it 'should be ok for creating a mailbox and connect to the right remote server' do
30
+ mailbox = WsMailBox.new server
31
+ expect(mailbox).to be
32
+
33
+ EM.run {
34
+ Server.new( :records => records, :port => port ).start
35
+
36
+ mailbox.connect { |err|
37
+ expect(err).to be_nil
38
+ EM.stop_event_loop
39
+ }
40
+ }
41
+ end
42
+
43
+ it 'should return an error if connect fail' do
44
+ bad_server = { :server_id => 'area-server-1', :host => '127.0.0.1', :port => -1000 }
45
+
46
+ mailbox = WsMailBox.new bad_server
47
+ expect(mailbox).to be
48
+
49
+ EM.run {
50
+ mailbox.connect { |err|
51
+ expect(err).to be
52
+ EM.stop_event_loop
53
+ }
54
+ }
55
+ end
56
+ end
57
+
58
+ describe '#send' do
59
+ it 'should send request to the right remote server and get response from callback' do
60
+ EM.run {
61
+ Server.new( :records => records, :port => port ).start
62
+
63
+ mailbox = WsMailBox.new server
64
+ expect(mailbox).to be
65
+
66
+ mailbox.connect { |err|
67
+ expect(err).to be_nil
68
+
69
+ mailbox.send(msg, nil, proc{ |send_err, err, res|
70
+ expect(res).to be
71
+ expect(res).to eql msg['args'][0] + 1
72
+ EM.stop_event_loop
73
+ })
74
+ }
75
+ }
76
+ end
77
+
78
+ it 'should distinguish different services and keep the right request/response relationship' do
79
+ callback_count = 0
80
+
81
+ value = 1
82
+ msg1 = {
83
+ 'namespace' => 'user',
84
+ 'server_type' => 'area',
85
+ 'service' => 'addOneRemote',
86
+ 'method' => 'do',
87
+ 'args' => [value]
88
+ }
89
+ msg2 = {
90
+ 'namespace' => 'user',
91
+ 'server_type' => 'area',
92
+ 'service' => 'addOneRemote',
93
+ 'method' => 'add_two',
94
+ 'args' => [value]
95
+ }
96
+ msg3 = {
97
+ 'namespace' => 'user',
98
+ 'server_type' => 'area',
99
+ 'service' => 'addThreeRemote',
100
+ 'method' => 'do',
101
+ 'args' => [value]
102
+ }
103
+
104
+ EM.run {
105
+ Server.new( :records => records, :port => port ).start
106
+
107
+ mailbox = WsMailBox.new server
108
+ expect(mailbox).to be
109
+
110
+ mailbox.connect { |err|
111
+ err.should be_nil
112
+
113
+ mailbox.send(msg1, nil, proc{ |send_err, err, res|
114
+ expect(res).to be
115
+ expect(res).to eql msg1['args'][0] + 1
116
+ callback_count = callback_count + 1
117
+ })
118
+
119
+ mailbox.send(msg2, nil, proc{ |send_err, err, res|
120
+ expect(res).to be
121
+ expect(res).to eql msg2['args'][0] + 2
122
+ callback_count = callback_count + 1
123
+ })
124
+
125
+ mailbox.send(msg3, nil, proc{ |send_err, err, res|
126
+ expect(res).to be
127
+ expect(res).to eql msg3['args'][0] + 3
128
+ callback_count = callback_count + 1
129
+ })
130
+ }
131
+
132
+ EM.add_timer(0.1) {
133
+ expect(callback_count).to eql 3
134
+ EM.stop_event_loop
135
+ }
136
+ }
137
+ end
138
+ end
139
+
140
+ describe '#close' do
141
+ it 'should emit a close event when mailbox close' do
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,6 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ class MockTcpClient
6
+ end
@@ -0,0 +1,48 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require 'websocket-eventmachine-client'
6
+
7
+ class MockWsClient
8
+ def initialize
9
+ @cur_id = 0
10
+ @requests = {}
11
+ end
12
+
13
+ def connect host, port, &block
14
+ @ws = WebSocket::EventMachine::Client.connect :uri => 'ws://' + host + ':' + port.to_s
15
+ @ws.onopen {
16
+ block.call
17
+ }
18
+ @ws.onmessage { |msg, type|
19
+ begin
20
+ pkg = JSON.parse msg
21
+ rescue => err
22
+ end
23
+
24
+ pkg_id = pkg['id']
25
+ pkg_resp = pkg['resp']
26
+
27
+ callback = @requests[pkg_id]
28
+ callback.call *pkg_resp
29
+
30
+ @requests.delete pkg_id
31
+ }
32
+ @ws.onerror { |err| }
33
+ @ws.onclose { |code, reason| }
34
+ end
35
+
36
+ def send msg, &callback
37
+ id = @cur_id
38
+ @requests[id] = callback
39
+
40
+ @cur_id = @cur_id + 1
41
+
42
+ @ws.send({ 'id' => id, 'msg' => msg }.to_json)
43
+ end
44
+
45
+ def close
46
+ @ws.close
47
+ end
48
+ end
@@ -0,0 +1,88 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+
7
+ describe Dispatcher do
8
+ include Dispatcher
9
+
10
+ services = {}
11
+ services['user'] = {}
12
+ services['sys'] = {}
13
+
14
+ services['user']['addOneRemote'] = Object.new
15
+ services['user']['addOneRemote'].define_singleton_method :do, proc{ |num, &block|
16
+ block.call nil, num + 1
17
+ }
18
+
19
+ services['sys']['addTwoRemote'] = Object.new
20
+ services['sys']['addTwoRemote'].define_singleton_method :do, proc{ |num, &block|
21
+ block.call nil, num + 2
22
+ }
23
+
24
+ it 'should dispatch message to the corresponding procedural' do
25
+ callback_count = 0
26
+
27
+ value = 0
28
+ msg1 = {
29
+ 'namespace' => 'user',
30
+ 'service' => 'addOneRemote',
31
+ 'method' => 'do',
32
+ 'args' => [value]
33
+ }
34
+ msg2 = {
35
+ 'namespace' => 'sys',
36
+ 'service' => 'addTwoRemote',
37
+ 'method' => 'do',
38
+ 'args' => [value]
39
+ }
40
+
41
+ dispatch(msg1, services) { |err, result|
42
+ expect(err).to be_nil
43
+ expect(result).to be
44
+ expect(result).to eql (value + 1)
45
+ callback_count = callback_count + 1
46
+ }
47
+
48
+ dispatch(msg2, services) { |err, result|
49
+ expect(err).to be_nil
50
+ expect(result).to be
51
+ expect(result).to eql (value + 2)
52
+ callback_count = callback_count + 1
53
+ }
54
+
55
+ expect(callback_count).to eql 2
56
+ end
57
+
58
+ it 'should return an error if the service or method not exist' do
59
+ callback_count = 0
60
+
61
+ value = 0
62
+ msg1 = {
63
+ 'namespace' => 'user',
64
+ 'service' => 'addXRemote',
65
+ 'method' => 'do',
66
+ 'args' => [value]
67
+ }
68
+ msg2 = {
69
+ 'namespace' => 'user',
70
+ 'service' => 'addOneRemote',
71
+ 'method' => 'foo',
72
+ 'args' => [value] }
73
+
74
+ dispatch(msg1, services) { |err, result|
75
+ expect(err).to be
76
+ expect(result).to be_nil
77
+ callback_count = callback_count + 1
78
+ }
79
+
80
+ dispatch(msg2, services) { |err, result|
81
+ expect(err).to be
82
+ expect(result).to be_nil
83
+ callback_count = callback_count + 1
84
+ }
85
+
86
+ expect(callback_count).to eql 2
87
+ end
88
+ end
@@ -0,0 +1,206 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+ require File.expand_path('../client/mock-ws-client', __FILE__)
7
+
8
+ describe Gateway do
9
+
10
+ services = {}
11
+ services['user'] = {}
12
+
13
+ services['user']['addOneRemote'] = Object.new
14
+ services['user']['addOneRemote'].define_singleton_method :do, proc{ |num, &block|
15
+ block.call nil, num + 1
16
+ }
17
+
18
+ services['user']['addTwoRemote'] = Object.new
19
+ services['user']['addTwoRemote'].define_singleton_method :do, proc{ |num, &block|
20
+ block.call nil, num + 2
21
+ }
22
+
23
+ port = 3333
24
+ args = { :services => services, :port => port }
25
+
26
+ describe '#start' do
27
+ it 'should be ok when listening on a valid port and closed event should be emitted when stopped' do
28
+ error_count = 0
29
+ close_count = 0
30
+
31
+ gateway = Gateway.new args
32
+ expect(gateway).to be
33
+
34
+ gateway.on(:error) { |err| error_count = error_count + 1 }
35
+ gateway.on(:closed) { |err| close_count = close_count + 1 }
36
+
37
+ EM.run {
38
+ gateway.start
39
+ gateway.stop
40
+
41
+ EM.add_timer(0.1) {
42
+ expect(error_count).to eql 0
43
+ expect(close_count).to eql 1
44
+ EM.stop_event_loop
45
+ }
46
+ }
47
+ end
48
+
49
+ it 'should emit an error when listening on a port in use' do
50
+ error_count = 0
51
+
52
+ gateway = Gateway.new :services => services, :port => 80
53
+ expect(gateway).to be
54
+
55
+ gateway.on(:error) { |err|
56
+ expect(err).to be
57
+ error_count = error_count + 1
58
+ }
59
+
60
+ EM.run {
61
+ gateway.start
62
+
63
+ EM.add_timer(0.1) {
64
+ expect(error_count).to eql 1
65
+ EM.stop_event_loop
66
+ }
67
+ }
68
+ end
69
+ end
70
+
71
+ describe '#service' do
72
+ it 'should provide rpc service to remote clients' do
73
+ callback_count = 0
74
+ value = 1
75
+ msg = {
76
+ 'namespace' => 'user',
77
+ 'service' => 'addOneRemote',
78
+ 'method' => 'do',
79
+ 'args' => [value]
80
+ }
81
+
82
+ gateway = Gateway.new args
83
+ expect(gateway).to be
84
+
85
+ EM.run {
86
+ gateway.start
87
+
88
+ client = MockWsClient.new
89
+ client.connect('127.0.0.1', port) {
90
+ client.send(msg) { |err, resp|
91
+ expect(resp).to eql (value + 1)
92
+ callback_count = callback_count + 1
93
+ }
94
+ }
95
+
96
+ EM.add_timer(0.1) {
97
+ expect(callback_count).to eql 1
98
+ EM.stop_event_loop
99
+ }
100
+ }
101
+ end
102
+
103
+ it "should send back an error if service doesn't exist" do
104
+ callback_count = 0
105
+ value = 1
106
+ msg = {
107
+ 'namespace' => 'user',
108
+ 'service' => 'addXRemote',
109
+ 'method' => 'do',
110
+ 'args' => [value]
111
+ }
112
+
113
+ gateway = Gateway.new args
114
+ expect(gateway).to be
115
+
116
+ EM.run do
117
+ gateway.start
118
+
119
+ client = MockWsClient.new
120
+ client.connect('127.0.0.1', port) {
121
+ client.send(msg) { |err, resp|
122
+ expect(err).to be
123
+ expect(resp).to be_nil
124
+ callback_count = callback_count + 1
125
+ }
126
+ }
127
+
128
+ EM.add_timer(0.1) {
129
+ expect(callback_count).to eql 1
130
+ EM.stop_event_loop
131
+ }
132
+ end
133
+ end
134
+
135
+ it 'should keep relationship with the request and response' do
136
+ callback_count = 0
137
+
138
+ value1 = 1
139
+ msg1 = {
140
+ 'namespace' => 'user',
141
+ 'service' => 'addOneRemote',
142
+ 'method' => 'do',
143
+ 'args' => [value1]
144
+ }
145
+
146
+ value2 = 2
147
+ msg2 = {
148
+ 'namespace' => 'user',
149
+ 'service' => 'addOneRemote',
150
+ 'method' => 'do',
151
+ 'args' => [value2]
152
+ }
153
+
154
+ value3 = 3
155
+ msg3 = {
156
+ 'namespace' => 'user',
157
+ 'service' => 'addTwoRemote',
158
+ 'method' => 'do',
159
+ 'args' => [value3]
160
+ }
161
+
162
+ value4 = 4
163
+ msg4 = {
164
+ 'namespace' => 'user',
165
+ 'service' => 'addTwoRemote',
166
+ 'method' => 'do',
167
+ 'args' => [value4]
168
+ }
169
+
170
+ gateway = Gateway.new args
171
+ expect(gateway).to be
172
+
173
+ EM.run {
174
+ gateway.start
175
+
176
+ client = MockWsClient.new
177
+ client.connect('127.0.0.1', port) {
178
+ client.send(msg1) { |err, resp|
179
+ expect(resp).to eql (value1 + 1)
180
+ callback_count = callback_count + 1
181
+ }
182
+
183
+ client.send(msg2) { |err, resp|
184
+ expect(resp).to eql (value2 + 1)
185
+ callback_count = callback_count + 1
186
+ }
187
+
188
+ client.send(msg3) { |err, resp|
189
+ expect(resp).to eql (value3 + 2)
190
+ callback_count = callback_count + 1
191
+ }
192
+
193
+ client.send(msg4) { |err, resp|
194
+ expect(resp).to eql (value4 + 2)
195
+ callback_count = callback_count + 1
196
+ }
197
+ }
198
+
199
+ EM::Timer.new(0.1) {
200
+ expect(callback_count).to eql 4
201
+ EM.stop_event_loop
202
+ }
203
+ }
204
+ end
205
+ end
206
+ end