diff options
author | Graham Dumpleton <Graham.Dumpleton@gmail.com> | 2014-12-15 21:47:04 +1100 |
---|---|---|
committer | Graham Dumpleton <Graham.Dumpleton@gmail.com> | 2014-12-15 21:47:04 +1100 |
commit | 8042adfb585d7f4a1dd6f4e9c77030778d63d2b4 (patch) | |
tree | 2f0c2d3d37044d102a24c53a6c6ece2780637d97 | |
parent | e2fd98c24846266227922ccd982ca85759af8135 (diff) | |
parent | 1dedfee7e189efcff25e788300c6a23cedcae1b0 (diff) | |
download | mod_wsgi-4.4.1.tar.gz |
Merge branch 'release/4.4.1'4.4.1
-rw-r--r-- | docs/release-notes/index.rst | 1 | ||||
-rw-r--r-- | docs/release-notes/version-4.4.0.rst | 2 | ||||
-rw-r--r-- | docs/release-notes/version-4.4.1.rst | 79 | ||||
-rw-r--r-- | setup.py | 21 | ||||
-rw-r--r-- | src/server/__init__.py | 276 | ||||
-rw-r--r-- | src/server/mod_wsgi.c | 82 | ||||
-rw-r--r-- | src/server/wsgi_daemon.c | 4 | ||||
-rw-r--r-- | src/server/wsgi_daemon.h | 1 | ||||
-rw-r--r-- | src/server/wsgi_interp.c | 2 | ||||
-rw-r--r-- | src/server/wsgi_metrics.c | 4 | ||||
-rw-r--r-- | src/server/wsgi_version.h | 4 | ||||
-rw-r--r-- | tests/access.wsgi | 3 | ||||
-rw-r--r-- | win32/ap22py26.mk | 44 | ||||
-rw-r--r-- | win32/ap22py27.mk | 44 | ||||
-rw-r--r-- | win32/ap22py31.mk | 42 | ||||
-rw-r--r-- | win32/ap22py32.mk | 8 | ||||
-rw-r--r-- | win32/ap22py33.mk | 8 | ||||
-rw-r--r-- | win32/ap22py34.mk | 8 | ||||
-rw-r--r-- | win32/ap24py26.mk | 8 | ||||
-rw-r--r-- | win32/ap24py27.mk | 8 | ||||
-rw-r--r-- | win32/ap24py32.mk | 8 | ||||
-rw-r--r-- | win32/ap24py33.mk | 8 | ||||
-rw-r--r-- | win32/ap24py34.mk | 8 | ||||
-rw-r--r-- | win32/common.mk | 54 |
24 files changed, 555 insertions, 172 deletions
diff --git a/docs/release-notes/index.rst b/docs/release-notes/index.rst index 2856135..36b981a 100644 --- a/docs/release-notes/index.rst +++ b/docs/release-notes/index.rst @@ -5,6 +5,7 @@ Release Notes .. toctree:: :maxdepth: 2 + version-4.4.1.rst version-4.4.0.rst version-4.3.2.rst diff --git a/docs/release-notes/version-4.4.0.rst b/docs/release-notes/version-4.4.0.rst index c5df205..51e8b64 100644 --- a/docs/release-notes/version-4.4.0.rst +++ b/docs/release-notes/version-4.4.0.rst @@ -269,7 +269,7 @@ for this feature to work. specified and at the same time the ``--debug-mode`` option is specified, then coverage analysis is enabled. When the server is exited, then the profiler data will be output to the ``pstats.dat`` file under the server -working directory, or the file specified using the ``profiler-output-file`` +working directory, or the file specified using the ``--profiler-output-file`` option. 12. Added the ``--python-path`` option to ``mod_wsgi-express`` to specify diff --git a/docs/release-notes/version-4.4.1.rst b/docs/release-notes/version-4.4.1.rst new file mode 100644 index 0000000..dd731af --- /dev/null +++ b/docs/release-notes/version-4.4.1.rst @@ -0,0 +1,79 @@ +============= +Version 4.4.1 +============= + +Version 4.4.1 of mod_wsgi can be obtained from: + + https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.4.1 + +Known Issues +------------ + +1. The makefiles for building mod_wsgi on Windows are currently broken and +need updating. As most new changes relate to mod_wsgi daemon mode, which is +not supported under Windows, you should keep using the last available +binary for version 3.X on Windows instead. + +Bugs Fixed +---------- + +1. Process crashes could occur when request content had been consumed by +the WSGI application. The trigger was when the Python ``wsgi.input`` was +still in existence after the web request had finished. The destruction of +the ``wsgi.input`` object was accessing memory which had already been +released back to the Apache memory pools and potentially reused. This could +cause crashes or other unexplained behaviour. This issue was introduced in +version 4.4.0 of mod_wsgi. + +Features Changed +---------------- + +1. When an error occurs in writing back a response to the HTTP client, +during the consumption of the iterable returned by the WSGI application, +the message will now be logged at debug level rather than error level. Note +that under Apache 2.2 it isn't possible to suppress the message generated +by Apache itself from the core_output_filter, so that may still appear. + +2. The ``--profiler-output-file`` option for ``mod_wsgi-express`` was +changed to ``--profiler-directory`` and now refers to a directory, with +individual pstats files being added to the directory for each session +rather than reusing the same name all the time. + +New Features +------------ + +1. Added the ``--server-mpm`` option to ``mod_wsgi-express``. With this +option, if you are using Apache 2.4 with dynamically loadable MPM modules +and more than one option for the MPM is available, you can specify your +preference for which is used. If not specified, then the precedence order +for MPMs is 'event', 'worker' and finally 'prefork'. + +2. Added ``static`` as an option for ``--application-type`` when running +``mod_wsgi-express``. When set as ``static``, only static files will be +served. One can still set specific handler types for different extensions +which may invoke a Python handler script, but there will be no global +fallback WSGI application for any URLs that do not map to static files. In +these cases a normal HTTP 404 response will be returned instead. + +3. Added ``--host-access-script`` option to ``mod_wsgi-express`` to allow +a Python script to be provided which can control host access. This uses +the ``WSGIAccessScript`` directive and the handler script should define an +``allow_access(environ, host)`` function which returns ``True`` if access is +allowed or ``False`` if blocked. + +4. Added ``--debugger-startup`` option to be used in conjunction with +the ``--enable-debugger`` option of ``mod_wsgi-express`` when in debug mode. +The option will cause the debugger to be activated on server start before +any requests are handled to allow breakpoints to be set. + +5. Added a ``socket-user`` option to ``WSGIDaemonProcess`` to allow the +owner of the UNIX listener socket for the daemon process group to be +overridden. This can be used when using mod_ruid2 to change the owner of +the socket from the default Apache user, to the user under which mod_ruid2 +will run Apache when handling requests. This is necessary otherwise the +Apache child worker process will not be able to connect to the listener +socket for the mod_wsgi daemon process to proxy the request to the WSGI +application. + +6. Added a ``--enable-recorder`` option for enabling request recording when +also using debug mode. @@ -175,6 +175,27 @@ def _version(): match = re.search(pattern, fp.read(), flags=re.MULTILINE) return match.group('version') +# Final check to make sure a shared library for Python does actually +# exist. Warn if one doesn't as we really want a shared library. + +SHARED_LIBRARY_WARNING = """ +WARNING: The Python installation you are using does not appear to have +been installed with a shared library, or in the case of MacOS X, as a +framework. Where these are not present, the compilation of mod_wsgi may +fail, or if it does succeed, will result in extra memory being used by +all processes at run time as a result of the static library needing to +be loaded in its entirety to every process. It is highly recommended +that you reinstall the Python installation being used from source code, +supplying the '--enable-shared' option to the 'configure' script when +configuring the source code prior to building and installing it. +""" + +if (not get_python_config('Py_ENABLE_SHARED') and + not get_python_config('PYTHONFRAMEWORK')): + print(SHARED_LIBRARY_WARNING) + +# Now finally run distutils. + setup(name = 'mod_wsgi', version = _version(), description = 'Installer for Apache/mod_wsgi.', 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']) diff --git a/src/server/mod_wsgi.c b/src/server/mod_wsgi.c index 4ccb3a4..5a90dd2 100644 --- a/src/server/mod_wsgi.c +++ b/src/server/mod_wsgi.c @@ -934,16 +934,23 @@ static InputObject *newInputObject(request_rec *r) static void Input_dealloc(InputObject *self) { + if (self->buffer) + free(self->buffer); + + PyObject_Del(self); +} + +static void Input_finish(InputObject *self) +{ if (self->bb) { Py_BEGIN_ALLOW_THREADS apr_brigade_destroy(self->bb); Py_END_ALLOW_THREADS - } - if (self->buffer) - free(self->buffer); + self->bb = NULL; + } - PyObject_Del(self); + self->r = NULL; } static PyObject *Input_close(InputObject *self, PyObject *args) @@ -2236,11 +2243,24 @@ static int Adapter_output(AdapterObject *self, const char *data, char status_buffer[512]; const char *error_message; - error_message = apr_psprintf(r->pool, "Apache/mod_wsgi failed " - "to write response data: %s.", apr_strerror(rv, - status_buffer, sizeof(status_buffer)-1), NULL); + if (!exception_when_aborted) { + error_message = apr_psprintf(r->pool, "Failed to write " + "response data: %s", apr_strerror(rv, status_buffer, + sizeof(status_buffer)-1), NULL); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r, + "mod_wsgi (pid=%d): %s.", getpid(), + error_message); + } + else { + error_message = apr_psprintf(r->pool, "Apache/mod_wsgi " + "failed to write response data: %s", + apr_strerror(rv, status_buffer, + sizeof(status_buffer)-1), NULL); + + PyErr_SetString(PyExc_IOError, error_message); + } - PyErr_SetString(PyExc_IOError, error_message); return 0; } @@ -3675,7 +3695,8 @@ static int wsgi_execute_script(request_rec *r) */ adapter->r = NULL; - adapter->input->r = NULL; + + Input_finish(adapter->input); /* Close the log object so data is flushed. */ @@ -6579,6 +6600,8 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig, int socket_timeout = 0; int queue_timeout = 0; + const char *socket_user = NULL; + int listen_backlog = WSGI_LISTEN_BACKLOG; const char *display_name = NULL; @@ -6860,6 +6883,25 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig, "or 0 for default."; } } + else if (!strcmp(option, "socket-user")) { + uid_t socket_uid; + + if (!*value) + return "Invalid socket user for WSGI daemon process."; + + socket_uid = ap_uname2id(value); + + if (*value == '#') { + struct passwd *entry = NULL; + + if ((entry = getpwuid(socket_uid)) == NULL) + return "Couldn't determine user name from socket user."; + + value = entry->pw_name; + } + + socket_user = value; + } else if (!strcmp(option, "script-user")) { uid_t script_uid; @@ -7039,6 +7081,8 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig, entry->socket_timeout = apr_time_from_sec(socket_timeout); entry->queue_timeout = apr_time_from_sec(queue_timeout); + entry->socket_user = apr_pstrdup(cmd->pool, socket_user); + entry->listen_backlog = listen_backlog; entry->display_name = display_name; @@ -7630,14 +7674,19 @@ static int wsgi_setup_socket(WSGIProcessGroup *process) if (!geteuid()) { #if defined(MPM_ITK) || defined(ITK_MPM) - if (chown(process->socket_path, process->uid, -1) < 0) { + uid_t socket_uid = process->uid; #else - if (chown(process->socket_path, ap_unixd_config.user_id, -1) < 0) { + uid_t socket_uid = ap_unixd_config.user_id; #endif + + if (process->socket_user) + socket_uid = ap_uname2id(process->socket_user); + + if (chown(process->socket_path, socket_uid, -1) < 0) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, "mod_wsgi (pid=%d): Couldn't change owner of unix " - "domain socket '%s'.", getpid(), - process->socket_path); + "domain socket '%s' to uid=%ld.", getpid(), + process->socket_path, (long)socket_uid); return -1; } } @@ -9723,8 +9772,9 @@ static int wsgi_connect_daemon(request_rec *r, WSGIDaemonSocket *daemon) else { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_wsgi (pid=%d): Unable to connect to " - "WSGI daemon process '%s' on '%s'.", - getpid(), daemon->name, daemon->socket_path); + "WSGI daemon process '%s' on '%s' as user " + "with uid=%ld.", getpid(), daemon->name, + daemon->socket_path, (long)geteuid()); apr_socket_close(daemon->socket); @@ -11805,7 +11855,7 @@ static int wsgi_hook_init(apr_pool_t *pconf, apr_pool_t *ptemp, if (data) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "mod_wsgi (pid=%d): The mod_python module can " - "not be used on conjunction with mod_wsgi 4.0+. " + "not be used in conjunction with mod_wsgi 4.0+. " "Remove the mod_python module from the Apache " "configuration.", getpid()); diff --git a/src/server/wsgi_daemon.c b/src/server/wsgi_daemon.c index c4f0d68..d1392ae 100644 --- a/src/server/wsgi_daemon.c +++ b/src/server/wsgi_daemon.c @@ -22,6 +22,8 @@ /* ------------------------------------------------------------------------- */ +#if defined(MOD_WSGI_WITH_DAEMONS) + int wsgi_daemon_count = 0; apr_hash_t *wsgi_daemon_index = NULL; apr_hash_t *wsgi_daemon_listeners = NULL; @@ -34,6 +36,8 @@ WSGIDaemonThread *wsgi_worker_threads = NULL; WSGIThreadStack *wsgi_worker_stack = NULL; +#endif + /* ------------------------------------------------------------------------- */ /* vi: set sw=4 expandtab : */ diff --git a/src/server/wsgi_daemon.h b/src/server/wsgi_daemon.h index 21190d3..fbe6d9c 100644 --- a/src/server/wsgi_daemon.h +++ b/src/server/wsgi_daemon.h @@ -113,6 +113,7 @@ typedef struct { apr_time_t connect_timeout; apr_time_t socket_timeout; apr_time_t queue_timeout; + const char *socket_user; int listen_backlog; const char *display_name; int send_buffer_size; diff --git a/src/server/wsgi_interp.c b/src/server/wsgi_interp.c index 7d0c1a7..60851d0 100644 --- a/src/server/wsgi_interp.c +++ b/src/server/wsgi_interp.c @@ -2139,8 +2139,8 @@ void wsgi_python_init(apr_pool_t *p) Py_SetPythonHome((char *)python_home); #endif } -#endif } +#endif /* * Set environment variable PYTHONHASHSEED. We need to diff --git a/src/server/wsgi_metrics.c b/src/server/wsgi_metrics.c index cee2031..4b0ccb0 100644 --- a/src/server/wsgi_metrics.c +++ b/src/server/wsgi_metrics.c @@ -93,6 +93,7 @@ static PyObject *wsgi_process_metrics(void) return Py_None; } } +#if defined(MOD_WSGI_WITH_DAEMONS) else { if (!wsgi_daemon_process->group->server_metrics) { Py_INCREF(Py_None); @@ -100,6 +101,7 @@ static PyObject *wsgi_process_metrics(void) return Py_None; } } +#endif result = PyDict_New(); @@ -221,6 +223,7 @@ static PyObject *wsgi_server_metrics(void) return Py_None; } } +#if defined(MOD_WSGI_WITH_DAEMONS) else { if (!wsgi_daemon_process->group->server_metrics) { Py_INCREF(Py_None); @@ -228,6 +231,7 @@ static PyObject *wsgi_server_metrics(void) return Py_None; } } +#endif gs_record = ap_get_scoreboard_global(); diff --git a/src/server/wsgi_version.h b/src/server/wsgi_version.h index 4dfe4ca..8917d2c 100644 --- a/src/server/wsgi_version.h +++ b/src/server/wsgi_version.h @@ -25,8 +25,8 @@ #define MOD_WSGI_MAJORVERSION_NUMBER 4 #define MOD_WSGI_MINORVERSION_NUMBER 4 -#define MOD_WSGI_MICROVERSION_NUMBER 0 -#define MOD_WSGI_VERSION_STRING "4.4.0" +#define MOD_WSGI_MICROVERSION_NUMBER 1 +#define MOD_WSGI_VERSION_STRING "4.4.1" /* ------------------------------------------------------------------------- */ diff --git a/tests/access.wsgi b/tests/access.wsgi new file mode 100644 index 0000000..a0ebfc3 --- /dev/null +++ b/tests/access.wsgi @@ -0,0 +1,3 @@ +def allow_access(environ, host): + print environ, host + return True diff --git a/win32/ap22py26.mk b/win32/ap22py26.mk index 99844f8..470881e 100644 --- a/win32/ap22py26.mk +++ b/win32/ap22py26.mk @@ -1,42 +1,8 @@ -CPPFLAGS = \ - /DWIN32 \ - /DNDEBUG \ - /I"c:\Program Files\Microsoft Visual Studio 9.0\VC\include" \ - /I"c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include" \ - /I"c:\Program Files\Apache Software Foundation\Apache2.2\include" \ - /I"c:\Python26\include" +APACHE_ROOTDIR = c:\Apache22 +APACHE_VERSION = 22 -CFLAGS = \ - /MD \ - /GF \ - /Gy \ - /O2 \ - /Wall \ - /Zc:wchar_t \ - /Zc:forScope +PYTHON_ROOTDIR = c:\Python26 +PYTHON_VERSION = 26 -LDFLAGS = \ - /link \ - "/LIBPATH:c:\Program Files\Microsoft Visual Studio 9.0\VC\lib" \ - "/LIBPATH:c:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib" \ - "/LIBPATH:c:\Program Files\Apache Software Foundation\Apache2.2\lib" \ - /LIBPATH:c:\Python26\libs \ - /OPT:REF \ - /OPT:ICF=2 \ - /RELEASE \ - /SUBSYSTEM:WINDOWS +include common.mk -LDLIBS = \ - python26.lib \ - libhttpd.lib \ - libapr-1.lib \ - libaprutil-1.lib - -SRCFILES = mod_wsgi.c wsgi_apache.c wsgi_convert.c wsgi_validate.c - -mod_wsgi.so : $(SRCFILES) - cl $(CPPFLAGS) $(CFLAGS) $(SRCFILES) /LD $(LDFLAGS) $(LDLIBS) /OUT:$@ - mt -manifest $@.manifest -outputresource:$@;2 - -clean : - del *.obj *.so *.so.manifest *.lib *.exp diff --git a/win32/ap22py27.mk b/win32/ap22py27.mk index c85f4c6..9611d1b 100644 --- a/win32/ap22py27.mk +++ b/win32/ap22py27.mk @@ -1,42 +1,8 @@ -CPPFLAGS = \ - /DWIN32 \ - /DNDEBUG \ - /I"c:\Program Files\Microsoft Visual Studio 9.0\VC\include" \ - /I"c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include" \ - /I"c:\Program Files\Apache Software Foundation\Apache2.2\include" \ - /I"c:\Python27\include" +APACHE_ROOTDIR = c:\Apache22 +APACHE_VERSION = 22 -CFLAGS = \ - /MD \ - /GF \ - /Gy \ - /O2 \ - /Wall \ - /Zc:wchar_t \ - /Zc:forScope +PYTHON_ROOTDIR = c:\Python27 +PYTHON_VERSION = 27 -LDFLAGS = \ - /link \ - "/LIBPATH:c:\Program Files\Microsoft Visual Studio 9.0\VC\lib" \ - "/LIBPATH:c:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib" \ - "/LIBPATH:c:\Program Files\Apache Software Foundation\Apache2.2\lib" \ - /LIBPATH:c:\Python27\libs \ - /OPT:REF \ - /OPT:ICF=2 \ - /RELEASE \ - /SUBSYSTEM:WINDOWS +include common.mk -LDLIBS = \ - python27.lib \ - libhttpd.lib \ - libapr-1.lib \ - libaprutil-1.lib - -SRCFILES = mod_wsgi.c wsgi_apache.c wsgi_convert.c wsgi_validate.c - -mod_wsgi.so : $(SRCFILES) - cl $(CPPFLAGS) $(CFLAGS) $(SRCFILES) /LD $(LDFLAGS) $(LDLIBS) /OUT:$@ - mt -manifest $@.manifest -outputresource:$@;2 - -clean : - del *.obj *.so *.so.manifest *.lib *.exp diff --git a/win32/ap22py31.mk b/win32/ap22py31.mk deleted file mode 100644 index 23e2ac6..0000000 --- a/win32/ap22py31.mk +++ /dev/null @@ -1,42 +0,0 @@ -CPPFLAGS = \ - /DWIN32 \ - /DNDEBUG \ - /I"c:\Program Files\Microsoft Visual Studio 9.0\VC\include" \ - /I"c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include" \ - /I"c:\Program Files\Apache Software Foundation\Apache2.2\include" \ - /I"c:\Python31\include" - -CFLAGS = \ - /MD \ - /GF \ - /Gy \ - /O2 \ - /Wall \ - /Zc:wchar_t \ - /Zc:forScope - -LDFLAGS = \ - /link \ - "/LIBPATH:c:\Program Files\Microsoft Visual Studio 9.0\VC\lib" \ - "/LIBPATH:c:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib" \ - "/LIBPATH:c:\Program Files\Apache Software Foundation\Apache2.2\lib" \ - /LIBPATH:c:\Python31\libs \ - /OPT:REF \ - /OPT:ICF=2 \ - /RELEASE \ - /SUBSYSTEM:WINDOWS - -LDLIBS = \ - python31.lib \ - libhttpd.lib \ - libapr-1.lib \ - libaprutil-1.lib - -SRCFILES = mod_wsgi.c wsgi_apache.c wsgi_convert.c wsgi_validate.c - -mod_wsgi.so : $(SRCFILES) - cl $(CPPFLAGS) $(CFLAGS) $(SRCFILES) /LD $(LDFLAGS) $(LDLIBS) /OUT:$@ - mt -manifest $@.manifest -outputresource:$@;2 - -clean : - del *.obj *.so *.so.manifest *.lib *.exp diff --git a/win32/ap22py32.mk b/win32/ap22py32.mk new file mode 100644 index 0000000..80f13c8 --- /dev/null +++ b/win32/ap22py32.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache22 +APACHE_VERSION = 22 + +PYTHON_ROOTDIR = c:\Python32 +PYTHON_VERSION = 32 + +include common.mk + diff --git a/win32/ap22py33.mk b/win32/ap22py33.mk new file mode 100644 index 0000000..d9ab722 --- /dev/null +++ b/win32/ap22py33.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache22 +APACHE_VERSION = 22 + +PYTHON_ROOTDIR = c:\Python33 +PYTHON_VERSION = 33 + +include common.mk + diff --git a/win32/ap22py34.mk b/win32/ap22py34.mk new file mode 100644 index 0000000..bd4337e --- /dev/null +++ b/win32/ap22py34.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache22 +APACHE_VERSION = 22 + +PYTHON_ROOTDIR = c:\Python34 +PYTHON_VERSION = 34 + +include common.mk + diff --git a/win32/ap24py26.mk b/win32/ap24py26.mk new file mode 100644 index 0000000..e321fb1 --- /dev/null +++ b/win32/ap24py26.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache24 +APACHE_VERSION = 24 + +PYTHON_ROOTDIR = c:\Python26 +PYTHON_VERSION = 26 + +include common.mk + diff --git a/win32/ap24py27.mk b/win32/ap24py27.mk new file mode 100644 index 0000000..389642c --- /dev/null +++ b/win32/ap24py27.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache24 +APACHE_VERSION = 24 + +PYTHON_ROOTDIR = c:\Python27 +PYTHON_VERSION = 27 + +include common.mk + diff --git a/win32/ap24py32.mk b/win32/ap24py32.mk new file mode 100644 index 0000000..549c2ca --- /dev/null +++ b/win32/ap24py32.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache24 +APACHE_VERSION = 24 + +PYTHON_ROOTDIR = c:\Python32 +PYTHON_VERSION = 32 + +include common.mk + diff --git a/win32/ap24py33.mk b/win32/ap24py33.mk new file mode 100644 index 0000000..e72a0be --- /dev/null +++ b/win32/ap24py33.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache24 +APACHE_VERSION = 24 + +PYTHON_ROOTDIR = c:\Python33 +PYTHON_VERSION = 33 + +include common.mk + diff --git a/win32/ap24py34.mk b/win32/ap24py34.mk new file mode 100644 index 0000000..2a2e8d2 --- /dev/null +++ b/win32/ap24py34.mk @@ -0,0 +1,8 @@ +APACHE_ROOTDIR = c:\Apache24 +APACHE_VERSION = 24 + +PYTHON_ROOTDIR = c:\Python34 +PYTHON_VERSION = 34 + +include common.mk + diff --git a/win32/common.mk b/win32/common.mk new file mode 100644 index 0000000..42d0776 --- /dev/null +++ b/win32/common.mk @@ -0,0 +1,54 @@ +CPPFLAGS = \ + /DWIN32 \ + /DNDEBUG \ + /I"$(APACHE_ROOTDIR)\include" \ + /I"$(PYTHON_ROOTDIR)\include" + +CFLAGS = \ + /MD \ + /GF \ + /Gy \ + /O2 \ + /Wall \ + /Zc:wchar_t \ + /Zc:forScope + +LDFLAGS = \ + /link \ + /LIBPATH:$(APACHE_ROOTDIR)\lib \ + /LIBPATH:$(PYTHON_ROOTDIR)\libs \ + /OPT:REF \ + /OPT:ICF=2 \ + /RELEASE \ + /SUBSYSTEM:WINDOWS + +LDLIBS = \ + python$(PYTHON_VERSION).lib \ + libhttpd.lib \ + libapr-1.lib \ + libaprutil-1.lib + +SRCFILES = ../src/server/*.c + +mod_wsgi.so : $(SRCFILES) + cl $(CPPFLAGS) $(CFLAGS) $(SRCFILES) /LD $(LDFLAGS) $(LDLIBS) /OUT:$@ + mt -manifest $@.manifest -outputresource:$@;2 + +VARIANT = ap$(APACHE_VERSION)py$(PYTHON_VERSION) + +install : mod_wsgi.so + copy $? $(APACHE_ROOTDIR)\modules\mod_wsgi-$(VARIANT).so + : + : + : + : + : You now need to edit $(APACHE_ROOTDIR)/conf/httpd.conf and add: + : + : LoadModule wsgi_module modules/mod_wsgi-$(VARIANT).so + : + : + : + : + +clean : + del *.obj *.so *.so.manifest *.lib *.exp |