passenger 4.0.2 → 4.0.3

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 (79) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +27 -0
  3. data/bin/passenger-config +6 -3
  4. data/bin/passenger-install-apache2-module +2 -2
  5. data/bin/passenger-install-nginx-module +16 -2
  6. data/build/agents.rb +4 -0
  7. data/build/apache2.rb +1 -1
  8. data/build/cplusplus_support.rb +1 -1
  9. data/build/cxx_tests.rb +3 -0
  10. data/build/packaging.rb +51 -8
  11. data/build/ruby_extension.rb +1 -1
  12. data/doc/Packaging.txt.md +20 -7
  13. data/doc/Users guide Apache.html +1 -1
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +5 -4
  16. data/doc/Users guide Nginx.txt +1 -1
  17. data/doc/users_guide_snippets/installation.txt +5 -3
  18. data/ext/apache2/Configuration.cpp +12 -0
  19. data/ext/apache2/Configuration.hpp +7 -4
  20. data/ext/apache2/Hooks.cpp +29 -19
  21. data/ext/common/AgentsStarter.cpp +85 -57
  22. data/ext/common/AgentsStarter.h +570 -42
  23. data/ext/common/ApplicationPool2/DirectSpawner.h +5 -2
  24. data/ext/common/ApplicationPool2/Implementation.cpp +7 -1
  25. data/ext/common/ApplicationPool2/Pool.h +6 -3
  26. data/ext/common/ApplicationPool2/Process.h +12 -3
  27. data/ext/common/ApplicationPool2/SmartSpawner.h +2 -1
  28. data/ext/common/Constants.h +4 -1
  29. data/ext/common/EventedBufferedInput.h +139 -16
  30. data/ext/common/MultiLibeio.cpp +4 -2
  31. data/ext/common/SafeLibev.h +15 -62
  32. data/ext/common/ServerInstanceDir.h +10 -26
  33. data/ext/common/Utils.cpp +1 -3
  34. data/ext/common/Utils.h +1 -1
  35. data/ext/common/Utils/StrIntUtils.cpp +9 -0
  36. data/ext/common/Utils/StrIntUtils.h +5 -0
  37. data/ext/common/Utils/VariantMap.h +63 -14
  38. data/ext/common/agents/Base.cpp +50 -15
  39. data/ext/common/agents/HelperAgent/AgentOptions.h +20 -12
  40. data/ext/common/agents/HelperAgent/FileBackedPipe.h +1 -1
  41. data/ext/common/agents/HelperAgent/Main.cpp +5 -4
  42. data/ext/common/agents/HelperAgent/RequestHandler.h +1 -1
  43. data/ext/common/agents/LoggingAgent/Main.cpp +0 -1
  44. data/ext/common/agents/LoggingAgent/RemoteSender.h +2 -2
  45. data/ext/common/agents/SpawnPreparer.cpp +23 -5
  46. data/ext/common/agents/Watchdog/AgentWatcher.cpp +508 -0
  47. data/ext/common/agents/Watchdog/HelperAgentWatcher.cpp +93 -0
  48. data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +68 -0
  49. data/ext/common/agents/Watchdog/Main.cpp +180 -802
  50. data/ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp +111 -0
  51. data/ext/nginx/Configuration.c +107 -92
  52. data/ext/nginx/Configuration.h +1 -0
  53. data/ext/nginx/ContentHandler.c +6 -6
  54. data/ext/nginx/ContentHandler.h +1 -1
  55. data/ext/nginx/config +8 -2
  56. data/ext/nginx/ngx_http_passenger_module.c +54 -60
  57. data/ext/nginx/ngx_http_passenger_module.h +6 -6
  58. data/lib/phusion_passenger.rb +17 -10
  59. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  60. data/lib/phusion_passenger/common_library.rb +0 -1
  61. data/lib/phusion_passenger/platform_info.rb +10 -1
  62. data/lib/phusion_passenger/platform_info/depcheck.rb +4 -4
  63. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +2 -2
  64. data/lib/phusion_passenger/platform_info/ruby.rb +7 -0
  65. data/lib/phusion_passenger/request_handler.rb +119 -42
  66. data/lib/phusion_passenger/request_handler/thread_handler.rb +25 -22
  67. data/lib/phusion_passenger/standalone/command.rb +2 -0
  68. data/lib/phusion_passenger/standalone/runtime_installer.rb +4 -3
  69. data/lib/phusion_passenger/standalone/start_command.rb +49 -37
  70. data/resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb +11 -0
  71. data/test/cxx/CxxTestMain.cpp +2 -0
  72. data/test/cxx/EventedBufferedInputTest.cpp +758 -0
  73. data/test/cxx/ServerInstanceDirTest.cpp +16 -31
  74. data/test/cxx/TestSupport.cpp +2 -1
  75. data/test/cxx/VariantMapTest.cpp +23 -11
  76. metadata +8 -4
  77. metadata.gz.asc +7 -7
  78. data/ext/common/AgentsStarter.hpp +0 -655
  79. data/lib/phusion_passenger/utils/robust_interruption.rb +0 -173
