serverengine 2.0.3-x64-mingw32 → 2.2.4-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9f7aa684e8163b98d7fa8bf32f515df6445a18db
4
- data.tar.gz: 97e2987789826974472c8b7d2dfc26bb2a637d75
2
+ SHA256:
3
+ metadata.gz: 40f46b384c30ca1948b502b420f3b53d92c296936f5c91bd9430f781fac08939
4
+ data.tar.gz: 3932f03c87996232a7a58645a8c77ffb2f57302f49b62c1cd6bfc944ab3e9faa
5
5
  SHA512:
6
- metadata.gz: 5c88f7984dff231d1265d21950d87f3e04f541f4e8b1138996f97f2202936a8d31a30d3e8a14ca88108449d2d0cf362da9b08fd89b6672542d9715ba9be415a9
7
- data.tar.gz: e9808147809ce5f84e274f759bcef7c5d5105ea3e7d37484b8b64fe32a402c5c7bab500d254189157f307865f4a2596df0bc84037e0b95e11cb188241d97eed3
6
+ metadata.gz: 34ddd5a05ef2a9f1ab41642671b57ebb76d29f9363051173d3dfc82ea3c6d735493821429710e290191effb6e553d932b2ce2fa6c9a0d0d6f407362d52c30b25
7
+ data.tar.gz: 2982698d8a5762b8305f20d92f4e0d8766db9ee735a33a6a3b01c192414fd9a5f9492176e26afab848d9a986d35827cf4a84c14850f7094a69323b860d32bf60
@@ -0,0 +1,26 @@
1
+ name: Testing on Ubuntu
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [ '3.0', '2.7', '2.6', '2.5' ]
12
+ os:
13
+ - ubuntu-latest
14
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ - name: unit testing
21
+ env:
22
+ CI: true
23
+ run: |
24
+ gem install bundler rake
25
+ bundle install --jobs 4 --retry 3
26
+ bundle exec rake spec
data/Changelog CHANGED
@@ -1,3 +1,49 @@
1
+ 2021-05-24 version 2.2.4:
2
+
3
+ * Ensure to get correct Win32 socket error on Ruby 3.0
4
+
5
+ 2021-02-17 version 2.2.3:
6
+
7
+ * Change SocketManager's port assignment strategy on Windows
8
+
9
+ 2020-11-02 version 2.2.2:
10
+
11
+ * Fix incomplete Windows support in spawn based multi worker
12
+
13
+ 2020-01-24 version 2.2.1:
14
+
15
+ * Fix IPv6 dual-stack mode issue for UDP
16
+ * experimental: Add SERVERENGINE_USE_SOCKET_REUSEPORT envvar to enable SO_REUSEPORT
17
+
18
+ 2019-11-16 version 2.2.0:
19
+
20
+ * Fix IPv6 dual-stack mode issue for TCP
21
+
22
+ 2019-04-22 version 2.1.1:
23
+
24
+ * Fix bug to ignore SIGDUMP_SIGNAL
25
+
26
+ 2018-11-14 version 2.1.0:
27
+
28
+ * Improve socket manager security
29
+
30
+ 2018-07-09 version 2.0.7:
31
+
32
+ * Add disable_sigdump option
33
+
34
+ 2018-02-09 version 2.0.6:
35
+
36
+ * Avoid thread error log in ruby 2.5
37
+
38
+ 2017-03-01 version 2.0.5:
39
+
40
+ * Support SERVERENGINE_SOCKETMANAGER_SOCK_DIR environment variable to change
41
+ base directory of SocketManager's socket path
42
+
43
+ 2016-11-24 version 2.0.4:
44
+
45
+ * Fix bug to crash Ruby VM when SocketManager fails to bind IPv6 addresses on Windows
46
+
1
47
  2016-11-22 version 2.0.3:
2
48
 
3
49
  * Fix bug about IPv6 handling on Windows
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
data/appveyor.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  ---
2
+ image: Visual Studio 2019
3
+
2
4
  install:
