require 'socket' require 'fileutils' require 'support/config' require 'support/test_helper' require 'support/apache2_controller' require 'phusion_passenger/platform_info' require 'integration_tests/mycook_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 "mod_passenger running in Apache 2" do include TestHelper before :all do check_hosts_configuration @apache2 = Apache2Controller.new 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 end describe ": MyCook(tm) beta running on root URI" do before :all do @web_server_supports_chunked_transfer_encoding = true @server = "http://passenger.test:#{@apache2.port}" @stub = setup_rails_stub('mycook') @apache2 << "RailsMaxPoolSize 1" @apache2.set_vhost("passenger.test", File.expand_path("#{@stub.app_root}/public")) @apache2.start end after :all do @stub.destroy end it_should_behave_like "MyCook(tm) beta" 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) begin socket.write("POST / HTTP/1.1\r\n") socket.write("Host: passenger.test\r\n") upload_data = File.read("stub/upload_data.txt") size_of_first_half = upload_data.size / 2 socket.write(upload_data[0..size_of_first_half]) socket.flush Timeout.timeout(10) do get('/').should =~ /Welcome to MyCook/ end ensure socket.close rescue nil end end it "doesn't block Rails while a large number of uploads are in progress" do get('/') # Force spawning so that the timeout below is enough. sockets = [] upload_data = File.read("stub/upload_data.txt") size_of_first_half = upload_data.size / 2 begin 10.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]) socket.flush end Timeout.timeout(10) do get('/').should =~ /Welcome to MyCook/ end ensure sockets.each do |socket| socket.close rescue nil end end end end describe ": MyCook(tm) beta running in a sub-URI" do before :all do @web_server_supports_chunked_transfer_encoding = true @stub = setup_rails_stub('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') @apache2.set_vhost('passenger.test', File.expand_path('tmp.webdir')) do |vhost| vhost << "RailsBaseURI /mycook" end @apache2.start end after :all do FileUtils.rm_rf('tmp.webdir') @stub.destroy end before :each do @server = "http://passenger.test:#{@apache2.port}/mycook" end it_should_behave_like "MyCook(tm) beta" it "does not interfere with the root website" do @server = "http://passenger.test:#{@apache2.port}" get('/').should =~ /Zed, you rock\!/ end end describe "configuration options" do before :all do @apache2 << "PassengerMaxPoolSize 3" @stub = setup_rails_stub('mycook') rails_dir = File.expand_path(@stub.app_root) + "/public" @apache2.set_vhost('mycook.passenger.test', rails_dir) @apache2.set_vhost('norails.passenger.test', rails_dir) do |vhost| vhost << "RailsAutoDetect off" end @stub2 = setup_rails_stub('foobar', 'tmp.stub2') rails_dir = File.expand_path(@stub2.app_root) + "/public" @apache2.set_vhost('passenger.test', rails_dir) do |vhost| vhost << "RailsEnv development" vhost << "RailsSpawnMethod conservative" vhost << "PassengerUseGlobalQueue on" vhost << "PassengerRestartDir #{rails_dir}" end @apache2.start end after :all do @stub.destroy @stub2.destroy end it "ignores the Rails application if RailsAutoDetect is off" do @server = "http://norails.passenger.test:#{@apache2.port}" get('/').should_not =~ /MyCook/ end specify "setting RailsAutoDetect for one virtual host should not interfere with others" do @server = "http://mycook.passenger.test:#{@apache2.port}" get('/').should =~ /MyCook/ end specify "RailsEnv is per-virtual host" do @server = "http://mycook.passenger.test:#{@apache2.port}" get('/welcome/rails_env').should == "production" @server = "http://passenger.test:#{@apache2.port}" get('/foo/rails_env').should == "development" end it "supports conservative spawning" do @server = "http://passenger.test:#{@apache2.port}" get('/foo/backtrace').should_not =~ /framework_spawner/ end specify "RailsSpawnMethod spawning is per-virtual host" do @server = "http://mycook.passenger.test:#{@apache2.port}" get('/welcome/backtrace').should =~ /application_spawner/ end it "looks for restart.txt in the directory specified by PassengerRestartDir" do @server = "http://passenger.test:#{@apache2.port}" controller = "#{@stub2.app_root}/app/controllers/bar_controller.rb" restart_file = "#{@stub2.app_root}/public/restart.txt" begin File.open(controller, 'w') do |f| f.write(%Q{ class BarController < ApplicationController def index render :text => 'hello world' end end }) end File.open(restart_file, 'w').close get('/bar').should == "hello world" File.open(controller, 'w') do |f| f.write(%Q{ class BarController < ApplicationController def index render :text => 'oh hai' end end }) end now = Time.now File.open(restart_file, 'w').close File.utime(now - 10, now - 10, restart_file) get('/bar').should == "oh hai" ensure File.unlink(controller) rescue nil File.unlink(restart_file) rescue nil end end describe "PassengerUseGlobalQueue" do after :each do # Restart Apache to reset the application pool's state. @apache2.start end it "is off by default" do @server = "http://mycook.passenger.test:#{@apache2.port}" # Spawn the application. get('/') threads = [] # Reserve all application pool slots. 3.times do |i| thread = Thread.new do File.unlink("#{@stub.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?("#{@stub.app_root}/waiting_0.txt") || !File.exist?("#{@stub.app_root}/waiting_1.txt") || !File.exist?("#{@stub.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 # One of the requests should still be blocked # if one application instance frees up. File.open("#{@stub.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 end (first_request_done || second_request_done).should be_true File.open("#{@stub.app_root}/0.txt", 'w') File.open("#{@stub.app_root}/1.txt", 'w') File.open("#{@stub.app_root}/2.txt", 'w') threads.each do |thread| thread.join end end it "works and is per-virtual host" do @server = "http://passenger.test:#{@apache2.port}" # Spawn the application. get('/') threads = [] # Reserve all application pool slots. 3.times do |i| thread = Thread.new do File.unlink("#{@stub2.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?("#{@stub2.app_root}/waiting_0.txt") || !File.exist?("#{@stub2.app_root}/waiting_1.txt") || !File.exist?("#{@stub2.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("#{@stub2.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 end first_request_done.should be_true second_request_done.should be_true File.open("#{@stub2.app_root}/0.txt", 'w') File.open("#{@stub2.app_root}/1.txt", 'w') File.open("#{@stub2.app_root}/2.txt", 'w') threads.each do |thread| thread.join end end end describe "PassengerAppRoot" do before :all do @stub3 = setup_rails_stub('mycook', 'tmp.stub3') doc_root = File.expand_path(@stub3.app_root) + "/sites/some.site/public" @apache2.set_vhost('passenger.test', doc_root) do |vhost| vhost << "PassengerAppRoot #{File.expand_path(@stub3.app_root).inspect}" end @apache2.start end after :all do @stub3.destroy end it "supports page caching on non-index URIs" do @server = "http://passenger.test:#{@apache2.port}" get('/welcome/cached.html').should =~ %r{This is the cached version of some.site/public/welcome/cached} end it "supports page caching on index URIs" do @server = "http://passenger.test:#{@apache2.port}" get('/uploads.html').should =~ %r{This is the cached version of some.site/public/uploads} end it "works as a rails application" do @server = "http://passenger.test:#{@apache2.port}" result = get('/welcome/parameters_test?hello=world&recipe[name]=Green+Bananas') result.should =~ %r{world} result.should =~ %r{} result.should =~ %r{Green Bananas} end end #################################### end 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| 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 @apache2.start end after :all do FileUtils.rm_rf('tmp.webdir') end before :each do @server = "http://zsfa.passenger.test:64506" @error_page_signature = // 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| 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| 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| 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 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") @apache2.start @server = "http://passenger.test:#{@apache2.port}" end after :all do @stub.destroy end it_should_behave_like "HelloWorld Rack application" end 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') @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') vhost << "RackBaseURI /rack" end @apache2.start @server = "http://passenger.test:#{@apache2.port}/rack" end after :all do @stub.destroy FileUtils.rm_rf('tmp.webdir') end 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") @apache2.start @server = "http://passenger.test:#{@apache2.port}" end after :all do @stub.destroy end it_should_behave_like "HelloWorld WSGI application" end ##### Helper methods ##### def start_web_server_if_necessary if !@apache2.running? @apache2.start end end end