bullring 0.7.4 → 0.8
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/lib/bullring.rb +16 -22
- data/lib/bullring/util/dummy_logger.rb +10 -0
- data/lib/bullring/util/exceptions.rb +0 -1
- data/lib/bullring/util/server_proxy.rb +96 -0
- data/lib/bullring/util/server_registry.rb +283 -0
- data/lib/bullring/version.rb +1 -1
- data/lib/bullring/worker.rb +33 -13
- data/lib/bullring/workers/common.rb +66 -0
- data/lib/bullring/workers/racer_worker.rb +77 -0
- data/lib/bullring/workers/rhino_server.rb +81 -120
- data/lib/bullring/workers/rhino_server.sh +6 -5
- data/lib/bullring/workers/rhino_server_worker.rb +32 -61
- data/test/bullring_test.rb +44 -183
- data/test/dummy/log/development.log +152042 -0
- metadata +25 -22
- data/lib/bullring/workers/racer_dev_worker.rb +0 -167
- data/lib/bullring/workers/rhino_simple_worker.rb +0 -0
data/lib/bullring/version.rb
CHANGED
data/lib/bullring/worker.rb
CHANGED
@@ -1,36 +1,56 @@
|
|
1
1
|
require 'bullring/util/exceptions'
|
2
2
|
|
3
|
-
# TODO this class may have the common uglifier code, maybe some common
|
4
|
-
# caching code
|
5
|
-
|
6
3
|
module Bullring
|
7
4
|
class Worker
|
8
5
|
|
9
|
-
|
10
|
-
raise AbstractMethodCalled
|
11
|
-
end
|
6
|
+
attr_reader :libraries
|
12
7
|
|
13
|
-
def
|
14
|
-
|
8
|
+
def initialize
|
9
|
+
@libraries = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_library(name, script)
|
13
|
+
Bullring.logger.debug { "Bullring: Adding library named '#{name}'" }
|
14
|
+
@libraries[name] = script
|
15
|
+
_add_library(name, script)
|
15
16
|
end
|
16
17
|
|
17
18
|
def check(script, options)
|
18
|
-
|
19
|
+
Bullring.logger.debug { "Bullring: Checking script with hash '#{script.hash}'" }
|
20
|
+
_check(script, options)
|
19
21
|
end
|
20
22
|
|
21
23
|
def run(script, options)
|
22
|
-
|
24
|
+
Bullring.logger.debug { "Bullring: Running script with hash '#{script.hash}'" }
|
25
|
+
_run(script, options)
|
23
26
|
end
|
24
27
|
|
25
28
|
def alive?
|
26
|
-
|
29
|
+
_alive?
|
27
30
|
end
|
28
31
|
|
29
32
|
def discard
|
30
|
-
|
33
|
+
Bullring.logger.debug { "Bullring: Attempting to discard." }
|
34
|
+
_discard
|
31
35
|
end
|
32
36
|
|
37
|
+
def refresh
|
38
|
+
Bullring.logger.debug { "Bullring: Attempting to refresh." }
|
39
|
+
_refresh
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# Quasi-template method pattern
|
45
|
+
def _add_library(name, script); end
|
46
|
+
def _check(script, options); raise AbstractMethodCalled; end
|
47
|
+
def _run(script, options); raise AbstractMethodCalled; end
|
48
|
+
def _alive?; raise AbstractMethodCalled; end
|
49
|
+
def _discard; raise AbstractMethodCalled; end
|
50
|
+
|
33
51
|
end
|
34
52
|
end
|
35
53
|
|
36
|
-
require 'bullring/workers/rhino_server_worker'
|
54
|
+
require 'bullring/workers/rhino_server_worker'
|
55
|
+
require 'bullring/workers/racer_worker'
|
56
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Bullring
|
4
|
+
|
5
|
+
class JSError < StandardError
|
6
|
+
def initialize(msg = nil)
|
7
|
+
@hash = {}
|
8
|
+
super(msg)
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
@hash[key] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@hash[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :hash
|
20
|
+
end
|
21
|
+
|
22
|
+
class Helper
|
23
|
+
def self.jslint_call(script)
|
24
|
+
jslintCall = <<-RACER_CALL
|
25
|
+
JSLINT("#{prepare_source(script)}", {devel: false,
|
26
|
+
bitwise: true,
|
27
|
+
undef: true,
|
28
|
+
continue: true,
|
29
|
+
unparam: true,
|
30
|
+
debug: true,
|
31
|
+
sloppy: true,
|
32
|
+
eqeq: true,
|
33
|
+
sub: true,
|
34
|
+
es5: true,
|
35
|
+
vars: true,
|
36
|
+
evil: true,
|
37
|
+
white: true,
|
38
|
+
forin: true,
|
39
|
+
passfail: false,
|
40
|
+
newcap: true,
|
41
|
+
nomen: true,
|
42
|
+
plusplus: true,
|
43
|
+
regexp: true,
|
44
|
+
maxerr: 50,
|
45
|
+
indent: 4}); JSLINT.errors
|
46
|
+
RACER_CALL
|
47
|
+
end
|
48
|
+
|
49
|
+
ESCAPE_MAP = {
|
50
|
+
'\\' => '\\\\',
|
51
|
+
"\r\n" => '\n',
|
52
|
+
"\n" => '\n',
|
53
|
+
"\r" => '\n',
|
54
|
+
'"' => '\"',
|
55
|
+
"'" => '\''
|
56
|
+
}
|
57
|
+
|
58
|
+
def self.prepare_source(source)
|
59
|
+
# escape javascript characters (similar to Rails escape_javascript)
|
60
|
+
source.gsub!(/(\\|\r\n|[\n\r"'])/u) {|match| ESCAPE_MAP[match] }
|
61
|
+
source
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Bullring
|
2
|
+
|
3
|
+
class RacerWorker < Bullring::Worker
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def check(script, options)
|
10
|
+
context = V8::Context.new
|
11
|
+
|
12
|
+
context_wrapper {context.load(File.expand_path("../../js/jslint.min.js", __FILE__))}
|
13
|
+
|
14
|
+
call = Bullring::Helper::jslint_call(script)
|
15
|
+
duration, result = context_wrapper {context.eval(call)}
|
16
|
+
|
17
|
+
result = result.collect{|obj| obj.respond_to?(:to_h) ? obj.to_h : obj}
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(script, options)
|
21
|
+
context = V8::Context.new
|
22
|
+
|
23
|
+
(options['library_names'] || []).each do |library_name|
|
24
|
+
library = libraries[library_name]
|
25
|
+
context_wrapper {context.eval(library)}
|
26
|
+
end
|
27
|
+
|
28
|
+
# context.timeout = Bullring.configuration.execution_timeout_secs * 1000
|
29
|
+
|
30
|
+
duration, result = context_wrapper {context.eval(script)}
|
31
|
+
result = result.respond_to?(:to_h) ? result.to_h : result
|
32
|
+
end
|
33
|
+
|
34
|
+
def alive?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def _discard; end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def context_wrapper
|
43
|
+
begin
|
44
|
+
|
45
|
+
|
46
|
+
start_time = Time.now
|
47
|
+
result = yield
|
48
|
+
duration = Time.now - start_time
|
49
|
+
|
50
|
+
Bullring.logger.debug {"#{logname}: Ran script (#{duration} secs); result: " + result.inspect}
|
51
|
+
|
52
|
+
return duration, result
|
53
|
+
|
54
|
+
rescue V8::JSError => e
|
55
|
+
Bullring.logger.debug {"#{logname}: JSError! Cause: " + e.cause + "; Message: " + e.message}
|
56
|
+
raise Bullring::JSError, e.message.to_s, caller
|
57
|
+
# rescue Rhino::RunawayScriptError, Rhino::ScriptTimeoutError => e
|
58
|
+
# logger.debug {"#{logname}: Runaway Script: " + e.inspect}
|
59
|
+
# raise Bullring::JSError, "Script took too long to run", caller
|
60
|
+
# rescue Timeout::Error => e
|
61
|
+
# Bullring.logger.error {"#{logname}: Runaway script: #{e.inspect}"}
|
62
|
+
rescue NameError => e
|
63
|
+
Bullring.logger.debug {"#{logname}: Name error: " + e.inspect}
|
64
|
+
rescue StandardError => e
|
65
|
+
Bullring.logger.debug {"#{logname}: StandardError: " + e.inspect}
|
66
|
+
raise
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def logname; "Bullring (Racer)"; end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
end
|
77
|
+
|
@@ -2,46 +2,54 @@ require 'rhino'
|
|
2
2
|
require 'drb'
|
3
3
|
require 'logger'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# ignore
|
5
|
+
unless Kernel.respond_to?(:require_relative)
|
6
|
+
module Kernel
|
7
|
+
def require_relative(path)
|
8
|
+
require File.join(File.dirname(caller[0]), path.to_str)
|
10
9
|
end
|
11
10
|
end
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def initialize
|
18
|
-
@library_scripts = {}
|
19
|
-
@setup_providers = []
|
20
|
-
configure
|
21
|
-
logger.info {"#{logname}: Started a RhinoServer instance at #{Time.now}"}
|
22
|
-
end
|
23
|
-
|
24
|
-
def configure(options={})
|
25
|
-
@options ||= { :run_is_sealed => false,
|
26
|
-
:run_is_restrictable => true,
|
27
|
-
:run_timeout_secs => 0.5 }
|
13
|
+
require_relative 'common'
|
14
|
+
require_relative '../util/dummy_logger'
|
15
|
+
require_relative '../util/server_registry'
|
28
16
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def
|
34
|
-
|
35
|
-
# Hopefully, by calling 'to_s' we are getting copies that live only on our
|
36
|
-
# side of DRb. Store the provider so we can go back to it later if we
|
37
|
-
# find that we don't have a required library. Use a list of providers b/c
|
38
|
-
# some providers may have died off.
|
17
|
+
module Bullring
|
18
|
+
|
19
|
+
class RhinoServer
|
20
|
+
|
21
|
+
def initialize(host, registry_port)
|
22
|
+
@library_cache = {}
|
39
23
|
|
40
|
-
|
41
|
-
|
24
|
+
@default_options = { :run_is_sealed => false,
|
25
|
+
:run_is_restrictable => true,
|
26
|
+
:run_timeout_secs => 0.5 }
|
27
|
+
|
28
|
+
# Connect to the server registry
|
29
|
+
@server_registry = ServerRegistry.new(host,registry_port, nil)
|
30
|
+
|
31
|
+
# Start up as a DRb server (get the port from the registry)
|
32
|
+
port = @server_registry.next_server_port
|
33
|
+
uri = "druby://#{host}:#{port}"
|
34
|
+
DRb.start_service uri, self
|
35
|
+
|
36
|
+
# Put ourselves on the registry
|
37
|
+
@server_registry.register_server(uri)
|
38
|
+
|
39
|
+
# Keep an eye on the registry, if it dies, we should die
|
40
|
+
Thread.new do
|
41
|
+
while (true) do
|
42
|
+
sleep(5)
|
43
|
+
begin
|
44
|
+
@server_registry.test!
|
45
|
+
rescue
|
46
|
+
DRb.stop_service
|
47
|
+
Thread.main.exit
|
48
|
+
end
|
49
|
+
end
|
42
50
|
end
|
43
51
|
|
44
|
-
|
52
|
+
DRb.thread.join
|
45
53
|
end
|
46
54
|
|
47
55
|
def logger=(logger)
|
@@ -49,62 +57,37 @@ module Bullring
|
|
49
57
|
end
|
50
58
|
|
51
59
|
def logger
|
52
|
-
@logger
|
60
|
+
@logger ||= Bullring::DummyLogger.new
|
53
61
|
end
|
54
|
-
|
55
|
-
def
|
56
|
-
@
|
57
|
-
end
|
58
|
-
|
59
|
-
def add_library_file(name, filename)
|
60
|
-
raise NotYetImplemented
|
61
|
-
script = read file into string
|
62
|
-
@library_scripts[name] = script
|
62
|
+
|
63
|
+
def get_library(name)
|
64
|
+
@library_cache[name] ||= fetch_library(name)
|
63
65
|
end
|
64
66
|
|
65
67
|
def check(script, options)
|
66
68
|
Rhino::Context.open do |context|
|
67
69
|
context_wrapper {context.load(File.expand_path("../../js/jslint.min.js", __FILE__))}
|
68
70
|
|
69
|
-
|
70
|
-
JSLINT("#{prepare_source(script)}", {devel: false,
|
71
|
-
bitwise: true,
|
72
|
-
undef: true,
|
73
|
-
continue: true,
|
74
|
-
unparam: true,
|
75
|
-
debug: true,
|
76
|
-
sloppy: true,
|
77
|
-
eqeq: true,
|
78
|
-
sub: true,
|
79
|
-
es5: true,
|
80
|
-
vars: true,
|
81
|
-
evil: true,
|
82
|
-
white: true,
|
83
|
-
forin: true,
|
84
|
-
passfail: false,
|
85
|
-
newcap: true,
|
86
|
-
nomen: true,
|
87
|
-
plusplus: true,
|
88
|
-
regexp: true,
|
89
|
-
maxerr: 50,
|
90
|
-
indent: 4});
|
91
|
-
RHINO_CALL
|
71
|
+
call = Bullring::Helper::jslint_call(script)
|
92
72
|
|
93
|
-
duration, result = context_wrapper {context.eval(
|
73
|
+
duration, result = context_wrapper {context.eval(call)}
|
94
74
|
|
95
75
|
result = result.collect{|obj| obj.respond_to?(:to_h) ? obj.to_h : obj}
|
96
76
|
end
|
97
77
|
end
|
98
78
|
|
99
79
|
def run(script, options)
|
100
|
-
|
80
|
+
# Don't do a merge b/c jruby and ruby don't play nicely for some reason
|
81
|
+
@default_options.each{|k,v| options[k] = v}
|
82
|
+
|
83
|
+
Rhino::Context.open(:sealed => options[:run_is_sealed], :restrictable => options[:run_is_restrictable]) do |context|
|
101
84
|
|
102
85
|
(options['library_names'] || []).each do |library_name|
|
103
|
-
library_script =
|
86
|
+
library_script = get_library(library_name)
|
104
87
|
context_wrapper {context.eval(library_script)}
|
105
88
|
end
|
106
89
|
|
107
|
-
context.timeout_limit =
|
90
|
+
context.timeout_limit = options[:run_timeout_secs]
|
108
91
|
|
109
92
|
duration, result = context_wrapper {context.eval(script)}
|
110
93
|
result.respond_to?(:to_h) ? result.to_h : result
|
@@ -120,12 +103,11 @@ module Bullring
|
|
120
103
|
exit
|
121
104
|
end
|
122
105
|
|
123
|
-
def self.start(
|
124
|
-
|
125
|
-
DRb.thread.join
|
106
|
+
def self.start(host, registry_port)
|
107
|
+
RhinoServer.new(host, registry_port)
|
126
108
|
end
|
127
109
|
|
128
|
-
|
110
|
+
protected
|
129
111
|
|
130
112
|
def context_wrapper
|
131
113
|
begin
|
@@ -137,11 +119,20 @@ module Bullring
|
|
137
119
|
|
138
120
|
return duration, result
|
139
121
|
rescue Rhino::JSError => e
|
140
|
-
logger.debug {"#{logname}: JSError!
|
141
|
-
|
122
|
+
logger.debug {"#{logname}: JSError! #{e.inspect}"}
|
123
|
+
|
124
|
+
error = Bullring::JSError.new(e.message.to_s)
|
125
|
+
|
126
|
+
if e.message.respond_to?(:keys)
|
127
|
+
e.message.each do |k,v|
|
128
|
+
error[k.to_s] = v.to_s
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
raise error
|
142
133
|
rescue Rhino::RunawayScriptError, Rhino::ScriptTimeoutError => e
|
143
134
|
logger.debug {"#{logname}: Runaway Script: " + e.inspect}
|
144
|
-
raise Bullring::JSError, "Script took too long to run"
|
135
|
+
raise Bullring::JSError, "Script took too long to run"
|
145
136
|
rescue NameError => e
|
146
137
|
logger.debug {"#{logname}: Name error: " + e.inspect}
|
147
138
|
rescue StandardError => e
|
@@ -149,53 +140,23 @@ module Bullring
|
|
149
140
|
raise
|
150
141
|
end
|
151
142
|
end
|
152
|
-
|
153
|
-
ESCAPE_MAP = {
|
154
|
-
'\\' => '\\\\',
|
155
|
-
"\r\n" => '\n',
|
156
|
-
"\n" => '\n',
|
157
|
-
"\r" => '\n',
|
158
|
-
'"' => '\"',
|
159
|
-
"'" => '\''
|
160
|
-
}
|
161
|
-
|
162
|
-
def prepare_source(source)
|
163
|
-
# escape javascript characters (similar to Rails escape_javascript)
|
164
|
-
source.gsub!(/(\\|\r\n|[\n\r"'])/u) {|match| ESCAPE_MAP[match] }
|
165
|
-
source
|
166
|
-
end
|
167
143
|
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
logger.debug {"#{logname}: The script named #{name} was not available so trying to fetch from clients"}
|
172
|
-
|
173
|
-
while (provider = @setup_providers.last)
|
174
|
-
begin
|
175
|
-
library_script = provider.libraries[name]
|
176
|
-
break if !library_script.nil?
|
177
|
-
rescue DRb::DRbConnError => e
|
178
|
-
logger.debug {"#{logname}: Could not connect to setup provider (its process probably died): " + e.inspect}
|
179
|
-
rescue StandardError => e
|
180
|
-
logger.error {"#{logname}: Encountered an unknown error searching setup providers for a script named #{name}: " + e.inspect}
|
181
|
-
ensure
|
182
|
-
# Toss the last element so we can continue searching prior elements
|
183
|
-
setup_providers.pop
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# If after looking through the providers we are still empty handed, raise an error
|
188
|
-
raise NameError, "Client doesn't have script named #{name}", caller if library_script.nil?
|
144
|
+
# Grab the library from the registry server
|
145
|
+
def fetch_library(name)
|
146
|
+
library_script = @server_registry['library', name]
|
189
147
|
|
190
|
-
|
148
|
+
logger.debug {"#{logname}: Tried to fetch script '#{name}' from the registry and it " +
|
149
|
+
"was #{'not ' if library_script.nil?}found."}
|
150
|
+
|
151
|
+
raise NameError, "Server cannot find a script named #{name}" if library_script.nil?
|
152
|
+
|
153
|
+
library_script
|
191
154
|
end
|
192
155
|
|
193
|
-
def logname; "Bullring Server"; end
|
156
|
+
def logname; "Bullring (Rhino Server)"; end
|
194
157
|
|
195
158
|
end
|
196
159
|
|
197
|
-
class JSError < StandardError; end
|
198
|
-
|
199
160
|
end
|
200
161
|
|
201
162
|
#
|
@@ -203,11 +164,11 @@ end
|
|
203
164
|
#
|
204
165
|
|
205
166
|
command = ARGV[0]
|
206
|
-
|
207
|
-
|
167
|
+
host = ARGV[1]
|
168
|
+
registry_port = ARGV[2]
|
208
169
|
|
209
170
|
case command
|
210
171
|
when "start"
|
211
|
-
Bullring::RhinoServer.start(
|
172
|
+
Bullring::RhinoServer.start(host, registry_port)
|
212
173
|
end
|
213
174
|
|