Your IP : 52.15.233.83
#!/usr/bin/python2
# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2010-2017 Phusion Holding B.V.
#
# "Passenger", "Phusion Passenger" and "Union Station" are registered
# trademarks of Phusion Holding B.V.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys, os, threading, signal, traceback, socket, select, struct, logging, errno
import tempfile, json, time
if sys.version_info[0] >= 3 and sys.version_info[1] >= 5:
from importlib import util
else:
import imp
options = {}
def abort(message):
sys.stderr.write(message + "\n")
sys.exit(1)
def try_write_file(path, contents):
try:
with open(path, 'w') as f:
f.write(contents)
except IOError as e:
logging.warn('Warning: unable to write to ' + path + ': ' + e.strerror)
def initialize_logging():
logging.basicConfig(
level = logging.WARNING,
format = "[ pid=%(process)d, time=%(asctime)s ]: %(message)s")
if hasattr(logging, 'captureWarnings'):
logging.captureWarnings(True)
def read_startup_arguments():
global options
work_dir = os.getenv('PASSENGER_SPAWN_WORK_DIR')
assert work_dir is not None
path = work_dir + '/args.json'
with open(path, 'r') as f:
options = json.load(f)
def record_journey_step_begin(step, state):
work_dir = os.getenv('PASSENGER_SPAWN_WORK_DIR')
assert work_dir is not None
step_dir = work_dir + '/response/steps/' + step.lower()
try_write_file(step_dir + '/state', state)
try_write_file(step_dir + '/begin_time', str(time.time()))
def record_journey_step_end(step, state):
work_dir = os.getenv('PASSENGER_SPAWN_WORK_DIR')
assert work_dir is not None
step_dir = work_dir + '/response/steps/' + step.lower()
try_write_file(step_dir + '/state', state)
if not os.path.exists(step_dir + '/begin_time') and not os.path.exists(step_dir + '/begin_time_monotonic'):
try_write_file(step_dir + '/begin_time', str(time.time()))
try_write_file(step_dir + '/end_time', str(time.time()))
def load_app():
global options
sys.path.insert(0, os.getcwd())
startup_file = options.get('startup_file', 'passenger_wsgi.py')
if sys.version_info[0] >= 3 and sys.version_info[1] >= 5:
spec = util.spec_from_file_location("passenger_wsgi", startup_file)
assert spec is not None
app_module = util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(app_module)
return app_module
else:
return imp.load_source('passenger_wsgi', startup_file)
def create_server_socket():
global options
UNIX_PATH_MAX = int(options.get('UNIX_PATH_MAX', 100))
if 'socket_dir' in options:
socket_dir = options['socket_dir']
socket_prefix = 'wsgi'
else:
socket_dir = tempfile.gettempdir()
socket_prefix = 'PsgWsgiApp'
i = 0
while i < 128:
try:
return make_socket(socket_dir, socket_prefix, UNIX_PATH_MAX)
except socket.error as e:
if e.errno == errno.EADDRINUSE:
i += 1
if i == 128:
raise e
else:
raise e
def make_socket(socket_dir, socket_prefix, UNIX_PATH_MAX):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socket_suffix = format(struct.unpack('Q', os.urandom(8))[0], 'x')
filename = socket_dir + '/' + socket_prefix + '.' + socket_suffix
filename = filename[0:UNIX_PATH_MAX]
s.bind(filename)
s.listen(1000)
return (filename, s)
def install_signal_handlers():
def debug(sig, frame):
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
code = []
for thread_id, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name.get(thread_id,""), thread_id))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append(' File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
print("\n".join(code))
def debug_and_exit(sig, frame):
debug(sig, frame)
sys.exit(1)
# Unfortunately, there's no way to install a signal handler that prints
# the backtrace without interrupting the current system call. os.siginterrupt()
# doesn't seem to work properly either. That is why we only have a SIGABRT
# handler and no SIGQUIT handler.
signal.signal(signal.SIGABRT, debug_and_exit)
def advertise_sockets(socket_filename):
work_dir = os.getenv('PASSENGER_SPAWN_WORK_DIR')
assert work_dir is not None
path = work_dir + '/response/properties.json'
doc = {
'sockets': [
{
'name': 'main',
'address': 'unix:' + socket_filename,
'protocol': 'session',
'concurrency': 1,
'accept_http_requests': True
}
]
}
with open(path, 'w') as f:
json.dump(doc, f)
def advertise_readiness():
work_dir = os.getenv('PASSENGER_SPAWN_WORK_DIR')
assert work_dir is not None
path = work_dir + '/response/finish'
with open(path, 'w') as f:
f.write('1')
if sys.version_info[0] >= 3:
def reraise_exception(exc_info):
raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
def bytes_to_str(b):
return b.decode('latin-1')
def str_to_bytes(s):
if isinstance(s, bytes):
return s
else:
return s.encode('latin-1')
else:
def reraise_exception(exc_info):
exec("raise exc_info[0], exc_info[1], exc_info[2]")
def bytes_to_str(b):
return b
def str_to_bytes(s):
return s
class RequestHandler:
def __init__(self, server_socket, owner_pipe, app):
self.server = server_socket
self.owner_pipe = owner_pipe
self.app = app
def main_loop(self):
done = False
try:
while not done:
client, address = self.accept_connection()
if not client:
done = True
break
socket_hijacked = False
try:
try:
env, input_stream = self.parse_request(client)
if env:
if env['REQUEST_METHOD'] == 'ping':
self.process_ping(client)
else:
socket_hijacked = self.process_request(env, input_stream, client)
except KeyboardInterrupt:
done = True
except IOError as e:
if not getattr(e, 'passenger', False) or e.errno != errno.EPIPE:
logging.exception("WSGI application raised an I/O exception!")
except Exception:
logging.exception("WSGI application raised an exception!")
finally:
if not socket_hijacked:
try:
# Shutdown the socket like this just in case the app
# spawned a child process that keeps it open.
client.shutdown(socket.SHUT_WR)
except:
pass
try:
client.close()
except:
pass
except KeyboardInterrupt:
pass
def accept_connection(self):
result = select.select([self.owner_pipe, self.server.fileno()], [], [])[0]
if self.server.fileno() in result:
return self.server.accept()
else:
return (None, None)
def parse_request(self, client):
buf = b''
while len(buf) < 4:
tmp = client.recv(4 - len(buf))
if len(tmp) == 0:
return (None, None)
buf += tmp
header_size = struct.unpack('>I', buf)[0]
buf = b''
while len(buf) < header_size:
tmp = client.recv(header_size - len(buf))
if len(tmp) == 0:
return (None, None)
buf += tmp
headers = buf.split(b"\0")
headers.pop() # Remove trailing "\0"
env = {}
i = 0
while i < len(headers):
env[bytes_to_str(headers[i])] = bytes_to_str(headers[i + 1])
i += 2
return (env, client)
if hasattr(socket, '_fileobject'):
def wrap_input_socket(self, sock):
return socket._fileobject(sock, 'rb', 512)
else:
def wrap_input_socket(self, sock):
return socket.socket.makefile(sock, 'rb', 512)
def process_request(self, env, input_stream, output_stream):
# The WSGI specification says that the input parameter object passed needs to
# implement a few file-like methods. This is the reason why we "wrap" the socket._socket
# into the _fileobject to solve this.
#
# Otherwise, the POST data won't be correctly retrieved by Django.
#
# See: http://www.python.org/dev/peps/pep-0333/#input-and-error-streams
env['wsgi.input'] = self.wrap_input_socket(input_stream)
env['wsgi.errors'] = sys.stderr
env['wsgi.version'] = (1, 0)
env['wsgi.multithread'] = False
env['wsgi.multiprocess'] = True
env['wsgi.run_once'] = False
if env.get('HTTPS','off') in ('on', '1', 'true', 'yes'):
env['wsgi.url_scheme'] = 'https'
else:
env['wsgi.url_scheme'] = 'http'
headers_set = []
headers_sent = []
is_head = env['REQUEST_METHOD'] == 'HEAD'
def write(data):
try:
if not headers_set:
raise AssertionError("write() before start_response()")
elif not headers_sent:
# Before the first output, send the stored headers.
status, response_headers = headers_sent[:] = headers_set
output_stream.sendall(str_to_bytes(
'HTTP/1.1 %s\r\nStatus: %s\r\nConnection: close\r\n' %
(status, status)))
for header in response_headers:
output_stream.sendall(str_to_bytes('%s: %s\r\n' % header))
output_stream.sendall(b'\r\n')
if not is_head:
output_stream.sendall(str_to_bytes(data))
except IOError as e:
# Mark this exception as coming from the Phusion Passenger
# socket and not some other socket.
setattr(e, 'passenger', True)
raise e
def start_response(status, response_headers, exc_info = None):
if exc_info:
try:
if headers_sent:
# Re-raise original exception if headers sent.
reraise_exception(exc_info)
finally:
# Avoid dangling circular ref.
exc_info = None
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
return write
# Django's django.template.base module goes through all WSGI
# environment values, and calls each value that is a callable.
# No idea why, but we work around that with the `do_it` parameter.
def hijack(do_it = False):
if do_it:
env['passenger.hijacked_socket'] = output_stream
return output_stream
env['passenger.hijack'] = hijack
result = self.app(env, start_response)
if 'passenger.hijacked_socket' in env:
# Socket connection hijacked. Don't do anything.
return True
try:
for data in result:
# Don't send headers until body appears.
if data:
write(data)
if not headers_sent:
# Send headers now if body was empty.
write(b'')
finally:
if hasattr(result, 'close'):
result.close()
return False
def process_ping(self, output_stream):
output_stream.sendall(b"pong")
if __name__ == "__main__":
initialize_logging()
record_journey_step_end('SUBPROCESS_EXEC_WRAPPER', 'STEP_PERFORMED')
record_journey_step_begin('SUBPROCESS_WRAPPER_PREPARATION', 'STEP_IN_PROGRESS')
try:
read_startup_arguments()
except Exception:
record_journey_step_end('SUBPROCESS_WRAPPER_PREPARATION', 'STEP_ERRORED')
raise
else:
record_journey_step_end('SUBPROCESS_WRAPPER_PREPARATION', 'STEP_PERFORMED')
record_journey_step_begin('SUBPROCESS_APP_LOAD_OR_EXEC', 'STEP_IN_PROGRESS')
try:
app_module = load_app()
except Exception:
record_journey_step_end('SUBPROCESS_APP_LOAD_OR_EXEC', 'STEP_ERRORED')
raise
else:
record_journey_step_end('SUBPROCESS_APP_LOAD_OR_EXEC', 'STEP_PERFORMED')
record_journey_step_begin('SUBPROCESS_LISTEN', 'STEP_IN_PROGRESS')
try:
tuple = create_server_socket()
assert tuple is not None
socket_filename, server_socket = tuple
install_signal_handlers()
handler = RequestHandler(server_socket, sys.stdin, app_module.application)
advertise_sockets(socket_filename)
except Exception:
record_journey_step_end('SUBPROCESS_LISTEN', 'STEP_ERRORED')
raise
else:
record_journey_step_end('SUBPROCESS_LISTEN', 'STEP_PERFORMED')
advertise_readiness()
handler.main_loop()
try:
os.remove(socket_filename)
except OSError:
pass