rjr 0.18.2 → 0.19.1
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.
- checksums.yaml +4 -4
- data/Rakefile +2 -0
- data/bin/rjr-client +16 -9
- data/bin/rjr-server +2 -1
- data/examples/client.rb +21 -19
- data/examples/server.rb +1 -1
- data/examples/structured_server.rb +1 -0
- data/examples/tcp.rb +1 -0
- data/lib/rjr/common.rb +1 -226
- data/lib/rjr/core_ext.rb +63 -0
- data/lib/rjr/dispatcher.rb +75 -219
- data/lib/rjr/messages.rb +8 -0
- data/lib/rjr/messages/compressed.rb +264 -0
- data/lib/rjr/messages/notification.rb +95 -0
- data/lib/rjr/messages/request.rb +99 -0
- data/lib/rjr/messages/response.rb +128 -0
- data/lib/rjr/node.rb +100 -97
- data/lib/rjr/node_callback.rb +43 -0
- data/lib/rjr/nodes/amqp.rb +12 -11
- data/lib/rjr/nodes/easy.rb +4 -4
- data/lib/rjr/nodes/local.rb +13 -12
- data/lib/rjr/nodes/multi.rb +1 -1
- data/lib/rjr/nodes/tcp.rb +15 -13
- data/lib/rjr/nodes/template.rb +4 -4
- data/lib/rjr/nodes/unix.rb +15 -13
- data/lib/rjr/nodes/web.rb +15 -14
- data/lib/rjr/nodes/ws.rb +12 -11
- data/lib/rjr/request.rb +128 -0
- data/lib/rjr/result.rb +75 -0
- data/lib/rjr/util/args.rb +145 -0
- data/lib/rjr/{em_adapter.rb → util/em_adapter.rb} +0 -0
- data/lib/rjr/util/handles_methods.rb +115 -0
- data/lib/rjr/util/has_messages.rb +50 -0
- data/lib/rjr/{inspect.rb → util/inspect.rb} +1 -1
- data/lib/rjr/util/json_parser.rb +101 -0
- data/lib/rjr/util/logger.rb +128 -0
- data/lib/rjr/{thread_pool.rb → util/thread_pool.rb} +2 -0
- data/lib/rjr/version.rb +1 -1
- data/site/jrw.js +1 -1
- data/specs/args_spec.rb +144 -0
- data/specs/dispatcher_spec.rb +399 -211
- data/specs/em_adapter_spec.rb +31 -18
- data/specs/handles_methods_spec.rb +154 -0
- data/specs/has_messages_spec.rb +54 -0
- data/specs/inspect_spec.rb +1 -1
- data/specs/json_parser_spec.rb +169 -0
- data/specs/messages/notification_spec.rb +59 -0
- data/specs/messages/request_spec.rb +66 -0
- data/specs/messages/response_spec.rb +94 -0
- data/specs/node_callbacks_spec.rb +47 -0
- data/specs/node_spec.rb +465 -56
- data/specs/request_spec.rb +147 -0
- data/specs/result_spec.rb +144 -0
- data/specs/thread_pool_spec.rb +1 -1
- metadata +41 -11
- data/lib/rjr/errors.rb +0 -23
- data/lib/rjr/message.rb +0 -351
- data/lib/rjr/semaphore.rb +0 -58
- data/specs/message_spec.rb +0 -229
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'rjr/request'
|
2
|
+
|
3
|
+
module RJR
|
4
|
+
describe Request do
|
5
|
+
describe "#initialize" do
|
6
|
+
it "sets rjr method" do
|
7
|
+
request = Request.new :rjr_method => 'foobar'
|
8
|
+
request.rjr_method.should == 'foobar'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "sets rjr method args" do
|
12
|
+
args = ['a', 'r', 'g']
|
13
|
+
request = Request.new :rjr_method_args => args
|
14
|
+
request.rjr_method_args.should == args
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sets rjr headers" do
|
18
|
+
headers = {:h => :s}
|
19
|
+
request = Request.new :rjr_headers => headers
|
20
|
+
request.rjr_headers.should == headers
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets client ip" do
|
24
|
+
request = Request.new :rjr_client_ip => '127.0.0.1'
|
25
|
+
request.rjr_client_ip.should == '127.0.0.1'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets client port" do
|
29
|
+
request = Request.new :rjr_client_port => 1234
|
30
|
+
request.rjr_client_port.should == 1234
|
31
|
+
end
|
32
|
+
|
33
|
+
it "sets callback" do
|
34
|
+
request = Request.new :rjr_callback => :cb
|
35
|
+
request.rjr_callback.should == :cb
|
36
|
+
end
|
37
|
+
|
38
|
+
it "sets node" do
|
39
|
+
request = Request.new :rjr_node => :node
|
40
|
+
request.rjr_node.should == :node
|
41
|
+
end
|
42
|
+
|
43
|
+
it "sets node id" do
|
44
|
+
request = Request.new :rjr_node_id => :node_id
|
45
|
+
request.rjr_node_id.should == :node_id
|
46
|
+
end
|
47
|
+
|
48
|
+
it "node type" do
|
49
|
+
request = Request.new :rjr_node_type => :node_type
|
50
|
+
request.rjr_node_type.should == :node_type
|
51
|
+
end
|
52
|
+
|
53
|
+
it "rjr handler" do
|
54
|
+
request = Request.new :rjr_handler => :handler
|
55
|
+
request.rjr_handler.should == :handler
|
56
|
+
end
|
57
|
+
|
58
|
+
it "initialies new RJR::Arguments object from argument list" do
|
59
|
+
args = ['a', 'r', 'g']
|
60
|
+
request = Request.new :rjr_method_args => args
|
61
|
+
request.rjr_method_args.should == args
|
62
|
+
request.rjr_args.should be_an_instance_of(Arguments)
|
63
|
+
request.rjr_args.args.should == args
|
64
|
+
end
|
65
|
+
|
66
|
+
it "sets default result to nil" do
|
67
|
+
Request.new.result.should be_nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#handle" do
|
72
|
+
it "invokes the registered handler with args" do
|
73
|
+
received = nil
|
74
|
+
handler = proc { |arg| received = arg }
|
75
|
+
request = Request.new :rjr_handler => handler,
|
76
|
+
:rjr_method_args => ['foo']
|
77
|
+
request.handle
|
78
|
+
received.should == 'foo'
|
79
|
+
end
|
80
|
+
|
81
|
+
it "invokes registered handler in request contenxt" do
|
82
|
+
received = nil
|
83
|
+
handler = proc { |arg| received = @var }
|
84
|
+
|
85
|
+
request = Request.new :rjr_handler => handler
|
86
|
+
request.instance_variable_set(:@var, 'foo')
|
87
|
+
request.handle
|
88
|
+
received.should == 'foo'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns the handler return value" do
|
92
|
+
handler = proc { |arg| 42 }
|
93
|
+
request = Request.new :rjr_handler => handler
|
94
|
+
request.handle.should == 42
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#to_json" do
|
99
|
+
it "returns the request in json format" do
|
100
|
+
request = Request.new :rjr_method => 'foobar',
|
101
|
+
:rjr_method_args => [:a, :b],
|
102
|
+
:rjr_headers => { :header1 => :val1 },
|
103
|
+
:rjr_node_type => :local,
|
104
|
+
:rjr_node_id => :loc1
|
105
|
+
j = request.to_json
|
106
|
+
j.should include('"json_class":"RJR::Request"')
|
107
|
+
j.should include('"rjr_method":"foobar"')
|
108
|
+
j.should include('"rjr_method_args":["a","b"]')
|
109
|
+
j.should include('"rjr_headers":{"header1":"val1"}')
|
110
|
+
j.should include('"rjr_node_type":"local"')
|
111
|
+
j.should include('"rjr_node_id":"loc1"')
|
112
|
+
end
|
113
|
+
|
114
|
+
it "includes the result in the json" do
|
115
|
+
request = Request.new
|
116
|
+
result = RJR::Result.new :result => 42,
|
117
|
+
:error_code => 123,
|
118
|
+
:error_msg => 'error occurred',
|
119
|
+
:error_class => 'ArgumentError'
|
120
|
+
request.result = result
|
121
|
+
|
122
|
+
j = request.to_json
|
123
|
+
j.should include('"result":42')
|
124
|
+
j.should include('"error_code":123')
|
125
|
+
j.should include('"error_msg":"error occurred"')
|
126
|
+
j.should include('"error_class":"ArgumentError"')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "#json_create" do
|
131
|
+
it "returns a new request from json" do
|
132
|
+
j = '{"json_class":"RJR::Request","data":{"request":{"rjr_method":"foobar","rjr_method_args":["a","b"],"rjr_headers":{"foo":"bar"},"rjr_node_type":"local","rjr_node_id":"loc1"},"result":{"result":42,"error_code":null,"error_msg":null,"error_class":null}}}'
|
133
|
+
request = JSON.parse(j, :create_additions => true)
|
134
|
+
|
135
|
+
request.should be_an_instance_of(RJR::Request)
|
136
|
+
request.rjr_method.should == 'foobar'
|
137
|
+
request.rjr_method_args.should == ['a', 'b']
|
138
|
+
request.rjr_headers.should == { 'foo' => 'bar' }
|
139
|
+
request.rjr_node_type.should == 'local'
|
140
|
+
request.rjr_node_id.should == 'loc1'
|
141
|
+
|
142
|
+
request.result.should be_an_instance_of(RJR::Result)
|
143
|
+
request.result.result.should == 42
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end # describe Request
|
147
|
+
end # module RJR
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'rjr/result'
|
2
|
+
|
3
|
+
module RJR
|
4
|
+
describe Result do
|
5
|
+
describe "#initialize" do
|
6
|
+
it "initializes default attributes" do
|
7
|
+
result = Result.new
|
8
|
+
result.result.should be_nil
|
9
|
+
result.error_code.should be_nil
|
10
|
+
result.error_msg.should be_nil
|
11
|
+
result.error_class.should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "stores result" do
|
15
|
+
result = Result.new :result => 'foobar'
|
16
|
+
result.result.should == 'foobar'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "stores error" do
|
20
|
+
result = Result.new :error_code => 123, :error_msg => 'abc',
|
21
|
+
:error_class => ArgumentError
|
22
|
+
result.error_code.should == 123
|
23
|
+
result.error_msg.should == 'abc'
|
24
|
+
result.error_class.should == ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when an error code is not specified" do
|
28
|
+
it "should be marked as successful" do
|
29
|
+
result = Result.new
|
30
|
+
result.success.should == true
|
31
|
+
result.failed.should == false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when an error code is specified" do
|
36
|
+
it "should be marked as failed" do
|
37
|
+
result = Result.new :error_code => 123
|
38
|
+
result.success.should == false
|
39
|
+
result.failed.should == true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # describe #initialize
|
43
|
+
|
44
|
+
describe "#==" do
|
45
|
+
context "all fields are equal" do
|
46
|
+
it "returns true" do
|
47
|
+
Result.new.should == Result.new
|
48
|
+
Result.new(:result => 1).should == Result.new(:result => 1)
|
49
|
+
Result.new(:error_code => 50).should == Result.new(:error_code => 50)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "'success' is different" do
|
54
|
+
it "returns false" do
|
55
|
+
r1 = Result.new
|
56
|
+
r2 = Result.new
|
57
|
+
r1.success = true
|
58
|
+
r2.success = false
|
59
|
+
r1.should_not == r2
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "'failed' is different" do
|
64
|
+
it "returns false" do
|
65
|
+
r1 = Result.new
|
66
|
+
r2 = Result.new
|
67
|
+
r1.failed = true
|
68
|
+
r2.failed = false
|
69
|
+
r1.should_not == r2
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "'result' is different" do
|
74
|
+
it "returns false" do
|
75
|
+
r1 = Result.new
|
76
|
+
r2 = Result.new
|
77
|
+
r1.result = 1
|
78
|
+
r2.result = 2
|
79
|
+
r1.should_not == r2
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "'error_code' is different" do
|
84
|
+
it "returns false" do
|
85
|
+
r1 = Result.new
|
86
|
+
r2 = Result.new
|
87
|
+
r1.error_code = 1
|
88
|
+
r2.error_code = 2
|
89
|
+
r1.should_not == r2
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "'error_msg' is different" do
|
94
|
+
it "returns false" do
|
95
|
+
r1 = Result.new
|
96
|
+
r2 = Result.new
|
97
|
+
r1.error_msg = 'something'
|
98
|
+
r2.error_msg = 'bad'
|
99
|
+
r1.should_not == r2
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "'error_class' is different" do
|
104
|
+
it "returns false" do
|
105
|
+
r1 = Result.new
|
106
|
+
r2 = Result.new
|
107
|
+
r1.error_class = 'something'
|
108
|
+
r2.error_class = 'bad'
|
109
|
+
r1.should_not == r2
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end # describe #==
|
113
|
+
|
114
|
+
describe "#invalid_request" do
|
115
|
+
it "is an instance of result" do
|
116
|
+
Result.invalid_request.should be_an_instance_of(Result)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "has error code -32600" do
|
120
|
+
Result.invalid_request.error_code.should == -32600
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has error message 'Invalid Request'" do
|
124
|
+
Result.invalid_request.error_msg.should == "Invalid Request"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#method_not_found" do
|
129
|
+
it "is an instance of result" do
|
130
|
+
Result.method_not_found('foobar').should be_an_instance_of(Result)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "has error code -32602" do
|
134
|
+
Result.method_not_found('foobar').error_code.should == -32602
|
135
|
+
end
|
136
|
+
|
137
|
+
it "has error message 'Method '<name>' not found'" do
|
138
|
+
Result.method_not_found('foobar').error_msg.should ==
|
139
|
+
"Method 'foobar' not found"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end # describe Result
|
144
|
+
end # module RJR
|
data/specs/thread_pool_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rjr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mo Morsi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -68,17 +68,27 @@ files:
|
|
68
68
|
- examples/local.rb
|
69
69
|
- examples/complete.rb
|
70
70
|
- examples/amqp.rb
|
71
|
+
- examples/structured_server.rb
|
71
72
|
- examples/client.rb
|
72
73
|
- examples/unix.rb
|
73
|
-
- lib/rjr/
|
74
|
-
- lib/rjr/
|
75
|
-
- lib/rjr/
|
76
|
-
- lib/rjr/
|
74
|
+
- lib/rjr/node_callback.rb
|
75
|
+
- lib/rjr/messages/compressed.rb
|
76
|
+
- lib/rjr/messages/response.rb
|
77
|
+
- lib/rjr/messages/request.rb
|
78
|
+
- lib/rjr/messages/notification.rb
|
79
|
+
- lib/rjr/messages.rb
|
77
80
|
- lib/rjr/version.rb
|
78
81
|
- lib/rjr/node.rb
|
79
82
|
- lib/rjr/common.rb
|
80
|
-
- lib/rjr/
|
81
|
-
- lib/rjr/
|
83
|
+
- lib/rjr/util/logger.rb
|
84
|
+
- lib/rjr/util/em_adapter.rb
|
85
|
+
- lib/rjr/util/handles_methods.rb
|
86
|
+
- lib/rjr/util/inspect.rb
|
87
|
+
- lib/rjr/util/has_messages.rb
|
88
|
+
- lib/rjr/util/thread_pool.rb
|
89
|
+
- lib/rjr/util/json_parser.rb
|
90
|
+
- lib/rjr/util/args.rb
|
91
|
+
- lib/rjr/request.rb
|
82
92
|
- lib/rjr/nodes/web.rb
|
83
93
|
- lib/rjr/nodes/udp.rb
|
84
94
|
- lib/rjr/nodes/tcp.rb
|
@@ -94,6 +104,8 @@ files:
|
|
94
104
|
- lib/rjr/nodes/unix.rb
|
95
105
|
- lib/rjr/nodes/missing.rb
|
96
106
|
- lib/rjr/nodes/multi.rb
|
107
|
+
- lib/rjr/core_ext.rb
|
108
|
+
- lib/rjr/result.rb
|
97
109
|
- lib/rjr/dispatcher.rb
|
98
110
|
- site/index.html
|
99
111
|
- site/jquery-latest.js
|
@@ -102,8 +114,18 @@ files:
|
|
102
114
|
- LICENSE
|
103
115
|
- Rakefile
|
104
116
|
- README.md
|
117
|
+
- specs/json_parser_spec.rb
|
118
|
+
- specs/messages/response_spec.rb
|
119
|
+
- specs/messages/request_spec.rb
|
120
|
+
- specs/messages/notification_spec.rb
|
105
121
|
- specs/dispatcher_spec.rb
|
122
|
+
- specs/handles_methods_spec.rb
|
123
|
+
- specs/has_messages_spec.rb
|
106
124
|
- specs/node_spec.rb
|
125
|
+
- specs/args_spec.rb
|
126
|
+
- specs/request_spec.rb
|
127
|
+
- specs/node_callbacks_spec.rb
|
128
|
+
- specs/result_spec.rb
|
107
129
|
- specs/nodes/unix_spec.rb
|
108
130
|
- specs/nodes/multi_spec.rb
|
109
131
|
- specs/nodes/tcp_spec.rb
|
@@ -114,7 +136,6 @@ files:
|
|
114
136
|
- specs/nodes/easy_spec.rb
|
115
137
|
- specs/inspect_spec.rb
|
116
138
|
- specs/thread_pool_spec.rb
|
117
|
-
- specs/message_spec.rb
|
118
139
|
- specs/em_adapter_spec.rb
|
119
140
|
- bin/rjr-server
|
120
141
|
- bin/rjr-client
|
@@ -148,13 +169,23 @@ requirements:
|
|
148
169
|
- The eventmachine_httpserver and em-http-request gems are needed to use the web node
|
149
170
|
- The em-websocket and em-websocket-client gems are needed to use the web socket node
|
150
171
|
rubyforge_project:
|
151
|
-
rubygems_version: 2.0.
|
172
|
+
rubygems_version: 2.0.14
|
152
173
|
signing_key:
|
153
174
|
specification_version: 4
|
154
175
|
summary: JSON RPC server and client library over amqp, websockets, http, etc
|
155
176
|
test_files:
|
177
|
+
- specs/json_parser_spec.rb
|
178
|
+
- specs/messages/response_spec.rb
|
179
|
+
- specs/messages/request_spec.rb
|
180
|
+
- specs/messages/notification_spec.rb
|
156
181
|
- specs/dispatcher_spec.rb
|
182
|
+
- specs/handles_methods_spec.rb
|
183
|
+
- specs/has_messages_spec.rb
|
157
184
|
- specs/node_spec.rb
|
185
|
+
- specs/args_spec.rb
|
186
|
+
- specs/request_spec.rb
|
187
|
+
- specs/node_callbacks_spec.rb
|
188
|
+
- specs/result_spec.rb
|
158
189
|
- specs/nodes/unix_spec.rb
|
159
190
|
- specs/nodes/multi_spec.rb
|
160
191
|
- specs/nodes/tcp_spec.rb
|
@@ -165,6 +196,5 @@ test_files:
|
|
165
196
|
- specs/nodes/easy_spec.rb
|
166
197
|
- specs/inspect_spec.rb
|
167
198
|
- specs/thread_pool_spec.rb
|
168
|
-
- specs/message_spec.rb
|
169
199
|
- specs/em_adapter_spec.rb
|
170
200
|
has_rdoc:
|
data/lib/rjr/errors.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# RJR Errors
|
2
|
-
#
|
3
|
-
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the Apache License, Version 2.0
|
5
|
-
|
6
|
-
module RJR
|
7
|
-
|
8
|
-
# The RJR::Errors module provides a mechanism to dynamically create errors
|
9
|
-
# on demand as they are needed. At some point this will go away / be replaced
|
10
|
-
# with a more rigidly / fixed defined error heirarchy.
|
11
|
-
module Errors
|
12
|
-
|
13
|
-
# Catches all Errors constants and define new RuntimeError subclass
|
14
|
-
def self.const_missing(error_name) # :nodoc:
|
15
|
-
if error_name.to_s =~ /Error\z/
|
16
|
-
const_set(error_name, Class.new(RuntimeError))
|
17
|
-
else
|
18
|
-
super
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
data/lib/rjr/message.rb
DELETED
@@ -1,351 +0,0 @@
|
|
1
|
-
# RJR Message
|
2
|
-
#
|
3
|
-
# Representations of json-rpc messages in accordance with the standard
|
4
|
-
#
|
5
|
-
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
6
|
-
# Licensed under the Apache License, Version 2.0
|
7
|
-
|
8
|
-
require 'json'
|
9
|
-
require 'rjr/common'
|
10
|
-
|
11
|
-
module RJR
|
12
|
-
|
13
|
-
# Message sent from client to server to invoke a json-rpc method
|
14
|
-
class RequestMessage
|
15
|
-
# Message string received from the source
|
16
|
-
attr_accessor :json_message
|
17
|
-
|
18
|
-
# Method source is invoking on the destination
|
19
|
-
attr_accessor :jr_method
|
20
|
-
|
21
|
-
# Arguments source is passing to destination method
|
22
|
-
attr_accessor :jr_args
|
23
|
-
|
24
|
-
# ID of the message in accordance w/ json-rpc specification
|
25
|
-
attr_accessor :msg_id
|
26
|
-
|
27
|
-
# Optional headers to add to json outside of standard json-rpc request
|
28
|
-
attr_accessor :headers
|
29
|
-
|
30
|
-
# RJR Request Message initializer
|
31
|
-
#
|
32
|
-
# This should be invoked with one of two argument sets. If creating a new message
|
33
|
-
# to send to the server, specify :method, :args, and :headers to include in the message
|
34
|
-
# (message id will be autogenerated). If handling an new request message sent from the
|
35
|
-
# client, simply specify :message and optionally any additional headers (they will
|
36
|
-
# be merged with the headers contained in the message)
|
37
|
-
#
|
38
|
-
# @param [Hash] args options to set on request
|
39
|
-
# @option args [String] :message json string received from sender
|
40
|
-
# @option args [Hash] :headers optional headers to set in request and subsequent messages
|
41
|
-
# @option args [String] :method method to invoke on server
|
42
|
-
# @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
|
43
|
-
def initialize(args = {})
|
44
|
-
if args.has_key?(:message)
|
45
|
-
begin
|
46
|
-
@json_message = args[:message]
|
47
|
-
request = RJR.parse_json(@json_message)
|
48
|
-
@jr_method = request['method']
|
49
|
-
@jr_args = request['params']
|
50
|
-
@msg_id = request['id']
|
51
|
-
@headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
|
52
|
-
|
53
|
-
request.keys.select { |k|
|
54
|
-
!['jsonrpc', 'id', 'method', 'params'].include?(k)
|
55
|
-
}.each { |k| @headers[k] = request[k] }
|
56
|
-
|
57
|
-
rescue Exception => e
|
58
|
-
#puts "Exception Parsing Request #{e}"
|
59
|
-
raise e
|
60
|
-
end
|
61
|
-
|
62
|
-
elsif args.has_key?(:method)
|
63
|
-
@jr_method = args[:method]
|
64
|
-
@jr_args = args[:args]
|
65
|
-
@headers = args[:headers]
|
66
|
-
@msg_id = gen_uuid
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Class helper to determine if the specified string is a valid json-rpc
|
72
|
-
# method request
|
73
|
-
# @param [String] message string message to check
|
74
|
-
# @return [true,false] indicating if message is request message
|
75
|
-
def self.is_request_message?(message)
|
76
|
-
begin
|
77
|
-
# FIXME log error
|
78
|
-
parsed = RJR.parse_json(message)
|
79
|
-
parsed.has_key?('method') && parsed.has_key?('id')
|
80
|
-
rescue Exception => e
|
81
|
-
false
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Convert request message to string json format
|
86
|
-
def to_s
|
87
|
-
request = { 'jsonrpc' => '2.0',
|
88
|
-
'method' => @jr_method,
|
89
|
-
'params' => @jr_args }
|
90
|
-
request['id'] = @msg_id unless @msg_id.nil?
|
91
|
-
request.merge!(@headers) unless @headers.nil?
|
92
|
-
request.to_json.to_s
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
# Message sent from server to client in response to json-rpc request message
|
98
|
-
class ResponseMessage
|
99
|
-
# Message string received from the source
|
100
|
-
attr_accessor :json_message
|
101
|
-
|
102
|
-
# ID of the message in accordance w/ json-rpc specification
|
103
|
-
attr_accessor :msg_id
|
104
|
-
|
105
|
-
# Result encapsulated in the response message
|
106
|
-
# @see RJR::Result
|
107
|
-
attr_accessor :result
|
108
|
-
|
109
|
-
# Optional headers to add to json outside of standard json-rpc request
|
110
|
-
attr_accessor :headers
|
111
|
-
|
112
|
-
# ResponseMessage initializer
|
113
|
-
#
|
114
|
-
# This should be invoked with one of two argument sets. If creating a new message
|
115
|
-
# to send to the client, specify :id, :result, and :headers to include in the message.
|
116
|
-
# If handling an new request message sent from the client, simply specify :message
|
117
|
-
# and optionally any additional headers (they will be merged with the headers contained
|
118
|
-
# in the message)
|
119
|
-
#
|
120
|
-
# @param [Hash] args options to set on request
|
121
|
-
# @option args [String] :message json string received from sender
|
122
|
-
# @option args [Hash] :headers optional headers to set in request and subsequent messages
|
123
|
-
# @option args [String] :id id to set in response message, should be same as that in received message
|
124
|
-
# @option args [RJR::Result] :result result of json-rpc method invocation
|
125
|
-
def initialize(args = {})
|
126
|
-
if args.has_key?(:message)
|
127
|
-
@json_message = args[:message]
|
128
|
-
response = RJR.parse_json(@json_message)
|
129
|
-
@msg_id = response['id']
|
130
|
-
@result = Result.new
|
131
|
-
@result.success = response.has_key?('result')
|
132
|
-
@result.failed = !response.has_key?('result')
|
133
|
-
@headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
|
134
|
-
|
135
|
-
if @result.success
|
136
|
-
@result.result = response['result']
|
137
|
-
|
138
|
-
elsif response.has_key?('error')
|
139
|
-
@result.error_code = response['error']['code']
|
140
|
-
@result.error_msg = response['error']['message']
|
141
|
-
@result.error_class = response['error']['class'] # TODO safely constantize this ?
|
142
|
-
|
143
|
-
end
|
144
|
-
|
145
|
-
response.keys.select { |k|
|
146
|
-
!['jsonrpc', 'id', 'result', 'error'].include?(k)
|
147
|
-
}.each { |k| @headers[k] = response[k] }
|
148
|
-
|
149
|
-
elsif args.has_key?(:result)
|
150
|
-
@msg_id = args[:id]
|
151
|
-
@result = args[:result]
|
152
|
-
@headers = args[:headers]
|
153
|
-
|
154
|
-
#else
|
155
|
-
# raise ArgumentError, "must specify :message or :result"
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
# Class helper to determine if the specified string is a valid json-rpc
|
162
|
-
# method response
|
163
|
-
# @param [String] message string message to check
|
164
|
-
# @return [true,false] indicating if message is response message
|
165
|
-
def self.is_response_message?(message)
|
166
|
-
begin
|
167
|
-
json = RJR.parse_json(message)
|
168
|
-
json.has_key?('result') || json.has_key?('error')
|
169
|
-
rescue Exception => e
|
170
|
-
# FIXME log error
|
171
|
-
puts e.to_s
|
172
|
-
false
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Convert request message to string json format
|
177
|
-
def to_s
|
178
|
-
s = ''
|
179
|
-
if result.success
|
180
|
-
s = {'jsonrpc' => '2.0',
|
181
|
-
'id' => @msg_id,
|
182
|
-
'result' => @result.result}
|
183
|
-
|
184
|
-
else
|
185
|
-
s = {'jsonrpc' => '2.0',
|
186
|
-
'id' => @msg_id,
|
187
|
-
'error' => { 'code' => @result.error_code,
|
188
|
-
'message' => @result.error_msg,
|
189
|
-
'class' => @result.error_class}}
|
190
|
-
end
|
191
|
-
|
192
|
-
s.merge! @headers unless headers.nil?
|
193
|
-
return s.to_json.to_s
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
# Message sent to a jsonrpc node to invoke a rpc method but
|
198
|
-
# indicate the result should _not_ be returned
|
199
|
-
class NotificationMessage
|
200
|
-
# Message string received from the source
|
201
|
-
attr_accessor :json_message
|
202
|
-
|
203
|
-
# Method source is invoking on the destination
|
204
|
-
attr_accessor :jr_method
|
205
|
-
|
206
|
-
# Arguments source is passing to destination method
|
207
|
-
attr_accessor :jr_args
|
208
|
-
|
209
|
-
# Optional headers to add to json outside of standard json-rpc request
|
210
|
-
attr_accessor :headers
|
211
|
-
|
212
|
-
# RJR Notification Message initializer
|
213
|
-
#
|
214
|
-
# This should be invoked with one of two argument sets. If creating a new message
|
215
|
-
# to send to the server, specify :method, :args, and :headers to include in the message
|
216
|
-
# If handling an new request message sent from the client, simply specify :message and
|
217
|
-
# optionally any additional headers (they will be merged with the headers contained in
|
218
|
-
# the message)
|
219
|
-
#
|
220
|
-
# No message id will be generated in accordance w/ the jsonrpc standard
|
221
|
-
#
|
222
|
-
# @param [Hash] args options to set on request
|
223
|
-
# @option args [String] :message json string received from sender
|
224
|
-
# @option args [Hash] :headers optional headers to set in request and subsequent messages
|
225
|
-
# @option args [String] :method method to invoke on server
|
226
|
-
# @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
|
227
|
-
def initialize(args = {})
|
228
|
-
if args.has_key?(:message)
|
229
|
-
begin
|
230
|
-
@json_message = args[:message]
|
231
|
-
notification = RJR.parse_json(@json_message)
|
232
|
-
@jr_method = notification['method']
|
233
|
-
@jr_args = notification['params']
|
234
|
-
@headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
|
235
|
-
|
236
|
-
notification.keys.select { |k|
|
237
|
-
!['jsonrpc', 'method', 'params'].include?(k)
|
238
|
-
}.each { |k| @headers[k] = notification[k] }
|
239
|
-
|
240
|
-
rescue Exception => e
|
241
|
-
#puts "Exception Parsing Notification #{e}"
|
242
|
-
raise e
|
243
|
-
end
|
244
|
-
|
245
|
-
elsif args.has_key?(:method)
|
246
|
-
@jr_method = args[:method]
|
247
|
-
@jr_args = args[:args]
|
248
|
-
@headers = args[:headers]
|
249
|
-
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
# Class helper to determine if the specified string is a valid json-rpc
|
254
|
-
# notification
|
255
|
-
#
|
256
|
-
# @param [String] message string message to check
|
257
|
-
# @return [true,false] indicating if message is a notification message
|
258
|
-
def self.is_notification_message?(message)
|
259
|
-
begin
|
260
|
-
# FIXME log error
|
261
|
-
parsed = RJR.parse_json(message)
|
262
|
-
parsed.has_key?('method') && !parsed.has_key?('id')
|
263
|
-
rescue Exception => e
|
264
|
-
false
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
# Convert notification message to string json format
|
269
|
-
def to_s
|
270
|
-
notification = { 'jsonrpc' => '2.0',
|
271
|
-
'method' => @jr_method,
|
272
|
-
'params' => @jr_args }
|
273
|
-
notification.merge!(@headers) unless @headers.nil?
|
274
|
-
notification.to_json.to_s
|
275
|
-
end
|
276
|
-
|
277
|
-
end
|
278
|
-
|
279
|
-
# Helper utilities for messages
|
280
|
-
class MessageUtil
|
281
|
-
# Retrieve and return a single json message from a data string.
|
282
|
-
#
|
283
|
-
# Returns the message and remaining portion of the data string,
|
284
|
-
# if message is found, else nil
|
285
|
-
#
|
286
|
-
# XXX really don't like having to do this, but a quick solution
|
287
|
-
# to the issue of multiple messages appearing in one tcp data packet.
|
288
|
-
#
|
289
|
-
# TODO efficiency can probably be optimized
|
290
|
-
# in the case closing '}' hasn't arrived yet
|
291
|
-
def self.retrieve_json(data)
|
292
|
-
return nil if data.nil? || data.empty?
|
293
|
-
start = 0
|
294
|
-
start += 1 until start == data.length || data[start].chr == '{'
|
295
|
-
on = mi = 0
|
296
|
-
start.upto(data.length - 1).each { |i|
|
297
|
-
if data[i].chr == '{'
|
298
|
-
on += 1
|
299
|
-
elsif data[i].chr == '}'
|
300
|
-
on -= 1
|
301
|
-
end
|
302
|
-
|
303
|
-
if on == 0
|
304
|
-
mi = i
|
305
|
-
break
|
306
|
-
end
|
307
|
-
}
|
308
|
-
|
309
|
-
return nil if mi == 0
|
310
|
-
return data[start..mi], data[(mi+1)..-1]
|
311
|
-
end
|
312
|
-
|
313
|
-
# Mechanism to register / retrieve preformatted message
|
314
|
-
#
|
315
|
-
# @param [Symbol] id id of message to get / set
|
316
|
-
# @param [String] msg optional preformatted message to store
|
317
|
-
# @return [String] json rpc message
|
318
|
-
def self.message(id, msg=nil)
|
319
|
-
@rjr_messages ||= {}
|
320
|
-
@rjr_messages[id] = msg unless msg.nil?
|
321
|
-
@rjr_messages[id]
|
322
|
-
end
|
323
|
-
|
324
|
-
# Clear preformatted messages
|
325
|
-
def self.clear
|
326
|
-
@rjr_messages = {}
|
327
|
-
end
|
328
|
-
|
329
|
-
# Return random message from registry.
|
330
|
-
#
|
331
|
-
# Optionally specify the transport which the message must accept
|
332
|
-
# (TODO turn this into a generic selection callback)
|
333
|
-
def self.rand_msg(transport = nil)
|
334
|
-
@rjr_messages ||= {}
|
335
|
-
messages = @rjr_messages.select { |mid,m| m[:transports].nil? || transport.nil? ||
|
336
|
-
m[:transports].include?(transport) }
|
337
|
-
messages[messages.keys[rand(messages.keys.size)]]
|
338
|
-
end
|
339
|
-
|
340
|
-
end # MessageUtil
|
341
|
-
|
342
|
-
# Module providing helper methods for messages
|
343
|
-
module MessageMixins
|
344
|
-
|
345
|
-
# Wrapper around MessageUtil.message
|
346
|
-
def define_message(name, &bl)
|
347
|
-
MessageUtil.message(name, bl.call)
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
end
|