passenger 5.0.6 → 5.0.7

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 (74) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +26 -0
  5. data/Rakefile +0 -1
  6. data/bin/passenger-install-apache2-module +46 -12
  7. data/bin/passenger-status +6 -3
  8. data/build/packaging.rb +9 -1
  9. data/dev/ci/run_travis.sh +0 -36
  10. data/doc/ServerOptimizationGuide.html +12 -11
  11. data/doc/ServerOptimizationGuide.txt.md +12 -11
  12. data/doc/Users guide Apache.html +81 -75
  13. data/doc/Users guide Apache.idmap.txt +15 -13
  14. data/doc/Users guide Apache.txt +9 -1
  15. data/doc/Users guide Nginx.html +81 -76
  16. data/doc/Users guide Nginx.idmap.txt +15 -13
  17. data/doc/Users guide Nginx.txt +8 -0
  18. data/doc/Users guide Standalone.html +183 -24
  19. data/doc/Users guide Standalone.idmap.txt +19 -11
  20. data/doc/Users guide Standalone.txt +4 -0
  21. data/doc/users_guide_snippets/environment_variables.txt +2 -1
  22. data/doc/users_guide_snippets/installation.txt +15 -2
  23. data/doc/users_guide_snippets/tips.txt +19 -31
  24. data/doc/users_guide_snippets/under_the_hood/relationship_with_ruby.txt +7 -0
  25. data/ext/apache2/ConfigurationCommands.cpp +7 -0
  26. data/ext/apache2/ConfigurationFields.hpp +2 -0
  27. data/ext/apache2/ConfigurationSetters.cpp +8 -0
  28. data/ext/apache2/CreateDirConfig.cpp +1 -0
  29. data/ext/apache2/MergeDirConfig.cpp +7 -0
  30. data/ext/apache2/SetHeaders.cpp +5 -0
  31. data/ext/common/ApplicationPool2/Options.h +9 -0
  32. data/ext/common/Constants.h +3 -1
  33. data/ext/common/Logging.cpp +2 -2
  34. data/ext/common/ServerKit/HttpHeaderParser.h +13 -1
  35. data/ext/common/ServerKit/Implementation.cpp +7 -1
  36. data/ext/common/agents/Base.cpp +1 -1
  37. data/ext/common/agents/HelperAgent/OptionParser.h +15 -0
  38. data/ext/common/agents/HelperAgent/RequestHandler.h +3 -1
  39. data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -3
  40. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +12 -1
  41. data/ext/common/agents/HelperAgent/RequestHandler/Utils.cpp +7 -3
  42. data/ext/common/agents/HelperAgent/ResponseCache.h +7 -1
  43. data/ext/common/agents/LoggingAgent/Main.cpp +4 -1
  44. data/ext/nginx/CacheLocationConfig.c +20 -0
  45. data/ext/nginx/Configuration.c +7 -0
  46. data/ext/nginx/ConfigurationCommands.c +10 -0
  47. data/ext/nginx/ConfigurationFields.h +2 -0
  48. data/ext/nginx/ContentHandler.c +10 -0
  49. data/ext/nginx/CreateLocationConfig.c +5 -0
  50. data/ext/nginx/MergeLocationConfig.c +6 -0
  51. data/helper-scripts/meteor-loader.rb +15 -2
  52. data/helper-scripts/rack-loader.rb +2 -6
  53. data/helper-scripts/rack-preloader.rb +1 -5
  54. data/lib/phusion_passenger.rb +3 -3
  55. data/lib/phusion_passenger/apache2/config_options.rb +5 -0
  56. data/lib/phusion_passenger/config/command.rb +9 -0
  57. data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +4 -0
  58. data/lib/phusion_passenger/config/validate_install_command.rb +478 -46
  59. data/lib/phusion_passenger/constants.rb +1 -0
  60. data/lib/phusion_passenger/loader_shared_helpers.rb +26 -3
  61. data/lib/phusion_passenger/nginx/config_options.rb +4 -0
  62. data/lib/phusion_passenger/packaging.rb +0 -8
  63. data/lib/phusion_passenger/platform_info/apache.rb +40 -28
  64. data/lib/phusion_passenger/platform_info/apache_detector.rb +29 -3
  65. data/lib/phusion_passenger/rack/thread_handler_extension.rb +12 -7
  66. data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -0
  67. data/lib/phusion_passenger/standalone/start_command.rb +46 -5
  68. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +5 -3
  69. data/resources/templates/apache2/config_snippets.txt.erb +1 -1
  70. data/resources/templates/apache2/run_installer_as_root_for_apache_analysis.txt.erb +9 -0
  71. data/resources/templates/standalone/config.erb +16 -1
  72. metadata +3 -3
  73. metadata.gz.asc +7 -7
  74. data/build/debian.rb +0 -213
