jscmd 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/bin/jscmd +19 -1
- data/examples/get_title.pl +7 -7
- data/lib/jscmd.rb +3 -0
- data/lib/jscmd/agent.js +52 -25
- data/lib/jscmd/message.rb +2 -1
- data/lib/jscmd/proxyserver.rb +58 -7
- data/lib/jscmd/shell.rb +12 -1
- data/lib/jscmd/version.rb +1 -1
- metadata +9 -6
data/History.txt
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
= *SVN*
|
2
2
|
|
3
|
+
= 0.2.0 2007-10-08
|
4
|
+
|
5
|
+
* Console API now works in Safari.
|
6
|
+
* Let polling time out after 15 seconds. (Can be configured with --reconnect-interval option)
|
7
|
+
* Fixed crashes when there are many commands sent from browser at once.
|
8
|
+
* Added --disable-proxy-injection option to turn off the debugging ability of proxy server.
|
9
|
+
* Added --trace-proxy-request option to trace the request and response sent to/from the proxy server.
|
10
|
+
|
11
|
+
= 0.1.0 2007-05-13
|
12
|
+
|
3
13
|
* Refactored everything
|
4
14
|
* Now acts as Stomp client
|
5
15
|
* Embedded Stomp Server
|
data/bin/jscmd
CHANGED
@@ -24,6 +24,7 @@ module JSCommander
|
|
24
24
|
:sbind => "localhost",
|
25
25
|
:sjournal => ".stompserver",
|
26
26
|
:connect_to => "stomp://localhost:61613/",
|
27
|
+
:reconnect_interval => 15
|
27
28
|
}
|
28
29
|
MANDATORY_OPTIONS = %w()
|
29
30
|
|
@@ -66,14 +67,21 @@ BANNER
|
|
66
67
|
opts.on("--connect-to=URL", String,
|
67
68
|
"Specify URL of Stomp server in stomp://[user:pass@]host:port/ format.",
|
68
69
|
"(default: stomp://localhost:61613/)") { |OPTIONS[:connect_to]| }
|
70
|
+
opts.on("--reconnect-interval=SECONDS", Integer,
|
71
|
+
"Configure polling timeout of the proxy server. (default: 15)") { |OPTIONS[:reconnect_interval]| }
|
72
|
+
opts.on("--disable-proxy-injection",
|
73
|
+
"Disable proxy injection and act as a plain proxy server.") { |OPTIONS[:disable_proxy_injection]| }
|
69
74
|
opts.on("--debug-proxy",
|
70
75
|
"Enable debugging in proxy server.") { |OPTIONS[:debug_proxy]| }
|
76
|
+
opts.on("--trace-proxy-request",
|
77
|
+
"Trace HTTP requests sent to proxy server.") { |OPTIONS[:trace_proxy_request]| }
|
71
78
|
opts.on("--debug-msg=[LOGFILE]",
|
72
79
|
"Print messages that are being sent.") { |OPTIONS[:debug_msg]| }
|
73
80
|
opts.on("--never-fork",
|
74
81
|
"Never fork - use threads only (experimental).") { |OPTIONS[:never_fork]| }
|
75
82
|
opts.on("-h", "--help",
|
76
83
|
"Show this help message.") { puts opts; exit }
|
84
|
+
opts.version = Jscmd::VERSION::STRING
|
77
85
|
opts.parse!(ARGV)
|
78
86
|
|
79
87
|
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
@@ -289,10 +297,20 @@ BANNER
|
|
289
297
|
|
290
298
|
class ProxyLauncher < ThreadLauncher
|
291
299
|
def run
|
292
|
-
proxy_options = {
|
300
|
+
proxy_options = {
|
301
|
+
:Port => OPTIONS[:port],
|
302
|
+
:BindAddress => OPTIONS[:bind],
|
303
|
+
:AccessLog => [],
|
304
|
+
:ProxyVia => nil,
|
305
|
+
:disable_proxy_injection => OPTIONS[:disable_proxy_injection],
|
306
|
+
:reconnect_interval => OPTIONS[:reconnect_interval]
|
307
|
+
}
|
293
308
|
if OPTIONS[:debug_proxy]
|
294
309
|
proxy_options[:Logger] = WEBrick::Log.new(nil, WEBrick::BasicLog::DEBUG)
|
295
310
|
end
|
311
|
+
if OPTIONS[:trace_proxy_request]
|
312
|
+
proxy_options[:trace_request] = true
|
313
|
+
end
|
296
314
|
proxy = ProxyServer.new(@broker, proxy_options)
|
297
315
|
@proxy = proxy
|
298
316
|
proxy.start
|
data/examples/get_title.pl
CHANGED
@@ -8,15 +8,15 @@ my $stomp = Net::Stomp->new({hostname => 'localhost', port => '61613'});
|
|
8
8
|
$stomp->connect({login => '', passcode => ''});
|
9
9
|
$stomp->subscribe({destination => "/topic/events"});
|
10
10
|
my $id = rand();
|
11
|
-
$stomp->send({destination => "/topic/commands",
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
$stomp->send({destination => "/topic/commands",
|
12
|
+
"jscmd.type" => "eval",
|
13
|
+
"jscmd.id" => $id,
|
14
|
+
body => "document.title"});
|
15
15
|
for (;;) {
|
16
16
|
my $frame = $stomp->receive_frame;
|
17
17
|
if ($id eq $frame->headers->{"jscmd.in-reply-to"}) {
|
18
|
-
|
19
|
-
|
18
|
+
print $frame->body . "\n";
|
19
|
+
last;
|
20
20
|
}
|
21
|
-
}
|
21
|
+
}
|
22
22
|
$stomp->disconnect;
|
data/lib/jscmd.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Dir[File.join(File.dirname(__FILE__), 'jscmd/**/*.rb')].sort.each { |lib| require lib }
|
2
2
|
|
3
|
+
require "jscmd/version"
|
4
|
+
|
3
5
|
# message
|
4
6
|
require "jscmd/message"
|
5
7
|
|
@@ -12,3 +14,4 @@ require "jscmd/stompproxy"
|
|
12
14
|
require "jscmd/proxyserver"
|
13
15
|
require "jscmd/shell"
|
14
16
|
require "jscmd/urlforwarder"
|
17
|
+
|
data/lib/jscmd/agent.js
CHANGED
@@ -53,9 +53,32 @@
|
|
53
53
|
return value;
|
54
54
|
},
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
send: function() {
|
57
|
+
if (this.queue.length == 0) return;
|
58
|
+
var self = this;
|
59
|
+
this.request("send", {
|
60
|
+
content: function() { return self.queue.shift() },
|
61
|
+
setup: function(xhr) { xhr.setRequestHeader("X-JSCmd-More", "true"); },
|
62
|
+
nextRequest: function() { self.send() }
|
63
|
+
});
|
64
|
+
},
|
65
|
+
|
66
|
+
receive: function() {
|
67
|
+
var self = this;
|
68
|
+
this.request("receive", {
|
69
|
+
content: function() { return self.queue.length > 0 ? self.queue.shift() : {type: "connect", body: ""}},
|
70
|
+
setup: function(xhr) {},
|
71
|
+
nextRequest: function() { self.receive() }
|
72
|
+
});
|
73
|
+
},
|
74
|
+
|
75
|
+
request: function(name, handler) {
|
76
|
+
if (this.currentXHR[name]) return;
|
77
|
+
if (this.aborted) return;
|
78
|
+
var content = handler.content();
|
79
|
+
if (!content) return;
|
58
80
|
var xhr = this.createXHR();
|
81
|
+
this.currentXHR[name] = xhr;
|
59
82
|
xhr.open("POST", "/_remote_js_proxy/poll", true);
|
60
83
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
61
84
|
xhr.setRequestHeader("X-JSCmd-Agent-Id", this.agentId);
|
@@ -65,7 +88,7 @@
|
|
65
88
|
var self = this;
|
66
89
|
xhr.onreadystatechange = function() {
|
67
90
|
if (xhr.readyState == 4) {
|
68
|
-
|
91
|
+
self.currentXHR[name] = null;
|
69
92
|
try {
|
70
93
|
if (xhr.status == 200) {
|
71
94
|
var commandId = xhr.getResponseHeader("X-JSCmd-Command-Id");
|
@@ -84,6 +107,7 @@
|
|
84
107
|
// just evaluate
|
85
108
|
value = self.evaluate.apply(window, [script, true]);
|
86
109
|
type = "value";
|
110
|
+
} else if (requestType == 'nop') {
|
87
111
|
} else {
|
88
112
|
value = "bad request: " + script;
|
89
113
|
type = "error";
|
@@ -92,27 +116,30 @@
|
|
92
116
|
value = e.toString();
|
93
117
|
type = "error";
|
94
118
|
}
|
95
|
-
|
119
|
+
if (type) {
|
120
|
+
self.queue.push({inReplyTo: commandId, type: type, body: value});
|
121
|
+
}
|
96
122
|
}
|
123
|
+
} else {
|
124
|
+
setTimeout(handler.nextRequest, 5000);
|
125
|
+
return;
|
97
126
|
}
|
98
127
|
} catch (e) {
|
99
|
-
|
128
|
+
setTimeout(handler.nextRequest, 5000);
|
129
|
+
return;
|
100
130
|
}
|
101
131
|
if (!self.aborted) {
|
102
|
-
|
103
|
-
self.poll();
|
104
|
-
} else {
|
105
|
-
// if polling failed, try again later
|
106
|
-
setTimeout(function() { self.poll() }, 5000);
|
107
|
-
}
|
132
|
+
setTimeout(handler.nextRequest, 0);
|
108
133
|
}
|
109
134
|
}
|
110
135
|
}
|
111
|
-
|
136
|
+
handler.setup(xhr);
|
137
|
+
/*
|
112
138
|
if (this.queue.length > 0) {
|
113
139
|
// mark that there are more messages pending
|
114
140
|
xhr.setRequestHeader("X-JSCmd-More", "true");
|
115
141
|
}
|
142
|
+
*/
|
116
143
|
xhr.setRequestHeader("X-JSCmd-Type", content.type);
|
117
144
|
xhr.send(encodeURI(content.body));
|
118
145
|
},
|
@@ -130,23 +157,21 @@
|
|
130
157
|
this.startPoll = function() {}
|
131
158
|
if (window.top.parent != window) return;
|
132
159
|
this.generateAgentId();
|
133
|
-
this.
|
160
|
+
this.receive();
|
161
|
+
var self = this;
|
162
|
+
setInterval(function() { self.receive() }, 5000);
|
134
163
|
},
|
135
164
|
|
136
|
-
abortPoll: function(
|
137
|
-
|
138
|
-
|
139
|
-
this.
|
140
|
-
}
|
141
|
-
if (this.currentXHR) {
|
142
|
-
this.currentXHR.abort();
|
165
|
+
abortPoll: function() {
|
166
|
+
this.aborted = true;
|
167
|
+
if (this.currentXHR["receive"]) {
|
168
|
+
this.currentXHR["receive"].abort();
|
143
169
|
}
|
144
170
|
},
|
145
171
|
|
146
172
|
pushValue: function(value) {
|
147
173
|
this.queue.push(value);
|
148
|
-
this.
|
149
|
-
this.startPoll(); // in case startPoll has not been called yet
|
174
|
+
this.send();
|
150
175
|
},
|
151
176
|
|
152
177
|
attachEvent: function(event, handler) {
|
@@ -253,7 +278,7 @@
|
|
253
278
|
}
|
254
279
|
setTimeout(function() { self.startPoll() }, 3000);
|
255
280
|
|
256
|
-
if (!window.console) {
|
281
|
+
//if (!window.console) {
|
257
282
|
window.console = {};
|
258
283
|
var levels = ["debug", "info", "warn", "error"];
|
259
284
|
for (var i = 0; i < levels.length; i++) {
|
@@ -270,6 +295,8 @@
|
|
270
295
|
})();
|
271
296
|
}
|
272
297
|
window.console.log = window.console.info;
|
273
|
-
}
|
274
|
-
}
|
298
|
+
//}
|
299
|
+
},
|
300
|
+
|
301
|
+
currentXHR: {}
|
275
302
|
}).init();
|
data/lib/jscmd/message.rb
CHANGED
@@ -22,7 +22,8 @@ type - Type of the message. (e.g. "eval")
|
|
22
22
|
id - unique value that identifies this command. (optional; only used with "eval" and "properties" types)
|
23
23
|
|
24
24
|
Types:
|
25
|
-
ping - Request
|
25
|
+
ping - Request the status of clients from shell to proxy server. It will not be routed to web browser. The body must be empty.
|
26
|
+
nop - Empty message sent from proxy server to web browser when there were no commands within reconnect_interval seconds.
|
26
27
|
eval - Evaluate body.
|
27
28
|
properties - Evaluate body and get list of properties.
|
28
29
|
|
data/lib/jscmd/proxyserver.rb
CHANGED
@@ -27,15 +27,18 @@ end
|
|
27
27
|
module JSCommander
|
28
28
|
class ProxyServer < WEBrick::AsyncHTTPProxyServer
|
29
29
|
SCRIPT_DIR = "/_remote_js_proxy/"
|
30
|
+
attr_reader :reconnect_interval
|
30
31
|
|
31
32
|
class CommandDispatcher
|
32
33
|
class CLIENT_ABORTED < StandardError; end
|
33
34
|
class SHUTDOWN < StandardError; end
|
34
35
|
|
35
36
|
def initialize(server, command_queue)
|
37
|
+
@server = server
|
36
38
|
@client_mutex = Mutex.new
|
37
39
|
@client_ready = ConditionVariable.new
|
38
40
|
@clients = []
|
41
|
+
@last_clients = nil
|
39
42
|
@thread = Thread.start do
|
40
43
|
while server.status != :Shutdown
|
41
44
|
command = command_queue.pop
|
@@ -55,12 +58,16 @@ module JSCommander
|
|
55
58
|
end
|
56
59
|
end
|
57
60
|
|
61
|
+
def last_clients
|
62
|
+
@last_clients
|
63
|
+
end
|
64
|
+
|
58
65
|
def format_clients
|
59
|
-
@clients.map{|c|"[#{c.hostname}]"}.join(",")
|
66
|
+
@last_clients = @clients.map{|c|"[#{c.hostname}]"}.join(",")
|
60
67
|
end
|
61
68
|
|
62
69
|
def new_client(req, &block)
|
63
|
-
client = Client.new(req)
|
70
|
+
client = Client.new(req, @server.reconnect_interval)
|
64
71
|
@client_mutex.synchronize do
|
65
72
|
@clients << client
|
66
73
|
@client_ready.signal
|
@@ -75,14 +82,25 @@ module JSCommander
|
|
75
82
|
end
|
76
83
|
|
77
84
|
class Client
|
78
|
-
def initialize(req)
|
85
|
+
def initialize(req, reconnect_interval)
|
79
86
|
@request = req
|
80
87
|
@queue = Queue.new
|
88
|
+
@pop_time = nil
|
81
89
|
req_socket = req.instance_eval{@socket}
|
82
90
|
@thread = Thread.start do
|
83
91
|
sleep 1 until req_socket.eof?
|
84
92
|
@queue.push CLIENT_ABORTED
|
85
93
|
end
|
94
|
+
@reconnect_thread = Thread.start do
|
95
|
+
loop do
|
96
|
+
if @pop_time && Time.now >= @pop_time + reconnect_interval
|
97
|
+
@pop_time = nil
|
98
|
+
@queue.push Message.new(nil, :type => "nop")
|
99
|
+
break
|
100
|
+
end
|
101
|
+
sleep 1
|
102
|
+
end
|
103
|
+
end
|
86
104
|
end
|
87
105
|
|
88
106
|
def hostname
|
@@ -98,12 +116,19 @@ module JSCommander
|
|
98
116
|
@queue.push(command)
|
99
117
|
end
|
100
118
|
|
119
|
+
def abort
|
120
|
+
@thread.kill
|
121
|
+
end
|
122
|
+
|
101
123
|
def pop_command
|
124
|
+
@pop_time = Time.now
|
102
125
|
r = @queue.pop
|
126
|
+
@pop_time = nil
|
103
127
|
# thread is automatically stopped if r == CLIENT_ABORTED
|
104
128
|
if r != CLIENT_ABORTED
|
105
129
|
@thread.kill
|
106
130
|
end
|
131
|
+
@reconnect_thread.kill
|
107
132
|
r
|
108
133
|
end
|
109
134
|
end
|
@@ -124,7 +149,17 @@ module JSCommander
|
|
124
149
|
end
|
125
150
|
end
|
126
151
|
@clients = []
|
127
|
-
|
152
|
+
options = args[:disable_proxy_injection] ? args : {:ProxyContentHandler => method(:handle_content).to_proc}.merge(args)
|
153
|
+
@trace_request = args[:trace_request]
|
154
|
+
@requests = 0
|
155
|
+
@reconnect_interval = args[:reconnect_interval]
|
156
|
+
super(options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def trace(msg)
|
160
|
+
if @trace_request
|
161
|
+
$stderr.puts "[TRACE] " + msg
|
162
|
+
end
|
128
163
|
end
|
129
164
|
|
130
165
|
def service(req, res)
|
@@ -145,6 +180,19 @@ module JSCommander
|
|
145
180
|
end
|
146
181
|
end
|
147
182
|
|
183
|
+
alias_method :do_service, :service
|
184
|
+
|
185
|
+
def service(req, res)
|
186
|
+
@requests += 1
|
187
|
+
trace(">" * @requests + " #{req.unparsed_uri}")
|
188
|
+
begin
|
189
|
+
do_service(req, res)
|
190
|
+
ensure
|
191
|
+
trace("<" * @requests + " #{req.unparsed_uri}")
|
192
|
+
@requests -= 1
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
148
196
|
def shutdown
|
149
197
|
@command_dispatcher.shutdown
|
150
198
|
super
|
@@ -156,7 +204,7 @@ module JSCommander
|
|
156
204
|
@command_dispatcher.new_client(req) do |client|
|
157
205
|
type = req.header["x-jscmd-type"].first
|
158
206
|
if type && type != "connect"
|
159
|
-
@broker.send("events", Message.new(URI.decode(req.body),
|
207
|
+
@broker.send("events", Message.new(URI.decode(req.body || ""),
|
160
208
|
:type => type,
|
161
209
|
:clients => @command_dispatcher.format_clients,
|
162
210
|
"in-reply-to" => req.header["x-jscmd-in-reply-to"].first))
|
@@ -164,12 +212,15 @@ module JSCommander
|
|
164
212
|
# immediately send empty response to wait for another event
|
165
213
|
res.content_type = "text/plain"
|
166
214
|
res.body = ''
|
215
|
+
client.abort
|
167
216
|
return
|
168
217
|
end
|
169
218
|
else
|
170
219
|
# new connection
|
171
|
-
@
|
172
|
-
|
220
|
+
if @command_dispatcher.last_clients != @command_dispatcher.format_clients
|
221
|
+
@broker.send("events", Message.new(nil, :type => "connect",
|
222
|
+
:clients => @command_dispatcher.format_clients))
|
223
|
+
end
|
173
224
|
end
|
174
225
|
|
175
226
|
command = client.pop_command
|
data/lib/jscmd/shell.rb
CHANGED
@@ -81,6 +81,9 @@ module JSCommander
|
|
81
81
|
@clients = nil
|
82
82
|
@msg_lock = Mutex.new
|
83
83
|
@wait_for_event = {}
|
84
|
+
@interrupt_lock = Mutex.new
|
85
|
+
@interrupt_ready_cv = ConditionVariable.new
|
86
|
+
@interrupt_ready = false
|
84
87
|
end
|
85
88
|
|
86
89
|
def generate_id
|
@@ -161,6 +164,10 @@ module JSCommander
|
|
161
164
|
when "connect"
|
162
165
|
puts
|
163
166
|
end
|
167
|
+
@interrupt_lock.synchronize do
|
168
|
+
@interrupt_ready_cv.wait(@interrupt_lock) until @interrupt_ready
|
169
|
+
@interrupt_ready = false
|
170
|
+
end
|
164
171
|
if Process.methods.include?("kill")
|
165
172
|
Process.kill "INT", $$ # interrupt readline
|
166
173
|
else
|
@@ -169,8 +176,12 @@ module JSCommander
|
|
169
176
|
end
|
170
177
|
end
|
171
178
|
end
|
172
|
-
|
179
|
+
|
173
180
|
begin
|
181
|
+
@interrupt_lock.synchronize do
|
182
|
+
@interrupt_ready = true
|
183
|
+
@interrupt_ready_cv.broadcast
|
184
|
+
end
|
174
185
|
loop do
|
175
186
|
break unless line = console.readline
|
176
187
|
if line.chomp == ''
|
data/lib/jscmd/version.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: jscmd
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-10-08 00:00:00 +09:00
|
8
8
|
summary: JavaScript console for any browser
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -57,10 +57,13 @@ test_files:
|
|
57
57
|
- test/test_helper.rb
|
58
58
|
- test/test_proxyserver.rb
|
59
59
|
- test/test_shell.rb
|
60
|
-
rdoc_options:
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
rdoc_options:
|
61
|
+
- --main
|
62
|
+
- README.txt
|
63
|
+
extra_rdoc_files:
|
64
|
+
- History.txt
|
65
|
+
- Manifest.txt
|
66
|
+
- README.txt
|
64
67
|
executables:
|
65
68
|
- jscmd
|
66
69
|
extensions: []
|