@@ -26,7 +26,6 @@ require 'phusion_passenger/debug_logging'
26
26
  require 'phusion_passenger/message_channel'
27
27
  require 'phusion_passenger/utils'
28
28
  require 'phusion_passenger/utils/unseekable_socket'
29
- require 'phusion_passenger/utils/robust_interruption'
30
29
 
31
30
  module PhusionPassenger
32
31
  class RequestHandler
@@ -36,7 +35,9 @@ class RequestHandler
36
35
  class ThreadHandler
37
36
  include DebugLogging
38
37
  include Utils
39
- include Utils::RobustInterruption
38
+
39
+ class Interrupted < StandardError
40
+ end
40
41
 
41
42
  REQUEST_METHOD = 'REQUEST_METHOD'.freeze
42
43
  PING = 'PING'.freeze
@@ -53,9 +54,10 @@ class ThreadHandler
53
54
  GC_SUPPORTS_TIME = GC.respond_to?(:time)
54
55
  GC_SUPPORTS_CLEAR_STATS = GC.respond_to?(:clear_stats)
55
56
 
57
+ attr_reader :thread
56
58
  attr_reader :stats_mutex
57
- attr_reader :iterations
58
- attr_reader :processed_requests
59
+ attr_reader :interruptable
60
+ attr_reader :iteration
59
61
 
60
62
  def initialize(request_handler, options = {})
61
63
  @request_handler = request_handler
@@ -69,9 +71,9 @@ class ThreadHandler
69
71
  :connect_password
70
72
  )
71
73
 
72
- @stats_mutex = Mutex.new
73
- @iterations = 0
74
- @processed_requests = 0
74
+ @stats_mutex = Mutex.new
75
+ @interruptable = false
76
+ @iteration = 0
75
77
 
76
78
  if @protocol == :session
77
79
  metaclass = class << self; self; end
@@ -89,8 +91,8 @@ class ThreadHandler
89
91
  end
90
92
 
91
93
  def install
92
- Thread.current[:handler] = self
93
- install_robust_interruption
94
+ @thread = Thread.current
95
+ Thread.current[:passenger_thread_handler] = self
94
96
  PhusionPassenger.call_event(:starting_request_handler_thread)
95
97
  end
96
98
 
@@ -102,26 +104,29 @@ class ThreadHandler
102
104
 
103
105
  begin
104
106
  finish_callback.call
105
- while !Utils::RobustInterruption.interrupted?
107
+ while true
106
108
  hijacked = accept_and_process_next_request(socket_wrapper, channel, buffer)
107
109
  socket_wrapper = Utils::UnseekableSocket.new if hijacked
108
110
  end
109
- rescue Utils::RobustInterruption::Interrupted
111
+ rescue Interrupted
110
112
  # Do nothing.
111
113
  end
112
114
  debug("Thread handler main loop exited normally")
113
- end
114
-
115
- def idle?
116
- @stats_mutex.synchronize { return !@processing }
115
+ ensure
116
+ @stats_mutex.synchronize { @interruptable = true }
117
117
  end
118
118
 
119
119
  private
120
120
  # Returns true if the socket has been hijacked, false otherwise.
121
121
  def accept_and_process_next_request(socket_wrapper, channel, buffer)
122
- @stats_mutex.synchronize { @iterations += 1 }
123
- connection = enable_interruptions { socket_wrapper.wrap(@server_socket.accept) }
124
- @stats_mutex.synchronize { @processing = true }
122
+ @stats_mutex.synchronize do
123
+ @interruptable = true
124
+ end
125
+ connection = socket_wrapper.wrap(@server_socket.accept)
126
+ @stats_mutex.synchronize do
127
+ @interruptable = false
128
+ @iteration += 1
129
+ end
125
130
  trace(3, "Accepted new request on socket #{@socket_name}")
126
131
  channel.io = connection
127
132
  if headers = parse_request(connection, channel, buffer)
@@ -149,6 +154,8 @@ private
149
154
  else
150
155
  trace(2, "No headers parsed; disconnecting client.")
151
156
  end
