serverengine 2.1.0 → 2.2.3

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: 0fc6a5674589bf35670db4884c9d5b24ebc7c3ad
4
- data.tar.gz: 2d90ee7be95869d687149d954d230462da3571c9
2
+ SHA256:
3
+ metadata.gz: c37776b7f6420e895262df160ce7156bf00b6c3adaccd8af016e2d35c2d49f28
4
+ data.tar.gz: 7109cb65899f1afd047c3a6ddce8dee6b51dcbf69d1c7bc0ef80937214a90850
5
5
  SHA512:
6
- metadata.gz: fa77bfaee529a011584d2ca8791a65e99193b4e5d21ce1ccdb326aea1fdea6016d32e6ec481ebe1f2bc395cdec8fccfe592578327dffdec73a90a60308f29082
7
- data.tar.gz: e3414b8d50c72c6c4a4eb82fafdfb4d2e9b3192c3390405b91b22db0b2929eba216a0544f1f377d80deafcd8fbae37bb01c7ea1d1c9f77e0597b92b54f196df2
6
+ metadata.gz: f92e476daba00bad71a9de2bb924187926bcc071266b510cba15741f584a3d14333d28533c9530d48f8b9820bd1fb9522bd1bfa5f3c1c858f238f44042a803cd
7
+ data.tar.gz: 0bd8378d0840b3ca89bae30999317925f9e3155d9c37fb3a9d5482c95f1be0350fa60b82c8d0706d5cdea40573f5bdf2801d4717f55b22da32469177895c15b5
data/.travis.yml CHANGED
@@ -1,11 +1,10 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.1.10
5
- - 2.2.9
6
- - 2.3.8
7
- - 2.4.5
8
- - 2.5.3
4
+ - 2.4.9
5
+ - 2.5.7
6
+ - 2.6.5
7
+ - 2.7
9
8
  - ruby-head
10
9
 
11
10
  branches:
data/Changelog CHANGED
@@ -1,3 +1,24 @@
1
+ 2021-02-17 version 2.2.3:
2
+
3
+ * Change SocketManager's port assignment strategy on Windows
4
+
5
+ 2020-11-02 version 2.2.2:
6
+
7
+ * Fix incomplete Windows support in spawn based multi worker
8
+
9
+ 2020-01-24 version 2.2.1:
10
+
11
+ * Fix IPv6 dual-stack mode issue for UDP
12
+ * experimental: Add SERVERENGINE_USE_SOCKET_REUSEPORT envvar to enable SO_REUSEPORT
13
+
14
+ 2019-11-16 version 2.2.0:
15
+
16
+ * Fix IPv6 dual-stack mode issue for TCP
17
+
18
+ 2019-04-22 version 2.1.1:
19
+
20
+ * Fix bug to ignore SIGDUMP_SIGNAL
21
+
1
22
  2018-11-14 version 2.1.0:
2
23
 
3
24
  * Improve socket manager security
@@ -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
@@ -71,7 +71,6 @@ module ServerEngine
71
71
  attr_reader :enable_heartbeat, :auto_heartbeat
72
72
 
73
73
  attr_accessor :command_sender
74
- attr_reader :command_sender_pipe
75
74
 
76
75
  CONFIG_PARAMS = {
77
76
  heartbeat_interval: 1,
@@ -180,10 +179,11 @@ module ServerEngine
180
179
  end
181
180
  end
182
181
 
182
+ command_sender_pipe = nil
183
183
  if @command_sender == "pipe"
184
- inpipe, @command_sender_pipe = IO.pipe
185
- @command_sender_pipe.sync = true
186
- @command_sender_pipe.binmode
184
+ inpipe, command_sender_pipe = IO.pipe
185
+ command_sender_pipe.sync = true
186
+ command_sender_pipe.binmode
187
187
  options[:in] = inpipe
188
188
  end
189
189
  env['SERVERENGINE_SOCKETMANAGER_INTERNAL_TOKEN'] = SocketManager::INTERNAL_TOKEN
@@ -193,6 +193,7 @@ module ServerEngine
193
193
  end
194
194
 
195
195
  m = Monitor.new(pid, monitor_options)
196
+ m.command_sender_pipe = command_sender_pipe
196
197
 
197
198
  @monitors << m
198
199
 
@@ -307,9 +308,11 @@ module ServerEngine
307
308
  @graceful_kill_start_time = nil
308
309
  @immediate_kill_start_time = nil
309
310
  @kill_count = 0
311
+
312
+ @command_sender_pipe = nil
310
313
  end
311
314
 
312
- attr_accessor :last_heartbeat_time
315
+ attr_accessor :last_heartbeat_time, :command_sender_pipe
313
316
  attr_reader :pid
314
317
 
315
318
  def heartbeat_delay
@@ -329,6 +332,10 @@ module ServerEngine
329
332
  end
330
333
  end
331
334
 
335
+ def send_command(command)
336
+ @command_sender_pipe.write(command) if @command_sender_pipe
337
+ end
338
+
332
339
  def try_join
333
340
  pid = @pid
334
341
  return true unless pid
@@ -366,15 +373,25 @@ module ServerEngine
366
373
  end
367
374
 
368
375
  def start_graceful_stop!
369
- now = Time.now
370
- @next_kill_time ||= now
371
- @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
372
384
  end
373
385
 
374
386
  def start_immediate_stop!
375
- now = Time.now
376
- @next_kill_time ||= now
377
- @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
378
395
  end
379
396
 
380
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
@@ -72,7 +72,10 @@ module ServerEngine
72
72
  class Server
73
73
  def self.generate_path
74
74
  if ServerEngine.windows?
75
- 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
76
79
  if `netstat -na | findstr "#{port}"`.length == 0
77
80
  return port
78
81
  end
@@ -160,6 +163,40 @@ module ServerEngine
160
163
  ensure
161
164
  peer.close
162
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
163
200
  end
164
201
 
165
202
  def self.send_peer(peer, obj)
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.3"
3
3
  end
@@ -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?
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.1.0
4
+ version: 2.2.3
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-11-15 00:00:00.000000000 Z
11
+ date: 2021-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sigdump
@@ -153,8 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
153
  - !ruby/object:Gem::Version
154
154
  version: '0'
155
155
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.6.14.1
156
+ rubygems_version: 3.1.4
158
157
  signing_key:
159
158
  specification_version: 4
160
159
  summary: ServerEngine - multiprocess server framework