passenger 4.0.14 → 4.0.16

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (121) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +69 -0
  3. data/bin/passenger-install-apache2-module +7 -1
  4. data/bin/passenger-install-nginx-module +18 -1
  5. data/build/apache2.rb +25 -1
  6. data/build/basics.rb +7 -4
  7. data/build/debian.rb +72 -25
  8. data/build/nginx.rb +24 -0
  9. data/build/packaging.rb +45 -3
  10. data/debian.template/{control → control.template} +17 -8
  11. data/debian.template/ruby-passenger.install.template +1 -0
  12. data/debian.template/rules.template +9 -3
  13. data/dev/run_travis.sh +14 -0
  14. data/doc/Users guide Apache.idmap.txt +64 -48
  15. data/doc/Users guide Apache.txt +127 -93
  16. data/doc/Users guide Nginx.idmap.txt +46 -28
  17. data/doc/Users guide Nginx.txt +100 -12
  18. data/doc/images/{conservative_spawning.png → direct_spawning.png} +0 -0
  19. data/doc/images/{conservative_spawning.svg → direct_spawning.svg} +0 -0
  20. data/doc/users_guide_snippets/installation.txt +107 -66
  21. data/doc/users_guide_snippets/passenger_spawn_method.txt +1 -1
  22. data/doc/users_guide_snippets/rvm_helper_tool.txt +0 -12
  23. data/doc/users_guide_snippets/tips.txt +70 -3
  24. data/doc/users_guide_snippets/under_the_hood/page_caching_support.txt +2 -0
  25. data/doc/users_guide_snippets/under_the_hood/relationship_with_ruby.txt +114 -0
  26. data/ext/apache2/Configuration.cpp +53 -101
  27. data/ext/apache2/Configuration.hpp +19 -41
  28. data/ext/apache2/ConfigurationCommands.cpp +95 -0
  29. data/ext/apache2/ConfigurationCommands.cpp.erb +91 -0
  30. data/ext/apache2/ConfigurationFields.hpp +59 -0
  31. data/ext/apache2/ConfigurationFields.hpp.erb +89 -0
  32. data/ext/apache2/ConfigurationSetters.cpp +223 -0
  33. data/ext/apache2/ConfigurationSetters.cpp.erb +126 -0
  34. data/ext/apache2/CreateDirConfig.cpp +50 -0
  35. data/ext/apache2/CreateDirConfig.cpp.erb +71 -0
  36. data/ext/apache2/Hooks.cpp +6 -0
  37. data/ext/apache2/MergeDirConfig.cpp +103 -0
  38. data/ext/apache2/MergeDirConfig.cpp.erb +81 -0
  39. data/ext/common/ApplicationPool2/AppTypes.cpp +2 -0
  40. data/ext/common/ApplicationPool2/AppTypes.h +2 -0
  41. data/ext/common/ApplicationPool2/Common.h +1 -1
  42. data/ext/common/ApplicationPool2/Group.h +56 -7
  43. data/ext/common/ApplicationPool2/Implementation.cpp +133 -31
  44. data/ext/common/ApplicationPool2/Options.h +23 -2
  45. data/ext/common/ApplicationPool2/Pool.h +8 -1
  46. data/ext/common/ApplicationPool2/Process.h +9 -0
  47. data/ext/common/ApplicationPool2/Session.h +1 -0
  48. data/ext/common/ApplicationPool2/Spawner.h +21 -19
  49. data/ext/common/ApplicationPool2/SuperGroup.h +1 -1
  50. data/ext/common/Constants.h +21 -17
  51. data/ext/common/Constants.h.erb +1 -1
  52. data/ext/common/Exceptions.h +19 -0
  53. data/ext/common/ServerInstanceDir.h +8 -4
  54. data/ext/common/Utils.cpp +33 -1
  55. data/ext/common/Utils.h +14 -0
  56. data/ext/common/Utils/StrIntUtils.cpp +16 -0
  57. data/ext/common/Utils/StrIntUtils.h +5 -0
  58. data/ext/common/agents/HelperAgent/Main.cpp +5 -5
  59. data/ext/common/agents/HelperAgent/RequestHandler.h +94 -45
  60. data/ext/common/agents/LoggingAgent/Main.cpp +10 -26
  61. data/ext/common/agents/Watchdog/Main.cpp +4 -15
  62. data/ext/nginx/CacheLocationConfig.c +501 -0
  63. data/ext/nginx/CacheLocationConfig.c.erb +140 -0
  64. data/ext/nginx/Configuration.c +29 -453
  65. data/ext/nginx/Configuration.h +3 -21
  66. data/ext/nginx/ConfigurationCommands.c +501 -0
  67. data/ext/nginx/ConfigurationCommands.c.erb +136 -0
  68. data/ext/nginx/ConfigurationFields.h +89 -0
  69. data/ext/nginx/ConfigurationFields.h.erb +85 -0
  70. data/ext/nginx/ContentHandler.c +3 -166
  71. data/ext/nginx/CreateLocationConfig.c +146 -0
  72. data/ext/nginx/CreateLocationConfig.c.erb +70 -0
  73. data/ext/nginx/MergeLocationConfig.c +166 -0
  74. data/ext/nginx/MergeLocationConfig.c.erb +72 -0
  75. data/ext/nginx/config +4 -0
  76. data/ext/oxt/detail/tracable_exception_disabled.hpp +21 -1
  77. data/ext/oxt/detail/tracable_exception_enabled.hpp +4 -1
  78. data/ext/oxt/implementation.cpp +7 -1
  79. data/ext/oxt/macros.hpp +9 -7
  80. data/helper-scripts/backtrace-sanitizer.rb +23 -0
  81. data/helper-scripts/classic-rails-loader.rb +23 -0
  82. data/helper-scripts/classic-rails-preloader.rb +23 -0
  83. data/helper-scripts/download_binaries/extconf.rb +10 -5
  84. data/helper-scripts/meteor-loader.rb +127 -0
  85. data/helper-scripts/node-loader.js +1 -1
  86. data/helper-scripts/rack-preloader.rb +23 -0
  87. data/helper-scripts/system-memory-stats.py +22 -0
  88. data/helper-scripts/touch-dir.sh +48 -0
  89. data/lib/phusion_passenger.rb +1 -1
  90. data/lib/phusion_passenger/apache2/config_options.rb +104 -0
  91. data/lib/phusion_passenger/constants.rb +8 -0
  92. data/lib/phusion_passenger/native_support.rb +9 -1
  93. data/lib/phusion_passenger/nginx/config_options.rb +328 -0
  94. data/lib/phusion_passenger/packaging.rb +2 -2
  95. data/lib/phusion_passenger/platform_info/apache.rb +8 -0
  96. data/lib/phusion_passenger/platform_info/compiler.rb +2 -2
  97. data/lib/phusion_passenger/rails3_extensions/init.rb +19 -4
  98. data/lib/phusion_passenger/ruby_core_enhancements.rb +4 -1
  99. data/lib/phusion_passenger/standalone/app_finder.rb +3 -2
  100. data/lib/phusion_passenger/standalone/command.rb +1 -0
  101. data/lib/phusion_passenger/standalone/runtime_installer.rb +1 -6
  102. data/lib/phusion_passenger/standalone/runtime_locator.rb +9 -2
  103. data/lib/phusion_passenger/standalone/start_command.rb +45 -9
  104. data/lib/phusion_passenger/utils.rb +4 -1
  105. data/resources/templates/{nginx → installer_common}/run_installer_as_root.txt.erb +3 -1
  106. data/resources/templates/nginx/nginx_module_sources_not_available.txt.erb +6 -0
  107. data/resources/templates/standalone/config.erb +8 -8
  108. data/test/cxx/ApplicationPool2/PoolTest.cpp +120 -3
  109. data/test/cxx/RequestHandlerTest.cpp +66 -3
  110. data/test/integration_tests/native_packaging_spec.rb +41 -0
  111. data/test/integration_tests/nginx_tests.rb +1 -0
  112. data/test/integration_tests/standalone_tests.rb +4 -0
  113. data/test/ruby/shared/rails/analytics_logging_extensions_sharedspec.rb +22 -0
  114. data/test/stub/rails3.0/config/initializers/passenger.rb +2 -1
  115. data/test/stub/rails3.1/config/initializers/passenger.rb +2 -1
  116. data/test/stub/rails3.2/config/initializers/passenger.rb +2 -1
  117. data/test/stub/rails4.0/config/initializers/passenger.rb +5 -1
  118. data/test/stub/wsgi/passenger_wsgi.py +5 -0
  119. metadata +32 -7
  120. metadata.gz.asc +7 -7
  121. data/resources/templates/apache2/run_installer_as_root.txt.erb +0 -8