3
5
  - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
6
  - ruby --version
@@ -6,19 +8,28 @@ install:
6
8
  - bundle install
7
9
  build: off
8
10
  test_script:
9
- - bundle exec rake -rdevkit
11
+ - bundle exec rake spec
10
12
 
11
13
  environment:
12
14
  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
15
+ - ruby_version: "30-x64"
16
+ - ruby_version: "27-x64"
17
+ - ruby_version: "26-x64"
18
+ - ruby_version: "25-x64"
19
+
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
+ for:
28
+ -
29
+ matrix:
30
+ only:
31
+ - ruby_version: "30-x64"
32
+ test_script:
33
+ - gem install fiddle --version 1.0.8
34
+ - set RUBYLIB=C:/Ruby%ruby_version%/lib/ruby/gems/3.0.0/gems/fiddle-1.0.8/lib
35
+ - bundle exec rake spec
@@ -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
@@ -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
@@ -17,9 +17,19 @@
17
17
  #
18
18
  require 'socket'
19
19
  require 'ipaddr'
20
+ require 'time'
21
+ require 'securerandom'
22
+ require 'json'
23
+ require 'base64'
20
24
 
21
25
  module ServerEngine
22
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
23
33
 
24
34
  class Client
25
35
  def initialize(path)
@@ -62,13 +72,17 @@ module ServerEngine
62
72
  class Server
63
73
  def self.generate_path
64
74
  if ServerEngine.windows?
65
- 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
66
79
  if `netstat -na | findstr "#{port}"`.length == 0
67
80
  return port
68
81
  end
69
82
  end
70
83
  else
71
- '/tmp/SERVERENGINE_SOCKETMANAGER_' + Time.now.to_s.gsub(' ', '') + '_' + Process.pid.to_s
84
+ base_dir = (ENV['SERVERENGINE_SOCKETMANAGER_SOCK_DIR'] || '/tmp')
85
+ File.join(base_dir, 'SERVERENGINE_SOCKETMANAGER_' + Time.now.utc.iso8601 + '_' + Process.pid.to_s)
72
86
  end
73
87
  end
74
88
 
@@ -136,7 +150,10 @@ module ServerEngine
136
150
 
137
151
  def process_peer(peer)
138
152
  while true
139
- 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
140
157
  begin
141
158
  send_socket(peer, pid, method, bind, port)
142
159
  rescue => e
@@ -146,18 +163,59 @@ module ServerEngine
146
163
  ensure
147
164
  peer.close
148
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
149
200
  end
150
201
 
151
202
  def self.send_peer(peer, obj)
152
- data = Marshal.dump(obj)
203
+ data = [SocketManager::INTERNAL_TOKEN, Base64.strict_encode64(Marshal.dump(obj))]
204
+ data = JSON.generate(data)
153
205
  peer.write [data.bytesize].pack('N')
154
206
  peer.write data
155
207
  end
156
208
 
157
209
  def self.recv_peer(peer)
158
- 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
159
214
  data = peer.read(len)
160
- 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))
161
219
  end
162
220
 
163
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.3"
2
+ VERSION = "2.2.4"
3
3
  end
@@ -78,6 +78,19 @@ module ServerEngine
78
78
  end
79
79
  end
80
80
 
81
+ def self.last_error
82
+ # On Ruby 3.0 calling WSAGetLastError here can't retrieve correct error
83
+ # code because Ruby's internal code resets it.
84
+ # See also:
85
+ # * https://github.com/ruby/fiddle/issues/72
86
+ # * https://bugs.ruby-lang.org/issues/17813
87
+ if Fiddle.respond_to?(:win32_last_socket_error)
88
+ Fiddle.win32_last_socket_error || 0
89
+ else
90
+ self.WSAGetLastError
91
+ end
92
+ end
93
+
81
94
  INVALID_SOCKET = -1
82
95
  end
83
96
 
