jscmd 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|