@@ -76,8 +76,8 @@ module Packaging
76
76
  'rpm/**/*',
77
77
  'helper-scripts/**/*',
78
78
  'ext/common/**/*.{cpp,c,h,hpp,md,erb}',
79
- 'ext/apache2/*.{cpp,h,hpp,c}',
80
- 'ext/nginx/*.{c,cpp,h}',
79
+ 'ext/apache2/*.{cpp,h,hpp,c,erb}',
80
+ 'ext/nginx/*.{c,cpp,h,erb}',
81
81
  'ext/nginx/config',
82
82
  'ext/boost/**/*',
83
83
  'ext/libev/{LICENSE,Changes,README,Makefile.am,Makefile.in}',
@@ -182,6 +182,14 @@ module PlatformInfo
182
182
  if filename.include?("${")
183
183
  # We couldn't substitute everything.
184
184
  return nil
185
+ end
186
+ if filename !~ /\A\//
187
+ # Not an absolute path. Infer from root.
188
+ if root = httpd_root(options)
189
+ return "#{root}/#{filename}"
190
+ else
191
+ return nil
192
+ end
185
193
  else
186
194
  return filename
187
195
  end
@@ -75,12 +75,12 @@ private
75
75
  output = `#{command} 2>&1`
76
76
  result = $?.exitstatus == 0
