jscmd 0.1.0 → 0.2.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 +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: []
|