rjr 0.5.3
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.
- 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
|