77
77
  rescue SystemCallError => e
78
- result = false
78
+ result = nil
79
79
  exec_error_reason = e.message
80
80
  end
81
81
  log("Output:\n" <<
82
82
  "-------------------------\n" <<
83
- output <<
83
+ output.to_s <<
84
84
  "\n-------------------------")
85
85
  elsif verbose?
86
86
  result = system(command)
@@ -27,8 +27,8 @@ require 'digest/md5'
27
27
  module PhusionPassenger
28
28
 
29
29
  module Rails3Extensions
30
- def self.init!(options)
31
- if !AnalyticsLogging.install!(options)
30
+ def self.init!(options, user_options = {})
31
+ if !AnalyticsLogging.install!(options, user_options)
32
32
  # Remove code to save memory.
33
33
  PhusionPassenger::Rails3Extensions.send(:remove_const, :AnalyticsLogging)
34
34
  PhusionPassenger.send(:remove_const, :Rails3Extensions)
@@ -38,7 +38,7 @@ end
38
38
 
39
39
  module Rails3Extensions
40
40
  class AnalyticsLogging < ActiveSupport::LogSubscriber
41
- def self.install!(options)
41
+ def self.install!(options, user_options)
42
42
  analytics_logger = options["analytics_logger"]
43
43
  app_group_name = options["app_group_name"]
44
44
  return false if !analytics_logger || !options["analytics"]
@@ -47,7 +47,7 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
47
47
  # so that the info can be logged.
48
48
  GC.enable_stats if GC.respond_to?(:enable_stats)
49
49
 
50
- subscriber = self.new
50
+ subscriber = self.new(user_options)
51
51
  AnalyticsLogging.attach_to(:action_controller, subscriber)
