jscmd 0.0.2 → 0.1.0
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/History.txt +8 -0
- data/Manifest.txt +11 -3
- data/Rakefile +1 -1
- data/bin/jscmd +326 -21
- data/examples/get_title.pl +22 -0
- data/lib/jscmd/agent.js +41 -19
- data/lib/jscmd/asynchttpproxy.rb +40 -4
- data/lib/jscmd/inprocessbroker.rb +31 -0
- data/lib/jscmd/message.rb +42 -0
- data/lib/jscmd/pipebroker.rb +62 -0
- data/lib/jscmd/proxyserver.rb +224 -0
- data/lib/jscmd/shell.rb +201 -0
- data/lib/jscmd/stompproxy.rb +36 -0
- data/lib/jscmd/urlforwarder.rb +43 -0
- data/lib/jscmd/version.rb +2 -2
- data/lib/jscmd.rb +14 -1
- data/test/test_helper.rb +4 -2
- data/test/{test_jscmd.rb → test_proxyserver.rb} +40 -46
- data/test/test_shell.rb +56 -0
- metadata +34 -8
- data/lib/jscmd/jscommander.rb +0 -339
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= *SVN*
|
2
|
+
|
3
|
+
* Refactored everything
|
4
|
+
* Now acts as Stomp client
|
5
|
+
* Embedded Stomp Server
|
6
|
+
* Now works in JRuby (partially, using threads) and mswin32 (using stompserver)
|
7
|
+
* Added URLForwarder that adds a bookmarklet that sends the URL of the current page to another browser
|
8
|
+
|
1
9
|
= 0.0.2 2007-05-08
|
2
10
|
|
3
11
|
* Fixed potential file descriptor leak
|
data/Manifest.txt
CHANGED
@@ -6,11 +6,19 @@ README.txt
|
|
6
6
|
Rakefile
|
7
7
|
bin/jscmd
|
8
8
|
lib/jscmd.rb
|
9
|
-
lib/jscmd/version.rb
|
10
|
-
lib/jscmd/jscommander.rb
|
11
9
|
lib/jscmd/asynchttpproxy.rb
|
10
|
+
lib/jscmd/inprocessbroker.rb
|
11
|
+
lib/jscmd/message.rb
|
12
|
+
lib/jscmd/pipebroker.rb
|
13
|
+
lib/jscmd/proxyserver.rb
|
14
|
+
lib/jscmd/shell.rb
|
15
|
+
lib/jscmd/stompproxy.rb
|
16
|
+
lib/jscmd/urlforwarder.rb
|
17
|
+
lib/jscmd/version.rb
|
12
18
|
lib/jscmd/agent.js
|
13
19
|
scripts/txt2html
|
14
20
|
setup.rb
|
15
21
|
test/test_helper.rb
|
16
|
-
test/
|
22
|
+
test/test_proxyserver.rb
|
23
|
+
test/test_shell.rb
|
24
|
+
examples/get_title.pl
|
data/Rakefile
CHANGED
@@ -49,7 +49,7 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
49
49
|
|
50
50
|
# == Optional
|
51
51
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
52
|
-
|
52
|
+
p.extra_deps = [['stomp', '>= 1.0.5'], ['stompserver', '>= 0.9.7']] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
53
53
|
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
54
54
|
end
|
55
55
|
|
data/bin/jscmd
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'rubygems'
|
@@ -13,32 +13,337 @@ end
|
|
13
13
|
require 'jscmd'
|
14
14
|
require 'optparse'
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
module JSCommander
|
17
|
+
class CommandLine
|
18
|
+
OPTIONS = {
|
19
|
+
:port => 9000,
|
20
|
+
:bind => "0.0.0.0",
|
21
|
+
:uport => 9001,
|
22
|
+
:ubind => "localhost",
|
23
|
+
:sport => 61613,
|
24
|
+
:sbind => "localhost",
|
25
|
+
:sjournal => ".stompserver",
|
26
|
+
:connect_to => "stomp://localhost:61613/",
|
27
|
+
}
|
28
|
+
MANDATORY_OPTIONS = %w()
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
|
32
|
+
parser = OptionParser.new do |opts|
|
33
|
+
opts.banner = <<BANNER
|
23
34
|
JS Commander: Remote JavaScript Console
|
24
35
|
|
25
36
|
Usage: #{File.basename($0)} [options]
|
26
37
|
|
38
|
+
If no options were specified, jscmd starts up Shell and Proxy Server with internal broker.
|
27
39
|
Options are:
|
28
40
|
BANNER
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
opts.separator ""
|
42
|
+
opts.on("-c", "--console",
|
43
|
+
"Startup console") { |OPTIONS[:console]| }
|
44
|
+
opts.on("-x", "--proxy",
|
45
|
+
"Startup proxy server") { |OPTIONS[:proxy]| }
|
46
|
+
opts.on("-p", "--port=PORT", Integer,
|
47
|
+
"Bind proxy server on specified TCP port. (default: 9000)") { |OPTIONS[:port]| }
|
48
|
+
opts.on("-b", "--bind=ADDRESS", String,
|
49
|
+
"Bind proxy server on specified IP address. (default: 0.0.0.0)") { |OPTIONS[:bind]| }
|
50
|
+
opts.on("-u", "--url-forwarder",
|
51
|
+
"Startup URL forwarder") { |OPTIONS[:url_forwarder]| }
|
52
|
+
opts.on("--uport=PORT", Integer,
|
53
|
+
"Bind URL forwarder on specified TCP port. (default: 9001)") { |OPTIONS[:uport]| }
|
54
|
+
opts.on("--ubind=ADDRESS", String,
|
55
|
+
"Bind URL forwarder on specified IP address. (default: localhost)") { |OPTIONS[:ubind]| }
|
56
|
+
opts.on("-S", "--server",
|
57
|
+
"Startup embedded Stomp server") { |v| OPTIONS[:server] = v || true }
|
58
|
+
opts.on("--sport=PORT", Integer,
|
59
|
+
"Bind Stomp server on specified TCP port. (default: 61613)") { |OPTIONS[:sport]| }
|
60
|
+
opts.on("--sbind=ADDRESS", String,
|
61
|
+
"Bind Stomp server on specified IP address. (default: localhost)") { |OPTIONS[:sbind]| }
|
62
|
+
opts.on("--sjournal=DIR", String,
|
63
|
+
"Specify journal directory of Stomp server. (default: ./.stompserver)") { |OPTIONS[:sjournal]| }
|
64
|
+
opts.on("-C", "--client", String,
|
65
|
+
"Acts as Stomp client.") {|OPTIONS[:client]|}
|
66
|
+
opts.on("--connect-to=URL", String,
|
67
|
+
"Specify URL of Stomp server in stomp://[user:pass@]host:port/ format.",
|
68
|
+
"(default: stomp://localhost:61613/)") { |OPTIONS[:connect_to]| }
|
69
|
+
opts.on("--debug-proxy",
|
70
|
+
"Enable debugging in proxy server.") { |OPTIONS[:debug_proxy]| }
|
71
|
+
opts.on("--debug-msg=[LOGFILE]",
|
72
|
+
"Print messages that are being sent.") { |OPTIONS[:debug_msg]| }
|
73
|
+
opts.on("--never-fork",
|
74
|
+
"Never fork - use threads only (experimental).") { |OPTIONS[:never_fork]| }
|
75
|
+
opts.on("-h", "--help",
|
76
|
+
"Show this help message.") { puts opts; exit }
|
77
|
+
opts.parse!(ARGV)
|
78
|
+
|
79
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
80
|
+
puts opts; exit
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
unless [:console, :proxy, :server, :url_forwarder].any?{|o| OPTIONS[o]}
|
85
|
+
# default is -xc if no processes were specified
|
86
|
+
OPTIONS[:console] = true
|
87
|
+
OPTIONS[:proxy] = true
|
88
|
+
if windows?
|
89
|
+
OPTIONS[:server] = true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
# puts "options: #{encode_options.join(' ')}"
|
93
|
+
|
94
|
+
@child_pid = nil
|
95
|
+
@threads = []
|
96
|
+
|
97
|
+
use_stomp = OPTIONS[:server] || OPTIONS[:client]
|
98
|
+
unless never_fork?
|
99
|
+
use_stomp ||= !OPTIONS[:console] || !OPTIONS[:proxy] || OPTIONS[:url_forwarder]
|
100
|
+
end
|
101
|
+
|
102
|
+
if use_stomp
|
103
|
+
# use stomp broker
|
104
|
+
startup_processes { StompProxy.new(OPTIONS[:connect_to]) }
|
105
|
+
else
|
106
|
+
# use internal broker
|
107
|
+
if !never_fork?
|
108
|
+
r1, w1 = IO.pipe
|
109
|
+
r2, w2 = IO.pipe
|
110
|
+
pid = $$
|
111
|
+
startup_processes do
|
112
|
+
if pid == $$
|
113
|
+
PipeBroker.new(r2, w1)
|
114
|
+
else
|
115
|
+
PipeBroker.new(r1, w2)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
else
|
119
|
+
$stderr.puts "Starting up in multi-threaded mode"
|
120
|
+
# JRuby
|
121
|
+
broker = InProcessBroker.new
|
122
|
+
startup_processes { broker }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def startup_non_console_processes(&broker_factory)
|
128
|
+
if OPTIONS[:server]
|
129
|
+
t = StompServerLauncher.new
|
130
|
+
t.start
|
131
|
+
@threads << t
|
132
|
+
end
|
133
|
+
if OPTIONS[:proxy]
|
134
|
+
t = ProxyLauncher.new(broker_factory.call)
|
135
|
+
t.start
|
136
|
+
@threads << t
|
137
|
+
end
|
138
|
+
if OPTIONS[:url_forwarder]
|
139
|
+
t = URLForwarderLauncher.new(broker_factory.call)
|
140
|
+
t.start
|
141
|
+
@threads << t
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def stop_threads
|
146
|
+
@threads.reverse.each do |t|
|
147
|
+
t.shutdown
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def join_threads
|
152
|
+
@threads.reverse.each do |t|
|
153
|
+
t.thread.join
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def encode_options
|
158
|
+
options = []
|
159
|
+
OPTIONS.each do |key, value|
|
160
|
+
option = "--#{key.to_s.gsub(/_/, "-")}"
|
161
|
+
case value
|
162
|
+
when true
|
163
|
+
options << option
|
164
|
+
when String, Integer
|
165
|
+
options << "#{option}=#{value}"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
options
|
169
|
+
end
|
170
|
+
|
171
|
+
def startup_processes(&broker_factory)
|
172
|
+
if OPTIONS[:debug_msg]
|
173
|
+
case OPTIONS[:debug_msg]
|
174
|
+
when String
|
175
|
+
$debug_msg_file = open(OPTIONS[:debug_msg], "a")
|
176
|
+
else
|
177
|
+
$debug_msg_file = $stderr
|
178
|
+
end
|
179
|
+
original_factory = broker_factory
|
180
|
+
broker_factory = proc do
|
181
|
+
broker = original_factory.call
|
182
|
+
def broker.send(name, msg)
|
183
|
+
$debug_msg_file.puts "send #{name}, #{msg.inspect}"
|
184
|
+
$debug_msg_file.flush
|
185
|
+
super
|
186
|
+
end
|
187
|
+
broker
|
188
|
+
end
|
189
|
+
end
|
190
|
+
if never_fork?
|
191
|
+
startup_non_console_processes(&broker_factory)
|
192
|
+
run_shell(broker_factory.call)
|
193
|
+
stop_threads
|
194
|
+
join_threads
|
195
|
+
else
|
196
|
+
if OPTIONS[:console]
|
197
|
+
if OPTIONS[:proxy] || OPTIONS[:server] || OPTIONS[:url_forwarder]
|
198
|
+
if windows?
|
199
|
+
require "win32/process"
|
200
|
+
options = encode_options.delete_if{|o|o=="--console"}.map{|o|%("#{o}")}.join(" ")
|
201
|
+
$stderr.puts "Lauching another process with options: #{options}"
|
202
|
+
@win32_process = Process.create(:app_name => "ruby \"#{$0}\" #{options}")
|
203
|
+
else
|
204
|
+
@child_pid = fork do
|
205
|
+
trap("TERM"){stop_threads}
|
206
|
+
trap("INT"){}
|
207
|
+
startup_non_console_processes(&broker_factory)
|
208
|
+
join_threads
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
wait_for_port(OPTIONS[:port]) if OPTIONS[:proxy]
|
213
|
+
wait_for_port(OPTIONS[:sport]) if OPTIONS[:server]
|
214
|
+
run_shell(broker_factory.call)
|
215
|
+
else
|
216
|
+
trap("TERM"){stop_threads}
|
217
|
+
trap("INT"){stop_threads}
|
218
|
+
startup_non_console_processes(&broker_factory)
|
219
|
+
join_threads
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def wait_for_port(port)
|
225
|
+
30.times do
|
226
|
+
begin
|
227
|
+
s = TCPSocket.open("localhost", port)
|
228
|
+
s.close
|
229
|
+
break
|
230
|
+
rescue
|
231
|
+
end
|
232
|
+
sleep 0.3
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def windows?
|
237
|
+
RUBY_PLATFORM =~ /-mswin32$/
|
238
|
+
end
|
239
|
+
|
240
|
+
def never_fork?
|
241
|
+
"java" == RUBY_PLATFORM || OPTIONS[:never_fork]
|
242
|
+
end
|
243
|
+
|
244
|
+
def run_shell(broker)
|
245
|
+
shell = Shell.new(broker)
|
246
|
+
shell.run
|
247
|
+
if @child_pid
|
248
|
+
Process.kill "TERM", @child_pid
|
249
|
+
Process.waitall
|
250
|
+
elsif @win32_process
|
251
|
+
Process.kill 4, @win32_process.process_id
|
252
|
+
Process.waitpid(@win32_process.process_id)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class ThreadLauncher
|
257
|
+
attr_reader :thread
|
258
|
+
|
259
|
+
def initialize(broker = nil)
|
260
|
+
@broker = broker
|
261
|
+
end
|
262
|
+
|
263
|
+
def start
|
264
|
+
@thread = Thread.start do
|
265
|
+
begin
|
266
|
+
run
|
267
|
+
rescue Exception
|
268
|
+
$stderr.puts $!
|
269
|
+
$stderr.puts $!.backtrace
|
270
|
+
end
|
271
|
+
end
|
272
|
+
wait if respond_to?(:wait)
|
273
|
+
@thread
|
274
|
+
end
|
275
|
+
|
276
|
+
def wait_for_port(port)
|
277
|
+
30.times do
|
278
|
+
break unless @thread.alive?
|
279
|
+
begin
|
280
|
+
s = TCPSocket.open("localhost", port)
|
281
|
+
s.close
|
282
|
+
break
|
283
|
+
rescue
|
284
|
+
end
|
285
|
+
sleep 0.3
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
class ProxyLauncher < ThreadLauncher
|
291
|
+
def run
|
292
|
+
proxy_options = {:Port => OPTIONS[:port], :BindAddress => OPTIONS[:bind], :AccessLog => [], :ProxyVia => nil}
|
293
|
+
if OPTIONS[:debug_proxy]
|
294
|
+
proxy_options[:Logger] = WEBrick::Log.new(nil, WEBrick::BasicLog::DEBUG)
|
295
|
+
end
|
296
|
+
proxy = ProxyServer.new(@broker, proxy_options)
|
297
|
+
@proxy = proxy
|
298
|
+
proxy.start
|
299
|
+
end
|
300
|
+
|
301
|
+
def wait
|
302
|
+
wait_for_port(OPTIONS[:port])
|
303
|
+
end
|
304
|
+
|
305
|
+
def shutdown
|
306
|
+
@proxy.shutdown
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class StompServerLauncher < ThreadLauncher
|
311
|
+
def start
|
312
|
+
require "stomp_server"
|
313
|
+
require "frame_journal"
|
314
|
+
super
|
315
|
+
end
|
316
|
+
|
317
|
+
def run
|
318
|
+
StompServer.setup(FrameJournal.new(OPTIONS[:sjournal]))
|
319
|
+
EventMachine.run do
|
320
|
+
puts "Stomp Server starting on port #{OPTIONS[:sport]}"
|
321
|
+
EventMachine.start_server OPTIONS[:sbind], OPTIONS[:sport], StompServer
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def wait
|
326
|
+
wait_for_port(OPTIONS[:sport])
|
327
|
+
end
|
328
|
+
|
329
|
+
def shutdown
|
330
|
+
EventMachine.stop
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
class URLForwarderLauncher < ThreadLauncher
|
335
|
+
def run
|
336
|
+
@server = URLForwarder.new(@broker,
|
337
|
+
:Port => OPTIONS[:uport],
|
338
|
+
:BindAddress => OPTIONS[:ubind])
|
339
|
+
@server.start
|
340
|
+
end
|
341
|
+
|
342
|
+
def shutdown
|
343
|
+
@server.shutdown
|
344
|
+
end
|
345
|
+
end
|
39
346
|
end
|
40
347
|
end
|
41
348
|
|
42
|
-
|
43
|
-
server = JSCommander::Broker.new(:Port => OPTIONS[:port], :AccessLog => [], :ProxyVia => nil)
|
44
|
-
server.start
|
349
|
+
JSCommander::CommandLine.new
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env perl
|
2
|
+
# An example script that prints title of the current page
|
3
|
+
use strict;
|
4
|
+
use warnings;
|
5
|
+
use Net::Stomp;
|
6
|
+
|
7
|
+
my $stomp = Net::Stomp->new({hostname => 'localhost', port => '61613'});
|
8
|
+
$stomp->connect({login => '', passcode => ''});
|
9
|
+
$stomp->subscribe({destination => "/topic/events"});
|
10
|
+
my $id = rand();
|
11
|
+
$stomp->send({destination => "/topic/commands",
|
12
|
+
"jscmd.type" => "eval",
|
13
|
+
"jscmd.id" => $id,
|
14
|
+
body => "document.title"});
|
15
|
+
for (;;) {
|
16
|
+
my $frame = $stomp->receive_frame;
|
17
|
+
if ($id eq $frame->headers->{"jscmd.in-reply-to"}) {
|
18
|
+
print $frame->body . "\n";
|
19
|
+
last;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
$stomp->disconnect;
|
data/lib/jscmd/agent.js
CHANGED
@@ -54,36 +54,45 @@
|
|
54
54
|
},
|
55
55
|
|
56
56
|
poll: function() {
|
57
|
-
var content = this.queue.length > 0 ? this.queue.shift() : "";
|
57
|
+
var content = this.queue.length > 0 ? this.queue.shift() : {type: "connect", body: ""};
|
58
58
|
var xhr = this.createXHR();
|
59
59
|
xhr.open("POST", "/_remote_js_proxy/poll", true);
|
60
60
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
61
|
+
xhr.setRequestHeader("X-JSCmd-Agent-Id", this.agentId);
|
62
|
+
if (content.inReplyTo) {
|
63
|
+
xhr.setRequestHeader("X-JSCmd-In-Reply-To", content.inReplyTo);
|
64
|
+
}
|
61
65
|
var self = this;
|
62
66
|
xhr.onreadystatechange = function() {
|
63
67
|
if (xhr.readyState == 4) {
|
64
68
|
delete self.currentXHR;
|
65
69
|
try {
|
66
70
|
if (xhr.status == 200) {
|
67
|
-
var
|
68
|
-
|
71
|
+
var commandId = xhr.getResponseHeader("X-JSCmd-Command-Id");
|
72
|
+
var requestType = xhr.getResponseHeader("X-JSCmd-Type");
|
73
|
+
var script = xhr.responseText;
|
74
|
+
if (requestType) {
|
69
75
|
var value = null;
|
76
|
+
var type = null;
|
70
77
|
try {
|
71
|
-
|
72
|
-
var script = text.substring(1, text.length);
|
73
|
-
if (option == 'P') {
|
78
|
+
if (requestType == 'properties') {
|
74
79
|
// get properties for code completion
|
75
80
|
var obj = self.evaluate.apply(window, [script, false]);
|
76
|
-
value =
|
77
|
-
|
81
|
+
value = self.getAllProperties(obj).join(",");
|
82
|
+
type = "value";
|
83
|
+
} else if (requestType == 'eval') {
|
78
84
|
// just evaluate
|
79
|
-
value =
|
85
|
+
value = self.evaluate.apply(window, [script, true]);
|
86
|
+
type = "value";
|
80
87
|
} else {
|
81
|
-
value = "
|
88
|
+
value = "bad request: " + script;
|
89
|
+
type = "error";
|
82
90
|
}
|
83
91
|
} catch (e) {
|
84
|
-
value =
|
92
|
+
value = e.toString();
|
93
|
+
type = "error";
|
85
94
|
}
|
86
|
-
self.queue.push(value);
|
95
|
+
self.queue.push({inReplyTo: commandId, type: type, body: value});
|
87
96
|
}
|
88
97
|
}
|
89
98
|
} catch (e) {
|
@@ -102,14 +111,25 @@
|
|
102
111
|
this.currentXHR = xhr;
|
103
112
|
if (this.queue.length > 0) {
|
104
113
|
// mark that there are more messages pending
|
105
|
-
|
114
|
+
xhr.setRequestHeader("X-JSCmd-More", "true");
|
115
|
+
}
|
116
|
+
xhr.setRequestHeader("X-JSCmd-Type", content.type);
|
117
|
+
xhr.send(encodeURI(content.body));
|
118
|
+
},
|
119
|
+
|
120
|
+
generateAgentId: function() {
|
121
|
+
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
122
|
+
var str = "";
|
123
|
+
for (var i = 0; i < 16; i++) {
|
124
|
+
str += chars.charAt(Math.floor(Math.random() * chars.length));
|
106
125
|
}
|
107
|
-
|
126
|
+
this.agentId = str;
|
108
127
|
},
|
109
128
|
|
110
129
|
startPoll: function() {
|
111
130
|
this.startPoll = function() {}
|
112
131
|
if (window.top.parent != window) return;
|
132
|
+
this.generateAgentId();
|
113
133
|
this.poll();
|
114
134
|
},
|
115
135
|
|
@@ -225,10 +245,11 @@
|
|
225
245
|
this.attachEvent("pageshow", function() { self.startPoll() });
|
226
246
|
|
227
247
|
window.onerror = function(message, file, line) {
|
228
|
-
self.pushValue("
|
229
|
-
|
230
|
-
|
231
|
-
|
248
|
+
self.pushValue({type: "error",
|
249
|
+
body:
|
250
|
+
"Message: " + message + "\n" +
|
251
|
+
"File: " + file + "\n" +
|
252
|
+
"Line: " + line + "\n"});
|
232
253
|
}
|
233
254
|
setTimeout(function() { self.startPoll() }, 3000);
|
234
255
|
|
@@ -243,7 +264,8 @@
|
|
243
264
|
for (var i = 0; i < arguments.length; i++) {
|
244
265
|
message.push(arguments[i]);
|
245
266
|
}
|
246
|
-
self.pushValue(
|
267
|
+
self.pushValue({type: "value",
|
268
|
+
body:"[" + level + "] " + message.join(", ")});
|
247
269
|
}
|
248
270
|
})();
|
249
271
|
}
|
data/lib/jscmd/asynchttpproxy.rb
CHANGED
@@ -51,7 +51,8 @@ module WEBrick
|
|
51
51
|
|
52
52
|
response = nil
|
53
53
|
q = Queue.new
|
54
|
-
|
54
|
+
data_queue = Queue.new
|
55
|
+
# p_reader, p_writer = IO.pipe
|
55
56
|
thread = Thread.start do
|
56
57
|
begin
|
57
58
|
@logger.debug "downloading #{uri}"
|
@@ -87,10 +88,12 @@ module WEBrick
|
|
87
88
|
if last_size / 500000 != size / 500000
|
88
89
|
@logger.debug "downloading #{uri}: size=#{size}"
|
89
90
|
end
|
90
|
-
p_writer.write str
|
91
|
+
# p_writer.write str
|
92
|
+
data_queue.push str
|
91
93
|
end
|
92
94
|
@logger.debug "finished downloading #{uri}: size=#{size}"
|
93
|
-
p_writer.close
|
95
|
+
# p_writer.close
|
96
|
+
data_queue.push nil
|
94
97
|
end
|
95
98
|
}
|
96
99
|
rescue Exception => err
|
@@ -115,7 +118,7 @@ module WEBrick
|
|
115
118
|
choose_header(response, res)
|
116
119
|
set_cookie(response, res)
|
117
120
|
set_via(res)
|
118
|
-
res.body = p_reader
|
121
|
+
res.body = QueueInput.new(data_queue) # p_reader
|
119
122
|
def res.flush_body
|
120
123
|
if @body.is_a?(IO)
|
121
124
|
begin
|
@@ -135,6 +138,39 @@ module WEBrick
|
|
135
138
|
handler.call(req, res)
|
136
139
|
end
|
137
140
|
end
|
141
|
+
|
142
|
+
class QueueInput < IO
|
143
|
+
def initialize(queue)
|
144
|
+
@queue = queue
|
145
|
+
@eof = false
|
146
|
+
end
|
147
|
+
|
148
|
+
def close; end
|
149
|
+
|
150
|
+
def eof?
|
151
|
+
@eof
|
152
|
+
end
|
153
|
+
|
154
|
+
def pop
|
155
|
+
buf = @queue.pop
|
156
|
+
@eof = true if buf.nil?
|
157
|
+
buf
|
158
|
+
end
|
159
|
+
|
160
|
+
def read(len = nil)
|
161
|
+
return nil if eof?
|
162
|
+
if len
|
163
|
+
pop
|
164
|
+
else
|
165
|
+
data = ""
|
166
|
+
while buf = pop
|
167
|
+
data << buf
|
168
|
+
end
|
169
|
+
data
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
138
173
|
end
|
174
|
+
|
139
175
|
end
|
140
176
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module JSCommander
|
2
|
+
class InProcessBroker
|
3
|
+
def initialize
|
4
|
+
@subscribers = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def subscribe(name, &block)
|
8
|
+
@subscribers[name] ||= []
|
9
|
+
@subscribers[name] << block
|
10
|
+
block
|
11
|
+
end
|
12
|
+
|
13
|
+
def unsubscribe(name, subscriber)
|
14
|
+
@subscribers[name].delete(subscriber)
|
15
|
+
end
|
16
|
+
|
17
|
+
def send(name, msg)
|
18
|
+
if @subscribers[name]
|
19
|
+
@subscribers[name].each do |sub|
|
20
|
+
Thread.start do
|
21
|
+
begin
|
22
|
+
sub.call(msg)
|
23
|
+
rescue Exception
|
24
|
+
$stderr.puts $!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
=begin
|
2
|
+
Messages sent between clients and broker
|
3
|
+
|
4
|
+
* Topic "events": proxy -> broker -> shell
|
5
|
+
|
6
|
+
Common attributes:
|
7
|
+
type - Type of the message. (e.g. "connect")
|
8
|
+
clients - List of clients that are connected. (e.g. "[www.google.com],[www.yahoo.com]")
|
9
|
+
in-reply-to - id of the command that caused this message (optional; only used with "value", "error" and "pong" types)
|
10
|
+
|
11
|
+
Types:
|
12
|
+
connect - Notify that the new client has connected. The body must be empty.
|
13
|
+
abort - Notify that the client aborted. The body must be empty.
|
14
|
+
pong - Reply to "ping" command. body must be empty.
|
15
|
+
value - Send value with body.
|
16
|
+
error - Send error message with body.
|
17
|
+
|
18
|
+
* Topic "commands": shell -> broker -> proxy
|
19
|
+
|
20
|
+
Common attributes:
|
21
|
+
type - Type of the message. (e.g. "eval")
|
22
|
+
id - unique value that identifies this command. (optional; only used with "eval" and "properties" types)
|
23
|
+
|
24
|
+
Types:
|
25
|
+
ping - Request "update_clients". The body must be empty.
|
26
|
+
eval - Evaluate body.
|
27
|
+
properties - Evaluate body and get list of properties.
|
28
|
+
|
29
|
+
=end
|
30
|
+
module JSCommander
|
31
|
+
class Message
|
32
|
+
attr_accessor :body, :attributes
|
33
|
+
|
34
|
+
def initialize(body = nil, attributes = {})
|
35
|
+
@body = body
|
36
|
+
@attributes = {}
|
37
|
+
attributes.each do |key, value|
|
38
|
+
@attributes[key.to_s] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|