diff options
author | Graham Dumpleton <Graham.Dumpleton@gmail.com> | 2015-02-02 14:26:29 +1100 |
---|---|---|
committer | Graham Dumpleton <Graham.Dumpleton@gmail.com> | 2015-02-02 14:26:29 +1100 |
commit | 8f3e537ad62873f72e211808fef6ef7c178ef8da (patch) | |
tree | f45a5aedc1592136ac7472f5af716074561ed819 | |
parent | 86b5d2da6816b4239570ea6efd5af2fc35c0bb82 (diff) | |
parent | b6f1291009109d9bb66330d16179b05680908469 (diff) | |
download | mod_wsgi-4.4.7.tar.gz |
Merge branch 'release/4.4.7'4.4.7
-rw-r--r-- | docs/release-notes/index.rst | 1 | ||||
-rw-r--r-- | docs/release-notes/version-4.4.7.rst | 69 | ||||
-rw-r--r-- | setup.py | 97 | ||||
-rw-r--r-- | src/server/__init__.py | 182 | ||||
-rw-r--r-- | src/server/mod_wsgi.c | 40 | ||||
-rw-r--r-- | src/server/wsgi_daemon.h | 3 | ||||
-rw-r--r-- | src/server/wsgi_version.h | 4 |
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. @@ -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" /* ------------------------------------------------------------------------- */ |