serverengine 2.0.5-x86-mingw32 → 2.2.5-x86-mingw32
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 +5 -5
- data/.github/workflows/linux.yml +31 -0
- data/.github/workflows/windows.yml +42 -0
- data/Changelog +41 -0
- data/NOTICE +1 -1
- data/README.md +3 -1
- data/lib/serverengine/multi_process_server.rb +15 -2
- data/lib/serverengine/multi_spawn_server.rb +0 -7
- data/lib/serverengine/process_manager.rb +30 -11
- data/lib/serverengine/server.rb +7 -2
- data/lib/serverengine/signal_thread.rb +1 -1
- data/lib/serverengine/signals.rb +1 -1
- data/lib/serverengine/socket_manager.rb +61 -5
- data/lib/serverengine/socket_manager_unix.rb +35 -12
- data/lib/serverengine/supervisor.rb +6 -2
- data/lib/serverengine/version.rb +1 -1
- data/lib/serverengine/winsock.rb +16 -9
- data/lib/serverengine/worker.rb +5 -2
- data/serverengine.gemspec +0 -1
- data/spec/blocking_flag_spec.rb +16 -4
- data/spec/daemon_spec.rb +4 -0
- data/spec/server_worker_context.rb +6 -1
- data/spec/socket_manager_spec.rb +36 -2
- data/spec/supervisor_spec.rb +8 -2
- data/spec/winsock_spec.rb +18 -0
- metadata +7 -6
- data/.travis.yml +0 -21
- data/appveyor.yml +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e2bf21c7402ac1749bef9cbd1941165a40112cb2875efe533b1a862b425d76f0
|
4
|
+
data.tar.gz: a603493e2e261de52879964a871a240499c9f8131c9f30ee78e3cd836a8484e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b5f80147a36a3089001dd07ddb680506a15f173d526202355451f8a10eb897bb942f74056093a1ecad4191b67c49cf0ce20c1c0998ceeb3729344f6bc0f7953
|
7
|
+
data.tar.gz: fa756ba5653d68631b8b6aaaf279e996fa7e3d1786ddd5f8c87bdae9f89dfd4519a0713aba73708de87afaa833b9187f5a779fa1fd6c907e5a79ef1b4ca40f6e
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: Testing on Ubuntu
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches: [master]
|
5
|
+
pull_request:
|
6
|
+
branches: [master]
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
test:
|
10
|
+
runs-on: ${{ matrix.os }}
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
ruby: [ '3.1', '3.0', '2.7', '2.6' ]
|
15
|
+
os:
|
16
|
+
- ubuntu-latest
|
17
|
+
name: Unit testing with Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- name: Set up Ruby
|
21
|
+
uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: ${{ matrix.ruby }}
|
24
|
+
- name: Install dependencies
|
25
|
+
run: |
|
26
|
+
gem install bundler rake
|
27
|
+
bundle install --jobs 4 --retry 3
|
28
|
+
- name: Run tests
|
29
|
+
env:
|
30
|
+
CI: true
|
31
|
+
run: bundle exec rake spec
|
@@ -0,0 +1,42 @@
|
|
1
|
+
name: Testing on Windows
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches: [master]
|
5
|
+
pull_request:
|
6
|
+
branches: [master]
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
test:
|
10
|
+
runs-on: ${{ matrix.os }}
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
ruby: [ '3.1', '2.7', '2.6' ]
|
15
|
+
os:
|
16
|
+
- windows-latest
|
17
|
+
include:
|
18
|
+
- ruby: '3.0.3'
|
19
|
+
os: windows-latest
|
20
|
+
# On Ruby 3.0, we need to use fiddle 1.0.8 or later to retrieve correct
|
21
|
+
# error code. In addition, we have to specify the path of fiddle by RUBYLIB
|
22
|
+
# because RubyInstaller loads Ruby's bundled fiddle before initializing gem.
|
23
|
+
# See also:
|
24
|
+
# * https://github.com/ruby/fiddle/issues/72
|
25
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
26
|
+
# * https://github.com/oneclick/rubyinstaller2/blob/8225034c22152d8195bc0aabc42a956c79d6c712/lib/ruby_installer/build/dll_directory.rb
|
27
|
+
ruby-lib-opt: RUBYLIB=%RUNNER_TOOL_CACHE%/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/fiddle-1.1.0/lib
|
28
|
+
|
29
|
+
name: Unit testing with Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
|
30
|
+
steps:
|
31
|
+
- uses: actions/checkout@v2
|
32
|
+
- name: Set up Ruby
|
33
|
+
uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
ruby-version: ${{ matrix.ruby }}
|
36
|
+
- name: Add Fiddle 1.1.0
|
37
|
+
if: ${{ matrix.ruby == '3.0.3' }}
|
38
|
+
run: gem install fiddle --version 1.1.0
|
39
|
+
- name: Install dependencies
|
40
|
+
run: ridk exec bundle install --jobs 4 --retry 3
|
41
|
+
- name: Run tests
|
42
|
+
run: bundle exec rake spec ${{ matrix.ruby-lib-opt }}
|
data/Changelog
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
2022-01-13 version 2.2.5:
|
2
|
+
|
3
|
+
* Fix DLL load error on Ruby 3.1 on Windows
|
4
|
+
|
5
|
+
2021-05-24 version 2.2.4:
|
6
|
+
|
7
|
+
* Ensure to get correct Win32 socket error on Ruby 3.0
|
8
|
+
|
9
|
+
2021-02-17 version 2.2.3:
|
10
|
+
|
11
|
+
* Change SocketManager's port assignment strategy on Windows
|
12
|
+
|
13
|
+
2020-11-02 version 2.2.2:
|
14
|
+
|
15
|
+
* Fix incomplete Windows support in spawn based multi worker
|
16
|
+
|
17
|
+
2020-01-24 version 2.2.1:
|
18
|
+
|
19
|
+
* Fix IPv6 dual-stack mode issue for UDP
|
20
|
+
* experimental: Add SERVERENGINE_USE_SOCKET_REUSEPORT envvar to enable SO_REUSEPORT
|
21
|
+
|
22
|
+
2019-11-16 version 2.2.0:
|
23
|
+
|
24
|
+
* Fix IPv6 dual-stack mode issue for TCP
|
25
|
+
|
26
|
+
2019-04-22 version 2.1.1:
|
27
|
+
|
28
|
+
* Fix bug to ignore SIGDUMP_SIGNAL
|
29
|
+
|
30
|
+
2018-11-14 version 2.1.0:
|
31
|
+
|
32
|
+
* Improve socket manager security
|
33
|
+
|
34
|
+
2018-07-09 version 2.0.7:
|
35
|
+
|
36
|
+
* Add disable_sigdump option
|
37
|
+
|
38
|
+
2018-02-09 version 2.0.6:
|
39
|
+
|
40
|
+
* Avoid thread error log in ruby 2.5
|
41
|
+
|
1
42
|
2017-03-01 version 2.0.5:
|
2
43
|
|
3
44
|
* Support SERVERENGINE_SOCKETMANAGER_SOCK_DIR environment variable to change
|
data/NOTICE
CHANGED
data/README.md
CHANGED
@@ -319,7 +319,8 @@ Send `USR2` signal to reload configuration file.
|
|
319
319
|
- **HUP:** immediate restart (available only when `worker_type` is "process")
|
320
320
|
- **USR2:** reload config file and reopen log file
|
321
321
|
- **INT:** detach process for live restarting (available only when `supervisor` and `enable_detach` parameters are true. otherwise graceful shutdown)
|
322
|
-
- **CONT:** dump stacktrace and memory information to /tmp/sigdump-<pid>.log file
|
322
|
+
- **CONT:** dump stacktrace and memory information to /tmp/sigdump-<pid>.log file. This can be
|
323
|
+
disabled by including `disable_sigdump: true` in the configuration.
|
323
324
|
|
324
325
|
Immediate shutdown and restart send SIGQUIT signal to worker processes which kills the processes.
|
325
326
|
Graceful shutdown and restart call `Worker#stop` method and wait for completion of `Worker#run` method.
|
@@ -463,6 +464,7 @@ Available methods are different depending on `worker_type`. ServerEngine support
|
|
463
464
|
- **daemonize** enables daemonize (default: false)
|
464
465
|
- **pid_path** sets the path to pid file (default: don't create pid file)
|
465
466
|
- **supervisor** enables supervisor if it's true (default: false)
|
467
|
+
- **disable_sigdump** disables the handling of the `SIGCONT` signal and dumping of the thread (default: false)
|
466
468
|
- **daemon_process_name** changes process name ($0) of server or supervisor process
|
467
469
|
- **chuser** changes execution user
|
468
470
|
- **chgroup** changes execution group
|
@@ -120,10 +120,19 @@ module ServerEngine
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def send_reload
|
123
|
-
|
123
|
+
return nil unless @pmon
|
124
|
+
if @pmon.command_sender_pipe
|
125
|
+
send_command("RELOAD\n")
|
126
|
+
else
|
127
|
+
@pmon.send_signal(@reload_signal)
|
128
|
+
end
|
124
129
|
nil
|
125
130
|
end
|
126
131
|
|
132
|
+
def send_command(command)
|
133
|
+
@pmon.send_command(command) if @pmon
|
134
|
+
end
|
135
|
+
|
127
136
|
def join
|
128
137
|
@pmon.join if @pmon
|
129
138
|
nil
|
@@ -133,7 +142,11 @@ module ServerEngine
|
|
133
142
|
return false unless @pmon
|
134
143
|
|
135
144
|
if stat = @pmon.try_join
|
136
|
-
|
145
|
+
if @stop
|
146
|
+
@worker.logger.info "Worker #{@wid} finished with #{ServerEngine.format_join_status(stat)}"
|
147
|
+
else
|
148
|
+
@worker.logger.error "Worker #{@wid} finished unexpectedly with #{ServerEngine.format_join_status(stat)}"
|
149
|
+
end
|
137
150
|
if stat.is_a?(Process::Status) && stat.exited? && @unrecoverable_exit_codes.include?(stat.exitstatus)
|
138
151
|
@unrecoverable_exit = true
|
139
152
|
@exitstatus = stat.exitstatus
|
@@ -46,13 +46,6 @@ module ServerEngine
|
|
46
46
|
@pm.command_sender = @command_sender
|
47
47
|
end
|
48
48
|
|
49
|
-
def stop(stop_graceful)
|
50
|
-
if @command_sender == "pipe"
|
51
|
-
@pm.command_sender_pipe.write(stop_graceful ? "GRACEFUL_STOP\n" : "IMMEDIATE_STOP\n")
|
52
|
-
end
|
53
|
-
super
|
54
|
-
end
|
55
|
-
|
56
49
|
def run
|
57
50
|
super
|
58
51
|
ensure
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
require 'fcntl'
|
19
|
+
require 'serverengine/socket_manager'
|
19
20
|
|
20
21
|
module ServerEngine
|
21
22
|
|
@@ -70,7 +71,6 @@ module ServerEngine
|
|
70
71
|
attr_reader :enable_heartbeat, :auto_heartbeat
|
71
72
|
|
72
73
|
attr_accessor :command_sender
|
73
|
-
attr_reader :command_sender_pipe
|
74
74
|
|
75
75
|
CONFIG_PARAMS = {
|
76
76
|
heartbeat_interval: 1,
|
@@ -179,18 +179,21 @@ module ServerEngine
|
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
|
+
command_sender_pipe = nil
|
182
183
|
if @command_sender == "pipe"
|
183
|
-
inpipe,
|
184
|
-
|
185
|
-
|
184
|
+
inpipe, command_sender_pipe = IO.pipe
|
185
|
+
command_sender_pipe.sync = true
|
186
|
+
command_sender_pipe.binmode
|
186
187
|
options[:in] = inpipe
|
187
188
|
end
|
189
|
+
env['SERVERENGINE_SOCKETMANAGER_INTERNAL_TOKEN'] = SocketManager::INTERNAL_TOKEN
|
188
190
|
pid = Process.spawn(env, *args, options)
|
189
191
|
if @command_sender == "pipe"
|
190
192
|
inpipe.close
|
191
193
|
end
|
192
194
|
|
193
195
|
m = Monitor.new(pid, monitor_options)
|
196
|
+
m.command_sender_pipe = command_sender_pipe
|
194
197
|
|
195
198
|
@monitors << m
|
196
199
|
|
@@ -305,9 +308,11 @@ module ServerEngine
|
|
305
308
|
@graceful_kill_start_time = nil
|
306
309
|
@immediate_kill_start_time = nil
|
307
310
|
@kill_count = 0
|
311
|
+
|
312
|
+
@command_sender_pipe = nil
|
308
313
|
end
|
309
314
|
|
310
|
-
attr_accessor :last_heartbeat_time
|
315
|
+
attr_accessor :last_heartbeat_time, :command_sender_pipe
|
311
316
|
attr_reader :pid
|
312
317
|
|
313
318
|
def heartbeat_delay
|
@@ -327,6 +332,10 @@ module ServerEngine
|
|
327
332
|
end
|
328
333
|
end
|
329
334
|
|
335
|
+
def send_command(command)
|
336
|
+
@command_sender_pipe.write(command) if @command_sender_pipe
|
337
|
+
end
|
338
|
+
|
330
339
|
def try_join
|
331
340
|
pid = @pid
|
332
341
|
return true unless pid
|
@@ -364,15 +373,25 @@ module ServerEngine
|
|
364
373
|
end
|
365
374
|
|
366
375
|
def start_graceful_stop!
|
367
|
-
|
368
|
-
|
369
|
-
|
376
|
+
if ServerEngine.windows?
|
377
|
+
# heartbeat isn't supported on Windows
|
378
|
+
send_command("GRACEFUL_STOP\n")
|
379
|
+
else
|
380
|
+
now = Time.now
|
381
|
+
@next_kill_time ||= now
|
382
|
+
@graceful_kill_start_time ||= now
|
383
|
+
end
|
370
384
|
end
|
371
385
|
|
372
386
|
def start_immediate_stop!
|
373
|
-
|
374
|
-
|
375
|
-
|
387
|
+
if ServerEngine.windows?
|
388
|
+
# heartbeat isn't supported on Windows
|
389
|
+
system("taskkill /f /pid #{@pid}")
|
390
|
+
else
|
391
|
+
now = Time.now
|
392
|
+
@next_kill_time ||= now
|
393
|
+
@immediate_kill_start_time ||= now
|
394
|
+
end
|
376
395
|
end
|
377
396
|
|
378
397
|
def tick(now=Time.now)
|
data/lib/serverengine/server.rb
CHANGED
@@ -70,6 +70,11 @@ module ServerEngine
|
|
70
70
|
nil
|
71
71
|
end
|
72
72
|
|
73
|
+
def dump
|
74
|
+
Sigdump.dump unless config[:disable_sigdump]
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
73
78
|
def install_signal_handlers
|
74
79
|
s = self
|
75
80
|
if @command_pipe
|
@@ -89,7 +94,7 @@ module ServerEngine
|
|
89
94
|
when "DETACH"
|
90
95
|
s.detach(true)
|
91
96
|
when "DUMP"
|
92
|
-
|
97
|
+
s.dump
|
93
98
|
end
|
94
99
|
end
|
95
100
|
end
|
@@ -104,7 +109,7 @@ module ServerEngine
|
|
104
109
|
st.trap(@config[:signal_graceful_restart] || Signals::GRACEFUL_RESTART) { s.restart(true) }
|
105
110
|
st.trap(@config[:signal_immediate_restart] || Signals::IMMEDIATE_RESTART) { s.restart(false) }
|
106
111
|
st.trap(@config[:signal_reload] || Signals::RELOAD) { s.reload }
|
107
|
-
st.trap(@config[:signal_dump] || Signals::DUMP) {
|
112
|
+
st.trap(@config[:signal_dump] || Signals::DUMP) { s.dump }
|
108
113
|
end
|
109
114
|
end
|
110
115
|
end
|
data/lib/serverengine/signals.rb
CHANGED
@@ -18,9 +18,18 @@
|
|
18
18
|
require 'socket'
|
19
19
|
require 'ipaddr'
|
20
20
|
require 'time'
|
21
|
+
require 'securerandom'
|
22
|
+
require 'json'
|
23
|
+
require 'base64'
|
21
24
|
|
22
25
|
module ServerEngine
|
23
26
|
module SocketManager
|
27
|
+
# This token is used for communication between peers. If token is mismatched, messages will be discarded
|
28
|
+
INTERNAL_TOKEN = if ENV.has_key?('SERVERENGINE_SOCKETMANAGER_INTERNAL_TOKEN')
|
29
|
+
ENV['SERVERENGINE_SOCKETMANAGER_INTERNAL_TOKEN']
|
30
|
+
else
|
31
|
+
SecureRandom.hex
|
32
|
+
end
|
24
33
|
|
25
34
|
class Client
|
26
35
|
def initialize(path)
|
@@ -63,7 +72,10 @@ module ServerEngine
|
|
63
72
|
class Server
|
64
73
|
def self.generate_path
|
65
74
|
if ServerEngine.windows?
|
66
|
-
|
75
|
+
port = ENV['SERVERENGINE_SOCKETMANAGER_PORT']
|
76
|
+
return port.to_i if port
|
77
|
+
|
78
|
+
for port in get_dynamic_port_range
|
67
79
|
if `netstat -na | findstr "#{port}"`.length == 0
|
68
80
|
return port
|
69
81
|
end
|
@@ -138,7 +150,10 @@ module ServerEngine
|
|
138
150
|
|
139
151
|
def process_peer(peer)
|
140
152
|
while true
|
141
|
-
|
153
|
+
res = SocketManager.recv_peer(peer)
|
154
|
+
return if res.nil?
|
155
|
+
|
156
|
+
pid, method, bind, port = *res
|
142
157
|
begin
|
143
158
|
send_socket(peer, pid, method, bind, port)
|
144
159
|
rescue => e
|
@@ -148,18 +163,59 @@ module ServerEngine
|
|
148
163
|
ensure
|
149
164
|
peer.close
|
150
165
|
end
|
166
|
+
|
167
|
+
if ServerEngine.windows?
|
168
|
+
def self.valid_dynamic_port_range(start_port, end_port)
|
169
|
+
return false if start_port < 1025 or start_port > 65535
|
170
|
+
return false if end_port < 1025 or end_port > 65535
|
171
|
+
return false if start_port > end_port
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.get_dynamic_port_range
|
176
|
+
numbers = []
|
177
|
+
# Example output of netsh (actual output is localized):
|
178
|
+
#
|
179
|
+
# Protocol tcp Dynamic Port Range
|
180
|
+
# ---------------------------------
|
181
|
+
# Start Port : 49152
|
182
|
+
# Number of Ports : 16384
|
183
|
+
#
|
184
|
+
str = `netsh int ipv4 show dynamicport tcp`.force_encoding("ASCII-8BIT")
|
185
|
+
str.each_line { |line| numbers << $1.to_i if line.match(/.*: (\d+)/) }
|
186
|
+
|
187
|
+
start_port, n_ports = numbers[0], numbers[1]
|
188
|
+
end_port = start_port + n_ports - 1
|
189
|
+
|
190
|
+
if valid_dynamic_port_range(start_port, end_port)
|
191
|
+
return start_port..end_port
|
192
|
+
else
|
193
|
+
# The default dynamic port range is 49152 - 65535 as of Windows Vista
|
194
|
+
# and Windows Server 2008.
|
195
|
+
# https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/default-dynamic-port-range-tcpip-chang
|
196
|
+
return 49152..65535
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
151
200
|
end
|
152
201
|
|
153
202
|
def self.send_peer(peer, obj)
|
154
|
-
data = Marshal.dump(obj)
|
203
|
+
data = [SocketManager::INTERNAL_TOKEN, Base64.strict_encode64(Marshal.dump(obj))]
|
204
|
+
data = JSON.generate(data)
|
155
205
|
peer.write [data.bytesize].pack('N')
|
156
206
|
peer.write data
|
157
207
|
end
|
158
208
|
|
159
209
|
def self.recv_peer(peer)
|
160
|
-
|
210
|
+
res = peer.read(4)
|
211
|
+
return nil if res.nil?
|
212
|
+
|
213
|
+
len = res.unpack('N').first
|
161
214
|
data = peer.read(len)
|
162
|
-
|
215
|
+
data = JSON.parse(data)
|
216
|
+
return nil if SocketManager::INTERNAL_TOKEN != data.first
|
217
|
+
|
218
|
+
Marshal.load(Base64.strict_decode64(data.last))
|
163
219
|
end
|
164
220
|
|
165
221
|
if ServerEngine.windows?
|
@@ -50,19 +50,30 @@ module ServerEngine
|
|
50
50
|
private
|
51
51
|
|
52
52
|
def listen_tcp_new(bind_ip, port)
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
if ENV['SERVERENGINE_USE_SOCKET_REUSEPORT'] == '1'
|
54
|
+
# Based on Addrinfo#listen
|
55
|
+
tsock = Socket.new(bind_ip.ipv6? ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
|
56
|
+
tsock.ipv6only! if bind_ip.ipv6?
|
57
|
+
tsock.setsockopt(:SOCKET, :REUSEPORT, true)
|
58
|
+
tsock.setsockopt(:SOCKET, :REUSEADDR, true)
|
59
|
+
tsock.bind(Addrinfo.tcp(bind_ip.to_s, port))
|
60
|
+
tsock.listen(::Socket::SOMAXCONN)
|
61
|
+
tsock.autoclose = false
|
62
|
+
TCPServer.for_fd(tsock.fileno)
|
63
|
+
else
|
64
|
+
# TCPServer.new doesn't set IPV6_V6ONLY flag, so use Addrinfo class instead.
|
65
|
+
# TODO: make backlog configurable if necessary
|
66
|
+
tsock = Addrinfo.tcp(bind_ip.to_s, port).listen(::Socket::SOMAXCONN)
|
67
|
+
tsock.autoclose = false
|
68
|
+
TCPServer.for_fd(tsock.fileno)
|
69
|
+
end
|
56
70
|
end
|
57
71
|
|
58
72
|
def listen_udp_new(bind_ip, port)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
sock.bind(bind_ip.to_s, port)
|
65
|
-
return sock
|
73
|
+
# UDPSocket.new doesn't set IPV6_V6ONLY flag, so use Addrinfo class instead.
|
74
|
+
usock = Addrinfo.udp(bind_ip.to_s, port).bind
|
75
|
+
usock.autoclose = false
|
76
|
+
UDPSocket.for_fd(usock.fileno)
|
66
77
|
end
|
67
78
|
|
68
79
|
def start_server(path)
|
@@ -70,7 +81,12 @@ module ServerEngine
|
|
70
81
|
# when client changed working directory
|
71
82
|
path = File.expand_path(path)
|
72
83
|
|
73
|
-
|
84
|
+
begin
|
85
|
+
old_umask = File.umask(0077) # Protect unix socket from other users
|
86
|
+
@server = UNIXServer.new(path)
|
87
|
+
ensure
|
88
|
+
File.umask(old_umask)
|
89
|
+
end
|
74
90
|
|
75
91
|
@thread = Thread.new do
|
76
92
|
begin
|
@@ -96,7 +112,14 @@ module ServerEngine
|
|
96
112
|
end
|
97
113
|
|
98
114
|
def send_socket(peer, pid, method, bind, port)
|
99
|
-
sock =
|
115
|
+
sock = case method
|
116
|
+
when :listen_tcp
|
117
|
+
listen_tcp(bind, port)
|
118
|
+
when :listen_udp
|
119
|
+
listen_udp(bind, port)
|
120
|
+
else
|
121
|
+
raise ArgumentError, "Unknown method: #{method.inspect}"
|
122
|
+
end
|
100
123
|
|
101
124
|
SocketManager.send_peer(peer, nil)
|
102
125
|
|
@@ -148,6 +148,10 @@ module ServerEngine
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
+
def dump
|
152
|
+
_dump
|
153
|
+
end
|
154
|
+
|
151
155
|
def install_signal_handlers
|
152
156
|
s = self
|
153
157
|
if @command_pipe
|
@@ -167,7 +171,7 @@ module ServerEngine
|
|
167
171
|
when "DETACH"
|
168
172
|
s.detach(true)
|
169
173
|
when "DUMP"
|
170
|
-
|
174
|
+
s.dump
|
171
175
|
end
|
172
176
|
end
|
173
177
|
end
|
@@ -179,7 +183,7 @@ module ServerEngine
|
|
179
183
|
st.trap(Signals::IMMEDIATE_RESTART) { s.restart(false) }
|
180
184
|
st.trap(Signals::RELOAD) { s.reload }
|
181
185
|
st.trap(Signals::DETACH) { s.detach(true) }
|
182
|
-
st.trap(Signals::DUMP) {
|
186
|
+
st.trap(Signals::DUMP) { s.dump }
|
183
187
|
end
|
184
188
|
end
|
185
189
|
end
|
data/lib/serverengine/version.rb
CHANGED
data/lib/serverengine/winsock.rb
CHANGED
@@ -21,6 +21,7 @@ module ServerEngine
|
|
21
21
|
require 'fiddle/import'
|
22
22
|
require 'fiddle/types'
|
23
23
|
require 'socket'
|
24
|
+
require 'rbconfig'
|
24
25
|
|
25
26
|
extend Fiddle::Importer
|
26
27
|
|
@@ -78,6 +79,19 @@ module ServerEngine
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
82
|
+
def self.last_error
|
83
|
+
# On Ruby 3.0 calling WSAGetLastError here can't retrieve correct error
|
84
|
+
# code because Ruby's internal code resets it.
|
85
|
+
# See also:
|
86
|
+
# * https://github.com/ruby/fiddle/issues/72
|
87
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
88
|
+
if Fiddle.respond_to?(:win32_last_socket_error)
|
89
|
+
Fiddle.win32_last_socket_error || 0
|
90
|
+
else
|
91
|
+
self.WSAGetLastError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
81
95
|
INVALID_SOCKET = -1
|
82
96
|
end
|
83
97
|
|
@@ -88,18 +102,11 @@ module ServerEngine
|
|
88
102
|
extern "int GetModuleFileNameA(int, char *, int)"
|
89
103
|
extern "int CloseHandle(int)"
|
90
104
|
|
91
|
-
|
92
|
-
GetModuleFileNameA(0, ruby_bin_path_buf, ruby_bin_path_buf.size)
|
93
|
-
|
94
|
-
ruby_bin_path = ruby_bin_path_buf.to_s.gsub(/\\/, '/')
|
95
|
-
ruby_dll_paths = File.dirname(ruby_bin_path) + '/*msvcr*ruby*.dll'
|
96
|
-
ruby_dll_path = Dir.glob(ruby_dll_paths).first
|
97
|
-
dlload ruby_dll_path
|
98
|
-
|
105
|
+
dlload RbConfig::CONFIG['LIBRUBY_SO']
|
99
106
|
extern "int rb_w32_map_errno(int)"
|
100
107
|
|
101
108
|
def self.raise_last_error(name)
|
102
|
-
errno = rb_w32_map_errno(WinSock.
|
109
|
+
errno = rb_w32_map_errno(WinSock.last_error)
|
103
110
|
raise SystemCallError.new(name, errno)
|
104
111
|
end
|
105
112
|
|
data/lib/serverengine/worker.rb
CHANGED
@@ -54,6 +54,10 @@ module ServerEngine
|
|
54
54
|
def after_start
|
55
55
|
end
|
56
56
|
|
57
|
+
def dump
|
58
|
+
Sigdump.dump unless config[:disable_sigdump]
|
59
|
+
end
|
60
|
+
|
57
61
|
def install_signal_handlers
|
58
62
|
w = self
|
59
63
|
SignalThread.new do |st|
|
@@ -69,7 +73,7 @@ module ServerEngine
|
|
69
73
|
}
|
70
74
|
st.trap(Signals::DETACH) { w.stop }
|
71
75
|
|
72
|
-
st.trap(Signals::DUMP) {
|
76
|
+
st.trap(Signals::DUMP) { w.dump }
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
@@ -77,5 +81,4 @@ module ServerEngine
|
|
77
81
|
run
|
78
82
|
end
|
79
83
|
end
|
80
|
-
|
81
84
|
end
|
data/serverengine.gemspec
CHANGED
@@ -15,7 +15,6 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
17
|
gem.require_paths = ["lib"]
|
18
|
-
gem.has_rdoc = false
|
19
18
|
|
20
19
|
gem.required_ruby_version = ">= 2.1.0"
|
21
20
|
|
data/spec/blocking_flag_spec.rb
CHANGED
@@ -20,10 +20,16 @@ describe ServerEngine::BlockingFlag do
|
|
20
20
|
it 'wait_for_set timeout' do
|
21
21
|
start = Time.now
|
22
22
|
|
23
|
-
subject.wait_for_set(0.
|
23
|
+
subject.wait_for_set(0.1)
|
24
24
|
elapsed = Time.now - start
|
25
25
|
|
26
|
-
|
26
|
+
if ServerEngine.windows? && ENV['CI'] == 'True'
|
27
|
+
# timer seems low accuracy on Windows CI container, often a bit shorter
|
28
|
+
# than expected
|
29
|
+
elapsed.should >= 0.1 * 0.95
|
30
|
+
else
|
31
|
+
elapsed.should >= 0.1
|
32
|
+
end
|
27
33
|
end
|
28
34
|
|
29
35
|
it 'wait_for_reset timeout' do
|
@@ -31,10 +37,16 @@ describe ServerEngine::BlockingFlag do
|
|
31
37
|
|
32
38
|
start = Time.now
|
33
39
|
|
34
|
-
subject.wait_for_reset(0.
|
40
|
+
subject.wait_for_reset(0.1)
|
35
41
|
elapsed = Time.now - start
|
36
42
|
|
37
|
-
|
43
|
+
if ServerEngine.windows? && ENV['CI'] == 'True'
|
44
|
+
# timer seems low accuracy on Windows CI container, often a bit shorter
|
45
|
+
# than expected
|
46
|
+
elapsed.should >= 0.1 * 0.95
|
47
|
+
else
|
48
|
+
elapsed.should >= 0.1
|
49
|
+
end
|
38
50
|
end
|
39
51
|
|
40
52
|
it 'wait' do
|
data/spec/daemon_spec.rb
CHANGED
@@ -44,6 +44,10 @@ describe ServerEngine::Daemon do
|
|
44
44
|
wait_for_stop
|
45
45
|
test_state(:server_restart_immediate).should == 1
|
46
46
|
|
47
|
+
dm.dump
|
48
|
+
wait_for_stop
|
49
|
+
test_state(:server_dump).should == 1
|
50
|
+
|
47
51
|
dm.stop(false)
|
48
52
|
wait_for_stop
|
49
53
|
test_state(:server_stop_immediate).should == 1
|
@@ -146,6 +146,11 @@ module TestServer
|
|
146
146
|
incr_test_state :server_detach
|
147
147
|
super
|
148
148
|
end
|
149
|
+
|
150
|
+
def dump
|
151
|
+
incr_test_state :server_dump
|
152
|
+
super
|
153
|
+
end
|
149
154
|
end
|
150
155
|
|
151
156
|
module TestWorker
|
@@ -249,7 +254,7 @@ shared_context 'test server and worker' do
|
|
249
254
|
before { reset_test_state }
|
250
255
|
|
251
256
|
def wait_for_fork
|
252
|
-
sleep
|
257
|
+
sleep 1.5
|
253
258
|
end
|
254
259
|
|
255
260
|
def wait_for_stop
|
data/spec/socket_manager_spec.rb
CHANGED
@@ -19,7 +19,21 @@ describe ServerEngine::SocketManager do
|
|
19
19
|
File.unlink(server_path) if server_path.is_a?(String) && File.exist?(server_path)
|
20
20
|
end
|
21
21
|
|
22
|
-
if
|
22
|
+
if ServerEngine.windows?
|
23
|
+
context 'Server.generate_path' do
|
24
|
+
it 'returns socket path as port number' do
|
25
|
+
path = SocketManager::Server.generate_path
|
26
|
+
expect(path).to be_between(49152, 65535)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can be changed via environment variable' do
|
30
|
+
ENV['SERVERENGINE_SOCKETMANAGER_PORT'] = '54321'
|
31
|
+
path = SocketManager::Server.generate_path
|
32
|
+
expect(path).to be 54321
|
33
|
+
ENV.delete('SERVERENGINE_SOCKETMANAGER_PORT')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
23
37
|
context 'Server.generate_path' do
|
24
38
|
it 'returns socket path under /tmp' do
|
25
39
|
path = SocketManager::Server.generate_path
|
@@ -155,7 +169,27 @@ describe ServerEngine::SocketManager do
|
|
155
169
|
test_state(:is_udp_socket).should == 1
|
156
170
|
test_state(:udp_data_sent).should == 1
|
157
171
|
end
|
158
|
-
end if (TCPServer.open("::1",0) rescue nil)
|
172
|
+
end if (TCPServer.open("::1", 0) rescue nil)
|
173
|
+
|
174
|
+
unless ServerEngine.windows?
|
175
|
+
context 'using ipv4/ipv6' do
|
176
|
+
it 'can bind ipv4/ipv6 together' do
|
177
|
+
server = SocketManager::Server.open(server_path)
|
178
|
+
client = ServerEngine::SocketManager::Client.new(server_path)
|
179
|
+
|
180
|
+
tcp_v4 = client.listen_tcp('0.0.0.0', test_port)
|
181
|
+
udp_v4 = client.listen_udp('0.0.0.0', test_port)
|
182
|
+
tcp_v6 = client.listen_tcp('::', test_port)
|
183
|
+
udp_v6 = client.listen_udp('::', test_port)
|
184
|
+
|
185
|
+
tcp_v4.close
|
186
|
+
udp_v4.close
|
187
|
+
tcp_v6.close
|
188
|
+
udp_v6.close
|
189
|
+
server.close
|
190
|
+
end
|
191
|
+
end if (TCPServer.open("::", 0) rescue nil)
|
192
|
+
end
|
159
193
|
end
|
160
194
|
|
161
195
|
if ServerEngine.windows?
|
data/spec/supervisor_spec.rb
CHANGED
@@ -191,13 +191,19 @@ describe ServerEngine::Supervisor do
|
|
191
191
|
sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
|
192
192
|
|
193
193
|
begin
|
194
|
-
sleep 2.
|
194
|
+
sleep 2.5
|
195
195
|
ensure
|
196
196
|
sv.stop(true)
|
197
197
|
t.join
|
198
198
|
end
|
199
199
|
|
200
|
-
|
200
|
+
if ServerEngine.windows?
|
201
|
+
# Because launching a process on Windows is high cost,
|
202
|
+
# it doesn't often reach to 3.
|
203
|
+
test_state(:worker_run).should <= 3
|
204
|
+
else
|
205
|
+
test_state(:worker_run).should == 3
|
206
|
+
end
|
201
207
|
end
|
202
208
|
end
|
203
209
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'windows/error' if ServerEngine.windows?
|
2
|
+
|
3
|
+
describe ServerEngine::WinSock do
|
4
|
+
# On Ruby 3.0, you need to use fiddle 1.0.8 or later to retrieve a correct
|
5
|
+
# error code. In addition, you need to specify the path of fiddle by RUBYLIB
|
6
|
+
# or `ruby -I` when you use RubyInstaller because it loads Ruby's bundled
|
7
|
+
# fiddle before initializing gem.
|
8
|
+
# See also:
|
9
|
+
# * https://github.com/ruby/fiddle/issues/72
|
10
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
11
|
+
# * https://github.com/oneclick/rubyinstaller2/blob/8225034c22152d8195bc0aabc42a956c79d6c712/lib/ruby_installer/build/dll_directory.rb
|
12
|
+
context 'last_error' do
|
13
|
+
it 'bind error' do
|
14
|
+
expect(WinSock.bind(0, nil, 0)).to be -1
|
15
|
+
expect(WinSock.last_error).to be Windows::Error::WSAENOTSOCK
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end if ServerEngine.windows?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serverengine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.5
|
5
5
|
platform: x86-mingw32
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sigdump
|
@@ -101,16 +101,16 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- ".github/workflows/linux.yml"
|
105
|
+
- ".github/workflows/windows.yml"
|
104
106
|
- ".gitignore"
|
105
107
|
- ".rspec"
|
106
|
-
- ".travis.yml"
|
107
108
|
- Changelog
|
108
109
|
- Gemfile
|
109
110
|
- LICENSE
|
110
111
|
- NOTICE
|
111
112
|
- README.md
|
112
113
|
- Rakefile
|
113
|
-
- appveyor.yml
|
114
114
|
- examples/server.rb
|
115
115
|
- examples/spawn_worker_script.rb
|
116
116
|
- lib/serverengine.rb
|
@@ -148,6 +148,7 @@ files:
|
|
148
148
|
- spec/socket_manager_spec.rb
|
149
149
|
- spec/spec_helper.rb
|
150
150
|
- spec/supervisor_spec.rb
|
151
|
+
- spec/winsock_spec.rb
|
151
152
|
homepage: https://github.com/fluent/serverengine
|
152
153
|
licenses:
|
153
154
|
- Apache 2.0
|
@@ -167,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
168
|
- !ruby/object:Gem::Version
|
168
169
|
version: '0'
|
169
170
|
requirements: []
|
170
|
-
|
171
|
-
rubygems_version: 2.6.8
|
171
|
+
rubygems_version: 3.2.5
|
172
172
|
signing_key:
|
173
173
|
specification_version: 4
|
174
174
|
summary: ServerEngine - multiprocess server framework
|
@@ -183,3 +183,4 @@ test_files:
|
|
183
183
|
- spec/socket_manager_spec.rb
|
184
184
|
- spec/spec_helper.rb
|
185
185
|
- spec/supervisor_spec.rb
|
186
|
+
- spec/winsock_spec.rb
|
data/.travis.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
|
3
|
-
rvm:
|
4
|
-
- 2.1.10
|
5
|
-
- 2.2.6
|
6
|
-
- 2.3.3
|
7
|
-
- 2.4.0
|
8
|
-
- ruby-head
|
9
|
-
|
10
|
-
branches:
|
11
|
-
only:
|
12
|
-
- master
|
13
|
-
|
14
|
-
script: bundle exec rake spec
|
15
|
-
|
16
|
-
before_install:
|
17
|
-
- gem update bundler
|
18
|
-
|
19
|
-
matrix:
|
20
|
-
allow_failures:
|
21
|
-
- rvm: ruby-head
|
data/appveyor.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
---
|
2
|
-
install:
|
3
|
-
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
4
|
-
- ruby --version
|
5
|
-
- gem --version
|
6
|
-
- bundle install
|
7
|
-
build: off
|
8
|
-
test_script:
|
9
|
-
- bundle exec rake -rdevkit
|
10
|
-
|
11
|
-
environment:
|
12
|
-
matrix:
|
13
|
-
- ruby_version: "23-x64"
|
14
|
-
devkit: C:\Ruby23-x64\DevKit
|
15
|
-
- ruby_version: "23"
|
16
|
-
devkit: C:\Ruby23\DevKit
|
17
|
-
- ruby_version: "22-x64"
|
18
|
-
devkit: C:\Ruby23-x64\DevKit
|
19
|
-
- ruby_version: "22"
|
20
|
-
devkit: C:\Ruby23\DevKit
|
21
|
-
- ruby_version: "21-x64"
|
22
|
-
devkit: C:\Ruby23-x64\DevKit
|
23
|
-
- ruby_version: "21"
|
24
|
-
devkit: C:\Ruby23\DevKit
|