rjr 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +661 -0
- data/README.rdoc +73 -0
- data/Rakefile +64 -0
- data/bin/rjr-server +51 -0
- data/lib/rjr/amqp_node.rb +164 -0
- data/lib/rjr/common.rb +60 -0
- data/lib/rjr/dispatcher.rb +169 -0
- data/lib/rjr/errors.rb +21 -0
- data/lib/rjr/local_node.rb +65 -0
- data/lib/rjr/message.rb +146 -0
- data/lib/rjr/multi_node.rb +35 -0
- data/lib/rjr/node.rb +81 -0
- data/lib/rjr/semaphore.rb +58 -0
- data/lib/rjr/tcp_node.rb +1 -0
- data/lib/rjr/thread_pool.rb +165 -0
- data/lib/rjr/udp_node.rb +1 -0
- data/lib/rjr/web_node.rb +112 -0
- data/lib/rjr/web_socket.rb +589 -0
- data/lib/rjr/ws_node.rb +100 -0
- data/lib/rjr.rb +20 -0
- metadata +103 -0
data/lib/rjr/message.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# RJR Message
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
# establish client connection w/ specified args and invoke block w/
|
7
|
+
# newly created client, returning it after block terminates
|
8
|
+
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
module RJR
|
12
|
+
|
13
|
+
# Message sent from client to server to invoke a json-rpc method
|
14
|
+
class RequestMessage
|
15
|
+
# Helper method to generate a random id
|
16
|
+
def self.gen_uuid
|
17
|
+
["%02x"*4, "%02x"*2, "%02x"*2, "%02x"*2, "%02x"*6].join("-") %
|
18
|
+
Array.new(16) {|x| rand(0xff) }
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :json_message
|
22
|
+
attr_accessor :jr_method
|
23
|
+
attr_accessor :jr_args
|
24
|
+
attr_accessor :msg_id
|
25
|
+
attr_accessor :headers
|
26
|
+
|
27
|
+
def initialize(args = {})
|
28
|
+
if args.has_key?(:message)
|
29
|
+
begin
|
30
|
+
request = JSON.parse(args[:message])
|
31
|
+
@json_message = args[:message]
|
32
|
+
@jr_method = request['method']
|
33
|
+
@jr_args = request['params']
|
34
|
+
@msg_id = request['id']
|
35
|
+
@headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
|
36
|
+
|
37
|
+
request.keys.select { |k|
|
38
|
+
!['jsonrpc', 'id', 'method', 'params'].include?(k)
|
39
|
+
}.each { |k| @headers[k] = request[k] }
|
40
|
+
|
41
|
+
rescue Exception => e
|
42
|
+
#puts "Exception Parsing Request #{e}"
|
43
|
+
# TODO
|
44
|
+
raise e
|
45
|
+
end
|
46
|
+
|
47
|
+
elsif args.has_key?(:method)
|
48
|
+
@jr_method = args[:method]
|
49
|
+
@jr_args = args[:args]
|
50
|
+
@headers = args[:headers]
|
51
|
+
@msg_id = RequestMessage.gen_uuid
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.is_request_message?(message)
|
57
|
+
begin
|
58
|
+
JSON.parse(message).has_key?('method')
|
59
|
+
rescue Exception => e
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
request = { 'jsonrpc' => '2.0',
|
66
|
+
'method' => @jr_method,
|
67
|
+
'params' => @jr_args }
|
68
|
+
request['id'] = @msg_id unless @msg_id.nil?
|
69
|
+
request.merge!(@headers) unless @headers.nil?
|
70
|
+
request.to_json.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# Message sent from server to client in response to request message
|
76
|
+
class ResponseMessage
|
77
|
+
attr_accessor :json_message
|
78
|
+
attr_accessor :msg_id
|
79
|
+
attr_accessor :result
|
80
|
+
attr_accessor :headers
|
81
|
+
|
82
|
+
def initialize(args = {})
|
83
|
+
if args.has_key?(:message)
|
84
|
+
response = JSON.parse(args[:message])
|
85
|
+
@json_message = args[:message]
|
86
|
+
@msg_id = response['id']
|
87
|
+
@result = Result.new
|
88
|
+
@result.success = response.has_key?('result')
|
89
|
+
@result.failed = !response.has_key?('result')
|
90
|
+
@headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
|
91
|
+
|
92
|
+
if @result.success
|
93
|
+
@result.result = response['result']
|
94
|
+
|
95
|
+
elsif response.has_key?('error')
|
96
|
+
@result.error_code = response['error']['code']
|
97
|
+
@result.error_msg = response['error']['message']
|
98
|
+
@result.error_class = response['error']['class'] # TODO safely constantize this
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
response.keys.select { |k|
|
103
|
+
!['jsonrpc', 'id', 'result', 'error'].include?(k)
|
104
|
+
}.each { |k| @headers[k] = response[k] }
|
105
|
+
|
106
|
+
elsif args.has_key?(:result)
|
107
|
+
@msg_id = args[:id]
|
108
|
+
@result = args[:result]
|
109
|
+
@headers = args[:headers]
|
110
|
+
|
111
|
+
#else
|
112
|
+
# raise ArgumentError, "must specify :message or :result"
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.is_response_message?(message)
|
119
|
+
begin
|
120
|
+
JSON.parse(message).has_key?('result')
|
121
|
+
rescue Exception => e
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
s = ''
|
128
|
+
if result.success
|
129
|
+
s = {'jsonrpc' => '2.0',
|
130
|
+
'id' => @msg_id,
|
131
|
+
'result' => @result.result}
|
132
|
+
|
133
|
+
else
|
134
|
+
s = {'jsonrpc' => '2.0',
|
135
|
+
'id' => @msg_id,
|
136
|
+
'error' => { 'code' => @result.error_code,
|
137
|
+
'message' => @result.error_msg,
|
138
|
+
'class' => @result.error_class}}
|
139
|
+
end
|
140
|
+
|
141
|
+
s.merge! @headers unless headers.nil?
|
142
|
+
return s.to_json.to_s
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# RJR MultiNode Endpoint
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
# establish client connection w/ specified args and invoke block w/
|
7
|
+
# newly created client, returning it after block terminates
|
8
|
+
|
9
|
+
require 'eventmachine'
|
10
|
+
require 'rjr/node'
|
11
|
+
require 'rjr/message'
|
12
|
+
|
13
|
+
module RJR
|
14
|
+
|
15
|
+
class MultiNode < RJR::Node
|
16
|
+
# initialize the node w/ the specified params
|
17
|
+
def initialize(args = {})
|
18
|
+
super(args)
|
19
|
+
@nodes = args[:nodes]
|
20
|
+
end
|
21
|
+
|
22
|
+
def <<(node)
|
23
|
+
@nodes << node
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# Instruct Node to start listening for and dispatching rpc requests
|
28
|
+
def listen
|
29
|
+
@nodes.each { |node|
|
30
|
+
node.listen
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/rjr/node.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# RJR Node
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
# establish client connection w/ specified args and invoke block w/
|
7
|
+
# newly created client, returning it after block terminates
|
8
|
+
|
9
|
+
require 'eventmachine'
|
10
|
+
require 'rjr/thread_pool'
|
11
|
+
|
12
|
+
module RJR
|
13
|
+
|
14
|
+
# Defines a node which can be used to dispatch rpc requests and/or register
|
15
|
+
# handlers for incomping requests.
|
16
|
+
class Node
|
17
|
+
# node always has a node id
|
18
|
+
attr_reader :node_id
|
19
|
+
|
20
|
+
# attitional parameters to set on messages
|
21
|
+
attr_accessor :message_headers
|
22
|
+
|
23
|
+
def initialize(args = {})
|
24
|
+
@node_id = args[:node_id]
|
25
|
+
|
26
|
+
@message_headers = {}
|
27
|
+
@message_headers.merge!(args[:headers]) if args.has_key?(:headers)
|
28
|
+
|
29
|
+
# threads pool to handle incoming requests
|
30
|
+
# FIXME make the # of threads and timeout configurable)
|
31
|
+
@thread_pool = ThreadPool.new(10, :timeout => 5)
|
32
|
+
end
|
33
|
+
|
34
|
+
# run job in event machine
|
35
|
+
def em_run(&bl)
|
36
|
+
@@em_jobs ||= 0
|
37
|
+
@@em_jobs += 1
|
38
|
+
|
39
|
+
@@em_thread ||= nil
|
40
|
+
|
41
|
+
if @@em_thread.nil?
|
42
|
+
@@em_thread =
|
43
|
+
Thread.new{
|
44
|
+
begin
|
45
|
+
EventMachine.run
|
46
|
+
rescue Exception => e
|
47
|
+
puts "Critical exception #{e}"
|
48
|
+
ensure
|
49
|
+
end
|
50
|
+
}
|
51
|
+
#sleep 0.5 until EventMachine.reactor_running? # XXX hacky way to do this
|
52
|
+
end
|
53
|
+
EventMachine.schedule bl
|
54
|
+
end
|
55
|
+
|
56
|
+
def em_running?
|
57
|
+
EventMachine.reactor_running?
|
58
|
+
end
|
59
|
+
|
60
|
+
def join
|
61
|
+
if @@em_thread
|
62
|
+
@@em_thread.join
|
63
|
+
@@em_thread = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def stop
|
68
|
+
@@em_jobs -= 1
|
69
|
+
if @@em_jobs == 0
|
70
|
+
EventMachine.stop
|
71
|
+
@thread_pool.stop
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def halt
|
76
|
+
@@em_jobs = 0
|
77
|
+
EventMachine.stop
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end # module RJR
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# $Id: semaphore.rb,v 1.2 2003/03/15 20:10:10 fukumoto Exp $
|
3
|
+
#
|
4
|
+
# Copied unmodified from:
|
5
|
+
# http://www.imasy.or.jp/~fukumoto/ruby/semaphore.rb
|
6
|
+
# Originally licensed under The Ruby License:
|
7
|
+
# http://raa.ruby-lang.org/project/semaphore/
|
8
|
+
|
9
|
+
class CountingSemaphore
|
10
|
+
|
11
|
+
def initialize(initvalue = 0)
|
12
|
+
@counter = initvalue
|
13
|
+
@waiting_list = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def wait
|
17
|
+
Thread.critical = true
|
18
|
+
if (@counter -= 1) < 0
|
19
|
+
@waiting_list.push(Thread.current)
|
20
|
+
Thread.stop
|
21
|
+
end
|
22
|
+
self
|
23
|
+
ensure
|
24
|
+
Thread.critical = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def signal
|
28
|
+
Thread.critical = true
|
29
|
+
begin
|
30
|
+
if (@counter += 1) <= 0
|
31
|
+
t = @waiting_list.shift
|
32
|
+
t.wakeup if t
|
33
|
+
end
|
34
|
+
rescue ThreadError
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
self
|
38
|
+
ensure
|
39
|
+
Thread.critical = false
|
40
|
+
end
|
41
|
+
|
42
|
+
alias down wait
|
43
|
+
alias up signal
|
44
|
+
alias P wait
|
45
|
+
alias V signal
|
46
|
+
|
47
|
+
def exclusive
|
48
|
+
wait
|
49
|
+
yield
|
50
|
+
ensure
|
51
|
+
signal
|
52
|
+
end
|
53
|
+
|
54
|
+
alias synchronize exclusive
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
Semaphore = CountingSemaphore
|
data/lib/rjr/tcp_node.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Thread Pool
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
# Work item to be executed in thread pool
|
27
|
+
class ThreadPoolJob
|
28
|
+
attr_accessor :handler
|
29
|
+
attr_accessor :params
|
30
|
+
|
31
|
+
def initialize(*params, &block)
|
32
|
+
@params = params
|
33
|
+
@handler = block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Launches a specified number of threads on instantiation,
|
39
|
+
# assigning work to them as it arrives
|
40
|
+
class ThreadPool
|
41
|
+
|
42
|
+
# Encapsulate each thread pool thread in object
|
43
|
+
class ThreadPoolJobRunner
|
44
|
+
attr_accessor :time_started
|
45
|
+
|
46
|
+
def initialize(thread_pool)
|
47
|
+
@thread_pool = thread_pool
|
48
|
+
@timeout_lock = Mutex.new
|
49
|
+
@thread_lock = Mutex.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
@thread_lock.synchronize {
|
54
|
+
@thread = Thread.new {
|
55
|
+
until @thread_pool.terminate
|
56
|
+
@timeout_lock.synchronize { @time_started = nil }
|
57
|
+
work = @thread_pool.next_job
|
58
|
+
@timeout_lock.synchronize { @time_started = Time.now }
|
59
|
+
unless work.nil?
|
60
|
+
begin
|
61
|
+
work.handler.call *work.params
|
62
|
+
rescue Exception => e
|
63
|
+
puts "Thread raised Fatal Exception #{e}"
|
64
|
+
puts "\n#{e.backtrace.join("\n")}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def running?
|
73
|
+
res = nil
|
74
|
+
@thread_lock.synchronize{
|
75
|
+
res = (@thread.status != false)
|
76
|
+
}
|
77
|
+
res
|
78
|
+
end
|
79
|
+
|
80
|
+
# TODO should not invoke after stop is called
|
81
|
+
def check_timeout(timeout)
|
82
|
+
@timeout_lock.synchronize {
|
83
|
+
if !@time_started.nil? && Time.now - @time_started > timeout
|
84
|
+
stop
|
85
|
+
run
|
86
|
+
end
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def stop
|
91
|
+
@thread_lock.synchronize {
|
92
|
+
if @thread.alive?
|
93
|
+
@thread.kill
|
94
|
+
@thread.join
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Create a thread pool with a specified number of threads
|
101
|
+
def initialize(num_threads, args = {})
|
102
|
+
@num_threads = num_threads
|
103
|
+
@timeout = args[:timeout]
|
104
|
+
@job_runners = []
|
105
|
+
@job_runners_lock = Mutex.new
|
106
|
+
@terminate = false
|
107
|
+
@terminate_lock = Mutex.new
|
108
|
+
|
109
|
+
@work_queue = Queue.new
|
110
|
+
|
111
|
+
0.upto(@num_threads) { |i|
|
112
|
+
runner = ThreadPoolJobRunner.new(self)
|
113
|
+
@job_runners << runner
|
114
|
+
runner.run
|
115
|
+
}
|
116
|
+
|
117
|
+
# optional timeout thread
|
118
|
+
unless @timeout.nil?
|
119
|
+
@timeout_thread = Thread.new {
|
120
|
+
until terminate
|
121
|
+
sleep @timeout
|
122
|
+
@job_runners_lock.synchronize {
|
123
|
+
@job_runners.each { |jr|
|
124
|
+
jr.check_timeout(@timeout)
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def running?
|
133
|
+
!terminate && (@timeout.nil? || @timeout_thread.status) &&
|
134
|
+
@job_runners.all? { |r| r.running? }
|
135
|
+
end
|
136
|
+
|
137
|
+
# terminate reader
|
138
|
+
def terminate
|
139
|
+
@terminate_lock.synchronize { @terminate }
|
140
|
+
end
|
141
|
+
|
142
|
+
# terminate setter
|
143
|
+
def terminate=(val)
|
144
|
+
@terminate_lock.synchronize { @terminate = val }
|
145
|
+
end
|
146
|
+
|
147
|
+
# Add work to the pool
|
148
|
+
def <<(work)
|
149
|
+
@work_queue.push work
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return the next job queued up
|
153
|
+
def next_job
|
154
|
+
@work_queue.pop
|
155
|
+
end
|
156
|
+
|
157
|
+
# Terminate the thread pool
|
158
|
+
def stop
|
159
|
+
terminate = true
|
160
|
+
@timeout_thread.join unless @timout_thread.nil?
|
161
|
+
@work_queue.clear
|
162
|
+
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.stop } }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
data/lib/rjr/udp_node.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
data/lib/rjr/web_node.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# RJR WWW Endpoint
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
# establish client connection w/ specified args and invoke block w/
|
7
|
+
# newly created client, returning it after block terminates
|
8
|
+
|
9
|
+
require 'curb'
|
10
|
+
|
11
|
+
require 'evma_httpserver'
|
12
|
+
#require 'em-http-request'
|
13
|
+
|
14
|
+
require 'rjr/node'
|
15
|
+
require 'rjr/message'
|
16
|
+
|
17
|
+
module RJR
|
18
|
+
|
19
|
+
# Web client node callback interface,
|
20
|
+
# currently does nothing as web connections aren't persistant
|
21
|
+
class WebNodeCallback
|
22
|
+
def initialize()
|
23
|
+
end
|
24
|
+
|
25
|
+
def invoke(callback_method, *data)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Web node definition, listen for and invoke json-rpc requests via web requests
|
30
|
+
class WebRequestHandler < EventMachine::Connection
|
31
|
+
include EventMachine::HttpServer
|
32
|
+
|
33
|
+
RJR_NODE_TYPE = :web
|
34
|
+
|
35
|
+
def initialize(*args)
|
36
|
+
@web_node = args[0]
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_request(message)
|
40
|
+
msg = nil
|
41
|
+
result = nil
|
42
|
+
begin
|
43
|
+
msg = RequestMessage.new(:message => message, :headers => @web_node.message_headers)
|
44
|
+
headers = @web_node.message_headers.merge(msg.headers)
|
45
|
+
result = Dispatcher.dispatch_request(msg.jr_method,
|
46
|
+
:method_args => msg.jr_args,
|
47
|
+
:headers => headers,
|
48
|
+
:rjr_node_id => @web_node.node_id,
|
49
|
+
:rjr_node_type => RJR_NODE_TYPE,
|
50
|
+
:rjr_callback => WebNodeCallback.new())
|
51
|
+
rescue JSON::ParserError => e
|
52
|
+
result = Result.invalid_request
|
53
|
+
end
|
54
|
+
|
55
|
+
msg_id = msg.nil? ? nil : msg.msg_id
|
56
|
+
response = ResponseMessage.new(:id => msg_id, :result => result, :headers => headers)
|
57
|
+
|
58
|
+
resp = EventMachine::DelegatedHttpResponse.new(self)
|
59
|
+
#resp.status = response.result.success ? 200 : 500
|
60
|
+
resp.status = 200
|
61
|
+
resp.content = response.to_s
|
62
|
+
resp.content_type "application/json"
|
63
|
+
resp.send_response
|
64
|
+
end
|
65
|
+
|
66
|
+
def process_http_request
|
67
|
+
# TODO support http protocols other than POST
|
68
|
+
# TODO should delete handler threads as they complete & should handle timeout
|
69
|
+
msg = @http_post_content.nil? ? '' : @http_post_content
|
70
|
+
#@thread_pool << ThreadPoolJob.new { handle_request(msg) }
|
71
|
+
handle_request(msg)
|
72
|
+
end
|
73
|
+
|
74
|
+
#def receive_data(data)
|
75
|
+
# puts "~~~~ #{data}"
|
76
|
+
#end
|
77
|
+
end
|
78
|
+
|
79
|
+
class WebNode < RJR::Node
|
80
|
+
# initialize the node w/ the specified params
|
81
|
+
def initialize(args = {})
|
82
|
+
super(args)
|
83
|
+
@host = args[:host]
|
84
|
+
@port = args[:port]
|
85
|
+
end
|
86
|
+
|
87
|
+
# Initialize the web subsystem
|
88
|
+
def init_node
|
89
|
+
end
|
90
|
+
|
91
|
+
# Instruct Node to start listening for and dispatching rpc requests
|
92
|
+
def listen
|
93
|
+
em_run do
|
94
|
+
init_node
|
95
|
+
EventMachine::start_server(@host, @port, WebRequestHandler, self)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Instructs node to send rpc request, and wait for / return response
|
100
|
+
def invoke_request(uri, rpc_method, *args)
|
101
|
+
init_node
|
102
|
+
message = RequestMessage.new :method => rpc_method,
|
103
|
+
:args => args,
|
104
|
+
:headers => @message_headers
|
105
|
+
res = Curl::Easy.http_post uri, message.to_s
|
106
|
+
msg = ResponseMessage.new(:message => res.body_str, :headers => @message_headers)
|
107
|
+
headers = @message_headers.merge(msg.headers)
|
108
|
+
return Dispatcher.handle_response(msg.result)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end # module RJR
|