summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Dumpleton <Graham.Dumpleton@gmail.com>2015-02-02 14:26:29 +1100
committerGraham Dumpleton <Graham.Dumpleton@gmail.com>2015-02-02 14:26:29 +1100
commit8f3e537ad62873f72e211808fef6ef7c178ef8da (patch)
treef45a5aedc1592136ac7472f5af716074561ed819
parent86b5d2da6816b4239570ea6efd5af2fc35c0bb82 (diff)
parentb6f1291009109d9bb66330d16179b05680908469 (diff)
downloadmod_wsgi-4.4.7.tar.gz
Merge branch 'release/4.4.7'4.4.7
-rw-r--r--docs/release-notes/index.rst1
-rw-r--r--docs/release-notes/version-4.4.7.rst69
-rw-r--r--setup.py97
-rw-r--r--src/server/__init__.py182
-rw-r--r--src/server/mod_wsgi.c40
-rw-r--r--src/server/wsgi_daemon.h3
-rw-r--r--src/server/wsgi_version.h4
7 files changed, 289 insertions, 107 deletions
diff --git a/docs/release-notes/index.rst b/docs/release-notes/index.rst
index e9cfb3e..5726dad 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.7.rst
version-4.4.6.rst
version-4.4.5.rst
version-4.4.4.rst
diff --git a/docs/release-notes/version-4.4.7.rst b/docs/release-notes/version-4.4.7.rst
new file mode 100644
index 0000000..a142438
--- /dev/null
+++ b/docs/release-notes/version-4.4.7.rst
@@ -0,0 +1,69 @@
+=============
+Version 4.4.7
+=============
+
+Version 4.4.7 of mod_wsgi can be obtained from:
+
+ https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.4.7
+
+For details on the availability of Windows binaries see:
+
+ https://github.com/GrahamDumpleton/mod_wsgi/tree/master/win32
+
+Features Changed
+----------------
+
+1. The ``proxy-buffer-size`` option to ``WSGIDaemonProcess`` directive
+was renamed to ``response-buffer-size`` to avoid confusion with options
+related to normal HTTP proxying. The ``--proxy-buffer-size`` option of
+``mod_wsgi-express`` was similarly renamed to ``--response-buffer-size``.
+
+New Features
+------------
+
+1. Added ``--service-script`` option to ``mod_wsgi-express`` to allow a
+Python script to be loaded and executed in the context of a distinct
+daemon process. This can be used for executing a service to be managed by
+Apache, even though it is a distinct application. The options take two
+arguments, a short name for the service and the path to the Python script
+for starting the service.
+
+If ``mod_wsgi-express`` is being run as root, then a user and group can be
+specified for the service using the ``--service-user`` and
+``--service-group`` options. The options take two arguments, a short name
+for the service and the user or group name respectively.
+
+2. Added ``--proxy-url-alias`` option to ``mod_wsgi-express`` for setting
+up proxying of a sub URL of the site to a remote URL.
+
+3. Added ``--proxy-virtual-host`` option to ``mod_wsgi-express`` for setting
+up proxying of a whole virtual host to a remote URL. Only supports proxying
+of HTTP requests and not HTTPS requests.
+
+4. Added ``eviction-timeout`` option to ``WSGIDaemonProcess`` directive.
+For the case where the graceful restart signal, usually ``SIGUSR1``, is
+sent to a daemon process to evict the WSGI application and restart the
+process, this controls how many seconds the process will wait, while still
+accepting new requests, before it reaches an idle state with no active
+requests and shuts down.
+
+The ``graceful-timeout`` option previously performed this exact role in
+this case previously, but a separate option is being added to allow a
+different timeout period to be specified for the case for forced eviction.
+The existing ``graceful-timeout`` option is still used when a maximum
+requests option or CPU usage limit is set. For backwards compatibility,
+if ``eviction-timeout`` isn't set, it will fall back to using any value
+specified using the ``graceful-timeout`` option.
+
+The ``--eviction-timeout`` option has also been added to
+``mod_wsgi-express`` and behaves in a similar fashion.
+
+5. Added support for new ``mod_wsgi-httpd`` package. The ``mod_wsgi-httpd``
+package is a pip installable package which will build the Apache httpd
+server and install it into the Python installation. If the
+``mod_wsgi-httpd`` package is installed before installing this package,
+then the Apache httpd server installation installed by ``mod_wsgi-httpd``
+will be used instead of any system installed version of the Apache httpd
+server when running ``mod_wsgi-express``. This allows you to workaround
+any inability to upgrade the main Apache installation, or install its 'dev'
+package if missing, or install it outright if not present.
diff --git a/setup.py b/setup.py
index 6d86ba2..b6b71db 100644
--- a/setup.py
+++ b/setup.py
@@ -19,49 +19,48 @@ from distutils.core import Extension
from distutils.sysconfig import get_config_var as get_python_config
from distutils.sysconfig import get_python_lib
-# Before anything else, this setup.py uses various tricks to install
-# precompiled Apache binaries for the Heroku and OpenShift environments.
-# Once they are installed, then the installation of the mod_wsgi package
-# itself will be triggered, ensuring that it can be built against the
-# precompiled Apache binaries which were installed.
+# Before anything else, this setup.py uses some tricks to potentially
+# install Apache. This can be from a local tarball, or from precompiled
+# Apache binaries for Heroku and OpenShift environments downloaded from
+# Amazon S3. Once they are installed, then the installation of the
+# mod_wsgi package itself will be triggered, ensuring that it can be
+# built against the precompiled Apache binaries which were installed.
#
-# We therefore first need to work out whether we are actually running on
-# either Heroku of OpenShift. If we are, then we identify the set of
-# precompiled binaries we are to use and copy it into the Python
-# installation.
+# First work out whether we are actually running on either Heroku or
+# OpenShift. If we are, then we identify the set of precompiled binaries
+# we are to use and copy it into the Python installation.
PREFIX = 'https://s3.amazonaws.com'
BUCKET = os.environ.get('MOD_WSGI_REMOTE_S3_BUCKET_NAME', 'modwsgi.org')
REMOTE_TARBALL_NAME = os.environ.get('MOD_WSGI_REMOTE_PACKAGES_NAME')
+LOCAL_TARBALL_FILE = os.environ.get('MOD_WSGI_LOCAL_PACKAGES_FILE')
TGZ_OPENSHIFT='mod_wsgi-packages-openshift-centos6-apache-2.4.10-1.tar.gz'
TGZ_HEROKU='mod_wsgi-packages-heroku-cedar14-apache-2.4.10-1.tar.gz'
-if not REMOTE_TARBALL_NAME:
+if not REMOTE_TARBALL_NAME and not LOCAL_TARBALL_FILE:
if os.environ.get('OPENSHIFT_HOMEDIR'):
REMOTE_TARBALL_NAME = TGZ_OPENSHIFT
elif os.path.isdir('/app/.heroku'):
REMOTE_TARBALL_NAME = TGZ_HEROKU
-LOCAL_TARBALL_FILE = os.environ.get('MOD_WSGI_LOCAL_PACKAGES_FILE')
-
REMOTE_TARBALL_URL = None
if LOCAL_TARBALL_FILE is None and REMOTE_TARBALL_NAME:
REMOTE_TARBALL_URL = '%s/%s/%s' % (PREFIX, BUCKET, REMOTE_TARBALL_NAME)
-WITH_PACKAGES = False
+WITH_TARBALL_PACKAGE = False
if REMOTE_TARBALL_URL or LOCAL_TARBALL_FILE:
- WITH_PACKAGES = True
+ WITH_TARBALL_PACKAGE = True
# If we are doing an install, download the tarball and unpack it into
# the 'packages' subdirectory. We will then add everything in that
# directory as package data so that it will be installed into the Python
# installation.
-if WITH_PACKAGES:
+if WITH_TARBALL_PACKAGE:
if REMOTE_TARBALL_URL:
if not os.path.isfile(REMOTE_TARBALL_NAME):
print('Downloading', REMOTE_TARBALL_URL)
@@ -69,11 +68,12 @@ if WITH_PACKAGES:
os.rename(REMOTE_TARBALL_NAME+'.download', REMOTE_TARBALL_NAME)
LOCAL_TARBALL_FILE = REMOTE_TARBALL_NAME
- shutil.rmtree('src/packages', ignore_errors=True)
+ if LOCAL_TARBALL_FILE:
+ shutil.rmtree('src/packages', ignore_errors=True)
- tar = tarfile.open(LOCAL_TARBALL_FILE)
- tar.extractall('src/packages')
- tar.close()
+ tar = tarfile.open(LOCAL_TARBALL_FILE)
+ tar.extractall('src/packages')
+ tar.close()
open('src/packages/__init__.py', 'a').close()
@@ -119,19 +119,27 @@ def find_program(names, default=None, paths=[]):
APXS = os.environ.get('APXS')
+WITH_HTTPD_PACKAGE = False
+
+if APXS is None:
+ APXS = find_program(['mod_wsgi-apxs'])
+ if APXS is not None:
+ WITH_HTTPD_PACKAGE = True
+
if APXS is None:
- APXS = find_program(['apxs2', 'apxs'], 'apxs', ['/usr/sbin', os.getcwd()])
+ APXS = find_program(['mod_wsgi-apxs', 'apxs2', 'apxs'],
+ 'apxs', ['/usr/sbin', os.getcwd()])
elif not os.path.isabs(APXS):
APXS = find_program([APXS], APXS, ['/usr/sbin', os.getcwd()])
-if not WITH_PACKAGES:
+if not WITH_TARBALL_PACKAGE:
if not os.path.isabs(APXS) or not os.access(APXS, os.X_OK):
raise RuntimeError('The %r command appears not to be installed or '
'is not executable. Please check the list of prerequisites '
'in the documentation for this package and install any '
'missing Apache httpd server packages.' % APXS)
-if WITH_PACKAGES:
+if WITH_TARBALL_PACKAGE:
SCRIPT_DIR = os.path.join(os.path.dirname(__file__), 'src', 'packages')
CONFIG_FILE = os.path.join(SCRIPT_DIR, 'apache/build/config_vars.mk')
@@ -189,29 +197,6 @@ if WITH_PACKAGES:
CONFIG['SBINDIR'] = get_apxs_config('sbindir')
CONFIG['PROGNAME'] = get_apxs_config('progname')
- _CFLAGS_NAMES = ['SHLTCFLAGS', 'CFLAGS', 'NOTEST_CPPFLAGS',
- 'EXTRA_CPPFLAGS', 'EXTRA_CFLAGS']
-
- _CFLAGS_VALUES = []
-
- for name in _CFLAGS_NAMES:
- value = get_apxs_config(name)
-
- # Heroku doesn't appear to run the same version of gcc
- # that a standard Ubuntu installation does and which was
- # used to originally build the Apache binaries. We need
- # therefore to strip out flags that the Heroku gcc may
- # not understand.
-
- if value:
- if os.path.isdir('/app/.heroku'):
- value = value.replace('-prefer-pic', '')
-
- if value:
- _CFLAGS_VALUES.append(value)
-
- CONFIG['CFLAGS'] = ' '.join(_CFLAGS_VALUES)
-
else:
def get_apxs_config(query):
p = subprocess.Popen([APXS, '-q', query],
@@ -248,18 +233,31 @@ SHLIBPATH_VAR = get_apxs_config('SHLIBPATH_VAR')
APXS_CONFIG_TEMPLATE = """
import os
-WITH_PACKAGES = %(WITH_PACKAGES)r
+WITH_TARBALL_PACKAGE = %(WITH_TARBALL_PACKAGE)r
+WITH_HTTPD_PACKAGE = %(WITH_HTTPD_PACKAGE)r
-if WITH_PACKAGES:
+if WITH_HTTPD_PACKAGE:
+ import mod_wsgi.httpd
+ PACKAGES = os.path.join(os.path.dirname(mod_wsgi.httpd.__file__))
+ BINDIR = os.path.join(PACKAGES, 'bin')
+ SBINDIR = BINDIR
+ LIBEXECDIR = os.path.join(PACKAGES, 'modules')
+ SHLIBPATH = os.path.join(PACKAGES, 'lib')
+elif WITH_TARBALL_PACKAGE:
import mod_wsgi.packages
PACKAGES = os.path.join(os.path.dirname(mod_wsgi.packages.__file__))
BINDIR = os.path.join(PACKAGES, 'apache', 'bin')
SBINDIR = BINDIR
LIBEXECDIR = os.path.join(PACKAGES, 'apache', 'modules')
+ SHLIBPATH = []
+ SHLIBPATH.append(os.path.join(PACKAGES, 'apr-util', 'lib'))
+ SHLIBPATH.append(os.path.join(PACKAGES, 'apr', 'lib'))
+ SHLIBPATH = ':'.join(SHLIBPATH)
else:
BINDIR = '%(BINDIR)s'
SBINDIR = '%(SBINDIR)s'
LIBEXECDIR = '%(LIBEXECDIR)s'
+ SHLIBPATH = ''
MPM_NAME = '%(MPM_NAME)s'
PROGNAME = '%(PROGNAME)s'
@@ -282,7 +280,9 @@ else:
with open(os.path.join(os.path.dirname(__file__),
'src/server/apxs_config.py'), 'w') as fp:
- print(APXS_CONFIG_TEMPLATE % dict(WITH_PACKAGES=WITH_PACKAGES,
+ print(APXS_CONFIG_TEMPLATE % dict(
+ WITH_TARBALL_PACKAGE=WITH_TARBALL_PACKAGE,
+ WITH_HTTPD_PACKAGE=WITH_HTTPD_PACKAGE,
BINDIR=BINDIR, SBINDIR=SBINDIR, LIBEXECDIR=LIBEXECDIR,
MPM_NAME=MPM_NAME, PROGNAME=PROGNAME,
SHLIBPATH_VAR=SHLIBPATH_VAR), file=fp)
@@ -429,5 +429,4 @@ setup(name = 'mod_wsgi',
ext_modules = [extension],
entry_points = { 'console_scripts':
['mod_wsgi-express = mod_wsgi.server:main'],},
- install_requires = ['mod_wsgi-metrics >= 1.0.0'],
)
diff --git a/src/server/__init__.py b/src/server/__init__.py
index 0209a97..6333024 100644
--- a/src/server/__init__.py
+++ b/src/server/__init__.py
@@ -77,7 +77,7 @@ def find_mimetypes():
APACHE_GENERAL_CONFIG = """
<IfModule !version_module>
-LoadModule version_module '%(modules_directory)s/mod_version.so'
+LoadModule version_module '${HTTPD_MODULES_DIRECTORY}/mod_version.so'
</IfModule>
ServerName %(host)s
@@ -110,7 +110,7 @@ LockFile '%(server_root)s/accept.lock'
<IfModule !mpm_worker_module>
<IfModule !mpm_prefork_module>
<IfDefine WSGI_MPM_EXISTS_PREFORK_MODULE>
-LoadModule mpm_prefork_module '%(modules_directory)s/mod_mpm_prefork.so'
+LoadModule mpm_prefork_module '${HTTPD_MODULES_DIRECTORY}/mod_mpm_prefork.so'
</IfDefine>
</IfModule>
</IfModule>
@@ -123,13 +123,13 @@ LoadModule mpm_prefork_module '%(modules_directory)s/mod_mpm_prefork.so'
<IfModule !mpm_worker_module>
<IfModule !mpm_prefork_module>
<IfDefine WSGI_MPM_ENABLE_EVENT_MODULE>
-LoadModule mpm_event_module '%(modules_directory)s/mod_mpm_event.so'
+LoadModule mpm_event_module '${HTTPD_MODULES_DIRECTORY}/mod_mpm_event.so'
</IfDefine>
<IfDefine WSGI_MPM_ENABLE_WORKER_MODULE>
-LoadModule mpm_worker_module '%(modules_directory)s/mod_mpm_worker.so'
+LoadModule mpm_worker_module '${HTTPD_MODULES_DIRECTORY}/mod_mpm_worker.so'
</IfDefine>
<IfDefine WSGI_MPM_ENABLE_PREFORK_MODULE>
-LoadModule mpm_prefork_module '%(modules_directory)s/mod_mpm_prefork.so'
+LoadModule mpm_prefork_module '${HTTPD_MODULES_DIRECTORY}/mod_mpm_prefork.so'
</IfDefine>
</IfModule>
</IfModule>
@@ -138,66 +138,75 @@ LoadModule mpm_prefork_module '%(modules_directory)s/mod_mpm_prefork.so'
<IfVersion >= 2.4>
<IfModule !access_compat_module>
-LoadModule access_compat_module '%(modules_directory)s/mod_access_compat.so'
+LoadModule access_compat_module '${HTTPD_MODULES_DIRECTORY}/mod_access_compat.so'
</IfModule>
<IfModule !unixd_module>
-LoadModule unixd_module '%(modules_directory)s/mod_unixd.so'
+LoadModule unixd_module '${HTTPD_MODULES_DIRECTORY}/mod_unixd.so'
</IfModule>
<IfModule !authn_core_module>
-LoadModule authn_core_module '%(modules_directory)s/mod_authn_core.so'
+LoadModule authn_core_module '${HTTPD_MODULES_DIRECTORY}/mod_authn_core.so'
</IfModule>
<IfModule !authz_core_module>
-LoadModule authz_core_module '%(modules_directory)s/mod_authz_core.so'
+LoadModule authz_core_module '${HTTPD_MODULES_DIRECTORY}/mod_authz_core.so'
</IfModule>
</IfVersion>
<IfModule !authz_host_module>
-LoadModule authz_host_module '%(modules_directory)s/mod_authz_host.so'
+LoadModule authz_host_module '${HTTPD_MODULES_DIRECTORY}/mod_authz_host.so'
</IfModule>
<IfModule !mime_module>
-LoadModule mime_module '%(modules_directory)s/mod_mime.so'
+LoadModule mime_module '${HTTPD_MODULES_DIRECTORY}/mod_mime.so'
</IfModule>
<IfModule !rewrite_module>
-LoadModule rewrite_module '%(modules_directory)s/mod_rewrite.so'
+LoadModule rewrite_module '${HTTPD_MODULES_DIRECTORY}/mod_rewrite.so'
</IfModule>
<IfModule !alias_module>
-LoadModule alias_module '%(modules_directory)s/mod_alias.so'
+LoadModule alias_module '${HTTPD_MODULES_DIRECTORY}/mod_alias.so'
</IfModule>
<IfModule !dir_module>
-LoadModule dir_module '%(modules_directory)s/mod_dir.so'
+LoadModule dir_module '${HTTPD_MODULES_DIRECTORY}/mod_dir.so'
</IfModule>
<IfModule !env_module>
-LoadModule env_module '%(modules_directory)s/mod_env.so'
+LoadModule env_module '${HTTPD_MODULES_DIRECTORY}/mod_env.so'
</IfModule>
<IfVersion >= 2.2.15>
<IfModule !reqtimeout_module>
-LoadModule reqtimeout_module '%(modules_directory)s/mod_reqtimeout.so'
+LoadModule reqtimeout_module '${HTTPD_MODULES_DIRECTORY}/mod_reqtimeout.so'
</IfModule>
</IfVersion>
<IfDefine WSGI_COMPRESS_RESPONSES>
<IfModule !deflate_module>
-LoadModule deflate_module '%(modules_directory)s/mod_deflate.so'
+LoadModule deflate_module '${HTTPD_MODULES_DIRECTORY}/mod_deflate.so'
</IfModule>
</IfDefine>
<IfDefine WSGI_AUTH_USER>
<IfModule !auth_basic_module>
-LoadModule auth_basic_module '%(modules_directory)s/mod_auth_basic.so'
+LoadModule auth_basic_module '${HTTPD_MODULES_DIRECTORY}/mod_auth_basic.so'
</IfModule>
<IfModule !auth_digest_module>
-LoadModule auth_digest_module '%(modules_directory)s/mod_auth_digest.so'
+LoadModule auth_digest_module '${HTTPD_MODULES_DIRECTORY}/mod_auth_digest.so'
</IfModule>
<IfModule !authz_user_module>
-LoadModule authz_user_module '%(modules_directory)s/mod_authz_user.so'
+LoadModule authz_user_module '${HTTPD_MODULES_DIRECTORY}/mod_authz_user.so'
+</IfModule>
+</IfDefine>
+
+<IfDefine WSGI_WITH_PROXY>
+<IfModule !proxy_module>
+LoadModule proxy_module ${HTTPD_MODULES_DIRECTORY}/mod_proxy.so
+</IfModule>
+<IfModule !proxy_http_module>
+LoadModule proxy_http_module ${HTTPD_MODULES_DIRECTORY}/mod_proxy_http.so
</IfModule>
</IfDefine>
<IfModule mpm_prefork_module>
<IfDefine WSGI_WITH_PHP5>
<IfModule !php5_module>
-Loadmodule php5_module '%(modules_directory)s/libphp5.so'
+Loadmodule php5_module '${HTTPD_MODULES_DIRECTORY}/libphp5.so'
</IfModule>
AddHandler application/x-httpd-php .php
</IfDefine>
@@ -207,7 +216,7 @@ LoadModule wsgi_module '%(mod_wsgi_so)s'
<IfDefine WSGI_SERVER_METRICS>
<IfModule !status_module>
-LoadModule status_module '%(modules_directory)s/mod_status.so'
+LoadModule status_module '${HTTPD_MODULES_DIRECTORY}/mod_status.so'
</IfModule>
</IfDefine>
@@ -258,11 +267,12 @@ WSGIDaemonProcess %(host)s:%(port)s \\
inactivity-timeout=%(inactivity_timeout)s \\
deadlock-timeout=%(deadlock_timeout)s \\
graceful-timeout=%(graceful_timeout)s \\
+ eviction-timeout=%(eviction_timeout)s \\
shutdown-timeout=%(shutdown_timeout)s \\
send-buffer-size=%(send_buffer_size)s \\
receive-buffer-size=%(receive_buffer_size)s \\
header-buffer-size=%(header_buffer_size)s \\
- proxy-buffer-size=%(proxy_buffer_size)s \\
+ response-buffer-size=%(response_buffer_size)s \\
server-metrics=%(daemon_server_metrics_flag)s
</IfDefine>
<IfDefine !WSGI_MULTIPROCESS>
@@ -282,10 +292,11 @@ WSGIDaemonProcess %(host)s:%(port)s \\
inactivity-timeout=%(inactivity_timeout)s \\
deadlock-timeout=%(deadlock_timeout)s \\
graceful-timeout=%(graceful_timeout)s \\
+ eviction-timeout=%(eviction_timeout)s \\
shutdown-timeout=%(shutdown_timeout)s \\
send-buffer-size=%(send_buffer_size)s \\
receive-buffer-size=%(receive_buffer_size)s \\
- proxy-buffer-size=%(proxy_buffer_size)s \\
+ response-buffer-size=%(response_buffer_size)s \\
server-metrics=%(daemon_server_metrics_flag)s
</IfDefine>
</IfDefine>
@@ -340,7 +351,7 @@ LogLevel %(log_level)s
<IfDefine WSGI_ACCESS_LOG>
<IfModule !log_config_module>
-LoadModule log_config_module %(modules_directory)s/mod_log_config.so
+LoadModule log_config_module ${HTTPD_MODULES_DIRECTORY}/mod_log_config.so
</IfModule>
LogFormat "%%h %%l %%u %%t \\"%%r\\" %%>s %%b" common
LogFormat "%%h %%l %%u %%t \\"%%r\\" %%>s %%b \\"%%{Referer}i\\" \\"%%{User-agent}i\\"" combined
@@ -360,7 +371,7 @@ WSGIChunkedRequest On
<IfDefine WSGI_WITH_HTTPS>
<IfModule !ssl_module>
-LoadModule ssl_module %(modules_directory)s/mod_ssl.so
+LoadModule ssl_module ${HTTPD_MODULES_DIRECTORY}/mod_ssl.so
</IfModule>
</IfDefine>
@@ -428,6 +439,14 @@ MaxRequestsPerChild 0
ThreadStackSize 262144
</IfModule>
+<IfDefine !WSGI_VIRTUAL_HOST>
+<IfVersion < 2.4>
+NameVirtualHost *:%(port)s
+</IfVersion>
+<VirtualHost _default_:%(port)s>
+</VirtualHost>
+</IfDefine>
+
<IfDefine WSGI_VIRTUAL_HOST>
<IfVersion < 2.4>
@@ -604,6 +623,17 @@ WSGIImportScript '%(server_root)s/handler.wsgi' \\
</IfDefine>
"""
+APACHE_PROXY_PASS_CONFIG = """
+ProxyPass '%(mount_point)s' '%(url)s'
+"""
+
+APACHE_PROXY_PASS_HOST_CONFIG = """
+<VirtualHost *:%(port)s>
+ServerName %(host)s
+ProxyPass / '%(url)s'
+</VirtualHost>
+"""
+
APACHE_ALIAS_DIRECTORY_CONFIG = """
Alias '%(mount_point)s' '%(directory)s'
@@ -674,10 +704,28 @@ WSGIImportScript '%(server_root)s/server-metrics.py' \\
process-group=express application-group=server-metrics
"""
+APACHE_SERVICE_CONFIG = """
+WSGIDaemonProcess 'service:%(name)s' display-name=%%{GROUP} threads=1 \\
+ user='%(user)s' group='%(group)s'
+WSGIImportScript '%(script)s' process-group='service:%(name)s' \\
+ application-group=%%{GLOBAL}
+"""
+
def generate_apache_config(options):
with open(options['httpd_conf'], 'w') as fp:
print(APACHE_GENERAL_CONFIG % options, file=fp)
+ if options['proxy_url_aliases']:
+ for mount_point, url in options['proxy_url_aliases']:
+ print(APACHE_PROXY_PASS_CONFIG % dict(
+ mount_point=mount_point, url=url), file=fp)
+
+ if options['proxy_virtual_hosts']:
+ for host, url in options['proxy_virtual_hosts']:
+ print(APACHE_PROXY_PASS_HOST_CONFIG % dict(
+ host=host, port=options['port'], url=url),
+ file=fp)
+
if options['url_aliases']:
for mount_point, target in sorted(options['url_aliases'],
reverse=True):
@@ -722,6 +770,15 @@ def generate_apache_config(options):
print(APACHE_EXTENSION_CONFIG % dict(extension=extension),
file=fp)
+ if options['service_scripts']:
+ users = dict(options['service_users'] or [])
+ groups = dict(options['service_groups'] or [])
+ for name, script in options['service_scripts']:
+ user = users.get(name, '${WSGI_RUN_USER}')
+ group = groups.get(name, '${WSGI_RUN_GROUP}')
+ print(APACHE_SERVICE_CONFIG % dict(name=name, user=user,
+ group=group, script=script), file=fp)
+
if options['include_files']:
for filename in options['include_files']:
filename = os.path.abspath(filename)
@@ -1338,6 +1395,9 @@ HTTPD_ARGS="%(httpd_arguments)s"
HTTPD_COMMAND="$HTTPD $HTTPD_ARGS"
+HTTPD_MODULES_DIRECTORY="%(modules_directory)s"
+export HTTPD_MODULES_DIRECTORY
+
SHLIBPATH="%(shlibpath)s"
if [ "x$SHLIBPATH" != "x" ]; then
@@ -1548,10 +1608,16 @@ option_list = (
optparse.make_option('--graceful-timeout', type='int', default=15,
metavar='SECONDS', help='Grace period for requests to complete '
- 'normally, without accepting new requests, when worker processes '
- 'are being shutdown and restarted due to maximum requests being '
- 'reached or due to graceful restart signal. Defaults to 15 '
- 'seconds.'),
+ 'normally, while still accepting new requests, when worker '
+ 'processes are being shutdown and restarted due to maximum '
+ 'requests being reached. Defaults to 15 seconds.'),
+ optparse.make_option('--eviction-timeout', type='int', default=0,
+ metavar='SECONDS', help='Grace period for requests to complete '
+ 'normally, while still accepting new requests, when the WSGI '
+ 'application is being evicted from the worker processes, and '
+ 'the process restarted, due to forced graceful restart signal. '
+ 'Defaults to timeout specified by \'--graceful-timeout\' '
+ 'option.'),
optparse.make_option('--deadlock-timeout', type='int', default=60,
metavar='SECONDS', help='Maximum number of seconds allowed '
@@ -1655,7 +1721,7 @@ option_list = (
metavar='NUMBER', help='Size of buffer used for reading '
'response headers from daemon processes. Defaults to 0, '
'indicating internal default of 32768 bytes is used.'),
- optparse.make_option('--proxy-buffer-size', type='int', default=0,
+ optparse.make_option('--response-buffer-size', type='int', default=0,
metavar='NUMBER', help='Maximum amount of response content '
'that will be allowed to be buffered in the Apache child '
'worker process when proxying the response from a daemon '
@@ -1670,12 +1736,12 @@ option_list = (
'the worker processes will still though be reloaded if the '
'WSGI script file itself is modified.'),
- optparse.make_option('--user', default=default_run_user(), metavar='NAME',
- help='When being run by the root user, the user that the WSGI '
- 'application should be run as.'),
+ optparse.make_option('--user', default=default_run_user(),
+ metavar='USERNAME', help='When being run by the root user, '
+ 'the user that the WSGI application should be run as.'),
optparse.make_option('--group', default=default_run_group(),
- metavar='NAME', help='When being run by the root user, the group '
- 'that the WSGI application should be run as.'),
+ metavar='GROUP', help='When being run by the root user, the '
+ 'group that the WSGI application should be run as.'),
optparse.make_option('--callable-object', default='application',
metavar='NAME', help='The name of the entry point for the WSGI '
@@ -1716,6 +1782,15 @@ option_list = (
default=False, help='Flag indicating whether Apache error '
'documents will override application error responses.'),
+ optparse.make_option('--proxy-url-alias', action='append', nargs=2,
+ dest='proxy_url_aliases', metavar='URL-PATH URL',
+ help='Map a sub URL such that any requests against it will be '
+ 'proxied to the specified URL.'),
+ optparse.make_option('--proxy-virtual-host', action='append', nargs=2,
+ dest='proxy_virtual_hosts', metavar='HOSTNAME URL',
+ help='Proxy any requests for the specified host name to the '
+ 'remote URL.'),
+
optparse.make_option('--keep-alive-timeout', type='int', default=0,
metavar='SECONDS', help='The number of seconds which a client '
'connection will be kept alive to allow subsequent requests '
@@ -1909,6 +1984,22 @@ option_list = (
optparse.make_option('--with-php5', action='store_true', default=False,
help='Flag indicating whether PHP 5 support should be enabled.'),
+ optparse.make_option('--service-script', action='append', nargs=2,
+ dest='service_scripts', metavar='SERVICE SCRIPT-PATH',
+ help='Specify the name of a Python script to be loaded and '
+ 'executed in the context of a distinct daemon process. Used '
+ 'for running a managed service.'),
+ optparse.make_option('--service-user', action='append', nargs=2,
+ dest='service_users', metavar='SERVICE USERNAME',
+ help='When being run by the root user, the user that the '
+ 'distinct daemon process started to run the managed service '
+ 'should be run as.'),
+ optparse.make_option('--service-group', action='append', nargs=2,
+ dest='service_groups', metavar='SERVICE GROUP',
+ help='When being run by the root user, the group that the '
+ 'distinct daemon process started to run the managed service '
+ 'should be run as.'),
+
optparse.make_option('--enable-docs', action='store_true', default=False,
help='Flag indicating whether the mod_wsgi documentation should '
'be made available at the /__wsgi__/docs sub URL.'),
@@ -2226,6 +2317,14 @@ def _cmd_setup_server(command, args, options):
if options['with_newrelic_platform']:
options['server_metrics'] = True
+ if options['service_scripts']:
+ service_scripts = []
+ for name, script in options['service_scripts']:
+ if not os.path.isabs(script):
+ script = os.path.abspath(script)
+ service_scripts.append((name, script))
+ options['service_scripts'] = service_scripts
+
max_clients = options['processes'] * options['threads']
if options['max_clients'] is not None:
@@ -2491,6 +2590,8 @@ def _cmd_setup_server(command, args, options):
options['httpd_arguments_list'].append('-DWSGI_CHUNKED_REQUEST')
if options['with_php5']:
options['httpd_arguments_list'].append('-DWSGI_WITH_PHP5')
+ if options['proxy_url_aliases'] or options['proxy_virtual_hosts']:
+ options['httpd_arguments_list'].append('-DWSGI_WITH_PROXY')
options['httpd_arguments_list'].extend(
_mpm_module_defines(options['modules_directory'],
@@ -2502,14 +2603,7 @@ def _cmd_setup_server(command, args, options):
options['python_executable'] = sys.executable
options['shlibpath_var'] = apxs_config.SHLIBPATH_VAR
-
- if apxs_config.WITH_PACKAGES:
- shlibpath = []
- shlibpath.append(os.path.join(apxs_config.PACKAGES, 'apr-util', 'lib'))
- shlibpath.append(os.path.join(apxs_config.PACKAGES, 'apr', 'lib'))
- options['shlibpath'] = ':'.join(shlibpath)
- else:
- options['shlibpath'] = ''
+ options['shlibpath'] = apxs_config.SHLIBPATH
generate_wsgi_handler_script(options)
diff --git a/src/server/mod_wsgi.c b/src/server/mod_wsgi.c
index 9624277..30ddbe5 100644
--- a/src/server/mod_wsgi.c
+++ b/src/server/mod_wsgi.c
@@ -85,6 +85,7 @@ static apr_interval_time_t wsgi_deadlock_timeout = 0;
static apr_interval_time_t wsgi_idle_timeout = 0;
static apr_interval_time_t wsgi_request_timeout = 0;
static apr_interval_time_t wsgi_graceful_timeout = 0;
+static apr_interval_time_t wsgi_eviction_timeout = 0;
static apr_time_t volatile wsgi_deadlock_shutdown_time = 0;
static apr_time_t volatile wsgi_idle_shutdown_time = 0;
static apr_time_t volatile wsgi_graceful_shutdown_time = 0;
@@ -6596,6 +6597,7 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
int inactivity_timeout = 0;
int request_timeout = 0;
int graceful_timeout = 0;
+ int eviction_timeout = 0;
int connect_timeout = 15;
int socket_timeout = 0;
int queue_timeout = 0;
@@ -6609,7 +6611,7 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
int send_buffer_size = 0;
int recv_buffer_size = 0;
int header_buffer_size = 0;
- int proxy_buffer_size = 0;
+ int response_buffer_size = 0;
const char *script_user = NULL;
const char *script_group = NULL;
@@ -6808,6 +6810,14 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
if (graceful_timeout < 0)
return "Invalid graceful timeout for WSGI daemon process.";
}
+ else if (!strcmp(option, "eviction-timeout")) {
+ if (!*value)
+ return "Invalid eviction timeout for WSGI daemon process.";
+
+ eviction_timeout = atoi(value);
+ if (eviction_timeout < 0)
+ return "Invalid eviction timeout for WSGI daemon process.";
+ }
else if (!strcmp(option, "connect-timeout")) {
if (!*value)
return "Invalid connect timeout for WSGI daemon process.";
@@ -6873,13 +6883,13 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
"or 0 for default.";
}
}
- else if (!strcmp(option, "proxy-buffer-size")) {
+ else if (!strcmp(option, "response-buffer-size")) {
if (!*value)
- return "Invalid proxy buffer size for WSGI daemon process.";
+ return "Invalid response buffer size for WSGI daemon process.";
- proxy_buffer_size = atoi(value);
- if (proxy_buffer_size < 65536 && proxy_buffer_size != 0) {
- return "Proxy buffer size must be >= 65536 bytes, "
+ response_buffer_size = atoi(value);
+ if (response_buffer_size < 65536 && response_buffer_size != 0) {
+ return "Response buffer size must be >= 65536 bytes, "
"or 0 for default.";
}
}
@@ -7077,6 +7087,7 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
entry->inactivity_timeout = apr_time_from_sec(inactivity_timeout);
entry->request_timeout = apr_time_from_sec(request_timeout);
entry->graceful_timeout = apr_time_from_sec(graceful_timeout);
+ entry->eviction_timeout = apr_time_from_sec(eviction_timeout);
entry->connect_timeout = apr_time_from_sec(connect_timeout);
entry->socket_timeout = apr_time_from_sec(socket_timeout);
entry->queue_timeout = apr_time_from_sec(queue_timeout);
@@ -7090,7 +7101,7 @@ static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
entry->send_buffer_size = send_buffer_size;
entry->recv_buffer_size = recv_buffer_size;
entry->header_buffer_size = header_buffer_size;
- entry->proxy_buffer_size = proxy_buffer_size;
+ entry->response_buffer_size = response_buffer_size;
entry->script_user = script_user;
entry->script_group = script_group;
@@ -8288,6 +8299,9 @@ static void *wsgi_monitor_thread(apr_thread_t *thd, void *data)
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
"mod_wsgi (pid=%d): Graceful timeout is %d.",
getpid(), (int)(apr_time_sec(wsgi_graceful_timeout)));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
+ "mod_wsgi (pid=%d): Eviction timeout is %d.",
+ getpid(), (int)(apr_time_sec(wsgi_eviction_timeout)));
}
while (1) {
@@ -8579,6 +8593,7 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
wsgi_idle_timeout = daemon->group->inactivity_timeout;
wsgi_request_timeout = daemon->group->request_timeout;
wsgi_graceful_timeout = daemon->group->graceful_timeout;
+ wsgi_eviction_timeout = daemon->group->eviction_timeout;
if (wsgi_deadlock_timeout || wsgi_idle_timeout) {
rv = apr_thread_create(&reaper, thread_attr, wsgi_monitor_thread,
@@ -8751,17 +8766,20 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
apr_thread_mutex_lock(wsgi_monitor_lock);
wsgi_graceful_shutdown_time = apr_time_now();
- wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
+ if (wsgi_eviction_timeout)
+ wsgi_graceful_shutdown_time += wsgi_eviction_timeout;
+ else
+ wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
apr_thread_mutex_unlock(wsgi_monitor_lock);
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
- "mod_wsgi (pid=%d): Graceful shutdown "
+ "mod_wsgi (pid=%d): Process eviction "
"requested, waiting for requests to complete "
"'%s'.", getpid(), daemon->group->name);
}
else {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
- "mod_wsgi (pid=%d): Graceful shutdown "
+ "mod_wsgi (pid=%d): Process eviction "
"requested, triggering immediate shutdown "
"'%s'.", getpid(), daemon->group->name);
@@ -11202,7 +11220,7 @@ static int wsgi_execute_remote(request_rec *r)
/* Transfer any response content. */
- return wsgi_transfer_response(r, bbin, group->proxy_buffer_size);
+ return wsgi_transfer_response(r, bbin, group->response_buffer_size);
}
static apr_status_t wsgi_socket_read(apr_socket_t *sock, void *vbuf,
diff --git a/src/server/wsgi_daemon.h b/src/server/wsgi_daemon.h
index ff625e4..999fca2 100644
--- a/src/server/wsgi_daemon.h
+++ b/src/server/wsgi_daemon.h
@@ -113,6 +113,7 @@ typedef struct {
apr_time_t inactivity_timeout;
apr_time_t request_timeout;
apr_time_t graceful_timeout;
+ apr_time_t eviction_timeout;
apr_time_t connect_timeout;
apr_time_t socket_timeout;
apr_time_t queue_timeout;
@@ -122,7 +123,7 @@ typedef struct {
int send_buffer_size;
int recv_buffer_size;
int header_buffer_size;
- int proxy_buffer_size;
+ int response_buffer_size;
const char *script_user;
const char *script_group;
int cpu_time_limit;
diff --git a/src/server/wsgi_version.h b/src/server/wsgi_version.h
index 9539c03..4fa83c4 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 6
-#define MOD_WSGI_VERSION_STRING "4.4.6"
+#define MOD_WSGI_MICROVERSION_NUMBER 7
+#define MOD_WSGI_VERSION_STRING "4.4.7"
/* ------------------------------------------------------------------------- */