@@ -97,11 +110,10 @@ module ServerEngine
97
110
  dlload ruby_dll_path
98
111
 
99
112
  extern "int rb_w32_map_errno(int)"
100
- extern "void rb_syserr_fail(int, char *)"
101
113
 
102
114
  def self.raise_last_error(name)
103
- errno = rb_w32_map_errno(WinSock.WSAGetLastError)
104
- rb_syserr_fail(errno, name)
115
+ errno = rb_w32_map_errno(WinSock.last_error)
116
+ raise SystemCallError.new(name, errno)
105
117
  end
106
118
 
107
119
  extern "int rb_w32_wrap_io_handle(int, int)"
@@ -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,13 +15,13 @@ 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
 
22
21
  gem.add_dependency "sigdump", ["~> 0.2.2"]
23
22
 
24
- gem.add_development_dependency "rake", [">= 0.9.2"]
23
+ # rake v12.x doesn't work with rspec 2. rspec should be updated to 3
24
+ gem.add_development_dependency "rake", ["~> 11.0"]
25
25
  gem.add_development_dependency "rspec", ["~> 2.13.0"]
26
26
 
27
27
  gem.add_development_dependency 'rake-compiler-dock', ['~> 0.5.0']
@@ -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,6 +19,36 @@ 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?
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
37
+ context 'Server.generate_path' do
38
+ it 'returns socket path under /tmp' do
39
+ path = SocketManager::Server.generate_path
40
+ expect(path).to include('/tmp/SERVERENGINE_SOCKETMANAGER_')
41
+ end
42
+
43
+ it 'can be changed via environment variable' do
44
+ ENV['SERVERENGINE_SOCKETMANAGER_SOCK_DIR'] = '/tmp/foo'
45
+ path = SocketManager::Server.generate_path
46
+ expect(path).to include('/tmp/foo/SERVERENGINE_SOCKETMANAGER_')
47
+ ENV.delete('SERVERENGINE_SOCKETMANAGER_SOCK_DIR')
48
+ end
49
+ end
50
+ end
51
+
22
52
  context 'with thread' do
23
53
  context 'using ipv4' do
24
54
  it 'works' do
@@ -139,7 +169,27 @@ describe ServerEngine::SocketManager do
139
169
  test_state(:is_udp_socket).should == 1
140
170
  test_state(:udp_data_sent).should == 1
141
171
  end
142
- 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
143
193
  end
144
194
 
145
195
  if ServerEngine.windows?
@@ -191,7 +191,7 @@ 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
@@ -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.3
4
+ version: 2.2.4
5
5
  platform: x64-mingw32
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-22 00:00:00.000000000 Z
11
+ date: 2021-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sigdump
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.9.2
33
+ version: '11.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.9.2
40
+ version: '11.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -101,9 +101,9 @@ executables: []
101
101
  extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
+ - ".github/workflows/linux.yml"
104
105
  - ".gitignore"
105
106
  - ".rspec"
106
- - ".travis.yml"
107
107
  - Changelog
108
108
  - Gemfile
109
109
  - LICENSE
@@ -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.5.1
171
+ rubygems_version: 3.2.5
172
172
  signing_key:
173
173
  specification_version: 4
174
174
  summary: ServerEngine - multiprocess server framework
@@ -183,4 +183,4 @@ test_files:
183
183
  - spec/socket_manager_spec.rb
184
184
  - spec/spec_helper.rb
185
185
  - spec/supervisor_spec.rb
186
- has_rdoc: false
186
+ - spec/winsock_spec.rb
data/.travis.yml DELETED
@@ -1,20 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.1.10
5
- - 2.2.4
6
- - 2.3.0
7
- - ruby-head
8
-
9
- branches:
10
- only:
11
- - master
12
-
13
- script: bundle exec rake spec
14
-
15
- before_install:
16
- - gem update bundler
17
-
18
- matrix:
19
- allow_failures:
20
- - rvm: ruby-head