157
+ rescue Interrupted
158
+ raise
152
159
  rescue => e
153
160
  if socket_wrapper && socket_wrapper.source_of_exception?(e)
154
161
  # EPIPE is harmless, it just means that the client closed the connection.
@@ -177,10 +184,6 @@ private
177
184
  rescue SystemCallError
178
185
  end
179
186
  end
180
- @stats_mutex.synchronize do
181
- @processed_requests += 1
182
- @processing = false
183
- end
184
187
  end
185
188
 
186
189
  def parse_session_request(connection, channel, buffer)
@@ -190,8 +190,10 @@ private
190
190
  else
191
191
  f.puts "agents=#{@runtime_dirs[:support_dir]}"
192
192
  end
193
+ f.puts "libdir=#{PhusionPassenger.lib_dir}"
193
194
  f.puts "helper_scripts=#{PhusionPassenger.helper_scripts_dir}"
194
195
  f.puts "resources=#{PhusionPassenger.resources_dir}"
196
+ f.puts "includedir=#{PhusionPassenger.include_dir}"
195
197
  f.puts "doc=#{PhusionPassenger.doc_dir}"
196
198
  f.puts "rubylib=#{PhusionPassenger.ruby_libdir}"
197
199
  f.puts "apache2_module=#{PhusionPassenger.apache2_module_path}"
@@ -95,7 +95,7 @@ protected
95
95
  'g++',
96
96
  'gmake',
97
97
  'download-tool',
98
- 'ruby-dev',
98
+ PlatformInfo.passenger_needs_ruby_dev_header? ? 'ruby-dev' : nil,
99
99
  'ruby-openssl',
100
100
  'rubygems',
101
101
  'rake',
@@ -105,7 +105,7 @@ protected
105
105
  'zlib-dev',
106
106
  'pcre-dev',
107
107
  'daemon_controller >= 1.1.0'
108
- ]
108
+ ].compact
109
109
  return [specs, ids]
110
110
  end
111
111
 
@@ -529,7 +529,8 @@ private
529
529
  nginx_libs = COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR).
530
530
  set_output_dir(output_dir).
531
531
  link_objects_as_string
532
- command << "env PASSENGER_LIBS='#{nginx_libs} #{output_dir}/../libboost_oxt.a' "
532
+ command << "env PASSENGER_INCLUDEDIR='#{PhusionPassenger.include_dir}'" <<
533
+ " PASSENGER_LIBS='#{nginx_libs} #{output_dir}/../libboost_oxt.a' "
533
534
  end
534
535
  # RPM thinks it's being smart by scanning binaries for
535
536
  # paths and refusing to create package if it detects any
@@ -42,7 +42,7 @@ class StartCommand < Command
42
42
  def self.description
43
43
  return "Start Phusion Passenger Standalone."
44
44
  end
45
-
45
+
46
46
  def initialize(args)
47
47
  super(args)
48
48
  @console_mutex = Mutex.new
@@ -51,11 +51,11 @@ class StartCommand < Command
51
51
  @interruptable_threads = []
52
52
  @plugin = PhusionPassenger::Plugin.new('standalone/start_command', self, @options)
53
53
  end
54
-
54
+
55
55
  def run
56
56
  parse_my_options
57
57
  sanity_check_options
58
-
58
+
59
59
  @runtime_dirs = determine_runtime_dirs
60
60
  ensure_nginx_installed
61
61
  determine_various_resource_locations
@@ -63,15 +63,21 @@ class StartCommand < Command
63
63
  @app_finder = AppFinder.new(@args, @options)
64
64
  @apps = @app_finder.scan
65
65
  @plugin.call_hook(:found_apps, @apps)
66
-
66
+
67
67
  extra_controller_options = {}
68
68
  @plugin.call_hook(:before_creating_nginx_controller, extra_controller_options)
69
69
  create_nginx_controller(extra_controller_options)
70
-
70
+
71
71
  begin
72
72
  start_nginx
73
73
  show_intro_message
74
- daemonize if @options[:daemonize]
74
+ if @options[:daemonize]
75
+ if PlatformInfo.ruby_supports_fork?
76
+ daemonize
77
+ else
78
+ daemonize_without_fork
79
+ end
80
+ end
75
81
  Thread.abort_on_exception = true
76
82
  @plugin.call_hook(:nginx_started, @nginx)
77
83
  ########################
@@ -108,7 +114,7 @@ private
108
114
  def require_file_utils
109
115
  require 'fileutils' unless defined?(FileUtils)
110
116
  end
111
-
117
+
112
118
  def parse_my_options
