diff options
Diffstat (limited to 'src/server/__init__.py')
-rw-r--r-- | src/server/__init__.py | 276 |
1 files changed, 244 insertions, 32 deletions
diff --git a/src/server/__init__.py b/src/server/__init__.py index 9b85f44..239ceb9 100644 --- a/src/server/__init__.py +++ b/src/server/__init__.py @@ -13,6 +13,9 @@ import imp import pwd import grp import re +import pprint +import time +import traceback try: import Queue as queue @@ -531,6 +534,7 @@ DocumentRoot '%(document_root)s' <IfDefine WSGI_DIRECTORY_INDEX> DirectoryIndex %(directory_index)s </IfDefine> +<IfDefine !WSGI_STATIC_ONLY> RewriteEngine On RewriteCond %%{REQUEST_FILENAME} !-f <IfDefine WSGI_DIRECTORY_INDEX> @@ -540,6 +544,7 @@ DocumentRoot '%(document_root)s' RewriteCond %%{REQUEST_URI} !/server-status </IfDefine> RewriteRule .* - [H=wsgi-handler] +</IfDefine> Order allow,deny Allow from all </Directory> @@ -548,6 +553,12 @@ DocumentRoot '%(document_root)s' WSGIErrorOverride On </IfDefine> +<IfDefine WSGI_HOST_ACCESS> +<Location /> + WSGIAccessScript '%(host_access_script)s' +</Location> +</IfDefine> + <IfDefine WSGI_AUTH_USER> <Location /> AuthType %(auth_type)s @@ -829,20 +840,29 @@ def start_reloader(interval=1.0): class PostMortemDebugger(object): - def __init__(self, application): + def __init__(self, application, startup): self.application = application self.generator = None - def debug_exception(self): import pdb - pdb.post_mortem() + self.debugger = pdb.Pdb() + + if startup: + self.activate_console() + + def activate_console(self): + self.debugger.set_trace(sys._getframe().f_back) + + def run_post_mortem(self): + self.debugger.reset() + self.debugger.interaction(None, sys.exc_info()[2]) def __call__(self, environ, start_response): try: self.generator = self.application(environ, start_response) return self except Exception: - self.debug_exception() + self.run_post_mortem() raise def __iter__(self): @@ -861,12 +881,115 @@ class PostMortemDebugger(object): self.debug_exception() raise +class RequestRecorder(object): + + def __init__(self, application, savedir): + self.application = application + self.savedir = savedir + self.lock = threading.Lock() + self.pid = os.getpid() + self.count = 0 + + def __call__(self, environ, start_response): + with self.lock: + self.count += 1 + count = self.count + + key = "%s-%s-%s" % (int(time.time()*1000000), self.pid, count) + + iheaders = os.path.join(self.savedir, key + ".iheaders") + iheaders_fp = open(iheaders, 'w') + + icontent = os.path.join(self.savedir, key + ".icontent") + icontent_fp = open(icontent, 'w+b') + + oheaders = os.path.join(self.savedir, key + ".oheaders") + oheaders_fp = open(oheaders, 'w') + + ocontent = os.path.join(self.savedir, key + ".ocontent") + ocontent_fp = open(ocontent, 'w+b') + + oaexcept = os.path.join(self.savedir, key + ".oaexcept") + oaexcept_fp = open(oaexcept, 'w') + + orexcept = os.path.join(self.savedir, key + ".orexcept") + orexcept_fp = open(orexcept, 'w') + + ofexcept = os.path.join(self.savedir, key + ".ofexcept") + ofexcept_fp = open(ofexcept, 'w') + + errors = environ['wsgi.errors'] + pprint.pprint(environ, stream=iheaders_fp) + iheaders_fp.close() + + input = environ['wsgi.input'] + + data = input.read(8192) + + while data: + icontent_fp.write(data) + data = input.read(8192) + + icontent_fp.flush() + icontent_fp.seek(0, os.SEEK_SET) + + environ['wsgi.input'] = icontent_fp + + def _start_response(status, response_headers, *args): + pprint.pprint(((status, response_headers)+args), + stream=oheaders_fp) + + _write = start_response(status, response_headers, *args) + + def write(self, data): + ocontent_fp.write(data) + ocontent_fp.flush() + return _write(data) + + return write + + try: + try: + result = self.application(environ, _start_response) + + except: + traceback.print_exception(*sys.exc_info(), file=oaexcept_fp) + raise + + try: + for data in result: + ocontent_fp.write(data) + ocontent_fp.flush() + yield data + + except: + traceback.print_exception(*sys.exc_info(), file=orexcept_fp) + raise + + finally: + try: + if hasattr(result, 'close'): + result.close() + + except: + traceback.print_exception(*sys.exc_info(), + file=ofexcept_fp) + raise + + finally: + oheaders_fp.close() + ocontent_fp.close() + oaexcept_fp.close() + orexcept_fp.close() + ofexcept_fp.close() + class ApplicationHandler(object): def __init__(self, entry_point, application_type='script', callable_object='application', mount_point='/', with_newrelic_agent=False, debug_mode=False, - enable_debugger=False): + enable_debugger=False, debugger_startup=False, + enable_recorder=False, recorder_directory=None): self.entry_point = entry_point self.application_type = application_type @@ -912,7 +1035,10 @@ class ApplicationHandler(object): self.enable_debugger = enable_debugger if enable_debugger: - self.setup_post_mortem_debugging() + self.setup_debugger(debugger_startup) + + if enable_recorder: + self.setup_recorder(recorder_directory) def setup_newrelic_agent(self): import newrelic.agent @@ -930,8 +1056,11 @@ class ApplicationHandler(object): self.application = newrelic.agent.WSGIApplicationWrapper( self.application) - def setup_post_mortem_debugging(self): - self.application = PostMortemDebugger(self.application) + def setup_debugger(self, startup): + self.application = PostMortemDebugger(self.application, startup) + + def setup_recorder(self, savedir): + self.application = RequestRecorder(self.application, savedir) def reload_required(self, environ): if self.debug_mode: @@ -1010,6 +1139,8 @@ WSGI_HANDLER_SCRIPT = """ import os import sys import atexit +import time + import mod_wsgi.server working_directory = '%(working_directory)s' @@ -1025,10 +1156,13 @@ newrelic_environment = '%(newrelic_environment)s' reload_on_changes = %(reload_on_changes)s debug_mode = %(debug_mode)s enable_debugger = %(enable_debugger)s +debugger_startup = %(debugger_startup)s enable_coverage = %(enable_coverage)s coverage_directory = '%(coverage_directory)s' enable_profiler = %(enable_profiler)s -profiler_output_file = '%(profiler_output_file)s' +profiler_directory = '%(profiler_directory)s' +enable_recorder = %(enable_recorder)s +recorder_directory = '%(recorder_directory)s' if python_paths: sys.path.extend(python_paths) @@ -1054,7 +1188,9 @@ if enable_coverage: def output_profiler_data(): profiler_info.disable() - profiler_info.dump_stats(profiler_output_file) + output_file = '%%s-%%d.pstats' %% (int(time.time()*1000000), os.getpid()) + output_file = os.path.join(profiler_directory, output_file) + profiler_info.dump_stats(output_file) if enable_profiler: from cProfile import Profile @@ -1071,7 +1207,9 @@ if with_newrelic_agent: handler = mod_wsgi.server.ApplicationHandler(entry_point, application_type=application_type, callable_object=callable_object, mount_point=mount_point, with_newrelic_agent=with_newrelic_agent, - debug_mode=debug_mode, enable_debugger=enable_debugger) + debug_mode=debug_mode, enable_debugger=enable_debugger, + debugger_startup=debugger_startup, enable_recorder=enable_recorder, + recorder_directory=recorder_directory) reload_required = handler.reload_required handle_request = handler.handle_request @@ -1199,6 +1337,21 @@ WSGI_RUN_GROUP="${WSGI_RUN_GROUP:-%(group)s}" export WSGI_RUN_USER export WSGI_RUN_GROUP +if [ `id -u` = "0" -a ${WSGI_RUN_USER} = "root" ]; then + cat << EOF + +WARNING: When running as the 'root' user, it is required that the options +'--user' and '--group' be specified to mod_wsgi-express. These should +define a non 'root' user and group under which the Apache child worker +processes and mod_wsgi daemon processes should be run. Failure to specify +these options will result in Apache and/or the mod_wsgi daemon processes +failing to start. See the mod_wsgi-express documentation for further +information on this restriction. + +EOF + +fi + LANG='%(lang)s' LC_ALL='%(locale)s' @@ -1214,7 +1367,7 @@ fi STATUSURL="http://%(host)s:%(port)s/server-status" -if [ "x$ARGV" = "x" ] ; then +if [ "x$ARGV" = "x" ]; then ARGV="-h" fi @@ -1552,6 +1705,9 @@ option_list = ( 'will be available at the /server-status sub URL. Defaults to ' 'being disabled.'), + optparse.make_option('--host-access-script', metavar='SCRIPT-PATH', + default=None, help='Specify a Python script file for ' + 'performing host access checks.'), optparse.make_option('--auth-user-script', metavar='SCRIPT-PATH', default=None, help='Specify a Python script file for ' 'performing user authentication.'), @@ -1615,6 +1771,14 @@ option_list = ( help='Specify an alternate directory for where the generated ' 'web server configuration, startup files and logs will be ' 'stored. Defaults to a sub directory of /tmp.'), + + optparse.make_option('--server-mpm', action='append', + dest='server_mpm_variables', metavar='NAME', help='Specify ' + 'preferred MPM to use when using Apache 2.4 with dynamically ' + 'loadable MPMs and more than one is available. By default ' + 'the MPM precedence order when no preference is given is ' + '\"event\", \"worker" and \"prefork\".'), + optparse.make_option('--log-directory', metavar='DIRECTORY-PATH', help='Specify an alternate directory for where the log files ' 'will be stored. Defaults to the server root directory.'), @@ -1717,10 +1881,15 @@ option_list = ( optparse.make_option('--enable-debugger', action='store_true', default=False, help='Flag indicating whether post mortem ' - 'debugging of any exceptions which propogate out from the ' + 'debugging of any exceptions which propagate out from the ' 'WSGI application when running in debug mode should be ' 'performed. Post mortem debugging is performed using the ' 'Python debugger (pdb).'), + optparse.make_option('--debugger-startup', action='store_true', + default=False, help='Flag indicating whether when post ' + 'mortem debugging is enabled, that the debugger should ' + 'also be thrown into the interactive console on initial ' + 'startup of the server to allow breakpoints to be setup.'), optparse.make_option('--enable-coverage', action='store_true', default=False, help='Flag indicating whether coverage analysis ' @@ -1733,9 +1902,18 @@ option_list = ( optparse.make_option('--enable-profiler', action='store_true', default=False, help='Flag indicating whether code profiling ' 'is enabled when running in debug mode.'), - optparse.make_option('--profiler-output-file', metavar='FILE-PATH', - default='', help='Override the path to the file into which ' - 'profiler data will be written when enabled under debug mode.'), + optparse.make_option('--profiler-directory', metavar='DIRECTORY-PATH', + default='', help='Override the path to the directory into ' + 'which profiler data will be written when enabled under debug ' + 'mode.'), + + optparse.make_option('--enable-recorder', action='store_true', + default=False, help='Flag indicating whether recording of ' + 'requests is enabled when running in debug mode.'), + optparse.make_option('--recorder-directory', metavar='DIRECTORY-PATH', + default='', help='Override the path to the directory into ' + 'which recorder data will be written when enabled under debug ' + 'mode.'), optparse.make_option('--setup-only', action='store_true', default=False, help='Flag indicating that after the configuration files have ' @@ -1758,17 +1936,18 @@ def cmd_setup_server(params): _cmd_setup_server('setup-server', args, vars(options)) -def _mpm_module_defines(modules_directory): +def _mpm_module_defines(modules_directory, preferred=None): result = [] workers = ['event', 'worker', 'prefork'] found = False for name in workers: - if os.path.exists(os.path.join(modules_directory, - 'mod_mpm_%s.so' % name)): - if not found: - result.append('-DWSGI_MPM_ENABLE_%s_MODULE' % name.upper()) - found = True - result.append('-DWSGI_MPM_EXISTS_%s_MODULE' % name.upper()) + if not preferred or name in preferred: + if os.path.exists(os.path.join(modules_directory, + 'mod_mpm_%s.so' % name)): + if not found: + result.append('-DWSGI_MPM_ENABLE_%s_MODULE' % name.upper()) + found = True + result.append('-DWSGI_MPM_EXISTS_%s_MODULE' % name.upper()) return result def _cmd_setup_server(command, args, options): @@ -1804,13 +1983,18 @@ def _cmd_setup_server(command, args, options): if not args: options['entry_point'] = os.path.join(options['server_root'], 'default.wsgi') - options['application_type'] = 'script' - options['enable_docs'] = True + if options['application_type'] != 'static': + options['application_type'] = 'script' + options['enable_docs'] = True elif options['application_type'] in ('script', 'paste'): options['entry_point'] = os.path.abspath(args[0]) else: options['entry_point'] = args[0] + if options['host_access_script']: + options['host_access_script'] = os.path.abspath( + options['host_access_script']) + if options['auth_user_script']: options['auth_user_script'] = os.path.abspath( options['auth_user_script']) @@ -2106,17 +2290,36 @@ def _cmd_setup_server(command, args, options): pass if options['enable_profiler']: - if not options['profiler_output_file']: - options['profiler_output_file'] = os.path.join( - options['server_root'], 'pstats.dat') + if not options['profiler_directory']: + options['profiler_directory'] = os.path.join( + options['server_root'], 'pstats') + else: + options['profiler_directory'] = os.path.abspath( + options['profiler_directory']) + + try: + os.mkdir(options['profiler_directory']) + except Exception: + pass + + if options['enable_recorder']: + if not options['recorder_directory']: + options['recorder_directory'] = os.path.join( + options['server_root'], 'archive') else: - options['profiler_output_file'] = os.path.abspath( - options['profiler_output_file']) + options['recorder_directory'] = os.path.abspath( + options['recorder_directory']) + + try: + os.mkdir(options['recorder_directory']) + except Exception: + pass else: options['enable_debugger'] = False options['enable_coverage'] = False options['enable_profiler'] = False + options['enable_recorder'] = False options['parent_domain'] = 'unspecified' @@ -2138,6 +2341,9 @@ def _cmd_setup_server(command, args, options): if options['allow_localhost']: options['httpd_arguments_list'].append('-DWSGI_ALLOW_LOCALHOST') + if options['application_type'] == 'static': + options['httpd_arguments_list'].append('-DWSGI_STATIC_ONLY') + if options['server_metrics']: options['httpd_arguments_list'].append('-DWSGI_SERVER_METRICS') if options['server_status']: @@ -2159,6 +2365,8 @@ def _cmd_setup_server(command, args, options): options['httpd_arguments_list'].append('-DWSGI_LISTENER_HOST') if options['error_override']: options['httpd_arguments_list'].append('-DWSGI_ERROR_OVERRIDE') + if options['host_access_script']: + options['httpd_arguments_list'].append('-DWSGI_HOST_ACCESS') if options['auth_user_script']: options['httpd_arguments_list'].append('-DWSGI_AUTH_USER') if options['auth_group_script']: @@ -2169,7 +2377,8 @@ def _cmd_setup_server(command, args, options): options['httpd_arguments_list'].append('-DWSGI_WITH_PHP5') options['httpd_arguments_list'].extend( - _mpm_module_defines(options['modules_directory'])) + _mpm_module_defines(options['modules_directory'], + options['server_mpm_variables'])) options['httpd_arguments'] = '-f %s %s' % (options['httpd_conf'], ' '.join(options['httpd_arguments_list'])) @@ -2208,7 +2417,10 @@ def _cmd_setup_server(command, args, options): options['coverage_directory'], 'index.html')) if options['enable_profiler']: - print('Profiler Output :', options['profiler_output_file']) + print('Profiler Output :', options['profiler_directory']) + + if options['enable_recorder']: + print('Recorder Output :', options['recorder_directory']) if options['envvars_script']: print('Environ Variables :', options['envvars_script']) |