serverengine 2.0.0pre1-x64-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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +20 -0
  5. data/Changelog +122 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +202 -0
  8. data/NOTICE +3 -0
  9. data/README.md +514 -0
  10. data/Rakefile +26 -0
  11. data/appveyor.yml +24 -0
  12. data/examples/server.rb +138 -0
  13. data/examples/spawn_worker_script.rb +38 -0
  14. data/lib/serverengine.rb +46 -0
  15. data/lib/serverengine/blocking_flag.rb +77 -0
  16. data/lib/serverengine/command_sender.rb +89 -0
  17. data/lib/serverengine/config_loader.rb +82 -0
  18. data/lib/serverengine/daemon.rb +233 -0
  19. data/lib/serverengine/daemon_logger.rb +135 -0
  20. data/lib/serverengine/embedded_server.rb +67 -0
  21. data/lib/serverengine/multi_process_server.rb +155 -0
  22. data/lib/serverengine/multi_spawn_server.rb +95 -0
  23. data/lib/serverengine/multi_thread_server.rb +80 -0
  24. data/lib/serverengine/multi_worker_server.rb +150 -0
  25. data/lib/serverengine/privilege.rb +57 -0
  26. data/lib/serverengine/process_manager.rb +508 -0
  27. data/lib/serverengine/server.rb +178 -0
  28. data/lib/serverengine/signal_thread.rb +116 -0
  29. data/lib/serverengine/signals.rb +31 -0
  30. data/lib/serverengine/socket_manager.rb +171 -0
  31. data/lib/serverengine/socket_manager_unix.rb +98 -0
  32. data/lib/serverengine/socket_manager_win.rb +154 -0
  33. data/lib/serverengine/supervisor.rb +313 -0
  34. data/lib/serverengine/utils.rb +62 -0
  35. data/lib/serverengine/version.rb +3 -0
  36. data/lib/serverengine/winsock.rb +128 -0
  37. data/lib/serverengine/worker.rb +81 -0
  38. data/serverengine.gemspec +37 -0
  39. data/spec/blocking_flag_spec.rb +59 -0
  40. data/spec/daemon_logger_spec.rb +175 -0
  41. data/spec/daemon_spec.rb +169 -0
  42. data/spec/multi_process_server_spec.rb +113 -0
  43. data/spec/server_worker_context.rb +232 -0
  44. data/spec/signal_thread_spec.rb +94 -0
  45. data/spec/socket_manager_spec.rb +119 -0
  46. data/spec/spec_helper.rb +19 -0
  47. data/spec/supervisor_spec.rb +215 -0
  48. metadata +184 -0
