summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Dumpleton <Graham.Dumpleton@gmail.com>2014-12-15 21:47:04 +1100
committerGraham Dumpleton <Graham.Dumpleton@gmail.com>2014-12-15 21:47:04 +1100
commit8042adfb585d7f4a1dd6f4e9c77030778d63d2b4 (patch)
tree2f0c2d3d37044d102a24c53a6c6ece2780637d97
parente2fd98c24846266227922ccd982ca85759af8135 (diff)
parent1dedfee7e189efcff25e788300c6a23cedcae1b0 (diff)
downloadmod_wsgi-4.4.1.tar.gz
Merge branch 'release/4.4.1'4.4.1
-rw-r--r--docs/release-notes/index.rst1
-rw-r--r--docs/release-notes/version-4.4.0.rst2
-rw-r--r--docs/release-notes/version-4.4.1.rst79
-rw-r--r--setup.py21
-rw-r--r--src/server/__init__.py276
-rw-r--r--src/server/mod_wsgi.c82
-rw-r--r--src/server/wsgi_daemon.c4
-rw-r--r--src/server/wsgi_daemon.h1
-rw-r--r--src/server/wsgi_interp.c2
-rw-r--r--src/server/wsgi_metrics.c4
-rw-r--r--src/server/wsgi_version.h4
-rw-r--r--tests/access.wsgi3
-rw-r--r--win32/ap22py26.mk44
-rw-r--r--win32/ap22py27.mk44
-rw-r--r--win32/ap22py31.mk42
-rw-r--r--win32/ap22py32.mk8
-rw-r--r--win32/ap22py33.mk8
-rw-r--r--win32/ap22py34.mk8
-rw-r--r--win32/ap24py26.mk8
-rw-r--r--win32/ap24py27.mk8
-rw-r--r--win32/ap24py32.mk8
-rw-r--r--win32/ap24py33.mk8
-rw-r--r--win32/ap24py34.mk8
-rw-r--r--win32/common.mk54
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.
diff --git a/setup.py b/setup.py
index 69ff1e5..42ee4fa 100644
--- a/setup.py
+++ b/setup.py
@@ -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