52
52
  AnalyticsLogging.attach_to(:active_record, subscriber)
53
53
  if defined?(ActiveSupport::Cache::Store)
@@ -87,6 +87,10 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
87
87
 
88
88
  return true
89
89
  end
90
+
91
+ def initialize(options)
92
+ install_event_preprocessor(options[:event_preprocessor]) if options[:event_preprocessor]
93
+ end
90
94
 
91
95
  def process_action(event)
92
96
  log = Thread.current[PASSENGER_ANALYTICS_WEB_LOG]
@@ -215,6 +219,17 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
215
219
  end
216
220
  end
217
221
  end
222
+
223
+ private
224
+ def install_event_preprocessor(event_preprocessor)
225
+ public_methods(false).each do |name|
226
+ singleton = class << self; self end
227
+ singleton.send(:define_method, name, lambda do |event|
228
+ event_preprocessor.call(event)
229
+ super(event)
230
+ end)
231
+ end
232
+ end
218
233
  end # class AnalyticsLogging
219
234
  end # module Rails3Extensions
220
235
 
@@ -22,7 +22,10 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  # THE SOFTWARE.
24
24
 
25
- require 'rubygems'
25
+ begin
26
+ require 'rubygems'
27
+ rescue LoadError
28
+ end
26
29
  require 'socket'
27
30
  require 'thread'
28
31
  if (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") && RUBY_VERSION < "1.8.7"
@@ -1,5 +1,5 @@
1
1
  # Phusion Passenger - https://www.phusionpassenger.com/
2
- # Copyright (c) 2010 Phusion
2
+ # Copyright (c) 2010-2013 Phusion
3
3
  #
4
4
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
5
5
  #
@@ -33,7 +33,8 @@ class AppFinder
33
33
  def self.looks_like_app_directory?(dir)
34
34
  return File.exist?("#{dir}/config.ru") ||
35
35
  File.exist?("#{dir}/config/environment.rb") ||
36
- File.exist?("#{dir}/passenger_wsgi.py")
36
+ File.exist?("#{dir}/passenger_wsgi.py") ||
37
+ File.exist?("#{dir}/passenger_node.js")
37
38
  end
38
39
 
39
40
  def initialize(dirs, options = {})
@@ -241,6 +241,7 @@ private
241
241
  def create_nginx_controller(extra_options = {})
242
242
  require_daemon_controller
243
243
  require 'socket' unless defined?(UNIXSocket)
244
+ require 'thread' unless defined?(Mutex)
244
245
  if @options[:socket_file]
245
246
  ping_spec = [:unix, @options[:socket_file]]
246
247
  else
@@ -572,12 +572,7 @@ private
572
572
  # work around the problem by configure Nginx with prefix
573
573
  # /tmp.
574
574
  command << "#{shell} ./configure --prefix=/tmp " <<
575
- "--with-cc-opt='-Wno-error' " <<
576
- "--without-http_fastcgi_module " <<
577
- "--without-http_scgi_module " <<
578
- "--without-http_uwsgi_module " <<
579
- "--with-http_gzip_static_module " <<
580
- "--with-http_stub_status_module " <<
575
+ "#{STANDALONE_NGINX_CONFIGURE_OPTIONS} " <<
581
576
  "'--add-module=#{PhusionPassenger.nginx_module_source_dir}'"
582
577
  run_command_with_throbber(command, "Preparing Nginx...") do |status_text|
583
578
  yield(0, 1, status_text)
@@ -138,8 +138,15 @@ class RuntimeLocator
138
138
 
139
139
  private
140
140
  def default_runtime_dir
141
- home = Etc.getpwuid.dir
142
- return "#{home}/#{USER_NAMESPACE_DIRNAME}/standalone"
141
+ if Process.uid == 0
142
+ # It is important that the default runtime dir for the root user
143
+ # is a publicly accessible directory, because when --user is given,
144
+ # the agents are run as non-root users.
145
+ return "/var/lib/#{GLOBAL_NAMESPACE_DIRNAME}/standalone"
146
+ else
147
+ home = Etc.getpwuid.dir
148
+ return "#{home}/#{USER_NAMESPACE_DIRNAME}/standalone"
149
+ end
143
150
  end
144
151
 
145
152
  def debugging?
@@ -1,5 +1,5 @@
1
1
  # Phusion Passenger - https://www.phusionpassenger.com/
2
- # Copyright (c) 2010-2012 Phusion
2
+ # Copyright (c) 2010-2013 Phusion
3
3
  #
4
4
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
5
5
  #
@@ -83,7 +83,7 @@ class StartCommand < Command
83
83
  ########################
84
84
  touch_temp_dir_in_background
85
85
  watch_log_files_in_background if should_watch_logs?
86
- wait_until_nginx_has_exited
86
+ wait_until_nginx_has_exited if should_wait_until_nginx_has_exited?
87
87
  rescue Interrupt
88
88
  stop_threads
89
89
  stop_nginx
@@ -101,6 +101,7 @@ class StartCommand < Command
101
101
  stop_nginx
102
102
  raise
103
103
  ensure
104
+ stop_touching_temp_dir_in_background if should_wait_until_nginx_has_exited?
104
105
  stop_threads
105
106
  end
106
107
  ensure
@@ -166,6 +167,18 @@ private
166
167
  wrap_desc("Disable passenger_friendly_error_pages")) do
