serverengine 2.0.7 → 2.2.5

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
2
  SHA256:
3
- metadata.gz: 849803d15dfe8e2b3e551b2849d8eb3d02deeffa271d4fa5f890ac4fb8db9a64
4
- data.tar.gz: 703964e48ed203464b7d98eeaee0a161c88c4fc9597c2c1028e4cc3860b52476
3
+ metadata.gz: 2175217415cd5751a1bd4a8d60c5abdb64e80429e722c41e8c1e97ec02b1e22a
4
+ data.tar.gz: 63a7af53a0dc906bb962394f902d1486f7958950c9b3b9bf41206e56789d83ad
5
5
  SHA512:
6
- metadata.gz: 7b372a236fbb4861d6a1143c35a17466483d5e43730e6e6ce3a6dbe9c8a85cd560fbf7cb8a73e84edf7f0a56cecf9a1ec0e596cceed552c174d61d83765f2d49
7
- data.tar.gz: '092f8d447cab9895701e566fe5185f6043ca483ff2cc905c57ae653f5551b80f6b080b9f0b51fb1fc42b8466eae463a5809e0941a27e770323479cf99e5e1f92'
6
+ metadata.gz: 85880cc1ec9b83e03b2cbe80756253e156e46954117ae0a008388bfccf3d412b96a54911f9f002b6387ec2dfa9ef35dd3a40bff1dff22f7b5ca180f667d88611
7
+ data.tar.gz: 624a1e6397faa39af9b871ef91571b432263d6b2b595fc4bdfaa9244a6153b25a67fe80c38c6f814c94db27b0dddcc1e4e19ae08515c8e48c2ee6208478775d0
@@ -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,36 @@
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
+
1
34
  2018-07-09 version 2.0.7:
2
35
 
3
36
  * Add disable_sigdump option
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
@@ -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)
@@ -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
@@ -151,10 +163,45 @@ module ServerEngine
151
163
  ensure
152
164
  peer.close
153
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
154
200
  end
155
201
 
156
202
  def self.send_peer(peer, obj)
157
- data = Marshal.dump(obj)
203
+ data = [SocketManager::INTERNAL_TOKEN, Base64.strict_encode64(Marshal.dump(obj))]
204
+ data = JSON.generate(data)
158
205
  peer.write [data.bytesize].pack('N')
159
206
  peer.write data
160
207
  end
@@ -165,7 +212,10 @@ module ServerEngine
165
212
 
166
213
  len = res.unpack('N').first
167
214
  data = peer.read(len)
168
- 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))
169
219
  end
170
220
 
171
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
 
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "2.0.7"
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
 
@@ -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
@@ -254,7 +254,7 @@ shared_context 'test server and worker' do
254
254
  before { reset_test_state }
255
255
 
256
256
  def wait_for_fork
257
- sleep 0.8
257
+ sleep 1.5
258
258
  end
259
259
 
260
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.7
4
+ version: 2.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-10 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
@@ -87,16 +87,16 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".github/workflows/linux.yml"
91
+ - ".github/workflows/windows.yml"
90
92
  - ".gitignore"
91
93
  - ".rspec"
92
- - ".travis.yml"
93
94
  - Changelog
94
95
  - Gemfile
95
96
  - LICENSE
96
97
  - NOTICE
97
98
  - README.md
98
99
  - Rakefile
99
- - appveyor.yml
100
100
  - examples/server.rb
101
101
  - examples/spawn_worker_script.rb
102
102
  - lib/serverengine.rb
@@ -134,6 +134,7 @@ files:
134
134
  - spec/socket_manager_spec.rb
135
135
  - spec/spec_helper.rb
136
136
  - spec/supervisor_spec.rb
137
+ - spec/winsock_spec.rb
137
138
  homepage: https://github.com/fluent/serverengine
138
139
  licenses:
139
140
  - Apache 2.0
@@ -153,8 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  - !ruby/object:Gem::Version
154
155
  version: '0'
155
156
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.7.6
157
+ rubygems_version: 3.2.5
158
158
  signing_key:
159
159
  specification_version: 4
160
160
  summary: ServerEngine - multiprocess server framework
@@ -169,3 +169,4 @@ test_files:
169
169
  - spec/socket_manager_spec.rb
170
170
  - spec/spec_helper.rb
171
171
  - spec/supervisor_spec.rb
172
+ - 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