113
119
  description = "Starts Phusion Passenger Standalone and serve one or more Ruby web applications."
114
120
  parse_options!("start [directory]", description) do |opts|
@@ -126,7 +132,7 @@ private
126
132
  wrap_desc("Bind to Unix domain socket instead of TCP socket")) do |value|
127
133
  @options[:socket_file] = value
128
134
  end
129
-
135
+
130
136
  opts.separator ""
131
137
  opts.on("-e", "--environment ENV", String,
132
138
  wrap_desc("Framework environment (default: #{@options[:env]})")) do |value|
@@ -168,14 +174,14 @@ private
168
174
  wrap_desc("Specify Union Station key")) do |value|
169
175
  @options[:union_station_key] = value
170
176
  end
171
-
177
+
172
178
  opts.separator ""
173
179
  opts.on("--ping-port NUMBER", Integer,
174
180
  wrap_desc("Use the given port number for checking whether Nginx is alive (default: same as the normal port)")) do |value|
175
181
  @options[:ping_port] = value
176
182
  end
177
183
  @plugin.call_hook(:parse_options, opts)
178
-
184
+
179
185
  opts.separator ""
180
186
  opts.on("-d", "--daemonize",
181
187
  wrap_desc("Daemonize into the background")) do
@@ -218,7 +224,7 @@ private
218
224
  end
219
225
  @plugin.call_hook(:done_parsing_options)
220
226
  end
221
-
227
+
222
228
  def sanity_check_options
223
229
  if @options[:tcp_explicitly_given] && @options[:socket_file]
224
230
  error "You cannot specify both --address/--port and --socket. Please choose either one."
@@ -227,7 +233,7 @@ private
227
233
  check_port_bind_permission_and_display_sudo_suggestion
228
234
  check_port_availability
229
235
  end
230
-
236
+
231
237
  # Most platforms don't allow non-root processes to bind to a port lower than 1024.
232
238
  # Check whether this is the case for the current platform and if so, tell the user
233
239
  # that it must re-run Phusion Passenger Standalone with sudo.
@@ -241,9 +247,9 @@ private
241
247
  error "Only the 'root' user can run this program on port #{@options[:port]}. " <<
242
248
  "You are currently running as '#{myself}'. Please re-run this program " <<
243
249
  "with root privileges with the following command:\n\n" <<
244
-
250
+
245
251
  " #{PlatformInfo.ruby_sudo_command} passenger start #{@original_args.join(' ')} --user=#{myself}\n\n" <<
246
-
252
+
247
253
  "Don't forget the '--user' part! That will make Phusion Passenger Standalone " <<
248
254
  "drop root privileges and switch to '#{myself}' after it has obtained " <<
249
255
  "port #{@options[:port]}."
@@ -254,7 +260,7 @@ private
254
260
 
255
261
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
256
262
  require 'java'
257
-
263
+
258
264
  def check_port(host_name, port)
259
265
  channel = java.nio.channels.SocketChannel.open
260
266
  begin
@@ -278,7 +284,7 @@ private
278
284
  throw e
279
285
  end
280
286
  end
281
-
287
+
282
288
  # Not done connecting and no error.
283
289
  sleep 0.01
284
290
  if Time.now.to_f >= deadline
@@ -320,7 +326,7 @@ private
320
326
  end
321
327
  end
322
328
  end
323
-
329
+
324
330
  def check_port_availability
325
331
  if !@options[:socket_file] && check_port(@options[:address], @options[:port])
326
332
  error "The address #{@options[:address]}:#{@options[:port]} is already " <<
@@ -332,11 +338,11 @@ private
332
338
  exit 1
333
339
  end
334
340
  end
335
-
341
+
336
342
  def should_watch_logs?
337
343
  return !@options[:daemonize] && @options[:log_file] != "/dev/null"
338
344
  end
339
-
345
+
340
346
  # Returns the URL that Nginx will be listening on.
341
347
  def listen_url
342
348
  if @options[:socket_file]
@@ -350,7 +356,7 @@ private
350
356
  return result
351
357
  end
352
358
  end
353
-
359
+
354
360
  def install_runtime(runtime_dirs)
355
361
  require 'phusion_passenger/standalone/runtime_installer'
356
362
  installer = RuntimeInstaller.new(
@@ -364,7 +370,7 @@ private
364
370
  :plugin => @plugin)
365
371
  installer.run
366
372
  end
367
-
373
+
368
374
  def determine_runtime_dirs
369
375
  require_platform_info_binary_compatibility
370
376
  if root = @options[:runtime_dir]
@@ -405,7 +411,7 @@ private
405
411
  end
406
412
  end
