/
application_spawner.rb
115 lines (99 loc) · 3.35 KB
/
application_spawner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Phusion Passenger - http://www.modrails.com/
# Copyright (C) 2008 Phusion
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../../../vendor/rack-0.9.1/lib"))
require 'rack'
require 'socket'
require 'phusion_passenger/application'
require 'phusion_passenger/message_channel'
require 'phusion_passenger/abstract_request_handler'
require 'phusion_passenger/utils'
require 'phusion_passenger/rack/request_handler'
module PhusionPassenger
module Rack
# Class for spawning Rack applications.
class ApplicationSpawner
include Utils
def self.spawn_application(*args)
@@instance ||= ApplicationSpawner.new
@@instance.spawn_application(*args)
end
# Spawn an instance of the given Rack application. When successful, an
# Application object will be returned, which represents the spawned
# application.
#
# Raises:
# - AppInitError: The Rack application raised an exception or called
# exit() during startup.
# - SystemCallError, IOError, SocketError: Something went wrong.
def spawn_application(app_root, options = {})
options = sanitize_spawn_options(options)
a, b = UNIXSocket.pair
pid = safe_fork(self.class.to_s, true) do
a.close
file_descriptors_to_leave_open = [0, 1, 2, b.fileno]
NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open)
close_all_io_objects_for_fds(file_descriptors_to_leave_open)
run(MessageChannel.new(b), app_root, options)
end
b.close
Process.waitpid(pid) rescue nil
channel = MessageChannel.new(a)
unmarshal_and_raise_errors(channel, "rack")
# No exception was raised, so spawning succeeded.
pid, socket_name, socket_type = channel.read
if pid.nil?
raise IOError, "Connection closed"
end
owner_pipe = channel.recv_io
return Application.new(@app_root, pid, socket_name,
socket_type, owner_pipe)
end
private
def run(channel, app_root, options)
$0 = "Rack: #{app_root}"
app = nil
success = report_app_init_status(channel) do
ENV['RACK_ENV'] = options["environment"]
Dir.chdir(app_root)
if options["lower_privilege"]
lower_privilege('config.ru', options["lowest_user"])
end
app = load_rack_app
end
if success
reader, writer = IO.pipe
begin
handler = RequestHandler.new(reader, app, options)
channel.write(Process.pid, handler.socket_name,
handler.socket_type)
channel.send_io(writer)
writer.close
channel.close
handler.main_loop
ensure
channel.close rescue nil
writer.close rescue nil
handler.cleanup rescue nil
end
end
end
def load_rack_app
rackup_code = File.read("config.ru")
eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, "config.ru")
end
end
end # module Rack
end # module PhusionPassenger