test/integration_tests/apache2_tests.rb in passenger-2.2.15 vs test/integration_tests/apache2_tests.rb in passenger-3.0.0.pre1

- old
+ new

@@ -1,45 +1,59 @@ +require File.expand_path(File.dirname(__FILE__) + "/spec_helper") require 'socket' require 'fileutils' -require 'support/config' -require 'support/test_helper' require 'support/apache2_controller' require 'phusion_passenger/platform_info' +require 'phusion_passenger/admin_tools' +require 'phusion_passenger/admin_tools/server_instance' require 'integration_tests/mycook_spec' +require 'integration_tests/cgi_environment_spec' require 'integration_tests/hello_world_rack_spec' require 'integration_tests/hello_world_wsgi_spec' # TODO: test the 'RailsUserSwitching' and 'RailsDefaultUser' option. # TODO: test custom page caching directory describe "Apache 2 module" do - include TestHelper - before :all do check_hosts_configuration @apache2 = Apache2Controller.new + @passenger_temp_dir = "/tmp/passenger-test.#{$$}" + Dir.mkdir(@passenger_temp_dir) + ENV['PASSENGER_TEMP_DIR'] = @passenger_temp_dir + @apache2.set(:passenger_temp_dir => @passenger_temp_dir) if Process.uid == 0 @apache2.set( :www_user => CONFIG['normal_user_1'], :www_group => Etc.getgrgid(Etc.getpwnam(CONFIG['normal_user_1']).gid).name ) end end after :all do @apache2.stop + FileUtils.chmod_R(0777, @passenger_temp_dir) + FileUtils.rm_rf(@passenger_temp_dir) end + before :each do + File.open("test.log", "a") do |f| + # Make sure that all Apache log output is prepended by the test description + # so that we know which messages are associated with which tests. + f.puts "\n#### #{self.class.description} : #{description}" + end + end + describe ": MyCook(tm) beta running on root URI" do before :all do @web_server_supports_chunked_transfer_encoding = true @base_uri = "" @server = "http://passenger.test:#{@apache2.port}" @apache2 << "RailsMaxPoolSize 1" - @stub = setup_rails_stub('mycook', 'tmp.mycook') - @apache2.set_vhost("passenger.test", File.expand_path("#{@stub.app_root}/public")) + @stub = RailsStub.new('2.3/mycook') + @apache2.set_vhost("passenger.test", "#{@stub.full_app_root}/public") @apache2.start end after :all do @stub.destroy @@ -48,10 +62,11 @@ before :each do @stub.reset end it_should_behave_like "MyCook(tm) beta" + include_shared_example_group "CGI environment variables compliance" it "doesn't block Rails while an upload is in progress" do get('/') # Force spawning so that the timeout below is enough. socket = TCPSocket.new('passenger.test', @apache2.port) @@ -79,11 +94,11 @@ upload_data = File.read("stub/upload_data.txt") size_of_first_half = upload_data.size / 2 begin - 10.times do |i| + 9.times do |i| socket = TCPSocket.new('passenger.test', @apache2.port) sockets << socket socket.write("POST / HTTP/1.1\r\n") socket.write("Host: passenger.test\r\n") socket.write(upload_data[0..size_of_first_half]) @@ -96,21 +111,27 @@ sockets.each do |socket| socket.close rescue nil end end end + + it "appends an X-Powered-By header containing the Phusion Passenger version number" do + response = get_response('/') + response["X-Powered-By"].should include("Phusion Passenger") + response["X-Powered-By"].should include(PhusionPassenger::VERSION_STRING) + end end describe ": MyCook(tm) beta running in a sub-URI" do before :all do @web_server_supports_chunked_transfer_encoding = true @base_uri = "/mycook" - @stub = setup_rails_stub('mycook') + @stub = RailsStub.new('2.3/mycook') FileUtils.rm_rf('tmp.webdir') FileUtils.mkdir_p('tmp.webdir') FileUtils.cp_r('stub/zsfa/.', 'tmp.webdir') - FileUtils.ln_sf(File.expand_path(@stub.app_root) + "/public", 'tmp.webdir/mycook') + FileUtils.ln_sf(@stub.full_app_root + "/public", 'tmp.webdir/mycook') @apache2.set_vhost('passenger.test', File.expand_path('tmp.webdir')) do |vhost| vhost << "RailsBaseURI /mycook" end @apache2.start @@ -125,10 +146,11 @@ @server = "http://passenger.test:#{@apache2.port}/mycook" @stub.reset end it_should_behave_like "MyCook(tm) beta" + include_shared_example_group "CGI environment variables compliance" it "does not interfere with the root website" do @server = "http://passenger.test:#{@apache2.port}" get('/').should =~ /Zed, you rock\!/ end @@ -136,13 +158,13 @@ describe "compatibility with other modules" do before :all do @apache2 << "RailsMaxPoolSize 1" - @mycook = setup_rails_stub('mycook', File.expand_path('tmp.mycook')) + @mycook = RailsStub.new('2.3/mycook') @mycook_url_root = "http://1.passenger.test:#{@apache2.port}" - @apache2.set_vhost("1.passenger.test", "#{@mycook.app_root}/public") do |vhost| + @apache2.set_vhost("1.passenger.test", "#{@mycook.full_app_root}/public") do |vhost| vhost << "RewriteEngine on" vhost << "RewriteRule ^/rewritten_welcome$ /welcome [PT,QSA,L]" vhost << "RewriteRule ^/rewritten_cgi_environment$ /welcome/cgi_environment [PT,QSA,L]" end @apache2.start @@ -186,47 +208,64 @@ describe "configuration options" do before :all do @apache2 << "PassengerMaxPoolSize 3" - @mycook = setup_rails_stub('mycook', File.expand_path("tmp.mycook")) + @mycook = RailsStub.new('2.3/mycook') @mycook_url_root = "http://1.passenger.test:#{@apache2.port}" - @apache2.set_vhost('1.passenger.test', "#{@mycook.app_root}/public") do |vhost| + @apache2.set_vhost('1.passenger.test', "#{@mycook.full_app_root}/public") do |vhost| vhost << "AllowEncodedSlashes on" end - @apache2.set_vhost('2.passenger.test', "#{@mycook.app_root}/public") do |vhost| + @apache2.set_vhost('2.passenger.test', "#{@mycook.full_app_root}/public") do |vhost| vhost << "RailsAutoDetect off" end - @foobar = setup_rails_stub('foobar', File.expand_path("tmp.foobar")) + @foobar = RailsStub.new('2.3/foobar') @foobar_url_root = "http://3.passenger.test:#{@apache2.port}" - @apache2.set_vhost('3.passenger.test', "#{@foobar.app_root}/public") do |vhost| + @apache2.set_vhost('3.passenger.test', "#{@foobar.full_app_root}/public") do |vhost| vhost << "RailsEnv development" - vhost << "RailsSpawnMethod conservative" - vhost << "PassengerUseGlobalQueue on" - vhost << "PassengerRestartDir #{@foobar.app_root}/public" + vhost << "PassengerSpawnMethod conservative" + vhost << "PassengerRestartDir #{@foobar.full_app_root}/public" end - @mycook2 = setup_rails_stub('mycook', File.expand_path("tmp.mycook2")) + @mycook2 = RailsStub.new('2.3/mycook') @mycook2_url_root = "http://4.passenger.test:#{@apache2.port}" - @apache2.set_vhost('4.passenger.test', "#{@mycook2.app_root}/sites/some.site/public") do |vhost| - vhost << "PassengerAppRoot #{@mycook2.app_root}" + @apache2.set_vhost('4.passenger.test', "#{@mycook2.full_app_root}/sites/some.site/public") do |vhost| + vhost << "PassengerAppRoot #{@mycook2.full_app_root}" end + # These are used by global queueing tests. + @mycook3 = RailsStub.new('2.3/mycook') + @mycook3_url_root = "http://5.passenger.test:#{@apache2.port}" + @apache2.set_vhost('5.passenger.test', "#{@mycook3.full_app_root}/sites/some.site/public") do |vhost| + vhost << "PassengerAppRoot #{@mycook3.full_app_root}" + vhost << "PassengerMinInstances 3" + end + @mycook4 = RailsStub.new('2.3/mycook') + @mycook4_url_root = "http://6.passenger.test:#{@apache2.port}" + @apache2.set_vhost('6.passenger.test', "#{@mycook4.full_app_root}/public") do |vhost| + vhost << "PassengerUseGlobalQueue on" + vhost << "PassengerMinInstances 3" + end + @apache2.start end after :all do @mycook.destroy @foobar.destroy @mycook2.destroy + @mycook3.destroy + @mycook4.destroy end before :each do @mycook.reset @foobar.reset @mycook2.reset + @mycook3.reset + @mycook4.reset end it "ignores the Rails application if RailsAutoDetect is off" do @server = "http://2.passenger.test:#{@apache2.port}" get('/').should_not =~ /MyCook/ @@ -250,11 +289,11 @@ # TODO: I think this assertion is no longer valid now that # smart-lv2 is the default spawn method... get('/foo/backtrace').should_not =~ /framework_spawner/ end - specify "RailsSpawnMethod spawning is per-virtual host" do + specify "PassengerSpawnMethod spawning is per-virtual host" do @server = @mycook_url_root get('/welcome/backtrace').should =~ /application_spawner/ end it "looks for restart.txt in the directory specified by PassengerRestartDir" do @@ -285,38 +324,51 @@ File.touch(restart_file, now - 10) get('/bar').should == "oh hai" end describe "PassengerUseGlobalQueue" do + before :each do + + end + after :each do - # Restart Apache to reset the application pool's state. - @apache2.start + # Restart Apache in order to reset the application pool's state. + @apache2.stop end - it "is off by default" do - @server = @mycook_url_root + it "works and is per-virtual host" do + @server = @mycook4_url_root - # Spawn the application. + # Spawn 3 application processes. get('/') + eventually do + inspect_server(:processes).size == 3 + end - threads = [] # Reserve all application pool slots. + threads = [] 3.times do |i| thread = Thread.new do - File.unlink("#{@mycook.app_root}/#{i}.txt") rescue nil + File.unlink("#{@mycook4.app_root}/#{i}.txt") rescue nil get("/welcome/sleep_until_exists?name=#{i}.txt") end threads << thread end # Wait until all application instances are waiting # for the quit file. - while !File.exist?("#{@mycook.app_root}/waiting_0.txt") || - !File.exist?("#{@mycook.app_root}/waiting_1.txt") || - !File.exist?("#{@mycook.app_root}/waiting_2.txt") - sleep 0.1 + eventually(5) do + File.exist?("#{@mycook4.app_root}/waiting_0.txt") && + File.exist?("#{@mycook4.app_root}/waiting_1.txt") && + File.exist?("#{@mycook4.app_root}/waiting_2.txt") end + processes = inspect_server(:processes) + processes.should have(3).items + processes.each do |process| + process.sessions.should == 1 + end + inspect_server(:global_queue_size).should == 0 # While all slots are reserved, make two more requests. first_request_done = false second_request_done = false thread = Thread.new do @@ -328,95 +380,29 @@ get("/") second_request_done = true end threads << thread - # These requests should both block. - sleep 0.5 - first_request_done.should be_false - second_request_done.should be_false - - # One of the requests should still be blocked - # if one application instance frees up. - File.open("#{@mycook.app_root}/2.txt", 'w') - begin - Timeout.timeout(5) do - while !first_request_done && !second_request_done - sleep 0.1 - end - end - rescue Timeout::Error + # These requests should both be waiting on the global queue. + sleep 1 + processes = inspect_server(:processes) + processes.should have(3).items + processes.each do |process| + process.sessions.should == 1 end - (first_request_done || second_request_done).should be_true + inspect_server(:global_queue_size).should == 2 - File.open("#{@mycook.app_root}/0.txt", 'w') - File.open("#{@mycook.app_root}/1.txt", 'w') - File.open("#{@mycook.app_root}/2.txt", 'w') - threads.each do |thread| - thread.join - end - end - - it "works and is per-virtual host" do - @server = @foobar_url_root - - # Spawn the application. - get('/') - - threads = [] - # Reserve all application pool slots. - 3.times do |i| - thread = Thread.new do - File.unlink("#{@foobar.app_root}/#{i}.txt") rescue nil - get("/foo/sleep_until_exists?name=#{i}.txt") - end - threads << thread - end - - # Wait until all application instances are waiting - # for the quit file. - while !File.exist?("#{@foobar.app_root}/waiting_0.txt") || - !File.exist?("#{@foobar.app_root}/waiting_1.txt") || - !File.exist?("#{@foobar.app_root}/waiting_2.txt") - sleep 0.1 - end - - # While all slots are reserved, make two more requests. - first_request_done = false - second_request_done = false - thread = Thread.new do - get("/") - first_request_done = true - end - threads << thread - thread = Thread.new do - get("/") - second_request_done = true - end - threads << thread - - # These requests should both block. - sleep 0.5 - first_request_done.should be_false - second_request_done.should be_false - # Both requests should be processed if one application instance frees up. - File.open("#{@foobar.app_root}/2.txt", 'w') - begin - Timeout.timeout(5) do - while !first_request_done || !second_request_done - sleep 0.1 - end - end - rescue Timeout::Error + File.touch("#{@mycook4.app_root}/2.txt") + eventually(5) do + first_request_done && second_request_done end - first_request_done.should be_true - second_request_done.should be_true + inspect_server(:global_queue_size).should == 0 - File.open("#{@foobar.app_root}/0.txt", 'w') - File.open("#{@foobar.app_root}/1.txt", 'w') - File.open("#{@foobar.app_root}/2.txt", 'w') + File.touch("#{@mycook4.app_root}/0.txt") + File.touch("#{@mycook4.app_root}/1.txt") + File.touch("#{@mycook4.app_root}/2.txt") threads.each do |thread| thread.join end end end @@ -469,61 +455,146 @@ describe "error handling" do before :all do FileUtils.rm_rf('tmp.webdir') FileUtils.mkdir_p('tmp.webdir') @webdir = File.expand_path('tmp.webdir') - @apache2.set_vhost('passenger.test', @webdir) do |vhost| + @apache2.set_vhost('1.passenger.test', @webdir) do |vhost| vhost << "RailsBaseURI /app-with-nonexistant-rails-version/public" vhost << "RailsBaseURI /app-that-crashes-during-startup/public" vhost << "RailsBaseURI /app-with-crashing-vendor-rails/public" end + + @mycook = RailsStub.new('2.3/mycook') + @mycook_url_root = "http://2.passenger.test:#{@apache2.port}" + @apache2.set_vhost('2.passenger.test', "#{@mycook.full_app_root}/public") + @apache2.start end after :all do FileUtils.rm_rf('tmp.webdir') + @mycook.destroy end before :each do - @server = "http://zsfa.passenger.test:64506" + @server = "http://1.passenger.test:#{@apache2.port}" @error_page_signature = /<meta name="generator" content="Phusion Passenger">/ + @mycook.reset end it "displays an error page if the Rails application requires a nonexistant Rails version" do - use_rails_stub('foobar', "#{@webdir}/app-with-nonexistant-rails-version") do |stub| + RailsStub.use('2.3/foobar', "#{@webdir}/app-with-nonexistant-rails-version") do |stub| File.write(stub.environment_rb) do |content| content.sub(/^RAILS_GEM_VERSION = .*$/, "RAILS_GEM_VERSION = '1.9.1234'") end get("/app-with-nonexistant-rails-version/public").should =~ @error_page_signature end end it "displays an error page if the Rails application crashes during startup" do - use_rails_stub('foobar', "#{@webdir}/app-that-crashes-during-startup") do |stub| + RailsStub.use('2.3/foobar', "#{@webdir}/app-that-crashes-during-startup") do |stub| File.prepend(stub.environment_rb, "raise 'app crash'") result = get("/app-that-crashes-during-startup/public") result.should =~ @error_page_signature result.should =~ /app crash/ end end it "displays an error page if the Rails application's vendor'ed Rails crashes" do - use_rails_stub('foobar', "#{@webdir}/app-with-crashing-vendor-rails") do |stub| + RailsStub.use('2.3/foobar', "#{@webdir}/app-with-crashing-vendor-rails") do |stub| stub.use_vendor_rails('minimal') File.append("#{stub.app_root}/vendor/rails/railties/lib/initializer.rb", "raise 'vendor crash'") result = get("/app-with-crashing-vendor-rails/public") result.should =~ @error_page_signature result.should =~ /vendor crash/ end end + + it "displays an error if a filesystem permission error was encountered while autodetecting the application type" do + @server = @mycook_url_root + # This test used to fail because we were improperly blocking mod_autoindex, + # resulting in it displaying a directory index before we could display an + # error message. + File.chmod(0000, "#{@mycook.app_root}/config") + # Don't let mod_rewrite kick in so that mod_autoindex has a chance to run. + File.unlink("#{@mycook.app_root}/public/.htaccess") + get('/').should =~ /Please fix the relevant file permissions/ + end + + it "doesn't display a Ruby spawn error page if PassengerFriendlyErrorPages is off" do + RailsStub.use('2.3/foobar', "#{@webdir}/app-that-crashes-during-startup") do |stub| + File.write("#{stub.app_root}/public/.htaccess", "PassengerFriendlyErrorPages off") + File.prepend(stub.environment_rb, "raise 'app crash'") + result = get("/app-that-crashes-during-startup/public") + result.should_not =~ @error_page_signature + result.should_not =~ /app crash/ + end + end end + describe "HelperServer" do + AdminTools = PhusionPassenger::AdminTools + + before :all do + @mycook = RailsStub.new('2.3/mycook') + @mycook_url_root = "http://1.passenger.test:#{@apache2.port}" + @apache2.set_vhost('1.passenger.test', "#{@mycook.full_app_root}/public") + @apache2.start + @server = "http://1.passenger.test:#{@apache2.port}" + end + + after :all do + @mycook.destroy + end + + before :each do + @mycook.reset + end + + it "is restarted if it crashes" do + # Make sure that all Apache worker processes have connected to + # the helper server. + 10.times do + get('/welcome').should =~ /Welcome to MyCook/ + sleep 0.1 + end + + # Now kill the helper server. + instance = AdminTools::ServerInstance.list.first + Process.kill('SIGKILL', instance.helper_server_pid) + sleep 0.01 # Give the signal a small amount of time to take effect. + + # Each worker process should detect that the old + # helper server has died, and should reconnect. + 10.times do + get('/welcome').should =~ /Welcome to MyCook/ + sleep 0.1 + end + end + + it "exposes the application pool for passenger-status" do + File.touch("#{@mycook.app_root}/tmp/restart.txt", 1) # Get rid of all previous app processes. + get('/welcome').should =~ /Welcome to MyCook/ + instance = AdminTools::ServerInstance.list.first + + # Wait until the server has processed the session close event. + sleep 0.1 + + processes = instance.connect(:passenger_status) do + instance.processes + end + processes.should have(1).item + processes[0].group.name.should == @mycook.full_app_root + processes[0].processed.should == 1 + end + end + describe "Rack application running in root URI" do before :all do - @stub = setup_stub('rack') - @apache2.set_vhost('passenger.test', File.expand_path(@stub.app_root) + "/public") + @stub = RackStub.new('rack') + @apache2.set_vhost('passenger.test', @stub.full_app_root + "/public") @apache2.start @server = "http://passenger.test:#{@apache2.port}" end after :all do @@ -535,13 +606,13 @@ describe "Rack application running in sub-URI" do before :all do FileUtils.rm_rf('tmp.webdir') FileUtils.mkdir_p('tmp.webdir') - @stub = setup_stub('rack') + @stub = RackStub.new('rack') @apache2.set_vhost('passenger.test', File.expand_path('tmp.webdir')) do |vhost| - FileUtils.ln_s(File.expand_path(@stub.app_root) + "/public", 'tmp.webdir/rack') + FileUtils.ln_s(@stub.full_app_root + "/public", 'tmp.webdir/rack') vhost << "RackBaseURI /rack" end @apache2.start @server = "http://passenger.test:#{@apache2.port}/rack" end @@ -554,13 +625,13 @@ it_should_behave_like "HelloWorld Rack application" end describe "Rack application running within Rails directory structure" do before :all do - @stub = setup_rails_stub('mycook') + @stub = RailsStub.new('2.3/mycook') FileUtils.cp_r("stub/rack/.", @stub.app_root) - @apache2.set_vhost('passenger.test', File.expand_path(@stub.app_root) + "/public") + @apache2.set_vhost('passenger.test', @stub.full_app_root + "/public") @apache2.start @server = "http://passenger.test:#{@apache2.port}" end after :all do @@ -570,11 +641,11 @@ it_should_behave_like "HelloWorld Rack application" end describe "WSGI application running in root URI" do before :all do - @stub = setup_stub('wsgi') - @apache2.set_vhost('passenger.test', File.expand_path(@stub.app_root) + "/public") + @stub = Stub.new('wsgi') + @apache2.set_vhost('passenger.test', @stub.full_app_root + "/public") @apache2.start @server = "http://passenger.test:#{@apache2.port}" end after :all do