strelka 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Manifest.txt +2 -0
- data/lib/strelka.rb +25 -1
- data/lib/strelka/command/start.rb +2 -2
- data/lib/strelka/multirunner.rb +123 -0
- data/lib/strelka/signal_handling.rb +117 -0
- data/spec/strelka_spec.rb +14 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dad453a5871872ca4f793e78253510d82e4ed27fbb173c82629501c60962477
|
4
|
+
data.tar.gz: 8bf1feafca4c80397808322ed09c93b9c6ed56cd917c5db12ab0f1e33cf8a59f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a220e3f1d3f6fdaf963173170dc2186ebf580a2b93c522017df7cfd7ba3ef3d45850943552a510e7bacbed11271dca7d0a42f1349b74651c64c5a9a957f852f5
|
7
|
+
data.tar.gz: b8eb408709e9fb4269d0516236d9d203a4ea3e59580b82c9b8d9939d7932c6722d79f0076aa44fd08638ce890dc7c6270e4c772ed9b064d698e043055596078e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/Manifest.txt
CHANGED
@@ -73,6 +73,7 @@ lib/strelka/httpresponse/negotiation.rb
|
|
73
73
|
lib/strelka/httpresponse/session.rb
|
74
74
|
lib/strelka/mixins.rb
|
75
75
|
lib/strelka/multipartparser.rb
|
76
|
+
lib/strelka/multirunner.rb
|
76
77
|
lib/strelka/paramvalidator.rb
|
77
78
|
lib/strelka/plugins.rb
|
78
79
|
lib/strelka/router.rb
|
@@ -81,6 +82,7 @@ lib/strelka/router/exclusive.rb
|
|
81
82
|
lib/strelka/session.rb
|
82
83
|
lib/strelka/session/db.rb
|
83
84
|
lib/strelka/session/default.rb
|
85
|
+
lib/strelka/signal_handling.rb
|
84
86
|
lib/strelka/testing.rb
|
85
87
|
lib/strelka/websocketserver.rb
|
86
88
|
lib/strelka/websocketserver/heartbeat.rb
|
data/lib/strelka.rb
CHANGED
@@ -22,7 +22,7 @@ module Strelka
|
|
22
22
|
extend Loggability
|
23
23
|
|
24
24
|
# Library version constant
|
25
|
-
VERSION = '0.
|
25
|
+
VERSION = '0.18.0'
|
26
26
|
|
27
27
|
# Version-control revision constant
|
28
28
|
REVISION = %q$Revision$
|
@@ -77,6 +77,11 @@ module Strelka
|
|
77
77
|
singleton_attr_reader :after_configure_hooks
|
78
78
|
@after_configure_hooks = Set.new
|
79
79
|
|
80
|
+
##
|
81
|
+
# An Array of callbacks to be run before forking a handler process
|
82
|
+
singleton_attr_reader :before_fork_hooks
|
83
|
+
@before_fork_hooks = Set.new
|
84
|
+
|
80
85
|
##
|
81
86
|
# True if the after_configure hooks have already (started to) run.
|
82
87
|
singleton_predicate_reader :after_configure_hooks_run
|
@@ -123,6 +128,25 @@ module Strelka
|
|
123
128
|
end
|
124
129
|
|
125
130
|
|
131
|
+
### Call the before-fork hooks.
|
132
|
+
def self::call_before_fork_hooks
|
133
|
+
self.log.debug " calling %d before-fork hooks" % [ self.before_fork_hooks.length ]
|
134
|
+
|
135
|
+
self.before_fork_hooks.to_a.each do |hook|
|
136
|
+
self.log.debug " %s line %s..." % hook.source_location
|
137
|
+
hook.call
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
### Register a callback to be run before forking a Strelka::Handler
|
143
|
+
### subprocess.
|
144
|
+
def self::before_fork( &block )
|
145
|
+
raise LocalJumpError, "no block given" unless block
|
146
|
+
self.before_fork_hooks << block
|
147
|
+
end
|
148
|
+
|
149
|
+
|
126
150
|
### Load the specified +config_file+, install the config in all objects with
|
127
151
|
### Configurability, and call any callbacks registered via #after_configure.
|
128
152
|
def self::load_config( config_file=nil, defaults=nil )
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# frozen-string-literal: true
|
4
4
|
|
5
5
|
require 'strelka/cli' unless defined?( Strelka::CLI )
|
6
|
+
require 'strelka/multirunner'
|
6
7
|
|
7
8
|
|
8
9
|
# Command to start a Strelka application
|
@@ -51,8 +52,7 @@ module Strelka::CLI::Start
|
|
51
52
|
if options.number == 1
|
52
53
|
app.run
|
53
54
|
else
|
54
|
-
options.number
|
55
|
-
Process.waitall
|
55
|
+
Strelka::MultiRunner.new( app, options.number ).run
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# frozen-string-literal: true
|
4
|
+
|
5
|
+
require 'strelka' unless defined?( Strelka )
|
6
|
+
require 'strelka/signal_handling'
|
7
|
+
|
8
|
+
# Load multiple simulatneous Strelka handlers (of a single type) with
|
9
|
+
# proper signal handling.
|
10
|
+
#
|
11
|
+
class Strelka::MultiRunner
|
12
|
+
extend Loggability
|
13
|
+
include Strelka::SignalHandling
|
14
|
+
|
15
|
+
# Loggability API -- set up logging under the 'strelka' log host
|
16
|
+
log_to :strelka
|
17
|
+
|
18
|
+
|
19
|
+
# Signals we understand.
|
20
|
+
QUEUE_SIGS = [ :QUIT, :INT, :TERM, :CHLD ]
|
21
|
+
|
22
|
+
|
23
|
+
### Create a new multirunner instance given a handler +app_class+,
|
24
|
+
### and the +number+ of instances to start.
|
25
|
+
def initialize( app_class, number=2 )
|
26
|
+
@handler_pids = []
|
27
|
+
@running = false
|
28
|
+
@app_class = app_class
|
29
|
+
@number = number
|
30
|
+
|
31
|
+
self.set_up_signal_handling
|
32
|
+
end
|
33
|
+
|
34
|
+
# The child handler pids.
|
35
|
+
attr_reader :handler_pids
|
36
|
+
|
37
|
+
# In this instance currently managing children?
|
38
|
+
attr_reader :running
|
39
|
+
|
40
|
+
# How many handler children to manage.
|
41
|
+
attr_reader :number
|
42
|
+
|
43
|
+
# The class name of the handler.
|
44
|
+
attr_reader :app_class
|
45
|
+
|
46
|
+
|
47
|
+
### Start the child handlers via fork(), block for signals.
|
48
|
+
def run
|
49
|
+
@running = true
|
50
|
+
|
51
|
+
# Set up traps for common signals
|
52
|
+
self.set_signal_traps( *QUEUE_SIGS )
|
53
|
+
|
54
|
+
self.log.debug "Starting multirunner loop..."
|
55
|
+
self.spawn_children
|
56
|
+
self.wait_for_signals while self.running
|
57
|
+
self.log.debug "Ending multirunner."
|
58
|
+
|
59
|
+
# Restore the default signal handlers
|
60
|
+
self.reset_signal_traps( *QUEUE_SIGS )
|
61
|
+
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
#########
|
67
|
+
protected
|
68
|
+
#########
|
69
|
+
|
70
|
+
### Start the handlers using fork().
|
71
|
+
def spawn_children
|
72
|
+
self.number.times do
|
73
|
+
Strelka.call_before_fork_hooks
|
74
|
+
pid = Process.fork do
|
75
|
+
Process.setpgrp
|
76
|
+
self.app_class.run
|
77
|
+
end
|
78
|
+
self.handler_pids << pid
|
79
|
+
Process.setpgid( pid, 0 )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
### Wait on the child associated with the given +pid+, deleting it from the
|
85
|
+
### running tasks Hash if successful.
|
86
|
+
def reap_children( signal )
|
87
|
+
self.handler_pids.dup.each do |pid|
|
88
|
+
self.log.debug " sending %p to pid %p" % [ signal, pid ]
|
89
|
+
Process.kill( signal, pid )
|
90
|
+
pid, status = Process.waitpid2( pid, Process::WUNTRACED )
|
91
|
+
self.log.debug " waitpid2 returned: [ %p, %p ]" % [ pid, status ]
|
92
|
+
self.handler_pids.delete( pid )
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
### Handle signals.
|
98
|
+
def handle_signal( sig )
|
99
|
+
self.log.debug "Handling signal %s in PID %d" % [ sig, Process.pid ]
|
100
|
+
case sig
|
101
|
+
when :INT, :TERM, :QUIT
|
102
|
+
if @running
|
103
|
+
self.log.warn "%s signal: graceful shutdown" % [ sig ]
|
104
|
+
self.reap_children( sig )
|
105
|
+
@running = false
|
106
|
+
else
|
107
|
+
self.ignore_signals
|
108
|
+
self.log.warn "%s signal: forceful shutdown" % [ sig ]
|
109
|
+
self.kill_children( :KILL )
|
110
|
+
exit!( 255 )
|
111
|
+
end
|
112
|
+
|
113
|
+
when :CHLD
|
114
|
+
self.log.info "Got SIGCHLD."
|
115
|
+
# Just need to wake up, nothing else necessary
|
116
|
+
|
117
|
+
else
|
118
|
+
self.log.warn "Unhandled signal %s" % [ sig ]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end # class Strelka::MultiRunner
|
123
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'strelka'
|
5
|
+
|
6
|
+
|
7
|
+
# A module containing signal-handling logic for the 'start' command
|
8
|
+
# line.
|
9
|
+
module Strelka::SignalHandling
|
10
|
+
|
11
|
+
### Wrap a block in signal-handling.
|
12
|
+
def with_signal_handler( *signals )
|
13
|
+
self.set_up_signal_handling
|
14
|
+
self.set_signal_traps( *signals )
|
15
|
+
self.start_signal_handler
|
16
|
+
|
17
|
+
return yield
|
18
|
+
|
19
|
+
ensure
|
20
|
+
self.stop_signal_handler
|
21
|
+
self.reset_signal_traps( *signals )
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
### Set up data structures for signal handling.
|
26
|
+
def set_up_signal_handling
|
27
|
+
# Self-pipe for deferred signal-handling (ala djb:
|
28
|
+
# http://cr.yp.to/docs/selfpipe.html)
|
29
|
+
reader, writer = IO.pipe
|
30
|
+
reader.close_on_exec = true
|
31
|
+
writer.close_on_exec = true
|
32
|
+
@selfpipe = { reader: reader, writer: writer }
|
33
|
+
|
34
|
+
# Set up a global signal queue
|
35
|
+
Thread.main[:signal_queue] = []
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
### The body of the signal handler. Wait for at least one signal to arrive and
|
40
|
+
### handle it, or timeout and return if a +timeout+ integer is provided. This
|
41
|
+
### should be called inside a loop, either in its own thread or in another loop
|
42
|
+
### that doesn't block anywhere else. Returns true if a signal was handled, or
|
43
|
+
### false if a timeout occurred.
|
44
|
+
def wait_for_signals( timeout=nil )
|
45
|
+
|
46
|
+
# Wait on the selfpipe for signals
|
47
|
+
# self.log.debug " waiting for the selfpipe"
|
48
|
+
fds = IO.select( [@selfpipe[:reader]], [], [], timeout )
|
49
|
+
begin
|
50
|
+
rval = @selfpipe[:reader].read_nonblock( 11 )
|
51
|
+
self.log.debug " read from the selfpipe: %p" % [ rval ]
|
52
|
+
rescue Errno::EAGAIN, Errno::EINTR => err
|
53
|
+
# ignore
|
54
|
+
end
|
55
|
+
|
56
|
+
# Look for any signals that arrived and handle them
|
57
|
+
while sig = Thread.main[:signal_queue].shift
|
58
|
+
self.log.debug " got a queued signal: %p" % [ sig ]
|
59
|
+
self.handle_signal( sig )
|
60
|
+
end
|
61
|
+
|
62
|
+
return fds ? true : false
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
### Wake the main thread up through the self-pipe.
|
67
|
+
### Note: since this is a signal-handler method, it needs to be re-entrant.
|
68
|
+
def wake_up
|
69
|
+
@selfpipe[:writer].write_nonblock('.')
|
70
|
+
rescue Errno::EAGAIN
|
71
|
+
# Ignore.
|
72
|
+
rescue Errno::EINTR
|
73
|
+
# Repeated signal. :TODO: Does this need a counter?
|
74
|
+
retry
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
### Set up signal handlers for common signals that will shut down, restart, etc.
|
79
|
+
def set_signal_traps( *signals )
|
80
|
+
self.log.debug "Setting up deferred signal handlers."
|
81
|
+
signals.each do |sig|
|
82
|
+
Signal.trap( sig ) do
|
83
|
+
Thread.main[:signal_queue] << sig
|
84
|
+
self.wake_up
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
### Set all signal handlers to ignore.
|
91
|
+
def ignore_signals( *signals )
|
92
|
+
self.log.debug "Ignoring signals."
|
93
|
+
signals.each do |sig|
|
94
|
+
next if sig == :CHLD
|
95
|
+
Signal.trap( sig, :IGNORE )
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
### Set the signal handlers back to their defaults.
|
101
|
+
def reset_signal_traps( *signals )
|
102
|
+
self.log.debug "Restoring default signal handlers."
|
103
|
+
signals.each do |sig|
|
104
|
+
Signal.trap( sig, :DEFAULT )
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
### Simulate the receipt of the specified +signal+ (probably only useful
|
110
|
+
### in testing).
|
111
|
+
def simulate_signal( signal )
|
112
|
+
Thread.main[:signal_queue] << signal.to_sym
|
113
|
+
self.wake_up
|
114
|
+
end
|
115
|
+
|
116
|
+
end # module Strelka::SignalHandling
|
117
|
+
|
data/spec/strelka_spec.rb
CHANGED
@@ -189,5 +189,19 @@ RSpec.describe Strelka do
|
|
189
189
|
end
|
190
190
|
|
191
191
|
|
192
|
+
it "provides a way to register blocks that should run before a fork" do
|
193
|
+
callback_ran = false
|
194
|
+
Strelka.before_fork { callback_ran = true }
|
195
|
+
Strelka.call_before_fork_hooks
|
196
|
+
|
197
|
+
expect( callback_ran ).to be( true )
|
198
|
+
end
|
199
|
+
|
200
|
+
it "raises an exception if .before_fork is called without a block" do
|
201
|
+
expect {
|
202
|
+
Strelka.before_fork
|
203
|
+
}.to raise_error( LocalJumpError, /no block given/i )
|
204
|
+
end
|
205
|
+
|
192
206
|
end
|
193
207
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strelka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mahlon E. Smith
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
g3nSb5geweeDxf7Phf3qyZgglWB4UGR0aUkzOwj6yFj1ugCU2R7CwNhqgmtdkvYm
|
36
36
|
tuLuv1oCfpuEmRh93FiLFsOLV3auiU+c
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2019-09-
|
38
|
+
date: 2019-09-25 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: configurability
|
@@ -408,6 +408,7 @@ files:
|
|
408
408
|
- lib/strelka/httpresponse/session.rb
|
409
409
|
- lib/strelka/mixins.rb
|
410
410
|
- lib/strelka/multipartparser.rb
|
411
|
+
- lib/strelka/multirunner.rb
|
411
412
|
- lib/strelka/paramvalidator.rb
|
412
413
|
- lib/strelka/plugins.rb
|
413
414
|
- lib/strelka/router.rb
|
@@ -416,6 +417,7 @@ files:
|
|
416
417
|
- lib/strelka/session.rb
|
417
418
|
- lib/strelka/session/db.rb
|
418
419
|
- lib/strelka/session/default.rb
|
420
|
+
- lib/strelka/signal_handling.rb
|
419
421
|
- lib/strelka/testing.rb
|
420
422
|
- lib/strelka/websocketserver.rb
|
421
423
|
- lib/strelka/websocketserver/heartbeat.rb
|
metadata.gz.sig
CHANGED
Binary file
|