Module: PhusionPassenger::Rack::ThreadHandlerExtension

Defined in:
lib/phusion_passenger/rack/thread_handler_extension.rb

Constant Summary collapse

RACK_VERSION =

Constants which exist to relieve Ruby’s garbage collector.

"rack.version"
RACK_VERSION_VALUE =

:nodoc:

[1, 2]
RACK_INPUT =

:nodoc:

"rack.input"
RACK_ERRORS =

:nodoc:

"rack.errors"
RACK_MULTITHREAD =

:nodoc:

"rack.multithread"
RACK_MULTIPROCESS =

:nodoc:

"rack.multiprocess"
RACK_RUN_ONCE =

:nodoc:

"rack.run_once"
RACK_URL_SCHEME =

:nodoc:

"rack.url_scheme"
RACK_HIJACK_P =

:nodoc:

"rack.hijack?"
RACK_HIJACK =

:nodoc:

"rack.hijack"
SCRIPT_NAME =

:nodoc:

"SCRIPT_NAME"
HTTPS =

:nodoc:

"HTTPS"
HTTPS_DOWNCASE =

:nodoc:

"https"
HTTP =

:nodoc:

"http"
YES =

:nodoc:

"yes"
ON =

:nodoc:

"on"
ONE =

:nodoc:

"1"
CRLF =

:nodoc:

"\r\n"
NEWLINE =

:nodoc:

"\n"
STATUS =

:nodoc:

"Status: "
NAME_VALUE_SEPARATOR =

:nodoc:

": "

Instance Method Summary collapse

Instance Method Details

#process_request(env, connection, socket_wrapper, full_http_response) ⇒ Object



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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/phusion_passenger/rack/thread_handler_extension.rb', line 54

def process_request(env, connection, socket_wrapper, full_http_response)
	rewindable_input = PhusionPassenger::Utils::TeeInput.new(connection, env)
	begin
		env[RACK_VERSION]      = RACK_VERSION_VALUE
		env[RACK_INPUT]        = rewindable_input
		env[RACK_ERRORS]       = STDERR
		env[RACK_MULTITHREAD]  = @request_handler.concurrency > 1
		env[RACK_MULTIPROCESS] = true
		env[RACK_RUN_ONCE]     = false
		if env[HTTPS] == YES || env[HTTPS] == ON || env[HTTPS] == ONE
			env[RACK_URL_SCHEME] = HTTPS_DOWNCASE
		else
			env[RACK_URL_SCHEME] = HTTP
		end
		env[RACK_HIJACK_P] = true
		env[RACK_HIJACK] = lambda do
			env[RACK_HIJACK_IO] ||= begin
				connection.stop_simulating_eof!
				connection
			end
		end
		
		begin
			status, headers, body = @app.call(env)
		rescue => e
			if should_reraise_app_error?(e, socket_wrapper)
				raise e
			elsif !should_swallow_app_error?(e, socket_wrapper)
				# It's a good idea to catch application exceptions here because
				# otherwise maliciously crafted responses can crash the app,
				# forcing it to be respawned, and thereby effectively DoSing it.
				print_exception("Rack application object", e)
			end
			return false
		end

		# Application requested a full socket hijack.
		return true if env[RACK_HIJACK_IO]

		begin
			if full_http_response
				connection.write("HTTP/1.1 #{status.to_i.to_s} Whatever#{CRLF}")
				connection.write("Connection: close#{CRLF}")
			end
			headers_output = [
				STATUS, status.to_i.to_s, CRLF
			]
			headers.each do |key, values|
				if values.is_a?(String)
					values = values.split(NEWLINE)
				elsif key == RACK_HIJACK
					# We do not check for this key name in every loop
					# iteration as an optimization.
					next
				end
				values.each do |value|
					headers_output << key
					headers_output << NAME_VALUE_SEPARATOR
					headers_output << value
					headers_output << CRLF
				end
			end
			headers_output << CRLF

			if hijack_callback = headers[RACK_HIJACK]
				# Application requested a partial socket hijack.
				body = nil
				connection.writev(headers_output)
				connection.flush
				hijacked_socket = env[RACK_HIJACK].call
				hijack_callback.call(hijacked_socket)
				return true
			elsif body.is_a?(Array)
				# The body may be an ActionController::StringCoercion::UglyBody
				# object instead of a real Array, even when #is_a? claims so.
				# Call #to_a just to be sure.
				connection.writev2(headers_output, body.to_a)
				return false
			elsif body.is_a?(String)
				headers_output << body
				connection.writev(headers_output)
				return false
			else
				connection.writev(headers_output)
				if body
					begin
						body.each do |s|
							connection.write(s)
						end
					rescue => e
						if should_reraise_app_error?(e, socket_wrapper)
							raise e
						elsif !should_swallow_app_error?(e, socket_wrapper)
							# Body objects can raise exceptions in #each.
							print_exception("Rack body object #each method", e)
						end
						return false
					end
				end
				return false
			end
		ensure
			body.close if body && body.respond_to?(:close)
		end
	ensure
		rewindable_input.close
	end
end