@@ -87,6 +87,7 @@ module PhusionPassenger
87
87
  # Misc
88
88
  FEEDBACK_FD = 3
89
89
  PROGRAM_NAME = "Phusion Passenger"
90
+ SHORT_PROGRAM_NAME = "Passenger"
90
91
  SERVER_TOKEN_NAME = "Phusion_Passenger"
91
92
  FLYING_PASSENGER_NAME = "Flying Passenger"
92
93
  INDEX_DOC_URL = "https://www.phusionpassenger.com/documentation/Users%20guide.html"
@@ -1,6 +1,6 @@
1
1
  # encoding: binary
2
2
  # Phusion Passenger - https://www.phusionpassenger.com/
3
- # Copyright (c) 2011-2013 Phusion
3
+ # Copyright (c) 2011-2015 Phusion
4
4
  #
5
5
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  #
@@ -274,8 +274,7 @@ module PhusionPassenger
274
274
  # there's always the load_path_setup_file option and
275
275
  # setup_load_paths.rb.
276
276
  running_bundler(options) do
277
- require 'rubygems'
278
- require 'bundler/setup'
277
+ activate_gem 'bundler', 'bundler/setup'
279
278
  end
280
279
  end
281
280
 
@@ -376,6 +375,30 @@ module PhusionPassenger
376
375
  PhusionPassenger.call_event(:stopping_worker_process)
377
376
  end
378
377
 
378
+ # Activate a gem and require it. This method exists in order to load
379
+ # a library from RubyGems instead of from vendor_ruby. For example,
380
+ # on Debian systems, Rack may be installed from APT, but that is usually
381
+ # a very old version which we don't want. This method ensures that the
382
+ # RubyGems-installed version is loaded, not the the version in vendor_ruby.
383
+ # See the following threads for discussion:
384
+ # https://github.com/phusion/passenger/issues/1478
385
+ # https://github.com/phusion/passenger/issues/1480
386
+ def activate_gem(gem_name, library_name = nil)
387
+ if !defined?(::Gem)
388
+ begin
389
+ require 'rubygems'
390
+ rescue LoadError
391
+ end
392
+ end
393
+ if Kernel.respond_to?(:gem, true)
394
+ begin
395
+ gem(gem_name)
396
+ rescue Gem::LoadError
397
+ end
398
+ end
399
+ require(library_name || gem_name)
400
+ end
401
+
379
402
  private
380
403
  def running_bundler(options)
381
404
  yield
@@ -106,6 +106,10 @@ LOCATION_CONFIGURATION_OPTIONS = [
106
106
  :name => 'passenger_nodejs',
107
107
  :type => :string
108
108
  },
109
+ {
110
+ :name => 'passenger_meteor_app_settings',
111
+ :type => :string
112
+ },
109
113
  {
110
114
  :name => 'passenger_app_env',
111
115
  :type => :string,
@@ -127,14 +127,10 @@ module PhusionPassenger
127
127
  'Vagrantfile',
128
128
  'Passenger.sublime-project',
129
129
  'Passenger.xcodeproj/**/*',
130
- 'debian.template/**/*',
131
130
  'packaging/**/*',
132
131
  'test/**/*'
133
132
  ]
134
133
 
135
- # Files that should be excluded from the Debian tarball.
136
- DEBIAN_EXCLUDE_GLOB = []
137
-
138
134
  # Files and directories that should be excluded from the Homebrew installation.
