pomelo-citrus-rpc 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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