talkshow 1.4.2
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 +7 -0
- data/lib/talkshow.rb +241 -0
- data/lib/talkshow/daemon.rb +99 -0
- data/lib/talkshow/javascript_error.rb +4 -0
- data/lib/talkshow/queue.rb +32 -0
- data/lib/talkshow/server.rb +218 -0
- data/lib/talkshow/timeout.rb +4 -0
- data/lib/talkshow/web_control.rb +85 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0defe1121946da84d88bfc719622b659c34d5033
|
4
|
+
data.tar.gz: c3e5c0855a41a9e698e543dcf3d1599bc067a80e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b3df5f71a04d10ceb8c07b713aff6de3c950c19ebbf1ac0606583ea34d94458de2a5a2cf22ff8cb40b42ec2594df659763342b08cdb3a5f91d4f33a5baef1784
|
7
|
+
data.tar.gz: 2fa658549eea26110bf46d37889768c44b1f6286ecffc790390c222d57cf164505292dd538710c4ecedcdd07dbe8e6c0785b73ed95f9c4c887c6a2d149407c84
|
data/lib/talkshow.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'thread'
|
3
|
+
require 'json'
|
4
|
+
require 'talkshow/server'
|
5
|
+
require 'talkshow/timeout'
|
6
|
+
require 'talkshow/javascript_error'
|
7
|
+
require 'talkshow/queue'
|
8
|
+
|
9
|
+
|
10
|
+
#Main class for talking to a talkshow instrumented js application.
|
11
|
+
#
|
12
|
+
#This is the only class you need to worry about, and there are only
|
13
|
+
#a few important methods.
|
14
|
+
#
|
15
|
+
# Create the Talkshow client object:
|
16
|
+
# ts = Talkshow.new()
|
17
|
+
# Start up the server
|
18
|
+
# ts.start_server()
|
19
|
+
# Start executing javascript:
|
20
|
+
# ts.execute( 'alert "Hello world!"' )
|
21
|
+
class Talkshow
|
22
|
+
attr_accessor :type
|
23
|
+
attr_accessor :thread
|
24
|
+
|
25
|
+
# Create a new Talkshow object to get going:
|
26
|
+
def initialize
|
27
|
+
end
|
28
|
+
|
29
|
+
# Start up the Talkshow webserver
|
30
|
+
# This will be triggered if you don't do it -- but it takes a few
|
31
|
+
# seconds to start up the thin server, so you are better off
|
32
|
+
# issuing this yourself
|
33
|
+
def start_server(options = {})
|
34
|
+
|
35
|
+
# Backward compatibility
|
36
|
+
if options.is_a? String
|
37
|
+
url = options
|
38
|
+
port = nil
|
39
|
+
logfile = nil
|
40
|
+
else
|
41
|
+
url = options[:url]
|
42
|
+
port = options[:port]
|
43
|
+
logfile = options[:logfile]
|
44
|
+
end
|
45
|
+
|
46
|
+
url = ENV['TALKSHOW_REMOTE_URL'] if ENV['TALKSHOW_REMOTE_URL']
|
47
|
+
port = ENV['TALKSHOW_PORT'] if ENV['TALKSHOW_PORT']
|
48
|
+
logfile = ENV['TALKSHOW_LOG'] if ENV['TALKSHOW_LOG']
|
49
|
+
|
50
|
+
Talkshow::Server.set_port port if port
|
51
|
+
Talkshow::Server.set_logfile logfile if logfile
|
52
|
+
|
53
|
+
if !url
|
54
|
+
@type = :thread
|
55
|
+
@question_queue = ::Queue.new
|
56
|
+
@answer_queue = ::Queue.new
|
57
|
+
@thread = Thread.new do
|
58
|
+
Talkshow::Server.question_queue(@question_queue)
|
59
|
+
Talkshow::Server.answer_queue(@answer_queue)
|
60
|
+
Talkshow::Server.run!
|
61
|
+
end
|
62
|
+
else
|
63
|
+
@type = :remote
|
64
|
+
@question_queue = Talkshow::Queue.new(url)
|
65
|
+
@answer_queue = Talkshow::Queue.new(url)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# Stop the webserver
|
71
|
+
def stop_server
|
72
|
+
@thread.exit
|
73
|
+
end
|
74
|
+
|
75
|
+
# Invoke a function in the javascript application
|
76
|
+
# invoke requires a function name (including the namespace).
|
77
|
+
# Arguments are specified as an array reference.
|
78
|
+
#
|
79
|
+
# Some examples:
|
80
|
+
# ts.invoke( 'alert' )
|
81
|
+
# ts.invoke( 'alert', ['Hello world'])
|
82
|
+
# ts.invoke( 'window.alert', ['Hello world'] )
|
83
|
+
def invoke( function, args, timeout=6 )
|
84
|
+
send_question( {
|
85
|
+
type: 'invocation',
|
86
|
+
function: function,
|
87
|
+
args: args
|
88
|
+
}, timeout)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Send a javascript instruction to the client
|
92
|
+
def execute( command, timeout=6 )
|
93
|
+
send_question( { type: 'code', message: command }, timeout)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Load in a javascript file and execute remotely
|
97
|
+
def execute_file( filename )
|
98
|
+
text = File.read(filename)
|
99
|
+
execute(text)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def soft_pop
|
105
|
+
begin
|
106
|
+
@answer_queue.pop(true)
|
107
|
+
rescue => e
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def non_blocking_pop(timeout)
|
113
|
+
sleep_time = 0.1
|
114
|
+
answer = nil
|
115
|
+
catch(:done) do
|
116
|
+
(timeout/sleep_time).to_i.times { |i|
|
117
|
+
answer = soft_pop
|
118
|
+
throw :done if answer
|
119
|
+
sleep sleep_time
|
120
|
+
}
|
121
|
+
end
|
122
|
+
answer
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
# listen for an answer for a specific id, with a timeout, and also reconstitute
|
127
|
+
# any chunked responses
|
128
|
+
def listen_for_answer(id, timeout)
|
129
|
+
|
130
|
+
if ENV['TIMEOUT_MULTIPLIER']
|
131
|
+
timeout = ENV['TIMEOUT_MULTIPLIER'].to_i * timeout
|
132
|
+
end
|
133
|
+
|
134
|
+
answer = non_blocking_pop(timeout)
|
135
|
+
if !answer
|
136
|
+
raise Talkshow::Timeout.new
|
137
|
+
end
|
138
|
+
|
139
|
+
mismatch_retry = 3
|
140
|
+
if answer[:id].to_i != id.to_i && mismatch_retry >= 0
|
141
|
+
puts "Talkshow warning: message mismatch (#{answer[:id]} vs #{id})" unless answer[:id].to_i == 0
|
142
|
+
answer = non_blocking_pop(timeout)
|
143
|
+
mismatch_retry -= 1
|
144
|
+
end
|
145
|
+
|
146
|
+
if !answer
|
147
|
+
raise Talkshow::Timeout.new
|
148
|
+
end
|
149
|
+
|
150
|
+
chunks = answer[:chunks]
|
151
|
+
if chunks
|
152
|
+
answers = [answer]
|
153
|
+
|
154
|
+
i = 1
|
155
|
+
nil_count = 0
|
156
|
+
while ( i < chunks.to_i && nil_count < 3 ) do
|
157
|
+
candidate = non_blocking_pop(1)
|
158
|
+
if !candidate
|
159
|
+
nil_count += 1
|
160
|
+
next
|
161
|
+
end
|
162
|
+
if candidate[:id].to_i != id.to_i
|
163
|
+
puts "Talkshow warning: message mismatch (#{candidate[:id]} vs #{id.to_i})"
|
164
|
+
next
|
165
|
+
end
|
166
|
+
|
167
|
+
nil_count = 0
|
168
|
+
i += 1
|
169
|
+
answers << candidate
|
170
|
+
end
|
171
|
+
|
172
|
+
if answers.count < chunks.to_i
|
173
|
+
raise "Couldn't reconstitute whole message"
|
174
|
+
end
|
175
|
+
|
176
|
+
sorted_answers = answers.sort_by{ |a| a[:payload].to_i }
|
177
|
+
data = sorted_answers.collect { |a| a[:data] }.join
|
178
|
+
answer[:data] = data
|
179
|
+
answer[:payload] = nil
|
180
|
+
end
|
181
|
+
|
182
|
+
answer
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# Send message to js application
|
187
|
+
# Message is a hash that looks like:
|
188
|
+
# {
|
189
|
+
# type => message_type,
|
190
|
+
# message => command,
|
191
|
+
# }
|
192
|
+
# Timeout is optional, but a negative timeout returns without
|
193
|
+
# looking for an answer
|
194
|
+
def send_question( message, timeout )
|
195
|
+
|
196
|
+
# Start the server if it hasn't been started already
|
197
|
+
self.start_server if (self.type == :thread && !self.thread)
|
198
|
+
|
199
|
+
@answer_queue.clear();
|
200
|
+
message[:id] = rand(99999)
|
201
|
+
|
202
|
+
@question_queue.push( message )
|
203
|
+
|
204
|
+
# Negative timeout - fire and forget
|
205
|
+
# Should only be used if it is known not to return an answer
|
206
|
+
return nil if timeout < 0
|
207
|
+
|
208
|
+
answer = listen_for_answer(message[:id], timeout)
|
209
|
+
|
210
|
+
if answer[:status] == 'error'
|
211
|
+
raise Talkshow::JavascriptError.new( answer[:data] )
|
212
|
+
end
|
213
|
+
|
214
|
+
case answer[:object]
|
215
|
+
when 'boolean'
|
216
|
+
answer[:data] == 'true'
|
217
|
+
when 'number'
|
218
|
+
if answer[:data].include?('.')
|
219
|
+
answer[:data].to_f
|
220
|
+
else
|
221
|
+
answer[:data].to_i
|
222
|
+
end
|
223
|
+
when 'undefined'
|
224
|
+
if answer[:data] == 'undefined'
|
225
|
+
nil
|
226
|
+
else
|
227
|
+
answer[:data]
|
228
|
+
end
|
229
|
+
when 'string'
|
230
|
+
answer[:data].to_s
|
231
|
+
else
|
232
|
+
begin
|
233
|
+
JSON.parse(answer[:data])
|
234
|
+
rescue StandardError => e
|
235
|
+
answer[:data]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
require 'thread'
|
5
|
+
require 'json'
|
6
|
+
require 'daemon'
|
7
|
+
|
8
|
+
require 'talkshow'
|
9
|
+
require 'talkshow/web_control'
|
10
|
+
require 'talkshow/server'
|
11
|
+
|
12
|
+
class Talkshow::Daemon
|
13
|
+
attr_accessor :thread
|
14
|
+
attr_accessor :port_requests
|
15
|
+
attr_accessor :processes
|
16
|
+
|
17
|
+
# Create a new Talkshow object to get going
|
18
|
+
def initialize
|
19
|
+
Dir.mkdir './logs' if !Dir.exists?('./logs')
|
20
|
+
Dir.mkdir './pids' if !Dir.exists?('./pids')
|
21
|
+
@processes = {}
|
22
|
+
@port_requests = ::Queue.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_server
|
26
|
+
@thread = Thread.new do
|
27
|
+
Talkshow::WebControl.port_requests(@port_requests)
|
28
|
+
Talkshow::WebControl.processes(@processes)
|
29
|
+
Talkshow::WebControl.run!
|
30
|
+
end
|
31
|
+
p @thread
|
32
|
+
sleep 10
|
33
|
+
end
|
34
|
+
|
35
|
+
# Stop the webserver
|
36
|
+
def stop_server
|
37
|
+
@thread.exit
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
self.start_server
|
42
|
+
loop do
|
43
|
+
deal_with_port_requests
|
44
|
+
sleep 5
|
45
|
+
check_processes
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def deal_with_port_requests
|
50
|
+
begin
|
51
|
+
port = @port_requests.pop(true)
|
52
|
+
rescue
|
53
|
+
port = nil
|
54
|
+
end
|
55
|
+
if port
|
56
|
+
if @processes[port]
|
57
|
+
puts "Port request -- checking aliveness"
|
58
|
+
if check_status(port) == 'dead'
|
59
|
+
@processes[port] = spawn_process(port)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
puts "New port request"
|
63
|
+
@processes[port] = spawn_process(port)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def spawn_process(port)
|
69
|
+
`TALKSHOW_PORT=#{port} bundle exec ./bin/talkshow_server.rb > logs/talkshow.#{port}.log 2>&1 &`
|
70
|
+
sleep 5
|
71
|
+
'starting'
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_status(port)
|
75
|
+
uri = URI.parse("http://localhost:#{port}/status")
|
76
|
+
begin
|
77
|
+
response = Net::HTTP.get_response(uri)
|
78
|
+
rescue
|
79
|
+
status = 'dead'
|
80
|
+
end
|
81
|
+
|
82
|
+
if !status
|
83
|
+
if response.code.to_i == 200
|
84
|
+
status = 'ok'
|
85
|
+
else
|
86
|
+
status = "dead #{response.code}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
status
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_processes()
|
93
|
+
@processes.each do |port, status|
|
94
|
+
@processes[port] = check_status(port)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Talkshow::Queue
|
6
|
+
attr_accessor :url
|
7
|
+
|
8
|
+
def initialize(url)
|
9
|
+
@uri = URI.parse(url)
|
10
|
+
@http = Net::HTTP.new(@uri.host, @uri.port)
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
response = @http.request(Net::HTTP::Get.new('/answerqueue/clear'))
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
def pop(ignored)
|
19
|
+
response = @http.request(Net::HTTP::Get.new('/answerqueue/pop'))
|
20
|
+
object = JSON.parse(response.body, :symbolize_names => true)
|
21
|
+
object[:message]
|
22
|
+
end
|
23
|
+
|
24
|
+
def push(obj)
|
25
|
+
serialized_object = obj.to_json.to_s
|
26
|
+
request = Net::HTTP::Post.new('/questionqueue/push')
|
27
|
+
request.set_form_data( {'message' => serialized_object } )
|
28
|
+
response = @http.request(request)
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'net/http'
|
3
|
+
require 'thread'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
|
8
|
+
class Queue
|
9
|
+
# Take a peek at what's in the array
|
10
|
+
def peek
|
11
|
+
@que
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sinatra server that is launched by your test code
|
16
|
+
# to talk to the instrumented javascript application
|
17
|
+
class Talkshow
|
18
|
+
class Talkshow::Server < Sinatra::Base
|
19
|
+
configure do
|
20
|
+
set :port, ENV['TALKSHOW_PORT'] if ENV['TALKSHOW_PORT']
|
21
|
+
set :protection, except: :path_traversal
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.set_port port
|
25
|
+
set :port, port
|
26
|
+
end
|
27
|
+
|
28
|
+
@@logfile = './talkshowserver.log'
|
29
|
+
def self.set_logfile file
|
30
|
+
@@logfile = file
|
31
|
+
@logger.close if @logger
|
32
|
+
@logger = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.question_queue(queue = nil)
|
36
|
+
if queue
|
37
|
+
@@question_queue = queue
|
38
|
+
end
|
39
|
+
@@question_queue
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.answer_queue(queue = nil)
|
43
|
+
if queue
|
44
|
+
@@answer_queue = queue
|
45
|
+
end
|
46
|
+
@@answer_queue
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def logger
|
51
|
+
if !@logger
|
52
|
+
@logger = Logger.new(@@logfile)
|
53
|
+
end
|
54
|
+
@logger
|
55
|
+
end
|
56
|
+
|
57
|
+
# Make this available externally
|
58
|
+
set :bind, '0.0.0.0'
|
59
|
+
|
60
|
+
get '/' do
|
61
|
+
questions = Talkshow::Server.question_queue.peek.to_json
|
62
|
+
answers = Talkshow::Server.answer_queue.peek.to_json
|
63
|
+
|
64
|
+
<<HERE
|
65
|
+
<html>
|
66
|
+
<body>
|
67
|
+
<div>
|
68
|
+
<h1>Talkshow process: #{$$}</h1>
|
69
|
+
<h2>Port: #{settings.port}</h2>
|
70
|
+
</div>
|
71
|
+
<div>
|
72
|
+
<p>#{questions}</p>
|
73
|
+
</div>
|
74
|
+
<div>
|
75
|
+
<p>#{answers}</p>
|
76
|
+
</div>
|
77
|
+
</body>
|
78
|
+
</html>
|
79
|
+
HERE
|
80
|
+
end
|
81
|
+
|
82
|
+
get '/status' do
|
83
|
+
200
|
84
|
+
end
|
85
|
+
|
86
|
+
get '/talkshowhost' do
|
87
|
+
"Talkshow running on " + request.host.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
get '/question/:poll_id' do
|
91
|
+
t = Time.new()
|
92
|
+
|
93
|
+
json_hash = {
|
94
|
+
:time => t.to_s,
|
95
|
+
}
|
96
|
+
|
97
|
+
content = nil;
|
98
|
+
if Talkshow::Server.question_queue.empty?
|
99
|
+
logger.debug("question: nop")
|
100
|
+
json_hash[:type] = "nop"
|
101
|
+
json_hash[:message] = ""
|
102
|
+
|
103
|
+
else
|
104
|
+
content = Talkshow::Server.question_queue.pop if !Talkshow::Server.question_queue.empty?
|
105
|
+
id = content[:id]
|
106
|
+
json_hash[:id] = id
|
107
|
+
logger.info( "question ##{id}: #{content.to_s}" )
|
108
|
+
|
109
|
+
type = content[:type]
|
110
|
+
json_hash[:type] = type
|
111
|
+
|
112
|
+
if type == 'code'
|
113
|
+
json_hash[:content] = content[:message]
|
114
|
+
elsif type == 'invocation'
|
115
|
+
json_hash[:function] = content[:function]
|
116
|
+
json_hash[:args] = content[:args]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
callback = params[:callback]
|
121
|
+
|
122
|
+
json = json_hash.to_json
|
123
|
+
|
124
|
+
logger.info( json )
|
125
|
+
|
126
|
+
if callback
|
127
|
+
content_type 'text/javascript'
|
128
|
+
"#{callback}( #{json} );"
|
129
|
+
else
|
130
|
+
content_type :json
|
131
|
+
json
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Deal with an answer, push it back to the main thread
|
136
|
+
def handle_answer(params, data)
|
137
|
+
if params[:status] != 'nop'
|
138
|
+
|
139
|
+
Talkshow::Server.answer_queue.push( {
|
140
|
+
:data => data,
|
141
|
+
:object => params[:object],
|
142
|
+
:status => params[:status],
|
143
|
+
:chunks => params[:chunks],
|
144
|
+
:payload => params[:payload],
|
145
|
+
:id => params[:id]
|
146
|
+
} )
|
147
|
+
end
|
148
|
+
|
149
|
+
logger.info( "/answer ##{params[:id]}"+ ( params[:chunks] ? "(#{params[:payload].to_i+1}/#{params[:chunks]})" : '') +": #{data}" )
|
150
|
+
if params[:id] == 0
|
151
|
+
logger.info( "Reset received, talkshow reloaded")
|
152
|
+
end
|
153
|
+
|
154
|
+
content_type 'text/javascript'
|
155
|
+
'ts.ack();'
|
156
|
+
end
|
157
|
+
|
158
|
+
# Capture an answer
|
159
|
+
get '/answer/:poll_id/:id/:status/:object/:data' do
|
160
|
+
handle_answer(params, params[:data])
|
161
|
+
end
|
162
|
+
|
163
|
+
# Capture the case when a response has no data (empty string)
|
164
|
+
get '/answer/:poll_id/:id/:status/:object/' do
|
165
|
+
handle_answer(params, '')
|
166
|
+
end
|
167
|
+
|
168
|
+
# Capture older talkshow.js implementations that didn't escape urls properly
|
169
|
+
get '/answer/:poll_id/:id/:status/:object/*' do
|
170
|
+
logger.warn("WARNING: Unescaped url passed as data component for route '#{request.fullpath}'")
|
171
|
+
data = params[:splat].join('/')
|
172
|
+
handle_answer(params, data)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Functions for remotely clearing queues
|
176
|
+
get '/answerqueue/clear' do
|
177
|
+
Talkshow::Server.answer_queue.clear()
|
178
|
+
end
|
179
|
+
|
180
|
+
get '/questionqueue/clear' do
|
181
|
+
Talkshow::Server.question_queue.clear()
|
182
|
+
end
|
183
|
+
|
184
|
+
# Push something onto the answer queue remotely
|
185
|
+
post '/questionqueue/push' do
|
186
|
+
logger.info( '/questionqueue/push' )
|
187
|
+
message = JSON.parse(params[:message], :symbolize_names => true)
|
188
|
+
logger.debug("Message pushed: #{message}")
|
189
|
+
Talkshow::Server.question_queue.push(message)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Pop something from the answer queue
|
193
|
+
get '/answerqueue/pop' do
|
194
|
+
logger.info('answerqueue/pop')
|
195
|
+
begin
|
196
|
+
message = Talkshow::Server.answer_queue.pop(true)
|
197
|
+
rescue
|
198
|
+
message = nil
|
199
|
+
end
|
200
|
+
{ :message => message }.to_json
|
201
|
+
end
|
202
|
+
|
203
|
+
get '/questionqueue' do
|
204
|
+
Talkshow::Server.question_queue.peek.to_json
|
205
|
+
end
|
206
|
+
|
207
|
+
get '/answerqueue' do
|
208
|
+
Talkshow::Server.answer_queue.peek.to_json
|
209
|
+
end
|
210
|
+
|
211
|
+
# Catch anything else and shout about it
|
212
|
+
get '/*' do
|
213
|
+
puts "[Talkshow server warning] Unhandled route: '#{request.fullpath}'"
|
214
|
+
logger.error("WARNING: Unhandled route: '#{request.fullpath}'")
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'net/http'
|
3
|
+
require 'thread'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
require 'talkshow/server'
|
8
|
+
require 'thread'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
class Talkshow
|
13
|
+
class Talkshow::WebControl < Sinatra::Base
|
14
|
+
|
15
|
+
set :bind, '0.0.0.0'
|
16
|
+
configure do
|
17
|
+
set :port, ENV['WEB_CONTROLLER_PORT']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Thread safe
|
21
|
+
def self.port_requests(queue = nil)
|
22
|
+
if queue
|
23
|
+
@@port_requests = queue
|
24
|
+
end
|
25
|
+
@@port_requests
|
26
|
+
end
|
27
|
+
|
28
|
+
# Read-only, not thread safe
|
29
|
+
def self.processes(hash)
|
30
|
+
if hash
|
31
|
+
@@processes = hash
|
32
|
+
end
|
33
|
+
@@processes
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def logger
|
38
|
+
if !@logger
|
39
|
+
@logger = Logger.new('talkshow_webcontrol.log')
|
40
|
+
end
|
41
|
+
@logger
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/' do
|
45
|
+
|
46
|
+
process_table = "<table>"
|
47
|
+
@@processes.each do |port, status|
|
48
|
+
process_table += "<tr><td>#{port}</td><td>#{status}</td></tr>"
|
49
|
+
end
|
50
|
+
process_table += "</table>"
|
51
|
+
|
52
|
+
<<HERE
|
53
|
+
<html>
|
54
|
+
<style>
|
55
|
+
html { height: 100%;}
|
56
|
+
body {background: #CCC; font-family: Arial, Helvetica, sans-serif; padding: 20px;}
|
57
|
+
</style>
|
58
|
+
<body>
|
59
|
+
<div>
|
60
|
+
<h1>Talkshow web control</h1>
|
61
|
+
<h2>PID: #{$$}</h2>
|
62
|
+
<h2>Port: #{settings.port}</h2>
|
63
|
+
</div>
|
64
|
+
<div>
|
65
|
+
<h2>Active Processes</h2>
|
66
|
+
#{process_table}
|
67
|
+
</div>
|
68
|
+
</body>
|
69
|
+
</html>
|
70
|
+
HERE
|
71
|
+
end
|
72
|
+
|
73
|
+
get '/port/:port' do
|
74
|
+
port = params[:port].to_i
|
75
|
+
if port > 4000
|
76
|
+
Talkshow::WebControl.port_requests.push(port)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
get '/status' do
|
81
|
+
200
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: talkshow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.4.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joseph Haig
|
8
|
+
- David Buckhurst
|
9
|
+
- Jenna Brown
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2016-04-01 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: thin
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: json
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
description: Ruby to Javascript communications bridge
|
58
|
+
email: joe.haig@bbc.co.uk
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- lib/talkshow.rb
|
64
|
+
- lib/talkshow/daemon.rb
|
65
|
+
- lib/talkshow/javascript_error.rb
|
66
|
+
- lib/talkshow/queue.rb
|
67
|
+
- lib/talkshow/server.rb
|
68
|
+
- lib/talkshow/timeout.rb
|
69
|
+
- lib/talkshow/web_control.rb
|
70
|
+
homepage: https://github.com/fmtvp/talkshow
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.5.0
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Talkshow ruby gem
|
94
|
+
test_files: []
|