167
168
  @options[:friendly_error_pages] = false
168
169
  end
170
+ opts.on("--ssl",
171
+ wrap_desc("Enable SSL support")) do
172
+ @options[:ssl] = true
173
+ end
174
+ opts.on("--ssl-certificate PATH", String,
175
+ wrap_desc("Specify the SSL certificate path")) do |val|
176
+ @options[:ssl_certificate] = File.expand_path(val)
177
+ end
178
+ opts.on("--ssl-certificate-key PATH", String,
179
+ wrap_desc("Specify the SSL key path")) do |val|
180
+ @options[:ssl_certificate_key] = File.expand_path(val)
181
+ end
169
182
  opts.on("--union-station-gateway HOST:PORT", String,
170
183
  wrap_desc("Specify Union Station Gateway host and port")) do |value|
171
184
  host, port = value.split(":", 2)
@@ -248,6 +261,14 @@ private
248
261
  error "You cannot specify both --address/--port and --socket. Please choose either one."
249
262
  exit 1
250
263
  end
264
+ if @options[:ssl] && !@options[:ssl_certificate]
265
+ error "You specified --ssl. Please specify --ssl-certificate as well."
266
+ exit 1
267
+ end
268
+ if @options[:ssl] && !@options[:ssl_certificate_key]
269
+ error "You specified --ssl. Please specify --ssl-certificate-key as well."
270
+ exit 1
271
+ end
251
272
  check_port_bind_permission_and_display_sudo_suggestion
252
273
  check_port_availability
253
274
  end
@@ -361,12 +382,21 @@ private
361
382
  return !@options[:daemonize] && @options[:log_file] != "/dev/null"
362
383
  end
363
384
 
385
+ def should_wait_until_nginx_has_exited?
386
+ return !@options[:daemonize]
387
+ end
388
+
364
389
  # Returns the URL that Nginx will be listening on.
365
390
  def listen_url
366
391
  if @options[:socket_file]
367
392
  return @options[:socket_file]
368
393
  else
369
- result = "http://#{@options[:address]}"
394
+ if @options[:ssl]
395
+ scheme = "https"
396
+ else
397
+ scheme = "http"
398
+ end
399
+ result = "#{scheme}://#{@options[:address]}"
370
400
  if @options[:port] != 80
371
401
  result << ":#{@options[:port]}"
372
402
  end
@@ -524,13 +554,19 @@ private
524
554
  end
525
555
 
526
556
  def touch_temp_dir_in_background