407
413
  end
408
-
414
+
409
415
  def start_nginx
410
416
  begin
411
417
  @nginx.start
@@ -426,15 +432,15 @@ private
426
432
  exit 1
427
433
  end
428
434
  end
429
-
435
+
430
436
  def show_intro_message
431
437
  puts "=============== Phusion Passenger Standalone web server started ==============="
432
438
  puts "PID file: #{@options[:pid_file]}"
433
439
  puts "Log file: #{@options[:log_file]}"
434
440
  puts "Environment: #{@options[:env]}"
435
-
441
+
436
442
  puts "Accessible via: #{listen_url}"
437
-
443
+
438
444
  puts
439
445
  if @options[:daemonize]
440
446
  puts "Serving in the background as a daemon."
@@ -443,7 +449,13 @@ private
443
449
  end
444
450
  puts "==============================================================================="
445
451
  end
446
-
452
+
453
+ def daemonize_without_fork
454
+ STDERR.puts "Unable to daemonize using the current Ruby interpreter " +
455
+ "(#{PlatformInfo.ruby_command}) because it does not support forking."
456
+ exit 1
457
+ end
458
+
447
459
  def daemonize
448
460
  pid = fork
449
461
  if pid
@@ -460,7 +472,7 @@ private
460
472
  Process.setsid
461
473
  end
462
474
  end
463
-
475
+
464
476
  # Wait until the termination pipe becomes readable (a hint for threads
465
477
  # to shut down), or until the timeout has been reached. Returns true if
466
478
  # the termination pipe became readable, false if the timeout has been reached.
@@ -468,7 +480,7 @@ private
468
480
  ios = select([@termination_pipe[0]], nil, nil, timeout)
469
481
  return !ios.nil?
470
482
  end
471
-
483
+
472
484
  def watch_log_file(log_file)
473
485
  if File.exist?(log_file)
474
486
  backward = 0
@@ -479,7 +491,7 @@ private
479
491
  end
480
492
  backward = 10
481
493
  end
482
-
494
+
483
495
  IO.popen("tail -f -n #{backward} \"#{log_file}\"", "rb") do |f|
484
496
  begin
485
497
  while true
@@ -498,7 +510,7 @@ private
498
510
  end
499
511
  end
500
512
  end
501
-
513
+
502
514
  def watch_log_files_in_background
503
515
  @apps.each do |app|
504
516
  thread = Thread.new do
@@ -513,7 +525,7 @@ private
513
525
  @threads << thread
514
526
  @interruptable_threads << thread
515
527
  end
516
-
528
+
517
529
  def wait_until_nginx_has_exited
518
530
  # Since Nginx is not our child process (it daemonizes or we daemonize)
519
531
  # we cannot use Process.waitpid to wait for it. A busy-sleep-loop with
@@ -535,7 +547,7 @@ private
535
547
  end
536
548
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET
537
549
  end
538
-
550
+
539
551
  def stop_nginx
540
552
  @console_mutex.synchronize do
541
553
  STDOUT.write("Stopping web server...")
@@ -545,7 +557,7 @@ private
545
557
  STDOUT.flush
546
558
  end
547
559
  end
548
-
560
+
549
561
  def stop_threads
550
562
  if !@termination_pipe[1].closed?
551
563
  @termination_pipe[1].write("x")
@@ -560,9 +572,9 @@ private
560
572
  end
561
573
  @threads = []
562
574
  end
563
-
575
+
564
576
  #### Config file template helpers ####
565
-
577
+
566
578
  def nginx_listen_address(options = @options, for_ping_port = false)
567
579
  if options[:socket_file]
568
580
  return "unix:" + File.expand_path(options[:socket_file])
@@ -575,13 +587,13 @@ private
575
587
  return "#{options[:address]}:#{port}"
576
588
  end
577
589
  end
578
-
590
+
579
591
  def default_group_for(username)
580
592
  user = Etc.getpwnam(username)
581
593
  group = Etc.getgrgid(user.gid)
582
594
  return group.name
583
595
  end
584
-
596
+
585
597
  #################
586
598
  end
587
599
 
@@ -0,0 +1,11 @@
1
+ <red><b>The PCRE checksum could not be verified</b></red>
2
+
3
+ Nginx requires PCRE for its rewrite module, so this installer will attempt to
4
+ install Nginx without the rewrite module.
5
+
6
+ If you want to make use of Nginx's rewrite module, please install PCRE manually
7
+ by downloading it from:
8
+
9
+ http://www.pcre.org/
10
+
11
+ Press ENTER to continue, or Ctrl-C to abort.