curb 1.2.1 → 1.3.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.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/Rakefile +22 -0
- data/ext/curb.c +282 -231
- data/ext/curb.h +4 -4
- data/ext/curb_easy.c +676 -228
- data/ext/curb_easy.h +6 -0
- data/ext/curb_errors.c +5 -5
- data/ext/curb_errors.h +1 -1
- data/ext/curb_macros.h +14 -14
- data/ext/curb_multi.c +639 -132
- data/ext/curb_multi.h +3 -1
- data/ext/curb_postfield.c +47 -21
- data/ext/curb_postfield.h +1 -0
- data/ext/curb_upload.c +32 -9
- data/ext/curb_upload.h +2 -0
- data/ext/extconf.rb +40 -0
- data/lib/curl/easy.rb +154 -13
- data/lib/curl/multi.rb +69 -9
- data/lib/curl.rb +193 -0
- data/tests/helper.rb +222 -36
- data/tests/leak_trace.rb +237 -0
- data/tests/tc_curl_download.rb +6 -2
- data/tests/tc_curl_easy.rb +450 -1
- data/tests/tc_curl_easy_request_target.rb +41 -0
- data/tests/tc_curl_multi.rb +573 -59
- data/tests/tc_curl_native_coverage.rb +130 -0
- data/tests/tc_curl_postfield.rb +161 -0
- data/tests/tc_fiber_scheduler.rb +342 -7
- data/tests/tc_ftp_options.rb +26 -0
- data/tests/tc_gc_compact.rb +223 -0
- data/tests/tc_test_server_methods.rb +110 -0
- metadata +16 -4
data/lib/curl.rb
CHANGED
|
@@ -6,6 +6,199 @@ require 'uri'
|
|
|
6
6
|
|
|
7
7
|
# expose shortcut methods
|
|
8
8
|
module Curl
|
|
9
|
+
def self.scheduler_active?
|
|
10
|
+
Fiber.respond_to?(:scheduler) && !Fiber.scheduler.nil?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.deferred_exception_source_id(state)
|
|
14
|
+
return unless state[:multi].instance_variable_defined?(:@__curb_deferred_exception_source_id)
|
|
15
|
+
|
|
16
|
+
state[:multi].instance_variable_get(:@__curb_deferred_exception_source_id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.scheduler_waiter_blocking_supported?
|
|
20
|
+
scheduler = Fiber.scheduler
|
|
21
|
+
scheduler && scheduler.respond_to?(:block) && scheduler.respond_to?(:unblock)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.wake_scheduler_waiter(waiter)
|
|
25
|
+
fiber = waiter[:fiber]
|
|
26
|
+
scheduler = waiter[:scheduler]
|
|
27
|
+
return unless fiber&.alive? && scheduler&.respond_to?(:unblock)
|
|
28
|
+
|
|
29
|
+
scheduler.unblock(waiter, fiber)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.complete_scheduler_waiter(waiter)
|
|
33
|
+
return if waiter[:done]
|
|
34
|
+
|
|
35
|
+
waiter[:done] = true
|
|
36
|
+
wake_scheduler_waiter(waiter)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.fail_scheduler_waiter(waiter, error)
|
|
40
|
+
return if waiter[:error]
|
|
41
|
+
|
|
42
|
+
waiter[:error] = error
|
|
43
|
+
wake_scheduler_waiter(waiter)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.release_scheduler_error(state, error)
|
|
47
|
+
source_waiter = state[:waiters][deferred_exception_source_id(state)]
|
|
48
|
+
|
|
49
|
+
if source_waiter
|
|
50
|
+
fail_scheduler_waiter(source_waiter, error)
|
|
51
|
+
else
|
|
52
|
+
state[:error] = error
|
|
53
|
+
state[:waiters].each_value { |waiter| wake_scheduler_waiter(waiter) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.block_scheduler_waiter(waiter)
|
|
58
|
+
unless scheduler_waiter_blocking_supported?
|
|
59
|
+
sleep 0
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
waiter[:fiber] = Fiber.current
|
|
64
|
+
waiter[:scheduler] ||= Fiber.scheduler
|
|
65
|
+
return if waiter[:done] || waiter[:error]
|
|
66
|
+
|
|
67
|
+
waiter[:scheduler].block(waiter, nil)
|
|
68
|
+
ensure
|
|
69
|
+
waiter[:fiber] = nil if waiter[:fiber].equal?(Fiber.current)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.scheduler_yield
|
|
73
|
+
scheduler = Fiber.scheduler
|
|
74
|
+
|
|
75
|
+
if scheduler&.respond_to?(:kernel_sleep)
|
|
76
|
+
scheduler.kernel_sleep(0)
|
|
77
|
+
else
|
|
78
|
+
sleep 0
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.release_scheduler_waiters(state)
|
|
83
|
+
source_id = deferred_exception_source_id(state)
|
|
84
|
+
|
|
85
|
+
state[:waiters].each do |easy_id, waiter|
|
|
86
|
+
next if source_id == easy_id
|
|
87
|
+
|
|
88
|
+
complete_scheduler_waiter(waiter) if waiter[:completed]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.perform_with_scheduler(easy)
|
|
93
|
+
state = scheduler_state
|
|
94
|
+
waiter = {completed: false, done: false, error: nil, fiber: nil, scheduler: Fiber.scheduler}
|
|
95
|
+
state[:waiters][easy.object_id] = waiter
|
|
96
|
+
previous_complete = easy.on_complete do |completed_easy|
|
|
97
|
+
previous_complete.call(completed_easy) if previous_complete
|
|
98
|
+
waiter[:completed] = true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
state[:pending] << easy
|
|
102
|
+
ensure_scheduler_driver(state)
|
|
103
|
+
|
|
104
|
+
until waiter[:done]
|
|
105
|
+
raise waiter[:error] if waiter[:error]
|
|
106
|
+
raise state[:error] if state[:error]
|
|
107
|
+
block_scheduler_waiter(waiter)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
while state[:driver_running] && state[:pending].empty? &&
|
|
111
|
+
state[:waiters].length == 1 && state[:waiters].key?(easy.object_id)
|
|
112
|
+
scheduler_yield
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
true
|
|
116
|
+
ensure
|
|
117
|
+
state[:waiters].delete(easy.object_id) if defined?(state) && state[:waiters]
|
|
118
|
+
if defined?(previous_complete)
|
|
119
|
+
if previous_complete
|
|
120
|
+
easy.on_complete(&previous_complete)
|
|
121
|
+
else
|
|
122
|
+
easy.on_complete
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def self.scheduler_state
|
|
128
|
+
Thread.current.thread_variable_get(:curb_scheduler_state) || begin
|
|
129
|
+
state = {
|
|
130
|
+
multi: Curl::Multi.new,
|
|
131
|
+
pending: [],
|
|
132
|
+
driver_running: false,
|
|
133
|
+
error: nil,
|
|
134
|
+
waiters: {},
|
|
135
|
+
}
|
|
136
|
+
Thread.current.thread_variable_set(:curb_scheduler_state, state)
|
|
137
|
+
state
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.ensure_scheduler_driver(state)
|
|
142
|
+
return if state[:driver_running]
|
|
143
|
+
|
|
144
|
+
state[:driver_running] = true
|
|
145
|
+
state[:error] = nil
|
|
146
|
+
|
|
147
|
+
runner = proc do
|
|
148
|
+
begin
|
|
149
|
+
# Give sibling fibers a chance to enqueue work so the shared multi can
|
|
150
|
+
# batch scheduler-driven Easy#perform calls together.
|
|
151
|
+
pending_count = -1
|
|
152
|
+
until pending_count == state[:pending].size
|
|
153
|
+
pending_count = state[:pending].size
|
|
154
|
+
scheduler_yield
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
loop do
|
|
158
|
+
drain_scheduler_pending(state)
|
|
159
|
+
break if state[:multi].idle?
|
|
160
|
+
|
|
161
|
+
begin
|
|
162
|
+
state[:multi].perform do
|
|
163
|
+
drain_scheduler_pending(state)
|
|
164
|
+
release_scheduler_waiters(state)
|
|
165
|
+
scheduler_yield
|
|
166
|
+
end
|
|
167
|
+
ensure
|
|
168
|
+
# Release any siblings that completed just before a deferred
|
|
169
|
+
# callback exception is re-raised.
|
|
170
|
+
release_scheduler_waiters(state)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
rescue => e
|
|
174
|
+
release_scheduler_waiters(state)
|
|
175
|
+
release_scheduler_error(state, e)
|
|
176
|
+
ensure
|
|
177
|
+
state[:driver_running] = false
|
|
178
|
+
ensure_scheduler_driver(state) if state[:error].nil? && !state[:pending].empty?
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
if Fiber.respond_to?(:schedule)
|
|
183
|
+
Fiber.schedule(&runner)
|
|
184
|
+
else
|
|
185
|
+
Fiber.new(blocking: false, &runner).resume
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def self.drain_scheduler_pending(state)
|
|
190
|
+
pending = state[:pending]
|
|
191
|
+
until pending.empty?
|
|
192
|
+
easy = pending.first
|
|
193
|
+
|
|
194
|
+
break if state[:multi].instance_variable_defined?(:@__curb_deferred_exception)
|
|
195
|
+
|
|
196
|
+
state[:multi].add(easy)
|
|
197
|
+
break unless state[:multi].requests.key?(easy.object_id)
|
|
198
|
+
|
|
199
|
+
pending.shift
|
|
200
|
+
end
|
|
201
|
+
end
|
|
9
202
|
|
|
10
203
|
def self.http(verb, url, post_body=nil, put_data=nil, &block)
|
|
11
204
|
if Thread.current[:curb_curl_yielding]
|
data/tests/helper.rb
CHANGED
|
@@ -4,6 +4,8 @@ $CURB_TESTING = true
|
|
|
4
4
|
require 'uri'
|
|
5
5
|
require 'stringio'
|
|
6
6
|
require 'digest/md5'
|
|
7
|
+
require 'rbconfig'
|
|
8
|
+
require File.join(RbConfig::CONFIG['rubylibdir'], 'timeout')
|
|
7
9
|
|
|
8
10
|
$TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
9
11
|
$EXTDIR = File.join($TOPDIR, 'ext')
|
|
@@ -57,6 +59,7 @@ $TEST_URL = "file://#{'/' if RUBY_DESCRIPTION =~ /mswin|msys|mingw|cygwin|bccwin
|
|
|
57
59
|
|
|
58
60
|
require 'thread'
|
|
59
61
|
require 'webrick'
|
|
62
|
+
require 'socket'
|
|
60
63
|
|
|
61
64
|
# set this to true to avoid testing with multiple threads
|
|
62
65
|
# or to test with multiple threads set it to false
|
|
@@ -64,21 +67,39 @@ require 'webrick'
|
|
|
64
67
|
# on the presence of multiple threads
|
|
65
68
|
TEST_SINGLE_THREADED=false
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
WEBRICK_TEST_LOG = WEBrick::Log.new(File.open(File::NULL, 'w'), WEBrick::BasicLog::ERROR)
|
|
71
|
+
|
|
72
|
+
module CurbTestResourceCleanup
|
|
73
|
+
def teardown
|
|
74
|
+
super
|
|
75
|
+
ensure
|
|
76
|
+
begin
|
|
77
|
+
if Curl::Easy.respond_to?(:flush_deferred_multi_closes)
|
|
78
|
+
Curl::Easy.flush_deferred_multi_closes(all_threads: true)
|
|
79
|
+
end
|
|
80
|
+
rescue StandardError
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
ObjectSpace.each_object(Curl::Multi) do |multi|
|
|
86
|
+
begin
|
|
87
|
+
next if multi.instance_variable_defined?(:@deferred_close) && multi.instance_variable_get(:@deferred_close)
|
|
88
|
+
multi.instance_variable_set(:@requests, {})
|
|
89
|
+
multi._close
|
|
90
|
+
rescue StandardError
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
rescue StandardError
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
70
97
|
|
|
71
|
-
::WEBrick::HTTPServer.class_eval do
|
|
72
|
-
def access_log(config, req, res)
|
|
73
|
-
# nop
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
::WEBrick::BasicLog.class_eval do
|
|
77
|
-
def log(level, data)
|
|
78
|
-
# nop
|
|
79
98
|
end
|
|
80
99
|
end
|
|
81
100
|
|
|
101
|
+
Test::Unit::TestCase.prepend(CurbTestResourceCleanup)
|
|
102
|
+
|
|
82
103
|
#
|
|
83
104
|
# Simple test server to record number of times a request is sent/recieved of a specific
|
|
84
105
|
# request type, e.g. GET,POST,PUT,DELETE
|
|
@@ -187,7 +208,7 @@ end
|
|
|
187
208
|
module BugTestServerSetupTeardown
|
|
188
209
|
def setup
|
|
189
210
|
@port ||= 9992
|
|
190
|
-
@server = WEBrick::HTTPServer.new(
|
|
211
|
+
@server = WEBrick::HTTPServer.new(:Port => @port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
191
212
|
@server.mount_proc("/test") do|req,res|
|
|
192
213
|
if @response_proc
|
|
193
214
|
@response_proc.call(res)
|
|
@@ -211,14 +232,170 @@ module BugTestServerSetupTeardown
|
|
|
211
232
|
end
|
|
212
233
|
|
|
213
234
|
module TestServerMethods
|
|
235
|
+
def server_responding?(port)
|
|
236
|
+
socket = TCPSocket.new('127.0.0.1', port)
|
|
237
|
+
socket.close
|
|
238
|
+
true
|
|
239
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, IOError
|
|
240
|
+
false
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def server_startup_timeout
|
|
244
|
+
ENV['RUBY_MEMCHECK_RUNNING'] ? 30 : 5
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def wait_for_server_ready(port, thread: nil)
|
|
248
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + server_startup_timeout
|
|
249
|
+
|
|
250
|
+
loop do
|
|
251
|
+
if thread && !thread.alive?
|
|
252
|
+
return false
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
return true if server_responding?(port)
|
|
256
|
+
|
|
257
|
+
raise "Failed to startup test server on port #{port}" if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
|
|
258
|
+
|
|
259
|
+
sleep 0.01
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def server_shutdown_timeout
|
|
264
|
+
[server_startup_timeout, 0.25].max
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def wait_for_server_stopped(port, thread: nil)
|
|
268
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + server_shutdown_timeout
|
|
269
|
+
|
|
270
|
+
loop do
|
|
271
|
+
return true unless server_responding?(port)
|
|
272
|
+
|
|
273
|
+
if thread && !thread.alive?
|
|
274
|
+
return !server_responding?(port)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
return false if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
|
|
278
|
+
|
|
279
|
+
sleep 0.01
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
214
283
|
def locked_file
|
|
215
284
|
File.join(File.dirname(__FILE__),"server_lock-#{@__port}")
|
|
216
285
|
end
|
|
217
286
|
|
|
287
|
+
def read_server_lock_pid
|
|
288
|
+
Integer(File.read(locked_file).strip, 10)
|
|
289
|
+
rescue Errno::ENOENT, ArgumentError, TypeError
|
|
290
|
+
nil
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def write_server_lock(pid = Process.pid)
|
|
294
|
+
File.open(locked_file, 'w') { |f| f << "#{pid}\n" }
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def process_alive?(pid)
|
|
298
|
+
return false unless pid && pid.positive?
|
|
299
|
+
|
|
300
|
+
Process.kill(0, pid)
|
|
301
|
+
true
|
|
302
|
+
rescue Errno::ESRCH
|
|
303
|
+
false
|
|
304
|
+
rescue Errno::EPERM
|
|
305
|
+
true
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def server_lock_fresh?
|
|
309
|
+
(Time.now - File.mtime(locked_file)) < server_startup_timeout
|
|
310
|
+
rescue Errno::ENOENT
|
|
311
|
+
false
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def stale_server_lock?(port)
|
|
315
|
+
return false unless File.exist?(locked_file)
|
|
316
|
+
|
|
317
|
+
pid = read_server_lock_pid
|
|
318
|
+
return false if pid && process_alive?(pid) && server_lock_fresh?
|
|
319
|
+
return false if server_responding?(port)
|
|
320
|
+
|
|
321
|
+
true
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def clear_stale_server_lock(port)
|
|
325
|
+
return unless stale_server_lock?(port)
|
|
326
|
+
|
|
327
|
+
File.unlink(locked_file)
|
|
328
|
+
rescue Errno::ENOENT
|
|
329
|
+
nil
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def stop_test_server
|
|
333
|
+
server = instance_variable_defined?(:@server) ? @server : nil
|
|
334
|
+
pid = instance_variable_defined?(:@__pid) ? @__pid : nil
|
|
335
|
+
return unless server || pid
|
|
336
|
+
|
|
337
|
+
if TEST_SINGLE_THREADED
|
|
338
|
+
@__pid = nil
|
|
339
|
+
|
|
340
|
+
if pid
|
|
341
|
+
begin
|
|
342
|
+
Process.kill('INT', pid)
|
|
343
|
+
rescue Errno::ESRCH
|
|
344
|
+
nil
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
begin
|
|
348
|
+
Process.wait(pid)
|
|
349
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
350
|
+
nil
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
else
|
|
354
|
+
thread = instance_variable_defined?(:@test_thread) ? @test_thread : nil
|
|
355
|
+
port = instance_variable_defined?(:@__port) ? @__port : nil
|
|
356
|
+
@server = nil
|
|
357
|
+
@test_thread = nil
|
|
358
|
+
|
|
359
|
+
server.shutdown if server
|
|
360
|
+
wait_for_server_stopped(port, thread: thread) if port
|
|
361
|
+
thread.join(server_shutdown_timeout) if thread
|
|
362
|
+
|
|
363
|
+
if thread&.alive?
|
|
364
|
+
thread.kill
|
|
365
|
+
thread.join(server_shutdown_timeout)
|
|
366
|
+
wait_for_server_stopped(port) if port
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
File.unlink(locked_file) if File.exist?(locked_file)
|
|
371
|
+
rescue Errno::ENOENT
|
|
372
|
+
nil
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def teardown
|
|
376
|
+
super
|
|
377
|
+
ensure
|
|
378
|
+
stop_test_server
|
|
379
|
+
end
|
|
380
|
+
|
|
218
381
|
def server_setup(port=9129,servlet=TestServlet)
|
|
219
382
|
@__port = port
|
|
220
|
-
|
|
221
|
-
|
|
383
|
+
@server = nil unless instance_variable_defined?(:@server)
|
|
384
|
+
@__pid = nil unless instance_variable_defined?(:@__pid)
|
|
385
|
+
@test_thread = nil unless instance_variable_defined?(:@test_thread)
|
|
386
|
+
clear_stale_server_lock(port)
|
|
387
|
+
|
|
388
|
+
if @server.nil? && File.exist?(locked_file)
|
|
389
|
+
begin
|
|
390
|
+
wait_for_server_ready(port)
|
|
391
|
+
return
|
|
392
|
+
rescue RuntimeError
|
|
393
|
+
clear_stale_server_lock(port)
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
if @server.nil? and !File.exist?(locked_file)
|
|
398
|
+
write_server_lock
|
|
222
399
|
if TEST_SINGLE_THREADED
|
|
223
400
|
rd, wr = IO.pipe
|
|
224
401
|
@__pid = fork do
|
|
@@ -226,33 +403,50 @@ module TestServerMethods
|
|
|
226
403
|
rd = nil
|
|
227
404
|
|
|
228
405
|
# start up a webrick server for testing delete
|
|
229
|
-
server = WEBrick::HTTPServer.new
|
|
406
|
+
server = WEBrick::HTTPServer.new(:Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__)), :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
230
407
|
|
|
231
408
|
server.mount(servlet.path, servlet)
|
|
232
409
|
server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
|
|
233
410
|
|
|
234
411
|
trap("INT") { server.shutdown }
|
|
235
412
|
GC.start
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
413
|
+
server_thread = Thread.new { server.start }
|
|
414
|
+
|
|
415
|
+
begin
|
|
416
|
+
if wait_for_server_ready(port, thread: server_thread)
|
|
417
|
+
wr.write('1')
|
|
418
|
+
else
|
|
419
|
+
wr.write('0')
|
|
420
|
+
end
|
|
421
|
+
rescue StandardError
|
|
422
|
+
wr.write('0')
|
|
423
|
+
ensure
|
|
424
|
+
wr.flush
|
|
425
|
+
wr.close
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
server_thread.join
|
|
239
429
|
end
|
|
240
430
|
wr.close
|
|
241
|
-
rd.read
|
|
431
|
+
ready = rd.read
|
|
242
432
|
rd.close
|
|
433
|
+
if ready != '1'
|
|
434
|
+
STDERR.puts "Failed to startup test server!"
|
|
435
|
+
exit(1)
|
|
436
|
+
end
|
|
243
437
|
else
|
|
244
438
|
# start up a webrick server for testing delete
|
|
245
|
-
|
|
439
|
+
server = WEBrick::HTTPServer.new(:Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__)), :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
|
246
440
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
queue = Queue.new # synchronize the thread startup to the main thread
|
|
441
|
+
server.mount(servlet.path, servlet)
|
|
442
|
+
server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
|
|
250
443
|
|
|
251
|
-
@
|
|
444
|
+
@server = server
|
|
445
|
+
# Keep a stable reference inside the thread so helper shutdown can clear
|
|
446
|
+
# @server without racing the server startup path.
|
|
447
|
+
@test_thread = Thread.new(server) { |srv| srv.start }
|
|
252
448
|
|
|
253
|
-
|
|
254
|
-
value = queue.pop
|
|
255
|
-
if !value
|
|
449
|
+
if !wait_for_server_ready(port, thread: @test_thread)
|
|
256
450
|
STDERR.puts "Failed to startup test server!"
|
|
257
451
|
exit(1)
|
|
258
452
|
end
|
|
@@ -261,15 +455,7 @@ module TestServerMethods
|
|
|
261
455
|
|
|
262
456
|
exit_code = lambda do
|
|
263
457
|
begin
|
|
264
|
-
|
|
265
|
-
File.unlink locked_file
|
|
266
|
-
if TEST_SINGLE_THREADED
|
|
267
|
-
Process.kill 'INT', @__pid
|
|
268
|
-
else
|
|
269
|
-
@server.shutdown unless @server.nil?
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
#@server.shutdown unless @server.nil?
|
|
458
|
+
stop_test_server
|
|
273
459
|
rescue Object => e
|
|
274
460
|
puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
|
|
275
461
|
end
|