rjr 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +144 -0
- data/Rakefile +5 -5
- data/bin/rjr-server +2 -1
- data/lib/rjr.rb +3 -0
- data/lib/rjr/amqp_node.rb +103 -52
- data/lib/rjr/common.rb +19 -3
- data/lib/rjr/dispatcher.rb +103 -7
- data/lib/rjr/errors.rb +5 -3
- data/lib/rjr/local_node.rb +38 -12
- data/lib/rjr/message.rb +56 -1
- data/lib/rjr/multi_node.rb +31 -4
- data/lib/rjr/node.rb +57 -8
- data/lib/rjr/tcp_node.rb +65 -11
- data/lib/rjr/thread_pool.rb +33 -9
- data/lib/rjr/web_node.rb +72 -20
- data/lib/rjr/ws_node.rb +54 -12
- metadata +35 -2
- data/README.rdoc +0 -73
data/lib/rjr/node.rb
CHANGED
@@ -11,19 +11,51 @@ require 'rjr/thread_pool'
|
|
11
11
|
|
12
12
|
module RJR
|
13
13
|
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# Base RJR Node interface. Nodes are the central transport mechanism of rjr,
|
15
|
+
# this class provides the core methods common among all transport types and
|
16
|
+
# mechanisms to start and run the eventmachine reactor which drives all requests.
|
17
|
+
#
|
18
|
+
# A subclass of RJR::Node should be defined for each transport that is supported,
|
19
|
+
# implementing the 'listen' operation to listen for new requests and 'invoke_request'
|
20
|
+
# to issue them.
|
16
21
|
class Node
|
17
|
-
|
22
|
+
class << self
|
23
|
+
# @!group Config options
|
24
|
+
|
25
|
+
# Default number of threads to instantiate in local worker pool
|
26
|
+
attr_accessor :default_threads
|
27
|
+
|
28
|
+
# Default timeout after which worker threads are killed
|
29
|
+
attr_accessor :default_timeout
|
30
|
+
|
31
|
+
# @!endgroup
|
32
|
+
end
|
33
|
+
|
34
|
+
# Unique string identifier of the node
|
18
35
|
attr_reader :node_id
|
19
36
|
|
20
|
-
#
|
37
|
+
# Attitional header fields to set on all
|
38
|
+
# requests and responses received and sent by node
|
21
39
|
attr_accessor :message_headers
|
22
40
|
|
41
|
+
# Nodes use internal thread pools to handle requests and free
|
42
|
+
# up the eventmachine reactor to continue processing requests
|
43
|
+
# @see ThreadPool
|
23
44
|
attr_reader :thread_pool
|
24
45
|
|
46
|
+
# RJR::Node initializer
|
47
|
+
# @param [Hash] args options to set on request
|
48
|
+
# @option args [String] :node_id unique id of the node *required*!!!
|
49
|
+
# @option args [Hash<String,String>] :headers optional headers to set on all json-rpc messages
|
50
|
+
# @option args [Integer] :threads number of handler to threads to instantiate in local worker pool
|
51
|
+
# @option args [Integer] :timeout timeout after which worker thread being run is killed
|
25
52
|
def initialize(args = {})
|
26
|
-
|
53
|
+
RJR::Node.default_threads ||= 15
|
54
|
+
RJR::Node.default_timeout ||= 5
|
55
|
+
|
56
|
+
@node_id = args[:node_id]
|
57
|
+
@num_threads = args[:threads] || RJR::Node.default_threads
|
58
|
+
@timeout = args[:timeout] || RJR::Node.default_timeout
|
27
59
|
|
28
60
|
@message_headers = {}
|
29
61
|
@message_headers.merge!(args[:headers]) if args.has_key?(:headers)
|
@@ -31,11 +63,23 @@ class Node
|
|
31
63
|
ObjectSpace.define_finalizer(self, self.class.finalize(self))
|
32
64
|
end
|
33
65
|
|
66
|
+
# Ruby ObjectSpace finalizer to ensure that node terminates all
|
67
|
+
# operations when object is destroyed
|
34
68
|
def self.finalize(node)
|
35
69
|
proc { node.halt ; node.join }
|
36
70
|
end
|
37
71
|
|
38
|
-
#
|
72
|
+
# Run a job in event machine.
|
73
|
+
#
|
74
|
+
# This will start the eventmachine reactor and thread pool if not already
|
75
|
+
# running, schedule the specified block to be run and immediately return.
|
76
|
+
#
|
77
|
+
# For use by subclasses to start listening and sending operations within
|
78
|
+
# the context of event machine.
|
79
|
+
#
|
80
|
+
# Keeps track of an internal counter of how many times this was invoked so
|
81
|
+
# a specific node can be shutdown / started up without affecting the
|
82
|
+
# eventmachine reactor (@see #stop)
|
39
83
|
def em_run(&bl)
|
40
84
|
@@em_jobs ||= 0
|
41
85
|
@@em_jobs += 1
|
@@ -44,8 +88,7 @@ class Node
|
|
44
88
|
|
45
89
|
unless !@thread_pool.nil? && @thread_pool.running?
|
46
90
|
# threads pool to handle incoming requests
|
47
|
-
|
48
|
-
@thread_pool = ThreadPool.new(10, :timeout => 5)
|
91
|
+
@thread_pool = ThreadPool.new(@num_threads, :timeout => @timeout)
|
49
92
|
end
|
50
93
|
|
51
94
|
if @@em_thread.nil?
|
@@ -63,10 +106,12 @@ class Node
|
|
63
106
|
EventMachine.schedule bl
|
64
107
|
end
|
65
108
|
|
109
|
+
# Returns boolean indicating if this node is still running or not
|
66
110
|
def em_running?
|
67
111
|
@@em_jobs > 0 && EventMachine.reactor_running?
|
68
112
|
end
|
69
113
|
|
114
|
+
# Block until the eventmachine reactor and thread pool have both completed running
|
70
115
|
def join
|
71
116
|
@@em_thread.join if @@em_thread
|
72
117
|
@@em_thread = nil
|
@@ -74,6 +119,8 @@ class Node
|
|
74
119
|
@thread_pool = nil
|
75
120
|
end
|
76
121
|
|
122
|
+
# Decrement the event machine job counter and if equal to zero,
|
123
|
+
# immediately terminate the node
|
77
124
|
def stop
|
78
125
|
@@em_jobs -= 1
|
79
126
|
if @@em_jobs == 0
|
@@ -82,6 +129,8 @@ class Node
|
|
82
129
|
end
|
83
130
|
end
|
84
131
|
|
132
|
+
# Immediately terminate the node, halting the eventmachine reactor and
|
133
|
+
# terminating the thread pool
|
85
134
|
def halt
|
86
135
|
@@em_jobs = 0
|
87
136
|
EventMachine.stop
|
data/lib/rjr/tcp_node.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RJR TCP Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests over the TCP protocol
|
4
|
+
#
|
3
5
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
6
|
# Licensed under the Apache License, Version 2.0
|
5
7
|
|
@@ -11,14 +13,24 @@ require 'rjr/message'
|
|
11
13
|
|
12
14
|
module RJR
|
13
15
|
|
14
|
-
# TCP
|
15
|
-
#
|
16
|
+
# TCP node callback interface, used to invoke json-rpc methods
|
17
|
+
# against a remote node via a tcp socket connection previously opened
|
18
|
+
#
|
19
|
+
# After a node sends a json-rpc request to another, the either node may send
|
20
|
+
# additional requests to each other via the socket already established until
|
21
|
+
# it is closed on either end
|
16
22
|
class TCPNodeCallback
|
23
|
+
|
24
|
+
# TCPNodeCallback initializer
|
25
|
+
# @param [Hash] args the options to create the tcp node callback with
|
26
|
+
# @option args [TCPNodeEndpoint] :endpoint tcp node endpoint used to send/receive messages
|
27
|
+
# @option args [Hash] :headers hash of rjr message headers present in client request when callback is established
|
17
28
|
def initialize(args = {})
|
18
29
|
@endpoint = args[:endpoint]
|
19
30
|
@message_headers = args[:headers]
|
20
31
|
end
|
21
32
|
|
33
|
+
# Implementation of {RJR::NodeCallback#invoke}
|
22
34
|
def invoke(callback_method, *data)
|
23
35
|
msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
|
24
36
|
# TODO surround w/ begin/rescue block incase of socket errors
|
@@ -26,9 +38,13 @@ class TCPNodeCallback
|
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
|
-
#
|
30
|
-
#
|
41
|
+
# @private
|
42
|
+
# Helper class intialized by eventmachine encapsulating a socket connection
|
31
43
|
class TCPNodeEndpoint < EventMachine::Connection
|
44
|
+
|
45
|
+
# TCPNodeEndpoint intializer
|
46
|
+
#
|
47
|
+
# specify the TCPNode establishing the connection and an optional first message to send
|
32
48
|
def initialize(args = {})
|
33
49
|
@rjr_node = args[:rjr_node]
|
34
50
|
|
@@ -36,6 +52,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
36
52
|
@send_message = args[:init_message]
|
37
53
|
end
|
38
54
|
|
55
|
+
# {EventMachine::Connection#post_init} callback, sends first message if specified
|
39
56
|
def post_init
|
40
57
|
unless @send_message.nil?
|
41
58
|
send_data @send_message.to_s
|
@@ -43,6 +60,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
43
60
|
end
|
44
61
|
end
|
45
62
|
|
63
|
+
# {EventMachine::Connection#receive_data} callback, handle request / response messages
|
46
64
|
def receive_data(data)
|
47
65
|
if RequestMessage.is_request_message?(data)
|
48
66
|
@rjr_node.thread_pool << ThreadPoolJob.new { handle_request(data) }
|
@@ -54,6 +72,9 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
54
72
|
end
|
55
73
|
|
56
74
|
|
75
|
+
private
|
76
|
+
|
77
|
+
# Internal helper, handle request message received
|
57
78
|
def handle_request(data)
|
58
79
|
client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
|
59
80
|
msg = RequestMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
@@ -73,6 +94,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
73
94
|
send_data(response.to_s)
|
74
95
|
end
|
75
96
|
|
97
|
+
# Internal helper, handle response message received
|
76
98
|
def handle_response(data)
|
77
99
|
msg = ResponseMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
78
100
|
res = err = nil
|
@@ -90,7 +112,26 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
90
112
|
end
|
91
113
|
end
|
92
114
|
|
93
|
-
# TCP node definition, listen for and invoke json-rpc requests via
|
115
|
+
# TCP node definition, listen for and invoke json-rpc requests via TCP sockets
|
116
|
+
#
|
117
|
+
# Clients should specify the hostname / port when listening for requests and
|
118
|
+
# when invoking them.
|
119
|
+
#
|
120
|
+
# @example Listening for json-rpc requests over tcp
|
121
|
+
# # register rjr dispatchers (see RJR::Dispatcher)
|
122
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
123
|
+
# "Hello #{name}!"
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# # initialize node, listen, and block
|
127
|
+
# server = RJR::TCPNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
|
128
|
+
# server.listen
|
129
|
+
# server.join
|
130
|
+
#
|
131
|
+
# @example Invoking json-rpc requests over tcp
|
132
|
+
# client = RJR::TCPNode.new :node_id => 'client', :host => 'localhost', :port => '8888'
|
133
|
+
# puts client.invoke_request('jsonrpc://localhost:7777', 'hello', 'mo')
|
134
|
+
#
|
94
135
|
class TCPNode < RJR::Node
|
95
136
|
RJR_NODE_TYPE = :tcp
|
96
137
|
|
@@ -98,8 +139,16 @@ class TCPNode < RJR::Node
|
|
98
139
|
attr_accessor :response_cv
|
99
140
|
attr_accessor :responses
|
100
141
|
|
142
|
+
private
|
143
|
+
# Initialize the tcp subsystem
|
144
|
+
def init_node
|
145
|
+
end
|
146
|
+
|
101
147
|
public
|
102
|
-
#
|
148
|
+
# TCPNode initializer
|
149
|
+
# @param [Hash] args the options to create the tcp node with
|
150
|
+
# @option args [String] :host the hostname/ip which to listen on
|
151
|
+
# @option args [Integer] :port the port which to listen on
|
103
152
|
def initialize(args = {})
|
104
153
|
super(args)
|
105
154
|
@host = args[:host]
|
@@ -114,18 +163,19 @@ class TCPNode < RJR::Node
|
|
114
163
|
@connection_event_handlers = {:closed => [], :error => []}
|
115
164
|
end
|
116
165
|
|
117
|
-
#
|
166
|
+
# Register connection event handler
|
167
|
+
# @param [:error, :close] event the event to register the handler for
|
168
|
+
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
169
|
+
# @yield [TCPNode] self is passed to each registered handler when event occurs
|
118
170
|
def on(event, &handler)
|
119
171
|
if @connection_event_handlers.keys.include?(event)
|
120
172
|
@connection_event_handlers[event] << handler
|
121
173
|
end
|
122
174
|
end
|
123
175
|
|
124
|
-
# Initialize the tcp subsystem
|
125
|
-
def init_node
|
126
|
-
end
|
127
|
-
|
128
176
|
# Instruct Node to start listening for and dispatching rpc requests
|
177
|
+
#
|
178
|
+
# Implementation of {RJR::Node#listen}
|
129
179
|
def listen
|
130
180
|
em_run {
|
131
181
|
init_node
|
@@ -134,6 +184,10 @@ class TCPNode < RJR::Node
|
|
134
184
|
end
|
135
185
|
|
136
186
|
# Instructs node to send rpc request, and wait for / return response
|
187
|
+
# @param [String] uri location of node to send request to, should be
|
188
|
+
# in format of jsonrpc://hostname:port
|
189
|
+
# @param [String] rpc_method json-rpc method to invoke on destination
|
190
|
+
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
137
191
|
def invoke_request(uri, rpc_method, *args)
|
138
192
|
uri = URI.parse(uri)
|
139
193
|
host,port = uri.host, uri.port
|
data/lib/rjr/thread_pool.rb
CHANGED
@@ -3,11 +3,14 @@
|
|
3
3
|
# Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
# Work item to be executed in thread pool
|
6
|
+
# Work item to be executed in a thread launched by pool
|
7
7
|
class ThreadPoolJob
|
8
8
|
attr_accessor :handler
|
9
9
|
attr_accessor :params
|
10
10
|
|
11
|
+
# ThreadPoolJob initializer
|
12
|
+
# @param [Array] params arguments to pass to the job when it is invoked
|
13
|
+
# @param [Callable] block handle to callable object corresponding to job to invoke
|
11
14
|
def initialize(*params, &block)
|
12
15
|
@params = params
|
13
16
|
@handler = block
|
@@ -15,11 +18,15 @@ class ThreadPoolJob
|
|
15
18
|
end
|
16
19
|
|
17
20
|
|
18
|
-
#
|
19
|
-
# assigning work to them as it arrives
|
21
|
+
# Utility to launches a specified number of threads on instantiation,
|
22
|
+
# assigning work to them in order as it arrives.
|
23
|
+
#
|
24
|
+
# Supports optional timeout which allows the developer to kill and restart
|
25
|
+
# threads if a job is taking too long to run.
|
20
26
|
class ThreadPool
|
21
27
|
|
22
|
-
#
|
28
|
+
# @private
|
29
|
+
# Helper class to encapsulate each thread pool thread
|
23
30
|
class ThreadPoolJobRunner
|
24
31
|
attr_accessor :time_started
|
25
32
|
|
@@ -29,6 +36,10 @@ class ThreadPool
|
|
29
36
|
@thread_lock = Mutex.new
|
30
37
|
end
|
31
38
|
|
39
|
+
# Start thread and pull a work items off the thread pool work queue and execute them.
|
40
|
+
#
|
41
|
+
# This method will return immediately after the worker thread is started but the
|
42
|
+
# thread launched will persist until {#stop} is invoked
|
32
43
|
def run
|
33
44
|
@thread_lock.synchronize {
|
34
45
|
@thread = Thread.new {
|
@@ -49,6 +60,7 @@ class ThreadPool
|
|
49
60
|
}
|
50
61
|
end
|
51
62
|
|
63
|
+
# Return boolean indicating if worker thread is running or not
|
52
64
|
def running?
|
53
65
|
res = nil
|
54
66
|
@thread_lock.synchronize{
|
@@ -57,7 +69,8 @@ class ThreadPool
|
|
57
69
|
res
|
58
70
|
end
|
59
71
|
|
60
|
-
#
|
72
|
+
# Return boolean indicating if worker thread run time has exceeded timeout
|
73
|
+
# Should not invoke after stop is called
|
61
74
|
def check_timeout(timeout)
|
62
75
|
@timeout_lock.synchronize {
|
63
76
|
if !@time_started.nil? && Time.now - @time_started > timeout
|
@@ -67,6 +80,7 @@ class ThreadPool
|
|
67
80
|
}
|
68
81
|
end
|
69
82
|
|
83
|
+
# Stop the worker thread being executed
|
70
84
|
def stop
|
71
85
|
@thread_lock.synchronize {
|
72
86
|
if @thread.alive?
|
@@ -77,6 +91,7 @@ class ThreadPool
|
|
77
91
|
}
|
78
92
|
end
|
79
93
|
|
94
|
+
# Block until the worker thread is finished
|
80
95
|
def join
|
81
96
|
@thread_lock.synchronize {
|
82
97
|
@thread.join unless @thread.nil?
|
@@ -85,6 +100,9 @@ class ThreadPool
|
|
85
100
|
end
|
86
101
|
|
87
102
|
# Create a thread pool with a specified number of threads
|
103
|
+
# @param [Integer] num_threads the number of worker threads to create
|
104
|
+
# @param [Hash] args optional arguments to initialize thread pool with
|
105
|
+
# @option args [Integer] :timeout optional timeout to use to kill long running worker jobs
|
88
106
|
def initialize(num_threads, args = {})
|
89
107
|
@num_threads = num_threads
|
90
108
|
@timeout = args[:timeout]
|
@@ -116,32 +134,37 @@ class ThreadPool
|
|
116
134
|
end
|
117
135
|
end
|
118
136
|
|
137
|
+
# Return boolean indicated if thread pool is running.
|
138
|
+
#
|
139
|
+
# If at least one worker thread isn't terminated, the pool is still considered running
|
119
140
|
def running?
|
120
141
|
!terminate && (@timeout.nil? || (!@timeout_thread.nil? && @timeout_thread.status)) &&
|
121
142
|
@job_runners.all? { |r| r.running? }
|
122
143
|
end
|
123
144
|
|
124
|
-
#
|
145
|
+
# Return boolean indicating if the thread pool should be terminated
|
125
146
|
def terminate
|
126
147
|
@terminate_lock.synchronize { @terminate }
|
127
148
|
end
|
128
149
|
|
129
|
-
# terminate
|
150
|
+
# Instruct thread pool to terminate
|
151
|
+
# @param [Boolean] val true/false indicating if thread pool should terminate
|
130
152
|
def terminate=(val)
|
131
153
|
@terminate_lock.synchronize { @terminate = val }
|
132
154
|
end
|
133
155
|
|
134
156
|
# Add work to the pool
|
157
|
+
# @param [ThreadPoolJob] work job to execute in first available thread
|
135
158
|
def <<(work)
|
136
159
|
@work_queue.push work
|
137
160
|
end
|
138
161
|
|
139
|
-
# Return the next job queued up
|
162
|
+
# Return the next job queued up and remove it from the queue
|
140
163
|
def next_job
|
141
164
|
@work_queue.pop
|
142
165
|
end
|
143
166
|
|
144
|
-
# Terminate the thread pool
|
167
|
+
# Terminate the thread pool, stopping all worker threads
|
145
168
|
def stop
|
146
169
|
terminate = true
|
147
170
|
unless @timout_thread.nil?
|
@@ -153,6 +176,7 @@ class ThreadPool
|
|
153
176
|
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.stop } }
|
154
177
|
end
|
155
178
|
|
179
|
+
# Block until all worker threads have finished executing
|
156
180
|
def join
|
157
181
|
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.join } }
|
158
182
|
end
|
data/lib/rjr/web_node.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# RJR WWW Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests over the HTTP protocol
|
4
|
+
#
|
5
|
+
# The web node does not support callbacks at the moment, though at some point we may
|
6
|
+
# allow a client to specify an optional webserver to send callback requests to. (TODO)
|
7
|
+
#
|
3
8
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
9
|
# Licensed under the Apache License, Version 2.0
|
5
10
|
|
@@ -16,8 +21,8 @@ require 'rjr/message'
|
|
16
21
|
|
17
22
|
module RJR
|
18
23
|
|
19
|
-
# Web
|
20
|
-
#
|
24
|
+
# Web node callback interface, *note* callbacks are not supported on the web
|
25
|
+
# node and thus this currently does nothing
|
21
26
|
class WebNodeCallback
|
22
27
|
def initialize()
|
23
28
|
end
|
@@ -26,16 +31,31 @@ class WebNodeCallback
|
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
29
|
-
#
|
34
|
+
# @private
|
35
|
+
# Helper class intialized by eventmachine encapsulating a http connection
|
30
36
|
class WebRequestHandler < EventMachine::Connection
|
31
37
|
include EventMachine::HttpServer
|
32
38
|
|
33
39
|
RJR_NODE_TYPE = :web
|
34
40
|
|
41
|
+
# WebRequestHandler initializer.
|
42
|
+
#
|
43
|
+
# specify the WebNode establishing the connection
|
35
44
|
def initialize(*args)
|
36
45
|
@web_node = args[0]
|
37
46
|
end
|
38
47
|
|
48
|
+
# {EventMachine::Connection#process_http_request} callback, handle request messages
|
49
|
+
def process_http_request
|
50
|
+
# TODO support http protocols other than POST
|
51
|
+
msg = @http_post_content.nil? ? '' : @http_post_content
|
52
|
+
#@thread_pool << ThreadPoolJob.new { handle_request(msg) }
|
53
|
+
handle_request(msg)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Internal helper, handle request message received
|
39
59
|
def handle_request(message)
|
40
60
|
msg = nil
|
41
61
|
result = nil
|
@@ -66,38 +86,66 @@ class WebRequestHandler < EventMachine::Connection
|
|
66
86
|
resp.content_type "application/json"
|
67
87
|
resp.send_response
|
68
88
|
end
|
89
|
+
end
|
69
90
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
91
|
+
# Web node definition, listen for and invoke json-rpc requests via web requests
|
92
|
+
#
|
93
|
+
# Clients should specify the hostname / port when listening for requests and
|
94
|
+
# when invoking them.
|
95
|
+
#
|
96
|
+
# *note* the RJR javascript client also supports sending / receiving json-rpc
|
97
|
+
# messages over http
|
98
|
+
#
|
99
|
+
# @example Listening for json-rpc requests over tcp
|
100
|
+
# # register rjr dispatchers (see RJR::Dispatcher)
|
101
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
102
|
+
# "Hello #{name}!"
|
103
|
+
# }
|
104
|
+
#
|
105
|
+
# # initialize node, listen, and block
|
106
|
+
# server = RJR::WebNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
|
107
|
+
# server.listen
|
108
|
+
# server.join
|
109
|
+
#
|
110
|
+
# @example Invoking json-rpc requests over http using rjr
|
111
|
+
# client = RJR::WebNode.new :node_id => 'client'
|
112
|
+
# puts client.invoke_request('http://localhost:7777', 'hello', 'mo')
|
113
|
+
#
|
114
|
+
# @example Invoking json-rpc requests over http using curl
|
115
|
+
# $ curl -X POST http://localhost:7777 -d '{"jsonrpc":"2.0","method":"hello","params":["mo"],"id":"123"}'
|
116
|
+
# > {"jsonrpc":"2.0","id":"123","result":"Hello mo!"}
|
117
|
+
#
|
118
|
+
class WebNode < RJR::Node
|
119
|
+
private
|
120
|
+
# Initialize the web subsystem
|
121
|
+
def init_node
|
75
122
|
end
|
76
123
|
|
77
|
-
|
78
|
-
# puts "~~~~ #{data}"
|
79
|
-
#end
|
80
|
-
end
|
124
|
+
public
|
81
125
|
|
82
|
-
|
83
|
-
#
|
126
|
+
# TCPNode initializer
|
127
|
+
# @param [Hash] args the options to create the tcp node with
|
128
|
+
# @option args [String] :host the hostname/ip which to listen on
|
129
|
+
# @option args [Integer] :port the port which to listen on
|
84
130
|
def initialize(args = {})
|
85
131
|
super(args)
|
86
132
|
@host = args[:host]
|
87
133
|
@port = args[:port]
|
88
134
|
end
|
89
135
|
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
#
|
95
|
-
#
|
136
|
+
# Register connection event handler,
|
137
|
+
#
|
138
|
+
# *note* Since web node connections aren't persistant, we don't do anything here.
|
139
|
+
# @param [:error, :close] event the event to register the handler for
|
140
|
+
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
141
|
+
# @yield [LocalNode] self is passed to each registered handler when event occurs
|
96
142
|
def on(event, &handler)
|
97
143
|
# TODO raise error?
|
98
144
|
end
|
99
145
|
|
100
146
|
# Instruct Node to start listening for and dispatching rpc requests
|
147
|
+
#
|
148
|
+
# Implementation of {RJR::Node#listen}
|
101
149
|
def listen
|
102
150
|
em_run do
|
103
151
|
init_node
|
@@ -106,6 +154,10 @@ class WebNode < RJR::Node
|
|
106
154
|
end
|
107
155
|
|
108
156
|
# Instructs node to send rpc request, and wait for / return response
|
157
|
+
# @param [String] uri location of node to send request to, should be
|
158
|
+
# in format of http://hostname:port
|
159
|
+
# @param [String] rpc_method json-rpc method to invoke on destination
|
160
|
+
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
109
161
|
def invoke_request(uri, rpc_method, *args)
|
110
162
|
init_node
|
111
163
|
message = RequestMessage.new :method => rpc_method,
|