jaws 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/jaws/server.rb +71 -18
- metadata +9 -9
data/Rakefile
CHANGED
@@ -11,8 +11,8 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/stormbrew/jaws"
|
12
12
|
gem.authors = ["Graham Batty"]
|
13
13
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
-
gem.add_dependency "http_parser", "
|
15
|
-
gem.add_dependency "rack", ">= 1.
|
14
|
+
gem.add_dependency "http_parser", "= 0.1.3"
|
15
|
+
gem.add_dependency "rack", ">= 1.0.0"
|
16
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
17
|
end
|
18
18
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/lib/jaws/server.rb
CHANGED
@@ -11,6 +11,7 @@ module Jaws
|
|
11
11
|
name.gsub(%r{(^|_)([a-z])}) { $2.upcase }
|
12
12
|
end
|
13
13
|
|
14
|
+
class GracefulExit < RuntimeError; end
|
14
15
|
class Server
|
15
16
|
DefaultOptions = {
|
16
17
|
:Host => '0.0.0.0',
|
@@ -75,9 +76,10 @@ module Jaws
|
|
75
76
|
# an object that responds to accept() by returning an open connection to a
|
76
77
|
# client. It also has to respond to synchronize and yield to the block
|
77
78
|
# given to that method and be thread safe in that block. It must also
|
78
|
-
# respond to close() by
|
79
|
-
#
|
80
|
-
#
|
79
|
+
# respond to close() by refusing to accept any further connections and
|
80
|
+
# returning true from closed?() thereafter. The accept() call may be interrupted
|
81
|
+
# by a GracefulExit error, it should not block or do anything special with this
|
82
|
+
# error.
|
81
83
|
def create_listener(options)
|
82
84
|
l = TCPServer.new(@host, @port)
|
83
85
|
# let 10 requests back up for each request we can handle concurrently.
|
@@ -249,16 +251,19 @@ module Jaws
|
|
249
251
|
# the connection closes.
|
250
252
|
def process_client(app)
|
251
253
|
loop do
|
254
|
+
client = nil
|
252
255
|
begin
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
256
|
+
make_interruptable do
|
257
|
+
client = @listener.synchronize do
|
258
|
+
begin
|
259
|
+
@listener && @listener.accept()
|
260
|
+
rescue => e
|
261
|
+
return # this means we've been turned off, so exit the loop.
|
262
|
+
end
|
263
|
+
end
|
264
|
+
if (!client)
|
265
|
+
return # nil return means we're quitting, exit loop.
|
258
266
|
end
|
259
|
-
end
|
260
|
-
if (!client)
|
261
|
-
return # nil return means we're quitting, exit loop.
|
262
267
|
end
|
263
268
|
|
264
269
|
req = Http::Parser.new()
|
@@ -285,8 +290,10 @@ module Jaws
|
|
285
290
|
client.close_write
|
286
291
|
end
|
287
292
|
end
|
288
|
-
|
293
|
+
rescue Errno::EPIPE
|
289
294
|
# do nothing, just let the connection close.
|
295
|
+
rescue SystemExit, GracefulExit
|
296
|
+
raise # pass it on.
|
290
297
|
rescue Object => e
|
291
298
|
$stderr.puts("Unhandled error #{e}:")
|
292
299
|
e.backtrace.each do |line|
|
@@ -299,31 +306,69 @@ module Jaws
|
|
299
306
|
end
|
300
307
|
private :process_client
|
301
308
|
|
309
|
+
# Sets the current thread as interruptable. This happens around
|
310
|
+
# the listen part of the thread. This means the thread is receptive
|
311
|
+
# to t.raise.
|
312
|
+
def make_interruptable
|
313
|
+
begin
|
314
|
+
@interruptable.synchronize do
|
315
|
+
@interruptable << Thread.current
|
316
|
+
end
|
317
|
+
yield
|
318
|
+
ensure
|
319
|
+
@interruptable.synchronize do
|
320
|
+
@interruptable.delete(Thread.current)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
302
325
|
# Runs the application through the configured handler.
|
303
326
|
# Can only be run once at a time. If you try to run it more than
|
304
327
|
# once, the second run will block until the first finishes.
|
305
328
|
def run(app)
|
306
329
|
synchronize do
|
330
|
+
@interruptable = []
|
331
|
+
int_orig = trap "INT" do
|
332
|
+
stop()
|
333
|
+
end
|
334
|
+
term_orig = trap "TERM" do
|
335
|
+
stop()
|
336
|
+
end
|
307
337
|
begin
|
308
338
|
@listener = create_listener(@options)
|
339
|
+
@interruptable.extend Mutex_m
|
309
340
|
if (@max_clients > 1)
|
310
341
|
@master = Thread.current
|
311
342
|
@workers = (0...@max_clients).collect do
|
312
343
|
Thread.new do
|
313
|
-
|
344
|
+
begin
|
345
|
+
process_client(app)
|
346
|
+
rescue GracefulExit, SystemExit => e
|
347
|
+
# let it exit.
|
348
|
+
rescue => e
|
349
|
+
$stderr.puts("Handler thread unexpectedly died with #{e}:", e.backtrace)
|
350
|
+
end
|
314
351
|
end
|
315
352
|
end
|
316
353
|
@workers.each do |worker|
|
317
354
|
worker.join
|
318
355
|
end
|
319
356
|
else
|
320
|
-
|
321
|
-
|
322
|
-
|
357
|
+
begin
|
358
|
+
@master = Thread.current
|
359
|
+
@workers = [Thread.current]
|
360
|
+
process_client(app)
|
361
|
+
rescue GracefulExit, SystemExit => e
|
362
|
+
# let it exit
|
363
|
+
rescue => e
|
364
|
+
$stderr.puts("Handler thread unexpectedly died with #{e}:", e.backtrace)
|
365
|
+
end
|
323
366
|
end
|
324
367
|
ensure
|
368
|
+
trap "INT", int_orig
|
369
|
+
trap "TERM", term_orig
|
325
370
|
@listener.close if (@listener && !@listener.closed?)
|
326
|
-
@listener = @master = @workers = nil
|
371
|
+
@interruptable = @listener = @master = @workers = nil
|
327
372
|
end
|
328
373
|
end
|
329
374
|
end
|
@@ -332,7 +377,15 @@ module Jaws
|
|
332
377
|
# close the connection, the handler threads will exit
|
333
378
|
# the next time they try to load.
|
334
379
|
# TODO: Make it force them to exit after a timeout.
|
335
|
-
|
380
|
+
$stderr.puts("Terminating request threads. To force immediate exit, send sigkill.")
|
381
|
+
@interruptable.synchronize do
|
382
|
+
@listener.close if !@listener.closed?
|
383
|
+
@workers.each do |worker|
|
384
|
+
if (@interruptable.include?(worker))
|
385
|
+
worker.raise GracefulExit, "Exiting"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
336
389
|
end
|
337
390
|
|
338
391
|
def running?
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 6
|
8
|
+
- 0
|
9
|
+
version: 0.6.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Graham Batty
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-19 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -36,13 +36,13 @@ dependencies:
|
|
36
36
|
prerelease: false
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - "="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
segments:
|
42
42
|
- 0
|
43
43
|
- 1
|
44
|
-
-
|
45
|
-
version: 0.1.
|
44
|
+
- 3
|
45
|
+
version: 0.1.3
|
46
46
|
type: :runtime
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
@@ -54,9 +54,9 @@ dependencies:
|
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
segments:
|
56
56
|
- 1
|
57
|
-
- 1
|
58
57
|
- 0
|
59
|
-
|
58
|
+
- 0
|
59
|
+
version: 1.0.0
|
60
60
|
type: :runtime
|
61
61
|
version_requirements: *id003
|
62
62
|
description: A Ruby web server designed to have a predictable and simple concurrency model, and to be capable of running in a pure-ruby environment.
|