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
data/lib/rjr/dispatcher.rb
CHANGED
@@ -1,181 +1,17 @@
|
|
1
|
-
# RJR
|
1
|
+
# RJR Dispatcher
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Mechanisms which to register methods to handle requests
|
4
|
+
# and return responses
|
5
5
|
#
|
6
|
-
# Copyright (C) 2012-
|
6
|
+
# Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
|
7
7
|
# Licensed under the Apache License, Version 2.0
|
8
8
|
|
9
|
-
require '
|
10
|
-
require 'rjr/
|
9
|
+
require 'rjr/request'
|
10
|
+
require 'rjr/result'
|
11
|
+
require 'rjr/util/logger'
|
11
12
|
|
12
13
|
module RJR
|
13
14
|
|
14
|
-
# JSON-RPC result representation
|
15
|
-
class Result
|
16
|
-
# Boolean indicating if request was successfully invoked
|
17
|
-
attr_accessor :success
|
18
|
-
|
19
|
-
# Boolean indicating if request failed in some manner
|
20
|
-
attr_accessor :failed
|
21
|
-
|
22
|
-
# Return value of the json-rpc call if successful
|
23
|
-
attr_accessor :result
|
24
|
-
|
25
|
-
# Code corresponding to json-rpc error if problem occured during request invocation
|
26
|
-
attr_accessor :error_code
|
27
|
-
|
28
|
-
# Message corresponding to json-rpc error if problem occured during request invocation
|
29
|
-
attr_accessor :error_msg
|
30
|
-
|
31
|
-
# Class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
32
|
-
attr_accessor :error_class
|
33
|
-
|
34
|
-
# RJR result intializer
|
35
|
-
# @param [Hash] args options to set on result
|
36
|
-
# @option args [Object] :result result of json-rpc method handler if successfully returned
|
37
|
-
# @option args [Integer] :error_code code corresponding to json-rpc error if problem occured during request invocation
|
38
|
-
# @option args [String] :error_msg message corresponding to json-rpc error if problem occured during request invocation
|
39
|
-
# @option args [Class] :error_class class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
40
|
-
def initialize(args = {})
|
41
|
-
@result = args[:result] || args['result']
|
42
|
-
@error_code = args[:error_code] || args['error_code']
|
43
|
-
@error_msg = args[:error_msg] || args['error_msg']
|
44
|
-
@error_class = args[:error_class] || args['error_class']
|
45
|
-
|
46
|
-
@success = @error_code.nil?
|
47
|
-
@failed = !@error_code.nil?
|
48
|
-
end
|
49
|
-
|
50
|
-
# Compare Result against other result, returning true if both correspond
|
51
|
-
# to equivalent json-rpc results else false
|
52
|
-
def ==(other)
|
53
|
-
@success == other.success &&
|
54
|
-
@failed == other.failed &&
|
55
|
-
@result == other.result &&
|
56
|
-
@error_code == other.error_code &&
|
57
|
-
@error_msg == other.error_msg &&
|
58
|
-
@error_class == other.error_class
|
59
|
-
end
|
60
|
-
|
61
|
-
# Convert Response to human consumable string
|
62
|
-
def to_s
|
63
|
-
"#{@success} #{@result} #{@error_code} #{@error_msg} #{@error_class}"
|
64
|
-
end
|
65
|
-
|
66
|
-
######### Specific request types
|
67
|
-
|
68
|
-
# JSON-RPC -32600 / Invalid Request
|
69
|
-
def self.invalid_request
|
70
|
-
return Result.new(:error_code => -32600,
|
71
|
-
:error_msg => ' Invalid Request')
|
72
|
-
end
|
73
|
-
|
74
|
-
# JSON-RPC -32602 / Method not found
|
75
|
-
def self.method_not_found(name)
|
76
|
-
return Result.new(:error_code => -32602,
|
77
|
-
:error_msg => "Method '#{name}' not found")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# JSON-RPC request representation.
|
82
|
-
#
|
83
|
-
# Registered request handlers will be invoked in the context of
|
84
|
-
# instances of this class, meaning all member variables will be available
|
85
|
-
# for use in the handler.
|
86
|
-
class Request
|
87
|
-
# Result of the request operation, set by dispatcher
|
88
|
-
attr_accessor :result
|
89
|
-
|
90
|
-
# Method which request is for
|
91
|
-
attr_accessor :rjr_method
|
92
|
-
|
93
|
-
# Arguments be passed to method
|
94
|
-
attr_accessor :rjr_method_args
|
95
|
-
|
96
|
-
# Headers which came w/ request
|
97
|
-
attr_accessor :rjr_headers
|
98
|
-
|
99
|
-
# Type of node which request came in on
|
100
|
-
attr_accessor :rjr_node_type
|
101
|
-
|
102
|
-
# ID of node which request came in on
|
103
|
-
attr_accessor :rjr_node_id
|
104
|
-
|
105
|
-
# RJR Request initializer
|
106
|
-
# @param [Hash] args options to set on request
|
107
|
-
# @option args [String] :rjr_method name of the method which request is for
|
108
|
-
# @option args [Array] :rjr_method_args array of arguments which to pass to the rpc method handler
|
109
|
-
# @option args [Hash] :rjr_headers hash of keys/values corresponding to optional headers received as part of of the request
|
110
|
-
# @option args [String] :rjr_client_ip ip address of client which invoked the request (if applicable)
|
111
|
-
# @option args [String] :rjr_client_port port of client which invoked the request (if applicable)
|
112
|
-
# @option args [RJR::Callback] :rjr_callback callback through which requests/notifications can be sent to remote node
|
113
|
-
# @option args [RJR::Node] :rjr_node rjr node which request was received on
|
114
|
-
# @option args [String] :rjr_node_id id of the rjr node which request was received on
|
115
|
-
# @option args [Symbol] :rjr_node_type type of the rjr node which request was received on
|
116
|
-
# @option args [Callable] :rjr_handler callable object registered to the specified method which to invoke request on with arguments
|
117
|
-
def initialize(args = {})
|
118
|
-
@rjr_method = args[:rjr_method] || args['rjr_method']
|
119
|
-
@rjr_method_args = args[:rjr_method_args] || args['rjr_method_args'] || []
|
120
|
-
@rjr_headers = args[:rjr_headers] || args['rjr_headers']
|
121
|
-
|
122
|
-
@rjr_client_ip = args[:rjr_client_ip]
|
123
|
-
@rjr_client_port = args[:rjr_client_port]
|
124
|
-
|
125
|
-
@rjr_callback = args[:rjr_callback]
|
126
|
-
@rjr_node = args[:rjr_node]
|
127
|
-
@rjr_node_id = args[:rjr_node_id] || args['rjr_node_id']
|
128
|
-
@rjr_node_type = args[:rjr_node_type] || args['rjr_node_type']
|
129
|
-
|
130
|
-
@rjr_handler = args[:rjr_handler]
|
131
|
-
|
132
|
-
@result = nil
|
133
|
-
end
|
134
|
-
|
135
|
-
# Invoke the request by calling the registered handler with the registered
|
136
|
-
# method parameters in the local scope
|
137
|
-
def handle
|
138
|
-
node_sig = "#{@rjr_node_id}(#{@rjr_node_type})"
|
139
|
-
method_sig = "#{@rjr_method}(#{@rjr_method_args.join(',')})"
|
140
|
-
|
141
|
-
RJR::Logger.info "#{node_sig}->#{method_sig}"
|
142
|
-
|
143
|
-
retval = instance_exec(*@rjr_method_args, &@rjr_handler)
|
144
|
-
|
145
|
-
RJR::Logger.info \
|
146
|
-
"#{node_sig}<-#{method_sig}<-#{retval.nil? ? "nil" : retval}"
|
147
|
-
|
148
|
-
return retval
|
149
|
-
end
|
150
|
-
|
151
|
-
# Convert request to json representation and return it
|
152
|
-
def to_json(*a)
|
153
|
-
{
|
154
|
-
'json_class' => self.class.name,
|
155
|
-
'data' =>
|
156
|
-
{:request => { :rjr_method => @rjr_method,
|
157
|
-
:rjr_method_args => @rjr_method_args,
|
158
|
-
:rjr_headers => @rjr_headers,
|
159
|
-
:rjr_node_type => @rjr_node_type,
|
160
|
-
:rjr_node_id => @rjr_node_id },
|
161
|
-
|
162
|
-
:result => { :result => @result.result,
|
163
|
-
:error_code => @result.error_code,
|
164
|
-
:error_msg => @result.error_msg,
|
165
|
-
:error_class => @result.error_class } }
|
166
|
-
}.to_json(*a)
|
167
|
-
end
|
168
|
-
|
169
|
-
# Create new request from json representation
|
170
|
-
def self.json_create(o)
|
171
|
-
result = Result.new(o['data']['result'])
|
172
|
-
request = Request.new(o['data']['request'])
|
173
|
-
request.result = result
|
174
|
-
return request
|
175
|
-
end
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
15
|
# Primary RJR JSON-RPC method dispatcher.
|
180
16
|
class Dispatcher
|
181
17
|
# Registered json-rpc request signatures and corresponding handlers
|
@@ -188,7 +24,14 @@ class Dispatcher
|
|
188
24
|
attr_accessor :keep_requests
|
189
25
|
|
190
26
|
# Requests which have been dispatched
|
191
|
-
def requests
|
27
|
+
def requests
|
28
|
+
@requests_lock.synchronize { Array.new(@requests) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Store request if configured to do so
|
32
|
+
def store_request(request)
|
33
|
+
@requests_lock.synchronize { @requests << request } if @keep_requests
|
34
|
+
end
|
192
35
|
|
193
36
|
# RJR::Dispatcher intializer
|
194
37
|
def initialize(args = {})
|
@@ -226,8 +69,8 @@ class Dispatcher
|
|
226
69
|
# Register json-rpc handler with dispatcher
|
227
70
|
#
|
228
71
|
# @param [String,Regex] signature request signature to match
|
229
|
-
# @param [Callable]
|
230
|
-
# @param [Callable]
|
72
|
+
# @param [Callable] callback callable object which to bind to signature
|
73
|
+
# @param [Callable] bl block parameter will be set to callback if specified
|
231
74
|
# @return self
|
232
75
|
def handle(signature, callback = nil, &bl)
|
233
76
|
if signature.is_a?(Array)
|
@@ -239,16 +82,28 @@ class Dispatcher
|
|
239
82
|
self
|
240
83
|
end
|
241
84
|
|
85
|
+
# Return handler for specified method.
|
86
|
+
#
|
87
|
+
# Currently we match method name string or regex against signature
|
88
|
+
# @param [String] rjr_method string rjr method to match
|
89
|
+
# @return [Callable, nil] callback proc registered to handle rjr_method
|
90
|
+
# or nil if not found
|
91
|
+
def handler_for(rjr_method)
|
92
|
+
# look for exact match first
|
93
|
+
handler = @handlers.find { |k,v| k == rjr_method }
|
94
|
+
|
95
|
+
# if not found try to match regex's
|
96
|
+
handler ||= @handlers.find { |k,v| k.is_a?(Regexp) && (k =~ rjr_method) }
|
97
|
+
|
98
|
+
handler.nil? ? nil : handler.last
|
99
|
+
end
|
100
|
+
|
242
101
|
# Return boolean indicating if dispatcher can handle method
|
243
102
|
#
|
244
|
-
# @param [String] string rjr method to match
|
103
|
+
# @param [String] rjr_method string rjr method to match
|
245
104
|
# @return [true,false] indicating if requests to specified method will be matched
|
246
105
|
def handles?(rjr_method)
|
247
|
-
|
248
|
-
k.is_a?(String) ?
|
249
|
-
k == rjr_method :
|
250
|
-
k =~ rjr_method
|
251
|
-
}.nil?
|
106
|
+
!handler_for(rjr_method).nil?
|
252
107
|
end
|
253
108
|
|
254
109
|
# Register environment to run json-rpc handler w/ dispatcher.
|
@@ -257,7 +112,7 @@ class Dispatcher
|
|
257
112
|
# will extend before executing handler
|
258
113
|
#
|
259
114
|
# @param [String,Regex] signature request signature to match
|
260
|
-
# @param [Module] module which to extend requests with
|
115
|
+
# @param [Module] environment module which to extend requests with
|
261
116
|
# @return self
|
262
117
|
def env(signature, environment)
|
263
118
|
if signature.is_a?(Array)
|
@@ -268,51 +123,52 @@ class Dispatcher
|
|
268
123
|
self
|
269
124
|
end
|
270
125
|
|
126
|
+
# Return the environment registered for the specified method
|
127
|
+
def env_for(rjr_method)
|
128
|
+
# look for exact match first
|
129
|
+
env = @environments.find { |k,v| k == rjr_method }
|
130
|
+
|
131
|
+
# if not found try to match regex's
|
132
|
+
env ||= @environments.find { |k,v| k.is_a?(Regexp) && (k =~ rjr_method) }
|
133
|
+
|
134
|
+
env.nil? ? nil : env.last
|
135
|
+
end
|
136
|
+
|
271
137
|
# Dispatch received request. (used internally by nodes).
|
272
138
|
#
|
273
139
|
# Arguments should include :rjr_method and other parameters
|
274
140
|
# required to construct a valid Request instance
|
275
141
|
def dispatch(args = {})
|
276
|
-
|
277
|
-
# TODO not using concurrent access protection, assumes all handlers are registered
|
278
|
-
# before first dispatch occurs
|
279
|
-
handler = @handlers.find { |k,v|
|
280
|
-
k.is_a?(String) ?
|
281
|
-
k == args[:rjr_method] :
|
282
|
-
k =~ args[:rjr_method] }
|
283
|
-
|
284
|
-
# TODO currently just using last environment that matches,
|
285
|
-
# allow multiple environments to be used?
|
286
|
-
environment = @environments.keys.select { |k|
|
287
|
-
k.is_a?(String) ?
|
288
|
-
k == args[:rjr_method] :
|
289
|
-
k =~ args[:rjr_method]
|
290
|
-
}.last
|
291
|
-
|
292
|
-
return Result.method_not_found(args[:rjr_method]) if handler.nil?
|
293
|
-
|
294
|
-
# TODO compare arity of handler to number of method_args passed in
|
295
|
-
request = Request.new args.merge(:rjr_handler => handler.last)
|
296
|
-
|
297
|
-
# set request environment
|
298
|
-
request.extend(@environments[environment]) unless environment.nil?
|
299
|
-
|
300
|
-
begin
|
301
|
-
retval = request.handle
|
302
|
-
request.result = Result.new(:result => retval)
|
303
|
-
|
304
|
-
rescue Exception => e
|
305
|
-
RJR::Logger.warn ["Exception Raised in #{args[:rjr_method]} handler #{e}"] +
|
306
|
-
e.backtrace
|
307
|
-
request.result =
|
308
|
-
Result.new(:error_code => -32000,
|
309
|
-
:error_msg => e.to_s,
|
310
|
-
:error_class => e.class)
|
142
|
+
rjr_method = args[:rjr_method]
|
311
143
|
|
312
|
-
|
144
|
+
# *note* not using concurrent access protection,
|
145
|
+
# assumes all handlers/enviroments are registered
|
146
|
+
# before first dispatch occurs
|
147
|
+
handler = handler_for(rjr_method)
|
148
|
+
environment = env_for(rjr_method)
|
149
|
+
|
150
|
+
return Result.method_not_found(rjr_method) if handler.nil?
|
151
|
+
|
152
|
+
request = Request.new args.merge(:rjr_handler => handler)
|
153
|
+
|
154
|
+
# set request environment
|
155
|
+
request.extend(environment) unless environment.nil?
|
156
|
+
|
157
|
+
begin
|
158
|
+
retval = request.handle
|
159
|
+
request.result = Result.new(:result => retval)
|
160
|
+
|
161
|
+
rescue Exception => e
|
162
|
+
warning = "Exception Raised in #{rjr_method} handler #{e}"
|
163
|
+
RJR::Logger.warn [warning] + e.backtrace
|
164
|
+
|
165
|
+
request.result = Result.new(:error_code => -32000,
|
166
|
+
:error_msg => e.to_s,
|
167
|
+
:error_class => e.class)
|
168
|
+
end
|
313
169
|
|
314
|
-
|
315
|
-
|
170
|
+
store_request request
|
171
|
+
return request.result
|
316
172
|
end
|
317
173
|
|
318
174
|
# Handle responses received from rjr requests. (used internally by nodes)
|
data/lib/rjr/messages.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
# RJR Compressed Messages
|
2
|
+
#
|
3
|
+
# *Note* this module is still expiremental
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014 Mohammed Morsi <mo@morsi.org>
|
6
|
+
# Licensed under the Apache License, Version 2.0
|
7
|
+
|
8
|
+
# TODO split up into seperate modules
|
9
|
+
# (under lib/rjr/messages/compressed/ w/ this file including all)
|
10
|
+
|
11
|
+
require 'zlib'
|
12
|
+
require 'base64'
|
13
|
+
|
14
|
+
require 'rjr/messages'
|
15
|
+
|
16
|
+
module RJR
|
17
|
+
module Messages
|
18
|
+
# Make backups of original class references
|
19
|
+
module Uncompressed
|
20
|
+
Request = RJR::Messages::Request
|
21
|
+
Response = RJR::Messages::Response
|
22
|
+
Notification = RJR::Messages::Notification
|
23
|
+
end
|
24
|
+
|
25
|
+
# Subclass original message classes w/ versions that first
|
26
|
+
# check for compressed messages before dispatching to superclass
|
27
|
+
module Compressed
|
28
|
+
class Request < Uncompressed::Request
|
29
|
+
COMPRESSED = true
|
30
|
+
|
31
|
+
def initialize(args = {})
|
32
|
+
parse_args(args)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_args(args)
|
38
|
+
args = Hash[args]
|
39
|
+
if args[:message]
|
40
|
+
message = args.delete(:message)
|
41
|
+
parse_message(message, args)
|
42
|
+
else
|
43
|
+
super(args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_message(message, args={})
|
48
|
+
@json_message = message
|
49
|
+
request = JSONParser.parse(@json_message)
|
50
|
+
|
51
|
+
if request.has_key?('m')
|
52
|
+
decoded = Base64.decode64(request['p'])
|
53
|
+
inflated = Zlib::Inflate.inflate(decoded)
|
54
|
+
converted = JSONParser.parse(inflated)
|
55
|
+
|
56
|
+
parse_args({:method => request['m'],
|
57
|
+
:id => request['i'],
|
58
|
+
:args => converted}.merge(args))
|
59
|
+
|
60
|
+
else
|
61
|
+
parse_args({:method => request['method'],
|
62
|
+
:args => request['params'],
|
63
|
+
:id => request['id']}.merge(args))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
public
|
68
|
+
|
69
|
+
def self.is_compressed_request_message?(message)
|
70
|
+
begin
|
71
|
+
parsed = JSONParser.parse(message)
|
72
|
+
parsed.has_key?('m') && parsed.has_key?('i')
|
73
|
+
rescue Exception => e
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.is_request_message?(message)
|
79
|
+
is_compressed_request_message?(message) || super(message)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_json(*a)
|
83
|
+
deflated = Zlib::Deflate.deflate(@jr_args.to_json.to_s)
|
84
|
+
encoded = Base64.encode64(deflated)
|
85
|
+
|
86
|
+
{'j' => 2.0,
|
87
|
+
'i' => @msg_id,
|
88
|
+
'm' => @jr_method,
|
89
|
+
'p' => encoded}.merge(@headers).to_json(*a)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Response < Uncompressed::Response
|
94
|
+
def initialize(args = {})
|
95
|
+
parse_args(args)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def parse_args(args)
|
101
|
+
args = Hash[args]
|
102
|
+
@request = args[:request]
|
103
|
+
|
104
|
+
if args[:message]
|
105
|
+
message = args.delete(:message)
|
106
|
+
parse_message(message, args)
|
107
|
+
else
|
108
|
+
super(args)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_message(message, args={})
|
113
|
+
@json_message = message
|
114
|
+
response = JSONParser.parse(@json_message)
|
115
|
+
|
116
|
+
if response.has_key?('r') || response.has_key?('e')
|
117
|
+
result = parse_compressed_result(response)
|
118
|
+
parse_args({:id => response['i'],
|
119
|
+
:result => result}.merge(args))
|
120
|
+
|
121
|
+
else
|
122
|
+
result = parse_result(response)
|
123
|
+
parse_args({:id => response['id'],
|
124
|
+
:result => result}.merge(args))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_compressed_result(response)
|
129
|
+
@result = Result.new
|
130
|
+
@result.success = response.has_key?('r')
|
131
|
+
@result.failed = !@result.success
|
132
|
+
|
133
|
+
if @result.success
|
134
|
+
decoded = Base64.decode64(response['r'])
|
135
|
+
inflated = Zlib::Inflate.inflate(decoded)
|
136
|
+
converted = JSONParser.parse(inflated).first
|
137
|
+
@result.result = converted
|
138
|
+
|
139
|
+
elsif response.has_key?('e')
|
140
|
+
@result.error_code = response['e']['co']
|
141
|
+
@result.error_msg = response['e']['m']
|
142
|
+
@result.error_class = response['e']['cl']
|
143
|
+
end
|
144
|
+
|
145
|
+
@result
|
146
|
+
end
|
147
|
+
|
148
|
+
public
|
149
|
+
|
150
|
+
def has_compressed_request?
|
151
|
+
!!@request && @request.class.const_defined?(:COMPRESSED)
|
152
|
+
end
|
153
|
+
|
154
|
+
def has_uncompressed_request?
|
155
|
+
!!@request && !@request.class.const_defined?(:COMPRESSED)
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.is_compressed_response_message?(message)
|
159
|
+
begin
|
160
|
+
json = JSONParser.parse(message)
|
161
|
+
json.has_key?('r') || json.has_key?('e')
|
162
|
+
rescue Exception => e
|
163
|
+
puts e.to_s
|
164
|
+
false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.is_response_message?(message)
|
169
|
+
is_compressed_response_message?(message) || super(message)
|
170
|
+
end
|
171
|
+
|
172
|
+
def compressed_success_json
|
173
|
+
# XXX encapsulate in array & extract above so as to
|
174
|
+
# guarantee to be able to be parsable JSON
|
175
|
+
deflated = Zlib::Deflate.deflate([@result.result].to_json.to_s)
|
176
|
+
encoded = Base64.encode64(deflated)
|
177
|
+
{'r' => encoded}
|
178
|
+
end
|
179
|
+
|
180
|
+
def compressed_error_json
|
181
|
+
{'e' => {'co' => @result.error_code,
|
182
|
+
'm' => @result.error_msg,
|
183
|
+
'cl' => @result.error_class}}
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_json(*a)
|
187
|
+
return super(*a) if has_uncompressed_request?
|
188
|
+
|
189
|
+
result_json = @result.success ?
|
190
|
+
compressed_success_json : compressed_error_json
|
191
|
+
|
192
|
+
response = {'j' => 2.0,
|
193
|
+
'i' => @msg_id}.merge(@headers).
|
194
|
+
merge(result_json).to_json(*a)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class Notification < Uncompressed::Notification
|
199
|
+
def initialize(args = {})
|
200
|
+
parse_args(args)
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def parse_args(args)
|
206
|
+
args = Hash[args]
|
207
|
+
if args[:message]
|
208
|
+
message = args.delete(:message)
|
209
|
+
parse_message(message, args)
|
210
|
+
else
|
211
|
+
super(args)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def parse_message(message, args={})
|
216
|
+
@json_message = message
|
217
|
+
request = JSONParser.parse(@json_message)
|
218
|
+
|
219
|
+
if request.has_key?('m')
|
220
|
+
decoded = Base64.decode64(request['p'])
|
221
|
+
inflated = Zlib::Inflate.inflate(decoded)
|
222
|
+
converted = JSONParser.parse(inflated)
|
223
|
+
parse_args({:method => request['m'],
|
224
|
+
:args => converted}.merge(args))
|
225
|
+
|
226
|
+
else
|
227
|
+
parse_args({:method => request['method'],
|
228
|
+
:args => request['params']}.merge(args))
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
public
|
233
|
+
|
234
|
+
def self.is_compressed_notification_message?(message)
|
235
|
+
begin
|
236
|
+
parsed = JSONParser.parse(message)
|
237
|
+
parsed.has_key?('m') && !parsed.has_key?('i')
|
238
|
+
rescue Exception => e
|
239
|
+
false
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.is_notification_message?(message)
|
244
|
+
is_compressed_notification_message?(message) || super(message)
|
245
|
+
end
|
246
|
+
|
247
|
+
def to_json(*a)
|
248
|
+
deflated = Zlib::Deflate.deflate(@jr_args.to_json.to_s)
|
249
|
+
encoded = Base64.encode64(deflated)
|
250
|
+
|
251
|
+
{'j' => 2.0,
|
252
|
+
'm' => @jr_method,
|
253
|
+
'p' => encoded}.merge(@headers).to_json(*a)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Override original classes w/ subclasses
|
259
|
+
Request = Compressed::Request
|
260
|
+
Response = Compressed::Response
|
261
|
+
Notification = Compressed::Notification
|
262
|
+
|
263
|
+
end # module Messages
|
264
|
+
end # module RJR
|