serverengine 2.0.7 → 2.2.2

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: 2daf0f4af407a699df75677a6cdfdaa798b6c870acd77ee75bdaced9c3944eb7
4
+ data.tar.gz: 40d6299f178209b2207966ceea322f1b36efef63f897a54f0a815fb28a2af71d
5
5
  SHA512:
6
- metadata.gz: 7b372a236fbb4861d6a1143c35a17466483d5e43730e6e6ce3a6dbe9c8a85cd560fbf7cb8a73e84edf7f0a56cecf9a1ec0e596cceed552c174d61d83765f2d49
7
- data.tar.gz: '092f8d447cab9895701e566fe5185f6043ca483ff2cc905c57ae653f5551b80f6b080b9f0b51fb1fc42b8466eae463a5809e0941a27e770323479cf99e5e1f92'
6
+ metadata.gz: 95bd026a937bbf8ab666a9c89832e7c32d7b60c5a88ef91f61d08c6e6bf7595268e73c4eb03f09e972edd328f424304a17eb354d99e2caa7e14182f13bb76dd8
7
+ data.tar.gz: 0f6a91d444ae252cc232bd1151804c706a43415388a23e65b8861cd53dc5ac3a490f557d14b6aebcda03d12371c5220e2f30859b2300a3124b03781650c8ec7f
@@ -2,9 +2,12 @@ language: ruby
2
2
 
3
3
  rvm:
4
4
  - 2.1.10
5
- - 2.2.6
6
- - 2.3.3
7
- - 2.4.0
5
+ - 2.2.9
6
+ - 2.3.8
7
+ - 2.4.9
8
+ - 2.5.7
9
+ - 2.6.5
10
+ - 2.7.0
8
11
  - ruby-head
9
12
 
10
13
  branches:
data/Changelog CHANGED
@@ -1,3 +1,24 @@
1
+ 2020-11-02 version 2.2.2:
2
+
3
+ * Fix incomplete Windows support in spawn based multi worker
4
+
5
+ 2020-01-24 version 2.2.1:
6
+
7
+ * Fix IPv6 dual-stack mode issue for UDP
8
+ * experimental: Add SERVERENGINE_USE_SOCKET_REUSEPORT envvar to enable SO_REUSEPORT
9
+
10
+ 2019-11-16 version 2.2.0:
11
+
12
+ * Fix IPv6 dual-stack mode issue for TCP
13
+
14
+ 2019-04-22 version 2.1.1:
15
+
16
+ * Fix bug to ignore SIGDUMP_SIGNAL
17
+
18
+ 2018-11-14 version 2.1.0:
19
+
20
+ * Improve socket manager security
21
+
1
22
  2018-07-09 version 2.0.7:
2
23
 
3
24
  * Add disable_sigdump option
@@ -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)
@@ -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)
@@ -154,7 +163,8 @@ module ServerEngine
154
163
  end
155
164
 
156
165
  def self.send_peer(peer, obj)
157
- data = Marshal.dump(obj)
166
+ data = [SocketManager::INTERNAL_TOKEN, Base64.strict_encode64(Marshal.dump(obj))]
167
+ data = JSON.generate(data)
158
168
  peer.write [data.bytesize].pack('N')
159
169
  peer.write data
160
170
  end
@@ -165,7 +175,10 @@ module ServerEngine
165
175
 
166
176
  len = res.unpack('N').first
167
177
  data = peer.read(len)
168
- Marshal.load(data)
178
+ data = JSON.parse(data)
179
+ return nil if SocketManager::INTERNAL_TOKEN != data.first
180
+
181
+ Marshal.load(Base64.strict_decode64(data.last))
169
182
  end
170
183
 
171
184
  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.2"
3
3
  end
@@ -155,7 +155,27 @@ describe ServerEngine::SocketManager do
155
155
  test_state(:is_udp_socket).should == 1
156
156
  test_state(:udp_data_sent).should == 1
157
157
  end
158
- end if (TCPServer.open("::1",0) rescue nil)
158
+ end if (TCPServer.open("::1", 0) rescue nil)
159
+
160
+ unless ServerEngine.windows?
161
+ context 'using ipv4/ipv6' do
162
+ it 'can bind ipv4/ipv6 together' do
163
+ server = SocketManager::Server.open(server_path)
164
+ client = ServerEngine::SocketManager::Client.new(server_path)
165
+
166
+ tcp_v4 = client.listen_tcp('0.0.0.0', test_port)
167
+ udp_v4 = client.listen_udp('0.0.0.0', test_port)
168
+ tcp_v6 = client.listen_tcp('::', test_port)
169
+ udp_v6 = client.listen_udp('::', test_port)
170
+
171
+ tcp_v4.close
172
+ udp_v4.close
173
+ tcp_v6.close
174
+ udp_v6.close
175
+ server.close
176
+ end
177
+ end if (TCPServer.open("::", 0) rescue nil)
178
+ end
159
179
  end
160
180
 
161
181
  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.2
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: 2020-11-02 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.7.6
156
+ rubygems_version: 3.0.3
158
157
  signing_key:
159
158
  specification_version: 4
160
159
  summary: ServerEngine - multiprocess server framework