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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 26439e07fed354d0e3905cbff551f9aa50a5d674
4
- data.tar.gz: 35207be927ebee981432fd567ab2949c22ba3ce1
2
+ SHA256:
3
+ metadata.gz: e2bf21c7402ac1749bef9cbd1941165a40112cb2875efe533b1a862b425d76f0
4
+ data.tar.gz: a603493e2e261de52879964a871a240499c9f8131c9f30ee78e3cd836a8484e5
5
5
  SHA512:
6
- metadata.gz: d309cd49d40c046b489f12d231955b37c3fea54b49aadd81a639b50fe27135ce9352a310e1e3bbdb8967446f3d9cde9176079a467808f68ba1aec20951b3e293
7
- data.tar.gz: 30cea237e564f012160ee11d5de801812318db0955e48a32c355056c5b207fb666d084e8973fb9cd538744266c27790546fc3f2e0bd63a855ca1c1bb31f816f4
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
@@ -1,3 +1,3 @@
1
1
  ServerEngine
2
- https://github.com/frsyuki/serverengine
2
+ https://github.com/treasure-data/serverengine
3
3
  Copyright (C) 2012-2013 Sadayuki Furuhashi
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
- @pmon.send_signal(@reload_signal) if @pmon
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
- @worker.logger.info "Worker #{@wid} finished#{@stop ? '' : ' unexpectedly'} with #{ServerEngine.format_join_status(stat)}"
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, @command_sender_pipe = IO.pipe
184
- @command_sender_pipe.sync = true
185
- @command_sender_pipe.binmode
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
- now = Time.now
368
- @next_kill_time ||= now
369
- @graceful_kill_start_time ||= now
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
- now = Time.now
374
- @next_kill_time ||= now
375
- @immediate_kill_start_time ||= now
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)
@@ -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
- Sigdump.dump
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) { Sigdump.dump }
112
+ st.trap(@config[:signal_dump] || Signals::DUMP) { s.dump }
108
113
  end
109
114
  end
110
115
  end
@@ -67,7 +67,7 @@ module ServerEngine
67
67
 
68
68
  def signal_handler_main(sig)
69
69
  # here always creates new thread to avoid
70
- # complicated race conditin in signal handlers
70
+ # complicated race condition in signal handlers
71
71
  Thread.new do
72
72
  begin
73
73
  enqueue(sig)
@@ -26,6 +26,6 @@ module ServerEngine
26
26
  IMMEDIATE_RESTART = :HUP
27
27
  RELOAD = :USR2
28
28
  DETACH = :INT
29
- DUMP = :CONT
29
+ DUMP = ENV.has_key?('SIGDUMP_SIGNAL') ? ENV['SIGDUMP_SIGNAL'].to_sym : :CONT
30
30
  end
31
31
  end
@@ -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
- for port in 10000..65535
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
- pid, method, bind, port = *SocketManager.recv_peer(peer)
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
- len = peer.read(4).unpack('N').first
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
- Marshal.load(data)
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
- sock = TCPServer.new(bind_ip.to_s, port)
54
- sock.listen(Socket::SOMAXCONN) # TODO make backlog configurable if necessary
55
- return sock
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
- if bind_ip.ipv6?
60
- sock = UDPSocket.new(Socket::AF_INET6)
61
- else
62
- sock = UDPSocket.new(Socket::AF_INET)
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
- @server = UNIXServer.new(path)
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 = send(method, bind, port) # calls listen_tcp or listen_udp
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
- Sigdump.dump
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) { Sigdump.dump }
186
+ st.trap(Signals::DUMP) { s.dump }
183
187
  end
184
188
  end
185
189
  end
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "2.0.5"
2
+ VERSION = "2.2.5"
3
3
  end
@@ -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
- ruby_bin_path_buf = Fiddle::Pointer.malloc(1000)
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.WSAGetLastError)
109
+ errno = rb_w32_map_errno(WinSock.last_error)
103
110
  raise SystemCallError.new(name, errno)
104
111
  end
105
112
 
@@ -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) { Sigdump.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
 
@@ -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.01)
23
+ subject.wait_for_set(0.1)
24
24
  elapsed = Time.now - start
25
25
 
26
- elapsed.should >= 0.01
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.01)
40
+ subject.wait_for_reset(0.1)
35
41
  elapsed = Time.now - start
36
42
 
37
- elapsed.should >= 0.01
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 0.8
257
+ sleep 1.5
253
258
  end
254
259
 
255
260
  def wait_for_stop
@@ -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 !ServerEngine.windows?
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?
@@ -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.2
194
+ sleep 2.5
195
195
  ensure
196
196
  sv.stop(true)
197
197
  t.join
198
198
  end
199
199
 
200
- test_state(:worker_run).should == 3
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.0.5
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: 2017-03-01 00:00:00.000000000 Z
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
- rubyforge_project:
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