serverengine 2.0.3-x86-mingw32 → 2.2.4-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: 30e143c93f13de994374897c12c6d5d8341121f6
4
- data.tar.gz: 97e2987789826974472c8b7d2dfc26bb2a637d75
2
+ SHA256:
3
+ metadata.gz: 14225458f5959eab0eefdff088da3f289816a4f0e9e871e291edbeb7aa1796df
4
+ data.tar.gz: 3932f03c87996232a7a58645a8c77ffb2f57302f49b62c1cd6bfc944ab3e9faa
5
5
  SHA512:
6
- metadata.gz: d338855b1925642c20a58ed8d73f4c05a7f160a0f18068b40f6370d595626d67914674988251a344fc2a3d6a86b4e4f57bfd2fff12be96cc6c6d26762f8fa0e7
7
- data.tar.gz: e9808147809ce5f84e274f759bcef7c5d5105ea3e7d37484b8b64fe32a402c5c7bab500d254189157f307865f4a2596df0bc84037e0b95e11cb188241d97eed3
6
+ metadata.gz: 6db094c6723490b9f4fba7d62bc70a52239aa22f4b9ac3f598d2b734d70052b27460be326283be3f84107127749cf2ed957e1a6f6ad8b0a7cfe731103cc2531c
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: x86-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