talkshow 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|