139
135
  HOMEBREW_EXCLUDE = [
140
136
  "package.json", "npm-shrinkwrap.json"
@@ -145,10 +141,6 @@ module PhusionPassenger
145
141
  result.reject! { |path| path =~ %r{/\.\.?$} }
146
142
  result
147
143
  end
148
-
149
- def self.debian_orig_tarball_files
150
- files - Dir[*DEBIAN_EXCLUDE_GLOB]
151
- end
152
144
  end
153
145
 
154
146
  end # module PhusionPassenger
@@ -1,6 +1,6 @@
1
1
  # encoding: binary
2
2
  # Phusion Passenger - https://www.phusionpassenger.com/
3
- # Copyright (c) 2010-2013 Phusion
3
+ # Copyright (c) 2010-2015 Phusion
4
4
  #
5
5
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  #
@@ -26,6 +26,7 @@ PhusionPassenger.require_passenger_lib 'platform_info'
26
26
  PhusionPassenger.require_passenger_lib 'platform_info/compiler'
27
27
  PhusionPassenger.require_passenger_lib 'platform_info/operating_system'
28
28
  PhusionPassenger.require_passenger_lib 'platform_info/linux'
29
+ PhusionPassenger.require_passenger_lib 'utils/shellwords'
29
30
 
30
31
  module PhusionPassenger
31
32
 
@@ -101,47 +102,58 @@ module PhusionPassenger
101
102
  end
102
103
  memoize :httpd_version
103
104
 
104
- # Run `httpd -V` and return its output. On some systems, such as Ubuntu 13.10,
105
- # `httpd -V` fails without the environment variables defined in various scripts.
106
- # Here we take care of evaluating those scripts before running `httpd -V`.
107
- def self.httpd_V(options = nil)
105
+ # Run `apache2ctl -V` and return its output.
106
+ #
107
+ # We used to run `httpd -V`, but on systems like Ubuntu it depends on various
108
+ # environment variables or directories, wich apache2ctl loads or initializes.
109
+ def self.apache2ctl_V(options = nil)
108
110
  if options
109
- httpd = options[:httpd] || self.httpd(options)
111
+ apache2ctl = options[:apache2ctl] || self.apache2ctl(options)
110
112
  else
111
- httpd = self.httpd
113
+ apache2ctl = self.apache2ctl
112
114
  end
113
- if httpd
114
- command = "#{httpd} -V"
115
- if envvars_file = httpd_envvars_file(options)
116
- command = ". '#{envvars_file}' && #{command}"
115
+ if apache2ctl
116
+ create_temp_file("apache2ctl_V") do |filename, f|
117
+ e_filename = Shellwords.escape(filename)
118
+ output = `#{apache2ctl} -V 2>#{e_filename}`
119
+
120
+ stderr_text = File.open(filename, "rb") do |f2|
121
+ f2.read
122
+ end
123
+ # This stderr message shows up on Ubuntu. We ignore it.
124
+ stderr_text.sub!(/.*Could not reliably determine the server's fully qualified domain name.*\r?\n?/, "")
125
+ # But we print the rest of stderr.
126
+ STDERR.write(stderr_text)
127
+ STDERR.flush
128
+
129
+ output
117
130
  end
118
- return `#{command}`
119
131
  else
120
- return nil
132
+ nil
121
133
  end
122
134
  end
123
- memoize :httpd_V
135
+ memoize :apache2ctl_V
124
136
 
125
137
  # The Apache executable's architectural bits. Returns 32 or 64,
126
138
  # or nil if unable to detect.
127
139
  def self.httpd_architecture_bits(options = nil)
128
140
  if options
129
- httpd = options[:httpd] || self.httpd(options)
141
+ info = apache2ctl_V(options)
130
142
  else
131
- httpd = self.httpd
143
+ info = apache2ctl_V
132
144
  end
133
- if httpd
134
- `#{httpd} -V` =~ %r{Architecture:(.*)}
145
+ if info
146
+ info =~ %r{Architecture:(.*)}
135
147
  text = $1
136
148
  if text =~ /32/
137
- return 32
149
+ 32
138
150
  elsif text =~ /64/
139
- return 64
151
+ 64
140
152
  else
141
- return nil
153
+ nil
142
154
  end
143
155
  else
144
- return nil
156
+ nil
145
157
  end
146
158
  end
147
159
  memoize :httpd_architecture_bits
@@ -150,9 +162,9 @@ module PhusionPassenger
150
162
  # This may be different from the value of the ServerRoot directive.
151
163
  def self.httpd_default_root(options = nil)
152
164
  if options
153
- info = httpd_V(options)
165
+ info = apache2ctl_V(options)
154
166
  else
155
- info = httpd_V
167
+ info = apache2ctl_V
156
168
  end
157
169
  if info
158
170
  info =~ / -D HTTPD_ROOT="(.+)"$/
@@ -166,9 +178,9 @@ module PhusionPassenger
166
178
  # The default Apache configuration file, or nil if Apache is not found.
167
179
  def self.httpd_default_config_file(options = nil)
168
180
  if options
169
- info = httpd_V(options)
181
+ info = apache2ctl_V(options)
170
182
  else
171
- info = httpd_V
183
+ info = apache2ctl_V
172
184
  end
173
185
  if info
174
186
  info =~ /-D SERVER_CONFIG_FILE="(.+)"$/
@@ -216,7 +228,7 @@ module PhusionPassenger
216
228
  # Returns nil if Apache is not detected, or if the default error log filename
217
229
  # cannot be detected.
218
230
  def self.httpd_default_error_log(options = nil)
219
- if info = httpd_V(options)
231
+ if info = apache2ctl_V(options)
220
232
  info =~ /-D DEFAULT_ERRORLOG="(.+)"$/
221
233
  filename = $1
222
234
  if filename =~ /\A\//
@@ -263,7 +275,7 @@ module PhusionPassenger
263
275
  elsif contents =~ /ErrorLog/i
264
276
  # The user apparently has ErrorLog set somewhere but
265
277
  # we can't parse it. The default error log location,
266
- # as reported by `httpd -V`, may be wrong (it is on OS X).
278
+ # as reported by `apache2ctl -V`, may be wrong (it is on OS X).
267
279
  # So to be safe, let's assume that we don't know.
268
280
  log "Unable to parse ErrorLog directive in Apache configuration file"
269
281
  return nil
@@ -85,8 +85,9 @@ module PhusionPassenger
85
85
  attr_reader :results
86
86
 
87
87
  def initialize(output)
88
- @output = output
89
- @results = []
88
+ @output = output
89
+ @results = []
90
+ @failures = 0
90
91
  PlatformInfo.verbose = true
91
92
  PlatformInfo.log_implementation = lambda do |message|
92
93
  if message =~ /: found$/
@@ -169,10 +170,31 @@ module PhusionPassenger
169
170
  end
170
171
 
171
172
  def report
173
+ if @failures > 0 && Process.uid != 0
174
+ user = `whoami`.strip
175
+ sudo_s_e = PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E")
176
+ ruby = PhusionPassenger::PlatformInfo.ruby_command
177
+ log ""
178
+ log "----------------------------"
179
+ log ""
180
+ log "<red>Permission problems</red>"
181
+ log ""
182
+ log "Sorry, this program doesn't have enough permissions to autodetect all your"
183
+ log "Apache installations, because it's running as the <b>#{`whoami`.strip}</b> user."
184
+ log "Please re-run this program with root privileges:"
185
+ log ""
186
+ log " <b>export ORIG_PATH=\"$PATH\"</b>"
187
+ log " <b>#{sudo_s_e}</b>"
188
+ log " <b>export PATH=\"$ORIG_PATH\"</b>"
189
+ log " <b>#{ruby} #{PhusionPassenger.bin_dir}/passenger-config --detect-apache2</b>"
190
+ return
191
+ end
192
+
172
193
  log "<banner>Final autodetection results</banner>"
173
194
  @results.each do |result|
174
195
  result.report
175
196
  end
197
+
176
198
  if @results.empty?
177
199
  log "<red>Sorry, this program cannot find an Apache installation.</red>"
178
200
  log ""
@@ -223,7 +245,11 @@ module PhusionPassenger
223
245
 
224
246
  def add_result
225
247
  result = Result.new(self)
226
- @results << result if yield(result)
248
+ if yield(result)
249
+ @results << result
250
+ else
251
+ @failures += 1
252
+ end
227
253
  end
228
254
  end
229
255
 
@@ -81,6 +81,10 @@ module PhusionPassenger
81
81
  end
82
82
  end
83
83
 
84
+ # Rails somehow modifies env['REQUEST_METHOD'], so we perform the comparison
85
+ # before the Rack application object is called.
86
+ is_head_request = env[REQUEST_METHOD] == HEAD
87
+
84
88
  begin
85
89
  status, headers, body = @app.call(env)
86
90
  rescue => e
@@ -101,7 +105,8 @@ module PhusionPassenger
101
105
  return true if env[RACK_HIJACK_IO]
102
106
 
103
107
  begin
104
- process_body(env, connection, socket_wrapper, status.to_i, headers, body)
108
+ process_body(env, connection, socket_wrapper, status.to_i, is_head_request,
109
+ headers, body)
105
110
  rescue Exception => e
106
111
  disable_keep_alive
107
112
  raise
@@ -115,7 +120,7 @@ module PhusionPassenger
115
120
 
116
121
  private
117
122
  # The code here is ugly, but it's necessary for performance.
118
- def process_body(env, connection, socket_wrapper, status, headers, body)
123
+ def process_body(env, connection, socket_wrapper, status, is_head_request, headers, body)
119
124
  if hijack_callback = headers[RACK_HIJACK]
120
125
  # Application requested a partial socket hijack.
121
126
  body = nil
@@ -132,7 +137,7 @@ module PhusionPassenger
132
137
  # object instead of a real Array, even when #is_a? claims so.
133
138
  # Call #to_a just to be sure.
134
139
  body = body.to_a
135
- output_body = should_output_body?(status, env)
140
+ output_body = should_output_body?(status, is_head_request)
136
141
  headers_output = generate_headers_array(status, headers)
137
142
  perform_keep_alive(env, headers_output)
138
143
  if output_body && should_add_message_length_header?(status, headers)
@@ -150,7 +155,7 @@ module PhusionPassenger
150
155
  end
151
156
  false
152
157
  elsif body.is_a?(String)
153
- output_body = should_output_body?(status, env)
158
+ output_body = should_output_body?(status, is_head_request)
154
159
  headers_output = generate_headers_array(status, headers)
155
160
  perform_keep_alive(env, headers_output)
156
161
  if output_body && should_add_message_length_header?(status, headers)
@@ -165,7 +170,7 @@ module PhusionPassenger
165
170
  connection.writev(headers_output)
166
171
  false
167
172
  else
168
- output_body = should_output_body?(status, env)
173
+ output_body = should_output_body?(status, is_head_request)
169
174
  headers_output = generate_headers_array(status, headers)
170
175
  perform_keep_alive(env, headers_output)
171
176
  chunk = output_body && should_add_message_length_header?(status, headers)
@@ -240,10 +245,10 @@ module PhusionPassenger
240
245
  @keepalive_performed = false
241
246
  end
242
247
 
243
- def should_output_body?(status, env)
248
+ def should_output_body?(status, is_head_request)
244
249
  return (status < 100 ||
245
250
  (status >= 200 && status != 204 && status != 205 && status != 304)) &&
246
- env[REQUEST_METHOD] != HEAD
251
+ !is_head_request
247
252
  end
248
253
 
249
254
  def should_add_message_length_header?(status, headers)
@@ -245,6 +245,11 @@ module PhusionPassenger
245
245
  request_method = $1
246
246
  request_uri = $2
247
247
  protocol = $3
248
+ if request_method.nil?
249
+ warn("*** Passenger RequestHandler warning: " <<
250
+ "Invalid HTTP request.")
251
+ return
252
+ end
248
253
  path_info, query_string = request_uri.split("?", 2)
249
254
  headers[REQUEST_METHOD] = request_method
250
255
  headers["REQUEST_URI"] = request_uri
@@ -29,6 +29,7 @@ PhusionPassenger.require_passenger_lib 'standalone/command'
29
29
  PhusionPassenger.require_passenger_lib 'standalone/config_utils'
30
30
  PhusionPassenger.require_passenger_lib 'utils'
31
31
  PhusionPassenger.require_passenger_lib 'utils/tmpio'
32
+ PhusionPassenger.require_passenger_lib 'platform_info/ruby'
32
33
 
33
34
  # We lazy load as many libraries as possible not only to improve startup performance,
34
35
  # but also to ensure that we don't require libraries before we've passed the dependency
@@ -73,20 +74,22 @@ module PhusionPassenger
73
74
  watch_log_files_in_background if should_watch_logs?
74
75
  wait_until_engine_has_exited if should_wait_until_engine_has_exited?
75
76
  rescue Interrupt
76
- shutdown_and_cleanup(true)
77
+ trapsafe_shutdown_and_cleanup(true)
77
78
  exit 2
78
79
  rescue SignalException => signal
79
- shutdown_and_cleanup(true)
80
+ trapsafe_shutdown_and_cleanup(true)
80
81
  if signal.message == 'SIGINT' || signal.message == 'SIGTERM'
81
82
  exit 2
82
83
  else
83
84
  raise
84
85
  end
85
86
  rescue Exception
86
- shutdown_and_cleanup(true)
87
+ trapsafe_shutdown_and_cleanup(true)
87
88
  raise
88
89
  else
89
- shutdown_and_cleanup(false)
90
+ trapsafe_shutdown_and_cleanup(false)
91
+ ensure
92
+ reset_traps_intterm
90
93
  end
91
94
  end
92
95
 
@@ -172,6 +175,19 @@ module PhusionPassenger
172
175
  "Default: #{DEFAULT_OPTIONS[:environment]}") do |value|
173
176
  options[:environment] = value
174
177
  end
178
+ opts.on("--ruby FILENAME", String, "Executable to use for Ruby apps#{nl}" +
179
+ "Default: " + PlatformInfo.ruby_command + " (current context)") do |value|
180
+ options[:ruby] = value
181
+ end
182
+ opts.on("--nodejs FILENAME", String, "Executable to use for NodeJs apps") do |value|
183
+ options[:nodejs] = value
184
+ end
185
+ opts.on("--python FILENAME", String, "Executable to use for Python apps") do |value|
186
+ options[:python] = value
187
+ end
188
+ opts.on("--meteor-app-settings FILENAME", String, "Settings file to use for (development mode) Meteor apps") do |value|
189
+ options[:meteor_app_settings] = value
190
+ end
175
191
  opts.on("-R", "--rackup FILE", String,
176
192
  "Consider application a Ruby app, and use#{nl}" +
177
193
  "the given rackup file") do |value|
@@ -704,7 +720,32 @@ module PhusionPassenger
704
720
 
705
721
  ################## Shut down and cleanup ##################
706
722
 
707
- def shutdown_and_cleanup(error_occurred)
723
+ def capture_traps_intterm
724
+ return if @traps_captured
725
+ @traps_captured = 1
726
+ trap("INT", &method(:trapped_intterm))
727
+ trap("TERM", &method(:trapped_intterm))
728
+ end
729
+
730
+ def reset_traps_intterm
731
+ @traps_captured = nil
732
+ trap("INT", "DEFAULT")
733
+ trap("TERM", "DEFAULT")
734
+ end
735
+
736
+ def trapped_intterm(signal)
737
+ if @traps_captured == 1
738
+ @traps_captured += 1
739
+ puts "Ignoring signal #{signal} during shutdown. Send it again to force exit."
740
+ else
741
+ exit!(1)
742
+ end
743
+ end
744
+
745
+ def trapsafe_shutdown_and_cleanup(error_occurred)
746
+ # Ignore INT and TERM once, to allow clean shutdown in e.g. Foreman
747
+ capture_traps_intterm
748
+
708
749
  # Stop engine
709
750
  if @engine && (error_occurred || should_wait_until_engine_has_exited?)
710
751
  @console_mutex.synchronize do