527
- @interruptable_threads << Thread.new do
528
- while true
529
- # Touch the temp dir every 30 minutes to prevent
530
- # /tmp cleaners from removing it.
531
- sleep 60 * 30
532
- system("find '#{@temp_dir}' | xargs touch")
557
+ require 'shellwords'
558
+ script = Shellwords.escape("#{PhusionPassenger.helper_scripts_dir}/touch-dir.sh")
559
+ dir = Shellwords.escape(@temp_dir)
560
+ @toucher = IO.popen("sh #{script} #{dir}", "r")
561
+ end
562
+
563
+ def stop_touching_temp_dir_in_background
564
+ if @toucher
565
+ begin
566
+ Process.kill('TERM', @toucher.pid)
567
+ rescue Errno::ESRCH, Errno::ECHILD
533
568
  end
569
+ @toucher.close
534
570
  end
535
571
  end
536
572
 
@@ -22,7 +22,10 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  # THE SOFTWARE.
24
24
 
25
- require 'rubygems'
25
+ begin
26
+ require 'rubygems'
27
+ rescue LoadError
28
+ end
26
29
  require 'phusion_passenger/native_support'
27
30
 
28
31
  module PhusionPassenger
@@ -5,4 +5,6 @@ This installer must be able to write to the following directory:
5
5
  <b><%= @dir %></b>
6
6
 
7
7
  But it can't do that, because you're running the installer as <b><%= `whoami`.strip %></b>.
8
- Please re-run this installer as <b>root</b>.
8
+ Please re-run this installer using <%= @sudo %>:
9
+
10
+ <b><%= @sudo_e %> <%= @ruby %> <%= @installer %></b>
@@ -0,0 +1,6 @@
1
+ <red>Nginx module sources not available</red>
2
+
3
+ You didn't install the Phusion Passenger Nginx module sources. If you're on
4
+ Debian or Ubuntu, please run <b>sudo apt-get install ruby-passenger-dev</b>
5
+ first. If you're not on Debian/Ubuntu, or if you cannot run apt-get, please
6
+ install Phusion Passenger from source: https://www.phusionpassenger.com/
@@ -10,13 +10,6 @@ worker_processes 1;
10
10
  daemon on;
11
11
  error_log '<%= @options[:log_file] %>' <% if debugging? %>info<% end %>;
12
12
  pid '<%= @options[:pid_file] %>';
13
- <% if Process.euid == 0 %>
14
- <% if @options[:user] %>
15
- user <%= @options[:user] %> <%= default_group_for(@options[:user]) %>;
16
- <% else %>
17
- user <%= current_user %> <%= default_group_for(current_user) %>;
18
- <% end %>
19
- <% end %>
20
13
 