@@ -0,0 +1,94 @@
1
+
2
+ describe ServerEngine::SignalThread do
3
+ it 'start and stop' do
4
+ t = SignalThread.new
5
+ t.stop.should == t
6
+ t.join
7
+ end
8
+
9
+ unless ServerEngine.windows?
10
+ it 'call handler' do
11
+ n = 0
12
+
13
+ t = SignalThread.new do |st|
14
+ st.trap('CONT') { n += 1 }
15
+ end
16
+
17
+ Process.kill('CONT', Process.pid)
18
+ sleep 0.5
19
+
20
+ t.stop.join
21
+
22
+ n.should == 1
23
+ end
24
+
25
+ it 'SIG_IGN' do
26
+ # IGNORE signal handler has possible race condition in Ruby 2.1
27
+ # https://bugs.ruby-lang.org/issues/9835
28
+ # That's why ignore this test in Ruby2.1
29
+ if RUBY_VERSION >= '2.2'
30
+ t = SignalThread.new do |st|
31
+ st.trap('QUIT', 'SIG_IGN')
32
+ end
33
+
34
+ Process.kill('QUIT', Process.pid)
35
+
36
+ t.stop.join
37
+ end
38
+ end
39
+
40
+ it 'signal in handler' do
41
+ n = 0
42
+
43
+ SignalThread.new do |st|
44
+ st.trap('QUIT') do
45
+ if n < 3
46
+ Process.kill('QUIT', Process.pid)
47
+ n += 1
48
+ end
49
+ end
50
+ end
51
+
52
+ Process.kill('QUIT', Process.pid)
53
+ sleep 0.5
54
+
55
+ n.should == 3
56
+ end
57
+
58
+ it 'stop in handler' do
59
+ t = SignalThread.new do |st|
60
+ st.trap('QUIT') { st.stop }
61
+ end
62
+
63
+ Process.kill('QUIT', Process.pid)
64
+ sleep 0.5
65
+
66
+ t.join
67
+ end
68
+
69
+ it 'should not deadlock' do
70
+ n = 0
71
+
72
+ SignalThread.new do |st|
73
+ st.trap('CONT') { n += 1 }
74
+ end
75
+
76
+ (1..10).map {
77
+ Thread.new do
78
+ 10.times {
79
+ Process.kill('CONT', Process.pid)
80
+ }
81
+ end
82
+ }.each { |t|
83
+ t.join
84
+ }
85
+
86
+ # give chance to run the signal thread
87
+ sleep 0.1
88
+
89
+ # result won't be 100 because of kernel limitation
90
+ n.should > 0
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,119 @@
1
+
2
+ describe ServerEngine::SocketManager do
3
+ include_context 'test server and worker'
4
+
5
+ let(:server_path) do
6
+ 'tmp/socket_manager_test.sock'
7
+ end
8
+
9
+ after(:each) do
10
+ File.unlink(server_path) if File.exist?(server_path)
11
+ end
12
+
13
+ if ServerEngine.windows?
14
+ let(:server_port) do
15
+ 24223
16
+ end
17
+
18
+ let(:test_port) do
19
+ 9101
20
+ end
21
+
22
+ it 'is windows' do
23
+ SocketManager::Client.is_a?(SocketManagerWin::ClientModule)
24
+ SocketManager::Server.is_a?(SocketManagerWin::ServerModule)
25
+ end
26
+
27
+ context 'with thread' do
28
+ it 'works' do
29
+ server = SocketManager::Server.open(server_port)
30
+
31
+ thread = Thread.new do
32
+
33
+ client = ServerEngine::SocketManager::Client.new(server_port)
34
+ tcp = client.listen_tcp('127.0.0.1', test_port)
35
+ udp = client.listen_udp('127.0.0.1', test_port)
36
+
37
+ incr_test_state(:is_tcp_server) if tcp.is_a?(TCPServer)
38
+ incr_test_state(:is_udp_socket) if udp.is_a?(UDPSocket)
39
+
40
+ data, _from = udp.recvfrom(10)
41
+ incr_test_state(:udp_data_sent) if data == "ok"
42
+
43
+ s = tcp.accept
44
+ s.write("ok")
45
+ s.close
46
+ end
47
+
48
+ sleep 1
49
+
50
+ u = UDPSocket.new
51
+ u.send "ok", 0, '127.0.0.1', test_port
52
+ u.close
53
+
54
+ t = TCPSocket.open('127.0.0.1', test_port)
55
+ t.read.should == "ok"
56
+ t.close
57
+
58
+ server.close
59
+ thread.join
60
+
61
+ test_state(:is_tcp_server).should == 1
62
+ test_state(:is_udp_socket).should == 1
63
+ test_state(:udp_data_sent).should == 1
64
+ end
65
+ end
66
+
67
+ else
68
+ let(:test_port) do
69
+ 9102
70
+ end
71
+
72
+ it 'is unix' do
73
+ SocketManager::Client.is_a?(SocketManagerUnix::ClientModule)
74
+ SocketManager::Server.is_a?(SocketManagerUnix::ServerModule)
75
+ end
76
+
77
+ context 'with fork' do
78
+ it 'works' do
79
+ server = SocketManager::Server.open(server_path)
80
+
81
+ fork do
82
+ server.close
83
+
84
+ client = server.new_client
85
+
86
+ tcp = client.listen_tcp('127.0.0.1', test_port)
87
+ udp = client.listen_udp('127.0.0.1', test_port)
88
+
89
+ incr_test_state(:is_tcp_server) if tcp.is_a?(TCPServer)
90
+ incr_test_state(:is_udp_socket) if udp.is_a?(UDPSocket)
91
+
92
+ data, _from = udp.recvfrom(10)
93
+ incr_test_state(:udp_data_sent) if data == "ok"
94
+
95
+ s = tcp.accept
96
+ s.write("ok")
97
+ s.close
98
+ end
99
+
100
+ wait_for_fork
101
+
102
+ u = UDPSocket.new
103
+ u.send "ok", 0, '127.0.0.1', test_port
104
+ u.close
105
+
106
+ t = TCPSocket.open('127.0.0.1', test_port)
107
+ t.read.should == "ok"
108
+ t.close
109
+
110
+ server.close
111
+
112
+ test_state(:is_tcp_server).should == 1
113
+ test_state(:is_udp_socket).should == 1
114
+ test_state(:udp_data_sent).should == 1
115
+ end
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+
3
+ begin
4
+ Bundler.setup(:default, :test)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'serverengine'
12
+ include ServerEngine
13
+
14
+ # require sigdump only in unix, because there is no suport for SIGCONT in windows.
15
+ unless ServerEngine.windows?
16
+ require 'sigdump/setup'
17
+ end
18
+ require 'server_worker_context'
19
+
@@ -0,0 +1,215 @@
1
+
2
+ describe ServerEngine::Supervisor do
3
+ include_context 'test server and worker'
4
+
5
+ def start_supervisor(worker = nil, **config)
6
+ if ServerEngine.windows?
7
+ config[:windows_daemon_cmdline] = windows_supervisor_cmdline(nil, worker, config)
8
+ end
9
+ sv = Supervisor.new(TestServer, worker || TestWorker, config)
10
+ t = Thread.new { sv.main }
11
+
12
+ return sv, t
13
+ end
14
+
15
+ def start_daemon(**config)
16
+ if ServerEngine.windows?
17
+ config[:windows_daemon_cmdline] = windows_daemon_cmdline
18
+ end
19
+ daemon = Daemon.new(nil, TestWorker, config)
20
+ t = Thread.new { daemon.main }
21
+
22
+ return daemon, t
23
+ end
24
+
25
+ context 'when :log=IO option is given' do
26
+ it 'can start' do
27
+ daemon, t = start_daemon(log: STDOUT)
28
+
29
+ begin
30
+ wait_for_fork
31
+ ensure
32
+ daemon.server.stop(true)
33
+ t.join
34
+ end
35
+
36
+ test_state(:worker_run).should == 1
37
+ daemon.server.logger.should be_an_instance_of(ServerEngine::DaemonLogger)
38
+ end
39
+ end
40
+
41
+ context 'when :logger option is given' do
42
+ it 'uses specified logger instance' do
43
+ logger = ServerEngine::DaemonLogger.new(STDOUT)
44
+ daemon, t = start_daemon(logger: logger)
45
+
46
+ begin
47
+ wait_for_fork
48
+ ensure
49
+ daemon.server.stop(true)
50
+ t.join
51
+ end
52
+
53
+ test_state(:worker_run).should == 1
54
+ daemon.server.logger.should == logger
55
+ end
56
+ end
57
+
58
+ context 'when both :logger and :log options are given' do
59
+ it 'start ignoring :log' do
60
+ logger = ServerEngine::DaemonLogger.new(STDOUT)
61
+ daemon, t = start_daemon(logger: logger, log: STDERR)
62
+
63
+ begin
64
+ wait_for_fork
65
+ ensure
66
+ daemon.server.stop(true)
67
+ t.join
68
+ end
69
+
70
+ test_state(:worker_run).should == 1
71
+ daemon.server.logger.should == logger
72
+ end
73
+ end
74
+
75
+ ['signal', 'pipe'].each do |sender|
76
+ context "when using #{sender} as command_sender" do
77
+
78
+ it 'start and graceful stop' do
79
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
80
+
81
+ sv, t = start_supervisor(command_sender: sender)
82
+
83
+ begin
84
+ wait_for_fork
85
+
86
+ test_state(:server_before_run).should == 1
87
+ test_state(:server_after_start).should == 1 # parent
88
+ ensure
89
+ sv.stop(true)
90
+ t.join
91
+ end
92
+
93
+ test_state(:server_stop).should == 1
94
+ test_state(:server_stop_graceful).should == 1
95
+ test_state(:server_restart).should == 0
96
+
97
+ test_state(:server_after_run).should == 1
98
+ test_state(:server_after_start).should == 1
99
+ end
100
+
101
+ it 'immediate stop' do
102
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
103
+
104
+ sv, t = start_supervisor(command_sender: sender)
105
+
106
+ begin
107
+ wait_for_fork
108
+ ensure
109
+ sv.stop(false)
110
+ t.join
111
+ end
112
+
113
+ test_state(:server_stop).should == 1
114
+ test_state(:server_stop_immediate).should == 1
115
+ test_state(:server_after_run).should == 1
116
+ test_state(:server_after_start).should == 1
117
+ end
118
+
119
+ it 'graceful restart' do
120
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
121
+
122
+ sv, t = start_supervisor(command_sender: sender)
123
+
124
+ begin
125
+ wait_for_fork
126
+
127
+ sv.restart(true)
128
+ wait_for_stop
129
+
130
+ ensure
131
+ sv.stop(true)
132
+ t.join
133
+ end
134
+
135
+ test_state(:server_stop).should == 1
136
+ test_state(:server_restart_graceful).should == 1
137
+
138
+ test_state(:server_before_run).should == 1
139
+ test_state(:server_after_run).should == 1
140
+ test_state(:server_after_start).should == 1
141
+ end
142
+
143
+ it 'immediate restart' do
144
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
145
+
146
+ sv, t = start_supervisor(command_sender: sender)
147
+
148
+ begin
149
+ wait_for_fork
150
+
151
+ sv.restart(false)
152
+ wait_for_stop
153
+
154
+ ensure
155
+ sv.stop(true)
156
+ t.join
157
+ end
158
+
159
+ test_state(:server_stop).should == 1
160
+ test_state(:server_restart_immediate).should == 1
161
+
162
+ test_state(:server_before_run).should == 1
163
+ test_state(:server_after_run).should == 1
164
+ test_state(:server_after_start).should == 1
165
+ end
166
+
167
+ it 'reload' do
168
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
169
+
170
+ sv, t = start_supervisor(command_sender: sender)
171
+
172
+ begin
173
+ wait_for_fork
174
+
175
+ sv.reload
176
+
177
+ ensure
178
+ sv.stop(true)
179
+ t.join
180
+ end
181
+
182
+ test_state(:server_stop).should == 1
183
+ test_state(:server_reload).should == 1
184
+ end
185
+
186
+ # TODO detach
187
+
188
+ it 'auto restart in limited ratio' do
189
+ pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
190
+
191
+ sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
192
+
193
+ begin
194
+ sleep 2.2
195
+ ensure
196
+ sv.stop(true)
197
+ t.join
198
+ end
199
+
200
+ test_state(:worker_run).should == 3
201
+ end
202
+ end
203
+ end
204
+
205
+ module InitializeErrorServer
206
+ def initialize
207
+ raise StandardError, "error test"
208
+ end
209
+ end
210
+
211
+ it 'initialize error' do
212
+ sv = Supervisor.new(InitializeErrorServer, TestWorker)
213
+ lambda { sv.main }.should raise_error(StandardError)
214
+ end
215
+ end