21
14
  events {
22
15
  worker_connections 1024;
@@ -34,9 +27,11 @@ http {
34
27
  <% if @options[:user] %>
35
28
  passenger_user <%= @options[:user] %>;
36
29
  passenger_default_user <%= @options[:user] %>;
30
+ passenger_analytics_log_user <%= @options[:user] %>;
37
31
  <% else %>
38
32
  passenger_user <%= current_user %>;
39
33
  passenger_default_user <%= current_user %>;
34
+ passenger_analytics_log_user <%= current_user %>;
40
35
  <% end %>
41
36
  <% if debugging? %>passenger_log_level 2;<% end %>
42
37
  <% if @options[:rolling_restarts] %>passenger_rolling_restarts on;<% end %>
@@ -79,7 +74,7 @@ http {
79
74
 
80
75
  <% for app in @apps %>
81
76
  server {
82
- listen <%= nginx_listen_address(app) %>;
77
+ listen <%= nginx_listen_address(app) %> <%= "ssl" if app[:ssl] %>;
83
78
  server_name <%= app[:server_names].join(' ') %>;
84
79
  root '<%= app[:root] %>/public';
85
80
  passenger_app_root '<%= app[:root] %>';
@@ -91,6 +86,11 @@ http {
91
86
  union_station_support on;
92
87
  union_station_key <%= app[:union_station_key] %>;
93
88
  <% end %>
89
+ <% if app[:ssl] %>
90
+ ssl on;
91
+ ssl_certificate <%= app[:ssl_certificate] %>;
92
+ ssl_certificate_key <%= app[:ssl_certificate_key] %>;
93
+ <% end %>
94
94
 
95
95
  # Rails asset pipeline support.
96
96
  location ~ ^/assets/ {
@@ -961,7 +961,8 @@ namespace tut {
961
961
  }
962
962
 
963
963
  TEST_METHOD(41) {
964
- // Disabling the sole process in a group should trigger a new process spawn.
964
+ // Disabling the sole process in a group, in case the pool settings allow
965
+ // spawning another process, should trigger a new process spawn.
965
966
  ensureMinProcesses(1);
966
967
  Options options = createOptions();
967
968
  SessionPtr session = pool->get(options, &ticket);
@@ -987,6 +988,20 @@ namespace tut {
987
988
  }
988
989
 
989
990
  TEST_METHOD(42) {
991
+ // Disabling the sole process in a group, in case pool settings don't allow
992
+ // spawning another process, should fail.
993
+ pool->setMax(1);
994
+ ensureMinProcesses(1);
995
+
996
+ vector<ProcessPtr> processes = pool->getProcesses();
997
+ ensure_equals("(1)", processes.size(), 1u);
998
+
999
+ DisableResult result = pool->disableProcess(processes[0]->gupid);
1000
+ ensure_equals("(2)", result, DR_ERROR);
1001
+ ensure_equals("(3)", pool->getProcessCount(), 1u);
1002
+ }
1003
+
1004
+ TEST_METHOD(43) {
990
1005
  // If there are no enabled processes in the group, then disabling should
991
1006
  // succeed after the new process has been spawned.
992
1007
  initPoolDebugging();
@@ -1031,7 +1046,7 @@ namespace tut {
1031
1046
  }
1032
1047
  }
1033
1048
 
1034
- TEST_METHOD(43) {
1049
+ TEST_METHOD(44) {
1035
1050
  // Suppose that a previous disable command triggered a new process spawn,
1036
1051
  // and the spawn fails. Then any disabling processes should become enabled
1037
1052
  // again, and the callbacks for the previous disable commands should be called.
@@ -1116,6 +1131,27 @@ namespace tut {
1116
1131
  // TODO: Enabling a process that's disabling succeeds immediately. The disable
1117
1132
  // callbacks will be called with DR_CANCELED.
1118
1133
 
1134
+ TEST_METHOD(51) {
1135
+ // If the number of processes is already at maximum, then disabling
1136
+ // a process will cause that process to be disabled, without spawning
1137
+ // a new process.
1138
+ pool->setMax(2);
1139
+ ensureMinProcesses(2);
1140
+
1141
+ vector<ProcessPtr> processes = pool->getProcesses();
1142
+ ensure_equals(processes.size(), 2u);
1143
+ DisableResult result = pool->disableProcess(processes[0]->gupid);
1144
+ ensure_equals(result, DR_SUCCESS);
1145
+
1146
+ {
1147
+ ScopedLock l(pool->syncher);
1148
+ GroupPtr group = processes[0]->getGroup();
1149
+ ensure_equals(group->enabledCount, 1);
1150
+ ensure_equals(group->disablingCount, 0);
1151
+ ensure_equals(group->disabledCount, 1);
1152
+ }
1153
+ }
1154
+
1119
1155
 
1120
1156
  /*********** Other tests ***********/
1121
1157
 
@@ -1587,6 +1623,87 @@ namespace tut {
1587
1623
  }
1588
1624
  }
1589
1625
 
1626
+ TEST_METHOD(76) {
1627
+ // No more than maxOutOfBandWorkInstances process will be performing
1628
+ // out-of-band work at the same time.
1629
+ TempDirCopy dir("stub/wsgi", "tmp.wsgi");
1630
+ Options options = createOptions();
1631
+ options.appRoot = "tmp.wsgi";
1632
+ options.appType = "wsgi";
1633
+ options.spawnMethod = "direct";
1634
+ options.maxOutOfBandWorkInstances = 2;
1635
+ initPoolDebugging();
1636
+ debug->restarting = false;
1637
+ debug->spawning = false;
1638
+ debug->oobw = true;
1639
+
1640
+ // Spawn 3 processes and initiate 2 OOBW requests.
1641
+ SessionPtr session1 = pool->get(options, &ticket);
1642
+ SessionPtr session2 = pool->get(options, &ticket);
1643
+ SessionPtr session3 = pool->get(options, &ticket);
1644
+ session1->requestOOBW();
1645
+ session1.reset();
1646
+ session2->requestOOBW();
1647
+ session2.reset();
1648
+
1649
+ // 2 OOBW requests eventually start.
1650
+ debug->debugger->recv("OOBW request about to start");
1651
+ debug->debugger->recv("OOBW request about to start");
1652
+
1653
+ // Request another OOBW, but this one is not initiated.
1654
+ session3->requestOOBW();
1655
+ session3.reset();
1656
+ SHOULD_NEVER_HAPPEN(100,
1657
+ result = debug->debugger->peek("OOBW request about to start") != NULL;
1658
+ );
1659
+
1660
+ // Let one OOBW request finish. The third one should eventually
1661
+ // start.
1662
+ debug->messages->send("Proceed with OOBW request");
1663
+ debug->debugger->recv("OOBW request about to start");
1664
+
1665
+ debug->messages->send("Proceed with OOBW request");
1666
+ debug->messages->send("Proceed with OOBW request");
1667
+ debug->debugger->recv("OOBW request finished");
1668
+ debug->debugger->recv("OOBW request finished");
1669
+ debug->debugger->recv("OOBW request finished");
1670
+ }
1671
+
1672
+ TEST_METHOD(77) {
1673
+ // If the getWaitlist already has maxRequestQueueSize items,
1674
+ // then an exception is returned.
1675
+ Options options = createOptions();
1676
+ options.appGroupName = "test1";
1677
+ options.maxRequestQueueSize = 3;
1678
+ GroupPtr group = pool->findOrCreateGroup(options);
1679
+ spawnerConfig->concurrency = 3;
1680
+ initPoolDebugging();
1681
+ pool->setMax(1);
1682
+
1683
+ for (int i = 0; i < 3; i++) {
1684
+ pool->asyncGet(options, callback);
1685
+ }
1686
+ ensure_equals(number, 0);
1687
+ {
1688
+ LockGuard l(pool->syncher);
1689
+ ensure_equals(group->getWaitlist.size(),
1690
+ 3u);
1691
+ }
1692
+
1693
+ try {
1694
+ pool->get(options, &ticket);
1695
+ fail("Expected RequestQueueFullException");
1696
+ } catch (const RequestQueueFullException &e) {
1697
+ // OK
1698
+ }
1699
+
1700
+ debug->messages->send("Proceed with spawn loop iteration 1");
1701
+ debug->messages->send("Spawn loop done");
1702
+ EVENTUALLY(5,
1703
+ result = number == 3;
1704
+ );
1705
+ }
1706
+
1590
1707
  // TODO: Persistent connections.
1591
1708
  // TODO: If one closes the session before it has reached EOF, and process's maximum concurrency
1592
1709
  // has already been reached, then the pool should ping the process so that it can detect
@@ -1595,7 +1712,7 @@ namespace tut {
1595
1712
 
1596
1713
  /*********** Test previously discovered bugs ***********/
1597
1714
 
1598
- TEST_METHOD(76) {
1715
+ TEST_METHOD(79) {
1599
1716
  // Test detaching, then restarting. This should not violate any invariants.
1600
1717
  TempDirCopy dir("stub/wsgi", "tmp.wsgi");
1601
1718
  Options options = createOptions();