summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Dumpleton <Graham.Dumpleton@gmail.com>2016-11-29 16:46:19 +1100
committerGraham Dumpleton <Graham.Dumpleton@gmail.com>2016-11-29 16:46:19 +1100
commit9cf9bade5c9a4ef642411bdc376ebfb9fc9e60a8 (patch)
tree5532b2d5d330c8c5b807fdc87801d2959a6be56a
parenteea49007c5358dc566878c7ac58d80f2860c6806 (diff)
parent6de8ac63719a5e8437399f36e020e48ff5ebfc0c (diff)
downloadmod_wsgi-4.5.8.tar.gz
Merge branch 'release/4.5.8'4.5.8
-rw-r--r--README.rst248
-rw-r--r--docs/configuration-directives/WSGIDaemonProcess.rst528
-rw-r--r--docs/release-notes.rst1
-rw-r--r--docs/release-notes/version-4.5.8.rst69
-rw-r--r--docs/user-guides/assorted-tips-and-tricks.rst4
-rw-r--r--docs/user-guides/configuration-guidelines.rst2
-rw-r--r--docs/user-guides/debugging-techniques.rst10
-rw-r--r--docs/user-guides/installation-issues.rst2
-rw-r--r--docs/user-guides/quick-configuration-guide.rst2
-rw-r--r--docs/user-guides/registering-cleanup-code.rst2
-rw-r--r--docs/user-guides/virtual-environments.rst596
-rw-r--r--setup.py126
-rw-r--r--src/server/__init__.py126
-rw-r--r--src/server/mod_wsgi.c106
-rw-r--r--src/server/wsgi_apache.h4
-rw-r--r--src/server/wsgi_memory.c13
-rw-r--r--src/server/wsgi_server.c1
-rw-r--r--src/server/wsgi_server.h1
-rw-r--r--src/server/wsgi_version.h4
-rw-r--r--tests/environ.wsgi11
20 files changed, 1422 insertions, 434 deletions
diff --git a/README.rst b/README.rst
index 2027b4f..3b7ae11 100644
--- a/README.rst
+++ b/README.rst
@@ -9,40 +9,60 @@ The mod_wsgi package provides an Apache module that implements a WSGI
compliant interface for hosting Python based web applications on top of the
Apache web server.
-Installation of mod_wsgi can now be performed in one of two ways.
+Installation of mod_wsgi from source code can be performed in one of two
+ways.
-The first way of installing mod_wsgi is the traditional way that has
-been used in the past, where it is installed as a module directly into your
-Apache installation.
+The first way of installing mod_wsgi is the traditional way that has been
+used by many software packages. This is where it is installed as a module
+directly into your Apache installation using the commands ``configure``,
+``make`` and ``make install``, a method sometimes referred to by the
+acyronym CMMI.
The second and newest way of installing mod_wsgi is to install it as a
-Python package into your Python installation.
+Python package into your Python installation using the Python ``pip
+install`` command.
-This new way of installing mod_wsgi will compile not only the Apache
-module for mod_wsgi, but will also install a set of Python modules and
-an admin script for running up Apache directly from the command line
-with an auto generated configuration.
-
-This later mechanism for running up Apache, which is referred to as the
-mod_wsgi express version, provides a much simpler way of getting starting
-with hosting your Python web application.
+This newer way of installing mod_wsgi will compile not only the Apache
+module for mod_wsgi, but will also install a Python module and admin script
+for starting up a standalone instance of Apache directly from the command
+line with an auto generated configuration.
+This later mechanism for installing mod_wsgi using Python ``pip`` is a much
+simpler way of getting starting with hosting your Python web application.
In particular, the new installation method makes it very easy to use
Apache/mod_wsgi in a development environment without the need to perform
any Apache configuration yourself.
+The Apache module for mod_wsgi created when using the ``pip install``
+method can still be used with the main Apache installation, via manual
+configuration if necessary.
+
+On some platforms, this latter method is actually the only option supported
+when using the operating system supplied Apache installation. For example,
+in MacOS X Sierra, Apple has completely broken the ability to install third
+party Apache modules using the ``apxs`` tool normally used for this task.
+History suggests that Apple will never fix the problem as they have broken
+things in the past in other ways and workarounds were required as they
+never fixed those problems either. This time there is no easy workaround as
+they no longer supply certain tools which are required to perform the
+installation.
+
System Requirements
-------------------
-With either installation method for mod_wsgi, you obviously must have
-Apache installed.
+With either installation method for mod_wsgi, you must have Apache
+installed. This must be a complete Apache installation. It is not enough to
+have only the runtime packages for Apache installed. You must have the
+corresponding development package for Apache installed, which contains the
+Apache header files, as these are required to be able compile and install
+third party Apache modules.
-If running Linux, any corresponding developer variant of the specific
-Apache package you are using also needs to be installed. This is required
-in order to be able to compile mod_wsgi from source code.
+Similarly with Python, you must have a complete Python installation which
+includes the corresponding development package, which contains the header
+files for the Python library.
-For example, on Ubuntu Linux with Apache 2.2, if you were using the Apache
-prefork MPM you would need both:
+If you are running Debian or Ubuntu Linux with Apache 2.2 system packages,
+and were using the Apache prefork MPM you would need both:
* apache2-mpm-prefork
* apache2-prefork-dev
@@ -52,39 +72,55 @@ If instead you were using the Apache worker MPM, you would need both:
* apache2-mpm-worker
* apache2-threaded-dev
-In general it is recommend you use the Apache worker MPM where you have
-a choice, although mod_wsgi will work with both, as well as the event
-and ITK MPM, plus winnt MPM on Windows.
+If you are running Debian or Ubuntu Linux with Apache 2.4 system packages,
+regardless of which Apache MPM is being used, you would need both:
+
+* apache2
+* apache2-dev
+
+If you are running RHEL, CentOS or Fedora, you would need both:
+
+* httpd
+* httpd-devel
+
+If you are using the Software Collections Library (SCL) packages with
+RHEL, CentOS or Fedora, you would need:
+
+* httpd24
-If you are running MacOS X, the Apache server and required developer
-files for compiling mod_wsgi are already present.
+If you are running MacOS X, you will need to have the Xcode command line
+tools installed. These can be installed by running ``xcode-select --install``.
Installation into Apache
------------------------
-For installation directly into your Apache installation, see the full
-documentation at:
+For installation directly into your Apache installation using the CMMI
+method, see the full documentation at:
* http://www.modwsgi.org/
-Also see the documentation if wishing to use mod_wsgi on Windows as the
-method of installing direct into your Python installation will not work
-on Windows.
+Alternatively, use the following instructions to install mod_wsgi into your
+Python installation and then either copy the mod_wsgi module into your
+Apache installation, or configure Apache to use the mod_wsgi module from
+the Python installation.
+
+When using this approach, you will still need to manually configure Apache
+to have mod_wsgi loaded into Apache, and for it to know about your WSGI
+application.
Installation into Python
------------------------
-To install the mod_wsgi express version directly into your Python
-installation, from within the source directory of the mod_wsgi package you
-can run::
+To install the mod_wsgi directly into your Python installation, from within
+the source directory of the mod_wsgi package you can run::
python setup.py install
This will compile mod_wsgi and install the resulting package into your
Python installation.
-If wishing to install an official release direct from PyPi, you can
-instead run::
+If wishing to install an official release direct from the Python Package
+Index (PyPi), you can instead run::
pip install mod_wsgi
@@ -96,7 +132,9 @@ before performing the installation.
Note that nothing will be copied into your Apache installation at this
point. As a result, you do not need to run this as the root user unless
installing it into a site wide Python installation rather than a Python
-virtual environment.
+virtual environment. It is recommended you always use Python virtual
+environments and never install any Python package direct into the system
+Python installation.
To verify that the installation was successful, run the ``mod_wsgi-express``
script with the ``start-server`` command::
@@ -123,13 +161,13 @@ running on port 80.
If you already have another web server running on port 8000, you can
override the port to be used using the ``--port`` option::
- mod_wsgi-express start-server wsgi.py --port 8001
+ mod_wsgi-express start-server wsgi.py --port 8080
For a complete list of options you can run::
mod_wsgi-express start-server --help
-Further information on using the mod_wsgi express version see the main
+For further information related to using ``mod_wsgi-express`` see the main
mod_wsgi documentation.
Non standard Apache installations
@@ -143,8 +181,8 @@ shell script which performs additional actions which can only be performed
as the ``root`` user.
In the case of the ``httpd`` executable simply being renamed, the
-executable will obviously not be found and mod_wsgi express will fail to
-start at all.
+executable will obviously not be found and ``mod_wsgi-express`` will fail
+to start at all.
In this case you should work out what the ``httpd`` executable was renamed
to and use the ``--httpd-executable`` option to specify its real location.
@@ -159,15 +197,16 @@ which performs additional actions before then executing the original
need to use the location of the original ``httpd`` executable the shell
script is in turn executing.
-Running mod_wsgi express as root
+Running mod_wsgi-express as root
--------------------------------
-The primary intention of mod_wsgi express is to make it easier for users
-to run up Apache on non privileged ports, especially during the development
-of a Python web application. If you want to be able to run Apache using
-mod_wsgi express on a privileged port such as the standard port 80 used by
-HTTP servers, then you will need to run ``mod_wsgi-express`` as root. In
-doing this, you will need to perform additional steps.
+The primary intention of ``mod_wsgi-express`` is to make it easier for
+users to run up Apache on non privileged ports, especially during the
+development of a Python web application. If you want to be able to run
+Apache using ``mod_wsgi-express`` on a privileged port such as the standard
+port 80 used by HTTP servers, then you will need to run
+``mod_wsgi-express`` as root. In doing this, you will need to perform
+additional steps.
The first thing you must do is supply the ``--user`` and ``--group``
options to say what user and group your Python web application should run
@@ -179,13 +218,13 @@ user account you have created for running the Python web application::
--user www-data --group www-data
This approach to running ``mod_wsgi-express`` will be fine so long as you
-are using a process supervisor which expects the started process to remain
+are using a process supervisor which expects the process being run to remain
in the foreground and not daemonize.
If however you are directly integrating into the system init scripts where
separate start and stop commands are expected, with the executing process
expected to be daemonized, then a different process is required to setup
-mod_wsgi express.
+``mod_wsgi-express``.
In this case, instead of simply using the ``start-server`` command to
``mod_wsgi-express`` you should use ``setup-server``::
@@ -195,7 +234,7 @@ In this case, instead of simply using the ``start-server`` command to
--server-root=/etc/mod_wsgi-express-80
In running this command, it will not actually startup Apache. All it will do
-is create the set of configuration files and startup script to be run.
+is create the set of configuration files and the startup script to be run.
So that these are not created in the default location of a directory under
``/tmp``, you should use the ``--server-root`` option to specify where they
@@ -215,26 +254,26 @@ You can also restart the Apache instance as necessary using::
/etc/mod_wsgi-express-80/apachectl restart
Using this approach, the original options you supplied to ``setup-server``
-will effectively be cached with the resulting configuration used each time.
-If you need to update the set of options, run ``setup-server`` again with
-the new set of options.
+will be cached with the same configuration used each time. If you need to
+update the set of options, run ``setup-server`` again with the new set of
+options.
Note that even taking all these steps, it is possible that running up
-Apache as ``root`` using mod_wsgi express may fail on systems where SELinux
-extensions are enabled. This is because the SELinux profile may not match
-what is being expected for the way that Apache is being started, or
+Apache as ``root`` using ``mod_wsgi-express`` may fail on systems where
+SELinux extensions are enabled. This is because the SELinux profile may not
+match what is being expected for the way that Apache is being started, or
alternatively, the locations that Apache has been specified as being
allowed to access, don't match where the directory specified using the
``--server-root`` directory was placed. You may therefore need to configure
SELinux or move the directory used with ``--server-root`` to an allowed
location.
-Using mod_wsgi express with Django
+Using mod_wsgi-express with Django
----------------------------------
-To use the mod_wsgi express version with Django, after having installed
-the mod_wsgi package into your Python installation, edit your Django
-settings module and add ``mod_wsgi.server`` to the list of installed apps.
+To use ``mod_wsgi-express`` with Django, after having installed the
+mod_wsgi package into your Python installation, edit your Django settings
+module and add ``mod_wsgi.server`` to the list of installed apps.
::
@@ -248,9 +287,9 @@ settings module and add ``mod_wsgi.server`` to the list of installed apps.
'mod_wsgi.server',
)
-To prepare for running of the mod_wsgi express version, ensure that you
-first collect up any Django static file assets into the directory specified
-for them in the Django settings file::
+To prepare for running ``mod_wsgi-express``, ensure that you first collect
+up any Django static file assets into the directory specified for them in
+the Django settings file::
python manage.py collectstatic
@@ -276,23 +315,66 @@ use the ``--setup-only`` option to the ``runmodwsgi`` management command.
python manage.py runmodwsgi --setup-only --port=80 \
--user www-data --group www-data \
--server-root=/etc/mod_wsgi-express-80
-
-Using mod_wsgi express with New Relic
--------------------------------------
-
-If using `New Relic <http://www.newrelic.com/>`_ for application
-performance monitoring, and you already have the ``newrelic`` package
-installed and your Python agent configuration file generated, you can use
-the ``--with-newrelic`` option.
-
-You do not need to use the ``newrelic-admin`` script that New Relic
-provides to wrap the execution of the server. You only need to set the
-``NEW_RELIC_CONFIG_FILE`` environment variable to the location of your
-agent configuration file.
-
-::
-
- NEW_RELIC_CONFIG_FILE=`pwd`/newrelic.ini
- export NEW_RELIC_CONFIG_FILE
-
- mod_wsgi-express start-server wsgi.py --with-newrelic
+
+This will setup all the required files and you can use ``apachectl`` to
+start and stop the Apache instance as explained previously.
+
+Connecting into Apache installation
+-----------------------------------
+
+If you want to use mod_wsgi in combination with your system Apache
+installation, the CMMI method for installing mod_wsgi would normally be
+used. If you are on MacOS X Sierra that is no longer possible. Even prior
+to MacOS X Sierra, the System Integrity Protection (SIP) system of MacOS X,
+prevented installing the mod_wsgi module into the Apache modules
+directory.
+
+The CMMI installation method also involves a bit more work as you need to
+separately download the mod_wsgi source code, run the ``configure`` tool
+and then run ``make`` and ``make install``.
+
+The alternative to using the CMMI installation method is to use the Apache
+mod_wsgi module created by running ``pip install``. This can be directly
+referenced from the Apache configuration, or copied into the Apache modules
+directory.
+
+To use the Apache mod_wsgi module from where ``pip install`` placed it,
+run the command ``mod_wsgi-express module-config``. This will output
+something like::
+
+ LoadModule wsgi_module /usr/local/lib/python2.7/site-packages/mod_wsgi/server/mod_wsgi-py27.so
+ WSGIPythonHome /usr/local/lib
+
+These are the directives needed to configure Apache to load the mod_wsgi
+module and tell mod_wsgi where the Python installation directory or virtual
+environment was located.
+
+This would be placed in the Apache ``httpd.conf`` file, or if the Linux
+distribution separates out module configuration into a ``mods-available``
+directory, in the ``wsgi.load`` file within the ``mods-available``
+directory. In the latter case where a ``mods-available`` directory is used,
+the module would then be enabled by running ``a2enmod wsgi`` as ``root``.
+If necessary Apache can then be restarted to verify the module is loading
+correctly. You can then configure Apache as necessary for your specific
+WSGI application.
+
+Note that because in this scenario the mod_wsgi module for Apache could be
+located in a Python virtual environment, if you destroy the Python virtual
+environment the module will also be deleted. In that case you would need to
+ensure you recreated the Python virtual environment and reinstalled the
+mod_wsgi package using ``pip``, or take out the mod_wsgi configuration from
+Apache before restarting Apache or it will fail to startup.
+
+Instead of referencing the mod_wsgi module from the Python installation,
+you can instead copy the mod_wsgi module into the Apache installation. To
+do that, run the ``mod_wsgi-express install-module`` command, running it as
+``root`` if necessary. This will output something like::
+
+ LoadModule wsgi_module modules/mod_wsgi-py27.so
+ WSGIPythonHome /usr/local/lib
+
+This is similar to above except that the mod_wsgi module was copied to the
+Apache modules directory first and the ``LoadModule`` directive references
+it from that location. You should take these lines and configure Apache in
+the same way as described above. Do note that copying the module like this
+will not work on recent versions of MacOS X due to the SIP feature of MacOS X.
diff --git a/docs/configuration-directives/WSGIDaemonProcess.rst b/docs/configuration-directives/WSGIDaemonProcess.rst
index 1fddeed..25e0a85 100644
--- a/docs/configuration-directives/WSGIDaemonProcess.rst
+++ b/docs/configuration-directives/WSGIDaemonProcess.rst
@@ -6,11 +6,11 @@ WSGIDaemonProcess
:Syntax: ``WSGIDaemonProcess`` *name* ``[`` *options* ``]``
:Context: server config, virtual host
-The WSGIDaemonProcess directive can be used to specify that distinct daemon
-processes should be created to which the running of WSGI applications can
-be delegated. Where Apache has been started as the ``root`` user, the
-daemon processes can be run as a user different to that which the Apache
-child processes would normally be run as.
+The ``WSGIDaemonProcess`` directive can be used to specify that distinct
+daemon processes should be created to which the running of WSGI
+applications can be delegated. Where Apache has been started as the
+``root`` user, the daemon processes can be run as a user different to that
+which the Apache child processes would normally be run as.
When distinct daemon processes are enabled and used, the process is
dedicated to mod_wsgi and the only thing that the processes do is run the
@@ -18,99 +18,205 @@ WSGI applications assigned to that process group. Any other Apache modules
such as PHP or activities such as serving up static files continue to be
run in the standard Apache child processes.
-Note that having denoted that daemon processes should be created by using
-the WSGIDaemonProcess directive, the WSGIProcessGroup directive still needs
-to be used to delegate specific WSGI applications to execute within those
-daemon processes.
+Note that having denoted that daemon processes should be created by
+using the ``WSGIDaemonProcess`` directive, the ``WSGIProcessGroup``
+directive, or the ``process-group`` option of ``WSGIScriptAlias`` still
+needs to be used to delegate specific WSGI applications to execute within
+those daemon processes.
Also note that the name of the daemon process group must be unique for the
whole server. That is, it is not possible to use the same daemon process
group name in different virtual hosts.
-Options which can be supplied to the WSGIDaemonProcess directive are:
+Options which can be supplied to the ``WSGIDaemonProcess`` directive are:
-**user=name | user=#uid**.rst
+**processes=num**
+ Defines the number of daemon processes that should be started in this
+ process group. If not defined then only one process will be run in this
+ process group.
+
+ Note that if this option is defined as ``processes=1``, then the WSGI
+ environment attribute called ``wsgi.multiprocess`` will be set to be
+ ``True`` whereas not providing the option at all will result in the
+ attribute being set to be ``False``. This distinction is to allow for
+ where some form of load balancing is used across process groups in the
+ same Apache instance, or separate Apache instances. If you need to
+ ensure that ``wsgi.multiprocess`` is ``False`` so that interactive
+ debuggers will work, simply do not specify the ``processes`` option and
+ allow the default single daemon process to be created in the process
+ group.
+
+**threads=num**
+ Defines the number of threads to be created to handle requests in each
+ daemon process within the process group.
+
+ If this option is not defined then the default will be to create 15
+ threads in each daemon process within the process group.
+
+ Do not get carried away and set this to a very large number in the
+ belief that it will somehow magically enable you to handle many more
+ concurrent users. Any sort of increased value would only be appropriate
+ where your code is I/O bound. If you code is CPU bound, you are better
+ of using at most 3 to 5 threads per process and using more processes.
+
+**display-name=value**
+ Defines a different name to show for the daemon process when using the
+ ``ps`` command to list processes. If the value is ``%{GROUP}`` then the
+ name will be ``(wsgi:group)`` where ``group`` is replaced with the name
+ of the daemon process group.
+
+ Note that only as many characters of the supplied value can be displayed
+ as were originally taken up by ``argv0`` of the executing process.
+ Anything in excess of this will be truncated.
+
+ This feature may not work as described on all platforms. Typically it
+ also requires a ``ps`` program with BSD heritage. Thus on some versions
+ of Solaris UNIX the ``/usr/bin/ps`` program doesn't work, but
+ ``/usr/ucb/ps`` does. Other programs which can display this value
+ include ``htop``.
+
+**home=directory**
+ Defines an absolute path of a directory which should be used as the
+ initial current working directory of the daemon processes within the
+ process group.
+
+ If this option is not defined the initial current working directory
+ will be set to be the home directory of the user that the daemon
+ process is configured to run as using the ``user`` option to the
+ ``WSGIDaemonProcess`` directive. Otherwise the current working
+ directory of Apache when started will be used, which if Apache is being
+ started from system init scripts, would usually be the system root
+ directory.
+
+**user=name | user=#uid**
Defines the UNIX user *name* or numeric user *uid* of the user that
the daemon processes should be run as. If this option is not supplied
the daemon processes will be run as the same user that Apache would
- run child processes and as defined by the `User`_ directive.
+ run child processes, as defined by the `User`_ directive, and it is
+ not necessary to set this to the Apache user yourself.
Note that this option is ignored if Apache wasn't started as the root
user, in which case no matter what the settings, the daemon processes
will be run as the user that Apache was started as.
- Also be aware that mod_wsgi will not allow you to run a daemon
- process group as the root user due to the security risk of running
- a web application as root.
+ Also be aware that mod_wsgi will not allow you to run a daemon process
+ group as the root user due to the security risk of running a web
+ application as root.
**group=name | group=#gid**
Defines the UNIX group *name* or numeric group *gid* of the primary
group that the daemon processes should be run as. If this option is not
supplied the daemon processes will be run as the same group that Apache
- would run child processes and as defined by the `Group`_ directive.
+ would run child processes, as defined by the `Group`_ directive, and it
+ is not necessary to set this to the Apache group yourself.
Note that this option is ignored if Apache wasn't started as the root
user, in which case no matter what the settings, the daemon processes
will be run as the group that Apache was started as.
-**processes=num**
- Defines the number of daemon processes that should be started in this
- process group. If not defined then only one process will be run in this
- process group.
-
- Note that if this option is defined as 'processes=1', then the WSGI
- environment attribute called 'wsgi.multiprocess' will be set to be True
- whereas not providing the option at all will result in the attribute
- being set to be False. This distinction is to allow for where some form
- of mapping mechanism might be used to distribute requests across
- multiple process groups and thus in effect it is still a multiprocess
- application. If you need to ensure that 'wsgi.multiprocess' is False so
- that interactive debuggers will work, simply do not specify the
- 'processes' option and allow the default single daemon process to be
- created in the process group.
-
-**threads=num**
- Defines the number of threads to be created to handle requests in each
- daemon process within the process group.
-
- If this option is not defined then the default will be to create 15
- threads in each daemon process within the process group.
+**supplementary-groups=group1 | supplementary-groups=group1,group2**
+ Defines a list of additional UNIX groups that the user the daemon
+ process group runs as, should be added to, in addition to primary
+ UNIX group associated with that user. When specifying more than one
+ group, separate the names of the groups with a comma.
**umask=0nnn**
Defines a value to be used for the umask of the daemon processes within
the process group. The value must be provided as an octal number.
-
+
If this option is not defined then the umask of the user that Apache is
initially started as will be inherited by the process. Typically the
inherited umask would be '0022'.
-**home=directory**
- Defines an absolute path of a directory which should be used as the
- initial current working directory of the daemon processes within the
- process group.
+**lang=locale**
+ Set the current language locale. This is the same as having set the
+ ``LANG`` environment variable.
- If this option is not defined, in mod_wsgi 1.X the current working
- directory of the Apache parent process will be inherited by the daemon
- processes within the process group. Normally the current working directory
- of the Apache parent process would be the root directory. In mod_wsgi 2.0+
- the initial current working directory will be set to be the home
- directory of the user that the daemon process runs as.
+ You will need to set this on many Linux systems where Apache when
+ started up from system init scripts uses the default C locale, meaning
+ that the default system encoding is ASCII. Unless you need a special
+ language locale, set this to ``en_US.UTF-8``.
+
+ Whether the ``lang`` or ``locale`` option works best can depend on the
+ system being used. Set both if you aren't sure which is appropriate.
+
+**locale=locale**
+ Set the current language locale. This is the same as having set the
+ ``LC_ALL`` environment variable.
+
+ You will need to set this on many Linux systems where Apache when
+ started up from system init scripts uses the default C locale, meaning
+ that the default system encoding is ASCII. Unless you need a special
+ language locale, set this to ``en_US.UTF-8``.
+
+ Whether the ``lang`` or ``locale`` option works best can depend on the
+ system being used. Set both if you aren't sure which is appropriate.
+
+**chroot=directory**
+ Run the daemon process group process within a chroot jail. Use of a
+ chroot jail is now deprecated due to the difficulty in setting up a
+ chroot environment. It is recommended that you use more modern
+ containerisation technologies such as Docker or runC.
+
+**script-user=name | script-user=#uid**
+ Sets the user that must be the owner of any WSGI script file delegated
+ to be run in the daemon process group. If the owner doesn't match a
+ HTTP Forbidden response will be returned for any request.
+
+ Note that this doesn't change what user the daemon process group runs
+ as at any time. If you want to set the user that the daemon process
+ group runs as, use the ``user`` option.
+
+ Only one of ``script-user`` or ``script-group`` option can be used at
+ the same time.
+
+**script-group=name | scrip-group=#gid**
+ Sets the group that must be the group of any WSGI script file delegated
+ to be run in the daemon process group. If the group doesn't match a
+ HTTP Forbidden response will be returned for any request.
+
+ Note that this doesn't change what group the daemon process group runs
+ as at any time. If you want to set the group that the daemon process
+ group runs as, use the ``group`` option.
+
+ Only one of ``script-user`` or ``script-group`` option can be used at
+ the same time.
+
+**python-home=directory**
+ Set the location of the Python virtual environment to be used by the
+ daemon processes. The directory to use is that which ``sys.prefix`` is
+ set to for the Python virtual environment. The virtual environment can
+ have been created by ``virtualenv``, ``pyvenv`` or ``python -m venv``.
+
+ Note that the Python virtual environment must have been created using
+ the same base Python version as was used to compile the mod_wsgi
+ module. You can't use this to force mod_wsgi to somehow use a different
+ Python version than it was compiled for. If you want to use a different
+ version of Python, you will need to reinstall mod_wsgi, compiling it
+ for the version you want. It is not possible for the one mod_wsgi
+ instance to run applications for both Python 2 and 3 at the same time.
**python-path=directory | python-path=directory:directory**
List of colon separated directories to add to the Python module search
path, ie., ``sys.path``.
- Note that this is not strictly the same as having set ``PYTHONPATH``
+ Note that this is not strictly the same as having set the ``PYTHONPATH``
environment variable when running normal command line Python. When this
option is used, the directories are added by calling
``site.addsitedir()``. As well as adding the directory to
``sys.path`` this function has the effect of opening and interpreting
- any '.pth' files located in the specified directories. The option
- therefore can be used to point at the ``site-packages`` directory
- corresponding to a Python virtual environment created by a tool such as
- ``virtualenv``, with any additional directories corresponding to
- Python eggs within that directory also being automatically added to
- ``sys.path``.
+ any ``.pth`` files located in the specified directories.
+
+ If using a Python virtual environment, rather than use this option to
+ refer to the ``site-packages`` directory of the Python virtual
+ environment, you should use the ``python-home`` option to specify the
+ root of the Python virtual environment instead.
+
+ In all cases, if the directory contains Python packages which have C
+ extension components, those packages must have been installed using the
+ same base Python version as was used to compile the mod_wsgi module.
+ You should not mix packages from different Python versions or
+ installations.
**python-eggs=directory**
Directory to be used as the Python egg cache directory. This is
@@ -120,41 +226,76 @@ Options which can be supplied to the WSGIDaemonProcess directive are:
Note that the directory specified must exist and be writable by the
user that the daemon process run as.
-**stack-size=nnn**
- The amount of virtual memory in bytes to be allocated for the stack
- corresponding to each thread created by mod_wsgi in a daemon process.
-
- This option would be used when running Linux in a VPS system which has
- been configured with a quite low 'Memory Limit' in relation to the
- 'Context RSS' and 'Max RSS Memory' limits. In particular, the default
- stack size for threads under Linux is 8MB is quite excessive and could
- for such a VPS result in the 'Memory Limit' being exceeded before the
- RSS limits were exceeded. In this situation, the stack size should be
- dropped down to be in the region of 512KB (524288 bytes).
-
**maximum-requests=nnn**
Defines a limit on the number of requests a daemon process should
- process before it is shutdown and restarted. Setting this to a non zero
- value has the benefit of limiting the amount of memory that a process
- can consume by (accidental) memory leakage.
+ process before it is shutdown and restarted.
+
+ This might be use to periodically force restart the WSGI application
+ processes when you have issues related to Python object reference count
+ cycles, or incorrect use of in memory caching, which causes constant
+ memory growth.
If this option is not defined, or is defined to be 0, then the daemon
process will be persistent and will continue to service requests until
Apache itself is restarted or shutdown.
+ Avoid setting this to a low number of requests on a site which handles
+ a lot of traffic. This is because the constant restarting and reloading
+ of your WSGI application may cause unecessary load on your system and
+ affect performance. Only use this option if you have no other choice
+ due to a memory usage issue. Stop using it as soon as any memory issue
+ has been resolved.
+
**inactivity-timeout=sss**
Defines the maximum number of seconds allowed to pass before the
daemon process is shutdown and restarted when the daemon process has
entered an idle state. For the purposes of this option, being idle
- means no new requests being received, or no attempts by current
- requests to read request content or generate response content for the
- defined period.
+ means there are no currently active requests and no new requests are
+ being received.
This option exists to allow infrequently used applications running in
a daemon process to be restarted, thus allowing memory being used to
be reclaimed, with process size dropping back to the initial startup
size before any application had been loaded or requests processed.
+ Note that after any restart of the WSGI application process, the WSGI
+ application will need to be reloaded. This can mean that the first
+ request received by a process after the process was restarted can be
+ slower. If you WSGI application has a very high startup cost on CPU and
+ time, it may not be a good idea to use the option.
+
+ See also the ``request-timeout`` option for forcing a process restart
+ when requests block for a specified period of time.
+
+ Note that similar functionality to that of the ``request-timeout``
+ option, for forcing a restart when requests blocked, was part of what
+ was implemented by the ``inactivity-timeout`` option. The request
+ timeout was broken out into a separate feature in version 4.1.0 of
+ mod_wsgi.
+
+**request-timeout=sss**
+ Defines the maximum number of seconds that a request is allowed to run
+ before the daemon process is restarted. This can be used to recover
+ from a scenario where a request blocks indefinitely, and where if all
+ request threads were consumed in this way, would result in the whole
+ WSGI application process being blocked.
+
+ How this option is seen to behave is different depending on whether a
+ daemon process uses only one thread, or more than one thread for
+ handling requests, as set by the ``threads`` option.
+
+ If there is only a single thread, and so the process can only handle
+ one request at a time, as soon as the timeout has passed, a restart of
+ the process will be initiated.
+
+ If there is more than one thread, the request timeout is applied to
+ the average running time for any requests, across all threads. This
+ means that a request can run longer than the request timeout. This is
+ done to reduce the possibility of interupting other running requests,
+ and causing a user to see a failure. So where there is still capacity
+ to handle more requests, restarting of the process will be delayed
+ if possible.
+
**deadlock-timeout=sss**
Defines the maximum number of seconds allowed to pass before the
daemon process is shutdown and restarted after a potential deadlock on
@@ -165,14 +306,50 @@ Options which can be supplied to the WSGIDaemonProcess directive are:
properly release the Python GIL when entering into a blocking or long
running operation.
+**startup-timeout=sss**
+ Defines the maximum number of seconds allowed to pass waiting to see if
+ a WSGI script file can be loaded successfully by a daemon process. When
+ the timeout is passed, the process will be restarted.
+
+ This can be used to force the reloading of a process when a transient
+ issue occurs on the first attempt to load the WSGI script file, but
+ subsequent attempts still fail because a Python package that was loaded
+ has retained state that prevents attempts to run initialisation a
+ second time within the same process. The Django package can cause this
+ scenario as the initialisation of Django itself can no longer be
+ attempted more than once in the same process.
+
+**graceful-timeout=sss**
+ When ``maximum-requests`` is used and the maximum has been reached,
+ or ``cpu-time-limit`` is used and the CPU limit reached, if
+ ``graceful-timeout`` is set, then the process will continue to run for
+ the number of second specified by this option, while still accepting
+ new requests, to see if the process reaches an idle state. If the
+ process reaches an idle state, it will then be resarted immediately.
+ If the process doesn't reach an idle state and the graceful restart
+ timeout expires, the process will be restarted, even if it means that
+ requests may be interrupted.
+
+**eviction-timeout=sss**
+ When a daemon process is sent the graceful restart signal, usually
+ ``SIGUSR1``, to restart a process, this timeout controls how many
+ seconds the process will wait, while still accepting new requests,
+ before it reaches an idle state with no active requests and shutdown.
+
+ If this timeout is not specified, then the value of the
+ ``graceful-timeout`` will instead be used. If the ``graceful-timeout``
+ is not specified, then the restart when sent the graceful restart
+ signal will instead happen immediately, with the process being forcibly
+ killed, if necessary, when the shutdown timeout has expired.
+
**shutdown-timeout=sss**
- Defines the maximum number of seconds allowed to pass when waiting
- for a daemon process to gracefully shutdown as a result of the maximum
- number of requests or inactivity timeout being reached, or when a user
- initiated SIGINT signal is sent to a daemon process. When this timeout
- has been reached the daemon process will be forced to exited even if
- there are still active requests or it is still running Python exit
- functions.
+ Defines the maximum number of seconds allowed to pass when waiting for
+ a daemon process to shutdown. When this timeout has been reached the
+ daemon process will be forced to exited even if there are still active
+ requests or it is still running Python exit functions. The shutdown
+ timeout is applied after any graceful restart timeout or eviction
+ timeout if they have been specified. No new requests are accepted
+ during the shutdown timeout is being applied.
If this option is not defined, then the shutdown timeout will be set
to 5 seconds. Note that this option does not change the shutdown
@@ -180,19 +357,95 @@ Options which can be supplied to the WSGIDaemonProcess directive are:
or restarted. That timeout value is defined internally to Apache as 3
seconds and cannot be overridden.
-**display-name=value**
- Defines a different name to show for the daemon process when using the
- 'ps' command to list processes. If the value is '%{GROUP}' then the
- name will be '(wsgi:group)' where 'group' is replaced with the name
- of the daemon process group.
+**connect-timeout=sss**
+ Defines the maximum amount of time for an Apache child process to wait
+ trying to get a successful connection to the mod_wsgi daemon processes.
+ This defaults to 15 seconds.
+
+**socket-timeout=sss**
+ Defines the timeout on individual reads/writes on the socket connection
+ between the Apache child processes and the mod_wsgi daemon processes.
+ If this is not specified, the number of seconds specified by the
+ Apache `Timeout`_ directive will be used instead.
+
+**queue-timeout=sss**
+ Defines the timeout on how long to wait for a mod_wsgi daemon process
+ to accept a request for processing.
+
+ This option is to allow one to control what to do when backlogging of
+ requests occurs. If the daemon process is overloaded and getting
+ behind, then it is more than likely that a user will have given up on
+ the request anyway if they have to wait too long. This option allows
+ you to specify that a request that was queued up waiting for too long
+ is discarded, allowing any transient backlog to be quickly discarded
+ and not simply cause the daemon process to become even more backlogged.
+ When this occurs the user will recieve a 504 Gateway Time Out response.
+
+**listen-backlog**
+ Defines the depth of the daemon process socket listener queue. By
+ default the limit is 100, although this is actually a hint, as
+ different operating systems can have different limits on the maximum
+ value or otherwise treat it in special ways.a
+
+ This option can be set, along with ``queue-timeout`` to try and better
+ handle back logging when the WGSI application gets overloaded.
+
+**socket-user=name | socket-user=#uid**
+ Set the owner of the UNIX listener socket for the daemon process group.
+
+ This can be used when using the Apache `PrivilegesMode`_ directive with
+ value of ``SECURE`` to change the owner of the socket from the default
+ Apache user, to the user under which the Apache child process which is
+ attempting to connect to the daemon process group, will run 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.
+
+ This option can also be used when using third party Apache modules such
+ as mod_ruid, mod_ruid2, mod_suid as well as the ITK MPM for Apache.
+
+**cpu-time-limit=sss**
+ Define the maximum amount of CPU time a daemon process is allowed to
+ consume before a shutdown is triggered and the daemon process
+ restarted. The point of this is to provide some means of controlling
+ potentially run away processes due to bad code that gets stuck in heavy
+ processing loops.
+
+ Note that CPU time used is recorded from when the daemon process is
+ first created. This means that a process will eventually reach the
+ limit in normal use and would be restarted. You can use the
+ ``graceful-timeout`` option to reduce the chances that an active
+ request will be interrupted.
+
+**cpu-priority=num**
+ Sets the scheduling priority set to the daemon processes. This can be
+ a number of the range -20 to 20. The default priority is 0. A lower
+ priority gives more favourable scheduling.
+
+**memory-limit=num**
+ Sets the maximum amount of memory a daemon process can use. This will
+ have no affect on some platforms as ``RLIMIT_AS``/``RLIMIT_DATA`` with
+ ``setrlimit()`` isn't always implemented. For example MacOS X and older
+ Linux kernel versions do not implement this feature. You will need to
+ test whether this feature works or not before depending on it.
+
+**virtual-memory-limit=num**
+ Sets the maximum amount of virtual memory a daemon process can use.
+ This will have no affect on some platforms as ``RLIMIT_VMEM`` with
+ ``setrlimit()`` isn't always implemented. You will need to test whether
+ this feature works or not before depending on it.
- Note that only as many characters of the supplied value can be displayed
- as were originally taken up by 'argv0' of the executing process. Anything
- in excess of this will be truncated.
+**stack-size=nnn**
+ The amount of virtual memory in bytes to be allocated for the stack
+ corresponding to each thread created by mod_wsgi in a daemon process.
- This feature may not work as described on all platforms. Typically it
- also requires a 'ps' program with BSD heritage. Thus on Solaris UNIX
- the '/usr/bin/ps' program doesn't work, but '/usr/ucb/ps' does.
+ This option would be used when running Linux in a VPS system which has
+ been configured with a quite low 'Memory Limit' in relation to the
+ 'Context RSS' and 'Max RSS Memory' limits. In particular, the default
+ stack size for threads under Linux is 8MB is quite excessive and could
+ for such a VPS result in the 'Memory Limit' being exceeded before the
+ RSS limits were exceeded. In this situation, the stack size should be
+ dropped down to be in the region of 512KB (524288 bytes).
**receive-buffer-size=nnn**
Defines the UNIX socket buffer size for data being received by the
@@ -200,11 +453,11 @@ Options which can be supplied to the WSGIDaemonProcess directive are:
This option may need to be used to override small default values set by
certain operating systems and would help avoid possibility of deadlock
- between Apache child process and daemon process when WSGI application
- generates large responses but doesn't consume request content. In
- general such deadlock problems would not arise with well behaved WSGI
- applications, but some spam bots attempting to post data to web sites
- are known to trigger the problem.
+ between Apache child process and daemon process when the WSGI
+ application generates large responses but doesn't consume request
+ content. In general such deadlock problems would not arise with well
+ behaved WSGI applications, but some spam bots attempting to post data
+ to web sites are known to trigger the problem.
The maximum possible value that can be set for the buffer size is
operating system dependent and will need to be calculated through trial
@@ -216,29 +469,72 @@ Options which can be supplied to the WSGIDaemonProcess directive are:
This option may need to be used to override small default values set by
certain operating systems and would help avoid possibility of deadlock
- between Apache child process and daemon process when WSGI application
- generates large responses but doesn't consume request content. In
- general such deadlock problems would not arise with well behaved WSGI
- applications, but some spam bots attempting to post data to web sites
- are known to trigger the problem.
+ between Apache child process and daemon process when the WSGI
+ application generates large responses but doesn't consume request
+ content. In general such deadlock problems would not arise with well
+ behaved WSGI applications, but some spam bots attempting to post data
+ to web sites are known to trigger the problem.
The maximum possible value that can be set for the buffer size is
operating system dependent and will need to be calculated through trial
and error.
+**header-buffer-size=nnn**
+ Defines the maximum size that a response header/value can be that is
+ returned from a WSGI application. The default size is 32768 bytes. This
+ might need to be overridden where excessively large response headers
+ are returned, such as in custom authentication challenge schemes which
+ use the ``WWW-Authenticate`` header.
+
+**response-buffer-size=nnn**
+ Defines the maximum number of bytes that will be buffered for a
+ response in the Apache child processes when proxying the response body
+ from the WSGI application. The default size is 65536 bytes. Be careful
+ increasing this to provide extra buffering of responses as it
+ contributes to the runtime memory size of the Apache child processes.
+
To delegate a particular WSGI application to run in a named set of daemon
-processes, the WSGIProcessGroup directive should be specified in
-appropriate context for that application. If WSGIProcessGroup is not used,
-the application will be run within the standard Apache child processes.
+processes, the ``WSGIProcessGroup`` directive should be specified in
+appropriate context for that application, or the ``process-group`` option
+used on the ``WSGIScriptAlias`` directive. If neither is used to delegate
+the WSGI application to run in a daemon process group, the application will
+be run within the standard Apache child processes.
-If the WSGIDaemonProcess directive is specified outside of all virtual
+If the ``WSGIDaemonProcess`` directive is specified outside of all virtual
host containers, any WSGI application can be delegated to be run within
-that daemon process group. If the WSGIDaemonProcess directive is specified
-within a virtual host container, only WSGI applications associated with
-virtual hosts with the same server name as that virtual host can be
-delegated to that set of daemon processes.
+that daemon process group. If the ``WSGIDaemonProcess`` directive is
+specified within a virtual host container, only WSGI applications
+associated with virtual hosts with the same server name as that virtual
+host can be delegated to that set of daemon processes.
+
+In the case where you have two separate ``VirtualHost`` definitions for
+the same ``ServerName``, but where one is for port 80 and the other for
+port 443, specify the ``WSGIDaemonProcess`` directive in the
+first ``VirtualHost``. You can then refer to that daemon process group
+by name from the second ``VirtualHost``. Using one daemon process group
+across the two virtual hosts in this case is preferred as then you do not
+have two whole separate instances of your application for port 80 and 443.
+
+::
+
+ <VirtualHost *:80>
+ ServerName www.site1.com
+
+ WSGIDaemonProcess www.site1.com user=joe group=joe processes=2 threads=25
+ WSGIProcessGroup www.site1.com
+
+ ...
+ </VirtualHost>
+
+ <VirtualHost *:443>
+ ServerName www.site1.com
+
+ WSGIProcessGroup www.site1.com
+
+ ...
+ </VirtualHost>
-When WSGIDaemonProcess is associated with a virtual host, the error log
+When ``WSGIDaemonProcess`` is associated with a virtual host, the error log
associated with that virtual host will be used for all Apache error log
output from mod_wsgi rather than it appear in the main Apache error log.
@@ -269,8 +565,10 @@ host, the following could be used::
...
</VirtualHost>
-Note that the WSGIDaemonProcess directive and corresponding features are
-not available on Windows or when running Apache 1.3.
+Note that the ``WSGIDaemonProcess`` directive and corresponding features are
+not available on Windows.
-.. _User: http://httpd.apache.org/docs/2.2/mod/mpm_common.html#user
-.. _Group: http://httpd.apache.org/docs/2.2/mod/mpm_common.html#group
+.. _User: http://httpd.apache.org/docs/2.4/mod/mod_unixd.html#user
+.. _Group: http://httpd.apache.org/docs/2.4/mod/mod_unixd.html#group
+.. _Timeout: http://httpd.apache.org/docs/2.4/mod/core.html#timeout
+.. _PrivilegesMode: https://httpd.apache.org/docs/2.4/mod/mod_privileges.html#privilegesmode
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index d031370..7b5f2db 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -5,6 +5,7 @@ Release Notes
.. toctree::
:maxdepth: 2
+ release-notes/version-4.5.8
release-notes/version-4.5.7
release-notes/version-4.5.6
release-notes/version-4.5.5
diff --git a/docs/release-notes/version-4.5.8.rst b/docs/release-notes/version-4.5.8.rst
new file mode 100644
index 0000000..1c55cd0
--- /dev/null
+++ b/docs/release-notes/version-4.5.8.rst
@@ -0,0 +1,69 @@
+=============
+Version 4.5.8
+=============
+
+Version 4.5.8 of mod_wsgi can be obtained from:
+
+ https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.5.8
+
+Bugs Fixed
+----------
+
+* When using HTTP/2 support and ``wsgi.file_wrapper``, the response could
+ be truncated when ``mod_h2`` was deferring the sending of the response
+ until after the WSGI request had been finalized.
+
+* Builds were failing on Windows. Insert appropriate ``#if`` conditional
+ around code which shouldn't have been getting included on Windows.
+
+* When ``mod_wsgi-express`` is run as ``root`` and ``--python-eggs``
+ option is used, if the directory for the Python eggs didn't exist, it
+ was created, but the ownership/group were not set to be the user and
+ group that Apache would run the WSGI application. As a result Python
+ eggs could not actually be unpacked into the directory. Now change
+ the ownership/group of the directory to user/group specified when
+ ``mod_wsgi-express`` was run.
+
+* Installation on MacOS X Sierra fails for both CMMI and ``pip install``
+ methods. This is because Apple removed ``apr-1-config`` and
+ ``apu-1-config`` tools needed by ``apxs`` to install third party
+ Apache module. A workaround has been incorporated so that installation
+ still works when using ``pip install``, but there is no workaround for
+ CMMI method. You will need to use ``pip install`` method and then use
+ ``mod_wsgi-express module-config`` to get the configuration to then
+ add into the Apache configuration so it knows how to load the mod_wsgi
+ module. Then configure Apache so it knows about your WSGI application.
+
+* Compilation would fail on MacOS X Sierra as the API was changed for
+ obtaining task information. This was used to get memory used by the
+ process.
+
+New Features
+------------
+
+* Add ``WSGIIgnoreActivity`` directive. This can be set to ``On`` inside of
+ a ``Location`` directive block for a specific URL path, and any requests
+ against matching URLs will not trigger a reset of the inactivity timeout
+ for a mod_wsgi daemon process. This can be used on health check URLs so
+ that periodic requests against the health check URL do not interfere with
+ the inactivity timeout and keep the process running, rather than allowing
+ the process to restart due to being otherwise idle.
+
+* Added the ``--ignore-activity`` option to ``mod_wsgi-express``. It will
+ set the ``WSGIIgnoreActivity`` directive to ``On`` for the specific URL
+ path passed as argument to the option. Any requests against the matching
+ URL path will not trigger a reset of the inactivity timeout for a
+ mod_wsgi daemon process.
+
+* Added the ``--module-config`` option to ``mod_wsgi-express`` to get the
+ Apache configuration snippet you would use to load the mod_wsgi module
+ from the Python installation direct into Apache, rather than installing
+ the module into the Apache modules directory.
+
+* Added experimental support for installing mod_wsgi on Windows using ``pip``.
+ Is only tested with Apache 2.4 and Python 3.5. The Apache installation
+ must be installed in ``C:\Apache24`` directory. Run ``pip install mod_wsgi``.
+ The run ``mod_wsgi-express module-config`` and it will generate the
+ required configuration to add into the Apache configuration file to load
+ the mod_wsgi module. You still need to separately configure Apache for
+ your specific WSGI application.
diff --git a/docs/user-guides/assorted-tips-and-tricks.rst b/docs/user-guides/assorted-tips-and-tricks.rst
index 6fb54b5..873b85e 100644
--- a/docs/user-guides/assorted-tips-and-tricks.rst
+++ b/docs/user-guides/assorted-tips-and-tricks.rst
@@ -112,9 +112,9 @@ the 'mod_wsgi.version' key within the WSGI environ dictionary::
def application(environ, start_response):
status = '200 OK'
if environ.has_key('mod_wsgi.version'):
- output = 'Hello mod_wsgi!'
+ output = b'Hello mod_wsgi!'
else:
- output = 'Hello other WSGI hosting mechanism!'
+ output = b'Hello other WSGI hosting mechanism!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/configuration-guidelines.rst b/docs/user-guides/configuration-guidelines.rst
index b080ff7..f10f730 100644
--- a/docs/user-guides/configuration-guidelines.rst
+++ b/docs/user-guides/configuration-guidelines.rst
@@ -31,7 +31,7 @@ called 'application'. For example::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!'
+ output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/debugging-techniques.rst b/docs/user-guides/debugging-techniques.rst
index 5188097..d14e4f8 100644
--- a/docs/user-guides/debugging-techniques.rst
+++ b/docs/user-guides/debugging-techniques.rst
@@ -74,7 +74,7 @@ below::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!'
+ output = b'Hello World!'
print >> environ['wsgi.errors'], "application debug #1"
@@ -487,7 +487,7 @@ application entry point::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!\n\n'
+ output = b'Hello World!\n'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
@@ -583,7 +583,7 @@ object::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!\n\n'
+ output = b'Hello World!\n'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
@@ -607,7 +607,7 @@ you can interactively debug your application from the window you ran the
19
20 def application(environ, start_response):
21 -> status = '200 OK'
- 22 output = 'Hello World!\n\n'
+ 22 output = b'Hello World!\n'
23
24 response_headers = [('Content-type', 'text/plain'),
25 ('Content-Length', str(len(output)))]
@@ -653,7 +653,7 @@ a wrapper around the application you wish to debug::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!\n\n'
+ output = b'Hello World!\n'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/installation-issues.rst b/docs/user-guides/installation-issues.rst
index 51f05af..666de18 100644
--- a/docs/user-guides/installation-issues.rst
+++ b/docs/user-guides/installation-issues.rst
@@ -226,7 +226,7 @@ which outputs the values of 'sys.prefix' and 'sys.path'. For example::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!'
+ output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/quick-configuration-guide.rst b/docs/user-guides/quick-configuration-guide.rst
index 4799060..2ece215 100644
--- a/docs/user-guides/quick-configuration-guide.rst
+++ b/docs/user-guides/quick-configuration-guide.rst
@@ -28,7 +28,7 @@ examples in this document, is as follows::
def application(environ, start_response):
status = '200 OK'
- output = 'Hello World!'
+ output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/registering-cleanup-code.rst b/docs/user-guides/registering-cleanup-code.rst
index 81889e5..2b367e0 100644
--- a/docs/user-guides/registering-cleanup-code.rst
+++ b/docs/user-guides/registering-cleanup-code.rst
@@ -16,7 +16,7 @@ block, with the cleanup code being triggered from the 'finally' block::
def _application(environ, start_response):
status = '200 OK'
- output = 'Hello World!'
+ output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
diff --git a/docs/user-guides/virtual-environments.rst b/docs/user-guides/virtual-environments.rst
index e416ba3..309deec 100644
--- a/docs/user-guides/virtual-environments.rst
+++ b/docs/user-guides/virtual-environments.rst
@@ -2,213 +2,481 @@
Virtual Environments
====================
-This document contains information about how to make use of Python virtual
-environments such as created by Ian Bicking's virtualenv with mod_wsgi.
+This document contains information about how to use Python virtual
+environments with mod_wsgi. You can use a Python virtual environment
+created using `virtualenv`_ and `virtualenvwrapper`_, or if using Python 3,
+the ``pyvenv`` or ``python -m venv`` commands.
- * http://pypi.python.org/pypi/virtualenv
-
-The purpose of such Python virtual environments is to allow one to create
+The purpose of a Python virtual environments is to allow one to create
multiple distinct Python environments for the same version of Python, but
-with different sets of Python modules and packages installed into the
-Python 'site-packages' directory.
+with different sets of Python modules and packages installed. It is
+recommended that you always use Python virtual environments and not install
+additional Python packages direct into your Python installation.
-A virtual Python environment is useful where it is necessary to run
+A Python virtual environment is also required where it is necessary to run
multiple WSGI applications which have conflicting requirements as to what
version of a Python module or package needs to be installed. They can also
-be used where Apache and daemon mode of mod_wsgi is used to host WSGI
-applications for different users and each user wants to be able to
+be used when distinct mod_wsgi daemon process groups are used to host WSGI
+applications for different users and each user needs to be able to
separately install their own Python modules and packages.
-Note that aspects of the configuration described here will not work if
-mod_python is being loaded into Apache at the same time as mod_wsgi. This
-is because mod_python will in that case be responsible for initialising the
-Python interpreter, thereby overriding what mod_wsgi is trying to do. For
-best results, you should therefore use only mod_wsgi and not try and use
-mod_python on the same server at the same time.
+How you configure mod_wsgi or setup your WSGI application script file for a
+Python virtual environment will depend on your specific requirements. The
+more common scenarios are explained below.
+
+Location of the Virtual Environment
+-----------------------------------
+
+Whichever method you use to create a Python virtual environment, before you
+use it with mod_wsgi, you should validate what the location of the Python
+virtual environment is. If using `virtualenvwrapper`_ this may be a non
+obvious directory hidden away under your home directory.
+
+The way to determine the location of the Python virtual environment is to
+activate the Python virtual environment from an interactive shell so it is
+being used, and then run the command::
+
+ python -c 'import sys; print(sys.prefix)'
+
+This will output the directory path you will use when setting up mod_wsgi
+to use the Python virtual environment. For the purposes of the examples
+below, it is assumed the location of any Python virtual environments are
+under the ``/usr/local/venvs`` directory. A specific Python virtual
+environment may thus return for ``sys.prefix``::
+
+ /usr/local/venvs/example
+
+Note that this should be the root directory of the Python virtual
+environment, which in turn contains the ``bin`` and ``lib`` directories for
+the Python virtual environment. It is a common mistake when setting up a
+Python virtual environment with mod_wsgi to use the full path to the
+``python`` executable instead of the root directory. That will not work, so
+do not use the path for the ``python`` executable as the location of the
+Python virtual environment, it has to be the root directory.
+
+Do be aware that the user that Apache runs your code as will need to be
+able to access the Python virtual environment. On some Linux distributions,
+the home directory of a user account is not accessible to other users.
+Rather than change the permissions on your home directory, it might be
+better to consider locating your WSGI application code and any Python
+virtual environment outside of your home directory.
+
+Virtual Environment and Python Version
+--------------------------------------
+
+When using a Python virtual environment with mod_wsgi, it is very important
+that it has been created using the same Python installation that mod_wsgi
+was originally compiled for. It is not possible to use a Python virtual
+environment to force mod_wsgi to use a different Python version, or even a
+different Python installation.
+
+You cannot for example force mod_wsgi to use a Python virtual environment
+created using Python 3.5 when mod_wsgi was originally compiled for Python
+2.7. This is because the Python library for the Python installation it was
+originally compiled against is linked directly into the mod_wsgi module.
+In other words, Python is embedded within mod_wsgi. When mod_wsgi is used
+it does not run the command line ``python`` program to run the interpreter
+and thus why you can't force it to use a different Python installation.
+
+The problem in trying to force mod_wsgi to use a different Python
+installation than what it was compiled for, even where it is the same
+Python version, is that the Python installation may itself not have been
+compiled with the same options. This is especially a problem when it comes
+to issues around how Python stores Unicode characters in memory.
+
+The end result is that if you want to use a different Python installation
+or version than what mod_wsgi was originally compiled for, you would need
+to re-install mod_wsgi such that it is compiled for the Python installation
+or version you do want to use. Do not try and use a Python virtual
+environment from one Python installation or version with mod_wsgi, when
+mod_wsgi was compiled for a different one.
+
+Daemon Mode (Single Application)
+--------------------------------
+
+The preferred way of setting up mod_wsgi is to run each WSGI application
+in its own daemon process group. This is called daemon mode. A typical
+configuration for running a WSGI application in daemon mode would be::
+
+ WSGIDaemonProcess myapp
+
+ WSGIProcessGroup myapp
+ WSGIApplicationGroup %{GLOBAL}
+
+ WSGIScriptAlias / /some/path/project/myapp.wsgi
+
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
+
+The ``WSGIDaemonProcess`` directive defines the daemon process group. The
+``WSGIProcessGroup`` directive indicates that the WSGI application should be
+run within the defined daemon process group.
+
+As only the single application is being run within the daemon process
+group, the ``WSGIApplicationGroup`` directive is also being used. When this
+is used with the ``%{GLOBAL}`` value, it forces the WSGI application to run
+in the main Python interpreter context of each process. This is preferred
+in this scenario as some third party packages for Python which include C
+extensions will not run in the Python sub interpreter contexts which
+mod_wsgi would use by default. By using the main Python interpreter context
+you eliminate the possibility of such third party packages for Python
+causing problems.
+
+To modify the configuration for this scenario to use a Python virtual
+environment, all you need to do is add the ``python-home`` option to the
+``WSGIDaemonProcess`` directive resulting in::
+
+ WSGIDaemonProcess myapp python-home=/usr/local/venvs/myapp
+
+All the additonal Python packages and modules would then be installed into
+that Python virtual environment.
+
+Daemon Mode (Multiple Applications)
+-----------------------------------
-Baseline Environment
---------------------
+If instead of running each WSGI application in a separate daemon process
+group as is the recommended practice, you are running multiple WSGI
+applications in one daemon process group, a different approach to using
+Python virtual environments is required.
-The first step in using virtual environments with mod_wsgi is to point
-mod_wsgi at a baseline Python environment. This step is actually optional
-and if not done the main Python installation for the system, usually that
-which mod_wsgi was compiled for, would be used as the baseline environment.
+For this scenario there are various ways the configuration could be set
+up. If mounting each WSGI application explicitly you might be using::
-Although the main Python installation can be used, especially in a shared
-environment where daemon mode of mod_wsgi is used to host WSGI applications
-for different users, it is better to make the baseline environment a virgin
-environment with an effectively empty 'site-packages' directory. This way
-there is no possibility of conflicts between modules and packages in a users
-individual Python virtual environment and the baseline environment.
+ WSGIDaemonProcess myapps
-To create a virgin environment using the 'virtualenv' program, the
-'--no-site-packages' option should be supplied when creating the environment::
+ WSGIProcessGroup myapps
- $ cd /usr/local/pythonenv
+ WSGIScriptAlias /myapp3 /some/path/project/myapp3.wsgi
+ WSGIScriptAlias /myapp2 /some/path/project/myapp2.wsgi
- $ virtualenv --no-site-packages BASELINE
- New python executable in BASELINE/bin/python
- Installing setuptools............done.
+ WSGIScriptAlias / /some/path/project/myapp1.wsgi
-Note that the version of Python from which this baseline environment is
-created must be the same version of Python that mod_wsgi was compiled for.
-It is not possible to mix environments based on different major/minor
-versions of Python.
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
-Once the baseline Python environment has been created, the WSGIPythonHome
-directive should be defined within the global part of the main Apache
-configuration files. The directive should refer to the top level directory
-for the baseline environment created by the 'virtualenv' script::
+If instead the directory containing the WSGI application script files is
+being mounted, you might be using::
- WSGIPythonHome /usr/local/pythonenv/BASELINE
+ WSGIDaemonProcess myapps
-This Python environment will now be used as the baseline environment for
-all WSGI applications running under mod_wsgi, whether they be run in
-embedded mode or daemon mode.
+ WSGIProcessGroup myapps
-There is no need to set the WSGIPythonHome directive if you want to use
-the main Python installation as the baseline environment.
+ WSGIScriptAlias / /some/path/project/
-Application Environments
-------------------------
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
-If for a specific WSGI application you have created a dedicated virtual
-environment, then this environment can now be overlayed on top of the
-baseline environment. If the baseline environment was a virgin environment,
-this virtual environment should also be initially created as a virgin
-environment.
+The use of the ``WSGIDaemonProcess`` and ``WSGIProcessGroup`` is the same as
+before, however the ``WSGIApplicationGroup`` directive is not being used.
-For example, to create a virtual environment dedicated to developing Pylons
-applications the following would be used::
+When the ``WSGIApplicationGroup`` directive isn't being used to override
+which Python interpreter context is being used, each WSGI application will
+be run in its own Python sub interpreter context of the processes. This is
+necessary as often WSGI application frameworks (Django being a prime
+example), do not support running more than one instance of a WSGI
+application using the framework, in the same Python interpreter context at
+the same time.
- $ virtualenv --no-site-packages PYLONS-1
- New python executable in
- PYLONS-1/bin/python
- Installing setuptools............done.
+In this scenario of running multiple WSGI applications in the same daemon
+process group, more than one change is possibly required. The changes
+required depend on whether or not all WSGI applications should share the
+same Python virtual environment.
- $ source PYLONS-1/bin/activate
+If all of the WSGI applications should share the same Python virtual
+environment, then the same change as was performed above for the single
+application case would be made. That is, add the ``python-home`` option
+to the ``WSGIDaemonProcess`` directive::
- (PYLONS-1)$ easy_install Pylons
- Searching for Pylons
- .......
+ WSGIDaemonProcess myapp python-home=/usr/local/venvs/myapps
-The Pylons instructions for creating a Pylons application would then be
-followed and the application tested using the Pylons inbuilt web server.
+All the additonal Python packages and modules that any of the WSGI
+applications required would then be installed into that Python virtual
+environment. Because it is a shared environment, they must all use the same
+version of any specific Python package or module.
-As an additional step however, the WSGI script file described in the
-instructions would be modified to overlay the virtual environment for the
-application on top of the baseline environment. This would be done by
-adding at the very start of the WSGI script file the following::
+If instead of all WSGI applications using the same Python virtual
+environment each needed their own, then a change will instead need to be
+made in each of the WSGI script files for the applications.
+How this is done will depend on how the Python virtual environment is
+created.
+
+If the Python virtual environment is created using `virtualenv`_ or
+`virtualenvwrapper`_, the WSGI script for each application should be
+modified to include code of the following form::
+
+ python_home = '/usr/local/envs/myapp1'
+
+ activate_this = python_home + '/bin/activate_this.py'
+ execfile(activate_this, dict(__file__=activate_this))
+
+Because each WSGI application is to use a separate Python virtual
+environment, the value of the ``python_home`` variable would be set
+differently for each WSGI script file, with it referring to the root
+directory of the respective Python virtual environments.
+
+This code should be placed in the WSGI script file before any other module
+imports in the WSGI script file, with the exception of ``from __future__``
+imports used to enable Python feature flags.
+
+Important to note is that when the Python virtual environment is activated
+from within the WSGI script, what happens is a bit different to when the
+``python-home`` option to ``WSGIDaemonProcess`` is used.
+
+When activating the Python virtual environment from within the WSGI script
+file, only the ``site-packages`` directory from the Python virtual
+environment is being used. This directory will be added to the Python
+module search path, along with any additional directories related to the
+``site-packages`` directory registered using ``.pth`` files present in the
+``site-packages`` directory. This will be placed at the start of the
+existing ``sys.path``.
+
+The consequence of this is that the Python virtual environment isn't
+completely overriding the original Python installation the Python virtual
+environment was created from. This means that if the main Python
+installation had additional Python packages installed they will also
+potentially be visible to the WSGI application.
+
+That this occurs could cause confusion as you might for example think you
+had all the packages you require listed in your ``requirements.txt`` file
+for ``pip``, but didn't and so a package may not have been installed. If
+that package was installed in the main Python installation, it would be
+picked up from there, but it might be the wrong version and have
+dependencies on versions of other packages for which you have different
+versions installed in your Python virtual environment and which are found
+instead of those in the main Python installation.
+
+To avoid such problems, when activating the Python virtual environment
+from within the WSGI script file, it is necessary to still set the
+``python-home`` option of the ``WSGIDaemonProcess`` directive, but set it to
+an empty Python virtual environment which has had no additional packages
+installed::
+
+ WSGIDaemonProcess myapp python-home=/usr/local/venvs/empty
+
+By doing this, the main Python installation will not be consulted and
+instead it will fallback to the empty Python virtual environment. This
+Python virtual environment should remain empty and you should not install
+additional Python packages or modules into it, or you will cause the same
+sort of conflicts that can arise with the main Python installation when it
+was being used.
+
+When needing to activate the Python virtual environment from within the
+WSGI script file as described, it is preferred that you be using the either
+`virtualenv`_ or `virtualenvwrapper`_ to create the Python virtual
+environment. This is because they both provide the ``activate_this.py``
+script file which does all the work of setting up ``sys.path``. When you
+use either ``pyvenv`` or ``python -m venv`` with Python 3, no such
+activation script is provided.
+
+So use `virtualenv`_ or `virtualenvwrapper`_ if you can. If you cannot for
+some reason and are stuck with ``pyvenv`` or ``python -m venv``, you can
+instead use the following code in the WSGI script file::
+
+ python_home = '/usr/local/envs/myapp1'
+
+ import sys
import site
- site.addsitedir('/usr/local/pythonenv/PYLONS-1/lib/python2.5/site-packages')
-
-Note that in this case the full path to the 'site-packages' directory for
-the virtual environment needs to be specified and not just the root of
-the virtual environment.
-
-Using 'site.addsitedir()' is a bit different to simply adding the directory
-to 'sys.path' as the function will open up any '.pth' files located in the
-directory and process them. This is necessary to ensure that any special
-directories related to Python eggs are automatically added to 'sys.path'.
-
-Note that although virtualenv includes the script 'activate_this.py', which
-the virtualenv documentation claims should be invoked using 'execfile()' in
-the context of mod_wsgi, you may want to be cautious using it. This is
-because the script modifies 'sys.prefix' which may actually cause problems
-with the operation of mod_wsgi or Python modules already loaded into the
-Python interpreter, if the code is dependent on the value of 'sys.prefix'
-not changing. The WSGIPythonHome directive already described should instead
-be used if wanting to associate Python as a whole with the virtual
-environment.
-
-Despite that, the 'activate_this.py' script is an attempt to resolve an
-issue with how 'site.addsitedir()' works. That is that any new directories
-which are added to 'sys.path' by 'site.addsitedir()' are actually appended
-to the end. The problem with this in the context of mod_wsgi is that if
-WSGIPythonHome was not used to associate mod_wsgi with a virgin baseline
-environment, then any packages/modules in the main Python installation will
-still take precedence over those in the virtual environment.
-
-To work around this problem, what 'activate_this.py' does is invoke
-'site.addsitedir()' but then also reorders 'sys.path' so any newly added
-directories are shifted to the front of 'sys.path'. This will then ensure
-that where there are different versions of packages in the virtual environment
-that they take precedence over those in the main Python installation.
-
-As explained, because 'activate_this.py' is doing other things which may
-not be appropriate in the context of mod_wsgi, if unable to set WSGIPythonHome
-to point mod_wsgi at a virgin baseline environment, instead of just calling
-'site.addsitedir()' you should use the code::
-
- ALLDIRS = ['usr/local/pythonenv/PYLONS-1/lib/python2.5/site-packages']
+
+ # Calculate path to site-packages directory.
+
+ python_version = '.'.join(map(str, sys.version_info[:2]))
+ site_packages = python_home + '/lib/python%s/site-packages' % python_version
+
+ # Add the site-packages directory.
+
+ site.addsitedir(site_packages)
+
+As before this code should be placed in the WSGI script file before any
+other module imports in the WSGI script file, with the exception of ``from
+__future__`` imports used to enable Python feature flags.
+
+When using this method, do be aware that the additions to the Python module
+search path are made at the end of ``sys.path``. For that reason, you must
+set the ``python-home`` option to ``WSGIDaemonProcess`` to the location of
+an empty Python virtual environment. If you do not do this, any additional
+Python package installed in the main Python installation will hide those in
+the Python virtual environment for the application.
+
+There is extra code you could add which would reorder ``sys.path`` to make
+it work in an equivalent way to the ``activate_this.py`` script provided
+when you use `virtualenv`_ or `virtualenvwrapper`_ but it is messy and more
+trouble than it is worth::
+
+ python_home = '/usr/local/envs/myapp1'
import sys
import site
+ # Calculate path to site-packages directory.
+
+ python_version = '.'.join(map(str, sys.version_info[:2]))
+ site_packages = python_home + '/lib/python%s/site-packages' % python_version
+ site.addsitedir(site_packages)
+
# Remember original sys.path.
+
prev_sys_path = list(sys.path)
- # Add each new site-packages directory.
- for directory in ALLDIRS:
- site.addsitedir(directory)
+ # Add the site-packages directory.
+
+ site.addsitedir(site_packages)
# Reorder sys.path so new directories at the front.
+
new_sys_path = []
+
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
+
sys.path[:0] = new_sys_path
-If you still want to use the activation script from virtualenv, then use::
+It is better to avoid needing to manually activate the Python virtual
+environment from inside of a WSGI script by using a separate daemon process
+group per WSGI application. At the minimum, at least avoid ``pyvenv`` and
+``python -m venv``.
- activate_this = '/usr/local/pythonenv/PYLONS-1/bin/activate_this.py'
- execfile(activate_this, dict(__file__=activate_this))
+Embedded Mode (Single Application)
+----------------------------------
+
+The situation for running a single WSGI application in embedded mode is not
+much different to running a single WSGI application in daemon mode. In the
+case of embedded mode, there is though no ``WSGIDaemonProcess`` directive.
+
+The typical configuration when running a single WSGI application in
+embedded module might be::
+
+ WSGIScriptAlias / /some/path/project/myapp.wsgi
+
+ WSGIApplicationGroup %{GLOBAL}
+
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
+
+The ``WSGIDaemonProcess`` and ``WSGIProcessGroup`` directives are gone, but
+the ``WSGIApplicationGroup`` directive is still used to force the WSGI
+application to run in the main Python interpreter context of each of the
+Apache worker processes. This is to avoid those issues with some third
+party packages for Python with C extensions as mentioned before.
+
+In this scenario, to set the location of the Python virtual environment
+to be used, the ``WSGIPythonHome`` directive is used::
+
+ WSGIPythonHome /usr/local/envs/myapp
+
+Note that if the WSGI application is being setup within the context of an
+Apache ``VirtualHost``, the ``WSGIPythonHome`` cannot be placed inside of
+the ``VirtualHost``. Instead it must be placed outside of all
+``VirtualHost`` definitions. This is because it applies to the whole Apache
+instance and not just the single ``VirtualHost``.
+
+Embedded Mode (Multiple Applications)
+-------------------------------------
+
+Running multiple applications in embedded mode is also similar to when
+running multiple WSGI applications in one daemon process group. You still
+need to ensure each WSGI application runs in its own Python sub interpreter
+context to avoid potential issues with Python web frameworks that don't
+allow more than one WSGI application to be using it at the same time in a
+Python interpreter context.
+
+If mounting each WSGI application explicitly you might be using::
+
+ WSGIScriptAlias /myapp3 /some/path/project/myapp3.wsgi
+ WSGIScriptAlias /myapp2 /some/path/project/myapp2.wsgi
+
+ WSGIScriptAlias / /some/path/project/myapp1.wsgi
+
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
+
+If instead the directory containing the WSGI application script files is
+being mounted, you might be using::
+
+ WSGIScriptAlias / /some/path/project/
+
+ <Directory /some/path/project>
+ Require all granted
+ </Directory>
+
+In this scenario, to set the location of the Python virtual environment
+to be used by all WSGI application, the ``WSGIPythonHome`` directive is used::
+
+ WSGIPythonHome /usr/local/envs/myapps
+
+If the WSGI application is being setup within the context of an Apache
+``VirtualHost``, the ``WSGIPythonHome`` cannot be placed inside of the
+``VirtualHost``. Instead it must be placed outside of all ``VirtualHost``
+definitions. This is because it applies to the whole Apache instance and
+not just the single ``VirtualHost``.
+
+If each WSGI application needs its own Python virtual environment, then
+activation of the Python virtual environment needs to be performed in the
+WSGI script itself as explained previously for the case of daemon mode
+being used. The ``WSGIPythonHome`` directive should be used to refer to an
+empty Python virtual environment if needed to ensure that any additional
+Python packages in the main Python installation don't interfere with what
+packages are installed in the Python virtual environment for each WSGI
+application.
+
+Adding Additional Module Directories
+------------------------------------
+
+The ``python-home`` option to ``WSGIDaemonProcess`` and the
+``WSGIPythonHome`` directive are the preferred way of specifying the
+location of the Python virtual environment to be used. If necessary,
+activation of the Python virtual environment can also be performed from the
+WSGI script file itself.
+
+If you need to add additional directories to search for Python packages or
+modules this can also be done. You may want to do this where you need to
+specify where the actual WSGI application is located, where a WSGI script
+file needs to import application specific modules.
+
+If you are using daemon mode and want to add additional directories to the
+Python module search path, you can use the ``python-path`` option to
+``WSGIDaemonProcess``::
+
+ WSGIDaemonProcess myapp python-path=/some/path/project
+
+This option would be in addition to the ``python-home`` option used to
+specify where the Python virtual environment is located.
+
+If you are using embedded mode, you can use the ``WSGIPythonPath``
+directive::
+
+ WSGIPythonPath /some/path/project
+
+This directive is in addition to the ``WSGIPythonHome`` directive used to
+specify where the Python virtual environment is located.
+
+In either case, if you need to specify more than one directory, they can be
+separated using a ':' character.
+
+If you are having to activate the Python virtual enviromment from within a
+WSGI script and need to add additional directories to the Python module
+search path, you should modify ``sys.path`` directly from the WSGI script
+file.
+
+Note that prior practice was that these ways of setting the Python module
+search path were used to specify the location of the Python virtual
+environment. Specifically, they were used to add the ``site-packages``
+directory of the Python virtual environment. You should not do that.
+
+The better way to specify the location of the Python virtual environment is
+using the ``python-home`` option of the ``WSGIDaemonProcess`` directive for
+daemon mode, or the ``WSGIPythonHome`` directive for embedded mode. These
+ways of specifying the Python virtual environment have been available since
+mod_wsgi 3.0 and Linux distributions have not shipped such an old version
+of mod_wsgi for quite some time. If you are using the older way, please
+update your configurations.
-If the fact that 'sys.prefix' has been modified doesn't give an issue, then
-great. If you see subtle unexplained problems that may be linked to the
-change to 'sys.prefix', then use the more long handed approach above whereby
-'site.addsitedir()' is used directly and 'sys.path' reorderd subsequently.
-
-Process Environments
---------------------
-
-When 'site.addsitedir()' is used from a WSGI script file to overlay a
-virtual environment on top of the baseline environment, it is only applied
-to the specific Python interpreter instance that the application has been
-delegated to run in. This means that WSGI applications running in the same
-process but within different Python interpreter instances can use different
-virtual environments.
-
-At the same time though, if needing all WSGI applications running in the
-same process but within different Python interpreters, to use the same
-virtual environment, you would need to setup 'sys.path' in the WSGI script
-file for all applications.
-
-Alternatively, if using mod_wsgi 2.0 and embedded mode, the WSGIPythonPath
-directive can be used to setup the virtual environment for all Python
-interpreters created within the process in one step::
-
- WSGIPythonPath /usr/local/pythonenv/PYLONS-1/lib/python2.5/site-packages
-
-Similarly, if using mod_wsgi 2.0 or later and daemon mode, the
-'python-path' option to the WSGIDaemonProcess directive can be used to
-setup the virtual environment::
-
- WSGIDaemonProcess pylons \
- python-path=/usr/local/pythonenv/PYLONS-1/lib/python2.5/site-packages
-
-Note that WSGIPythonPath does not have this effect for mod_wsgi prior to
-version 2.0. This is because in older versions WSGIPythonPath merely added
-any listed directories to 'sys.path', whereas in mod_wsgi 2.0 and later it
-calls 'site.addsitedir()' for each listed directory.
-
-Do note though that all mod_wsgi 2.X versions prior to mod_wsgi 2.4 do not
-perform the reordering of 'sys.path' as explained previously, when using
-WSGIPythonPath directive or 'python-path' option for WSGIDaemonProcess.
-Thus, you would need to be using WSGIPythonHome to reference a virgin
-baseline environment when using mod_wsgi 2.3 or earlier if the standard
-Python site-packages directory has conflicting packages. For mod_wsgi 2.4
-onwards this is not an issue and a virtual environments site-packages will
-always override that in standard Python installation.
+.. _virtualenv: http://pypi.python.org/pypi/virtualenv
+.. _virtualenvwrapper: https://pypi.python.org/pypi/virtualenvwrapper
diff --git a/setup.py b/setup.py
index 76d23cb..15d790b 100644
--- a/setup.py
+++ b/setup.py
@@ -131,14 +131,40 @@ if APXS is None:
elif not os.path.isabs(APXS):
APXS = find_program([APXS], APXS, ['/usr/sbin', os.getcwd()])
+WITHOUT_APXS = False
+WITH_WINDOWS_APACHE = None
+
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)
+ WITHOUT_APXS = True
+
+if WITHOUT_APXS and os.name == 'nt':
+ APACHE_ROOTDIR = os.environ.get('MOD_WSGI_APACHE_ROOTDIR', 'c:\\Apache24')
+ if os.path.exists(APACHE_ROOTDIR):
+ WITH_WINDOWS_APACHE = APACHE_ROOTDIR
+
+if WITHOUT_APXS and not WITH_WINDOWS_APACHE:
+ 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_WINDOWS_APACHE:
+ def get_apxs_config(name):
+ if name == 'INCLUDEDIR':
+ return WITH_WINDOWS_APACHE + '/include'
+ elif name == 'LIBEXECDIR':
+ return WITH_WINDOWS_APACHE + '/lib'
+ else:
+ return ''
+
+ def get_apr_includes():
+ return ''
+
+ def get_apu_includes():
+ return ''
-if WITH_TARBALL_PACKAGE:
+elif 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')
@@ -187,10 +213,10 @@ if WITH_TARBALL_PACKAGE:
sub_value = expand_vars(value)
return sub_value.replace('/mod_wsgi-packages/', SCRIPT_DIR+'/')
- def get_apr_includes(query):
+ def get_apr_includes():
return ''
- def get_apu_includes(query):
+ def get_apu_includes():
return ''
CONFIG['PREFIX'] = get_apxs_config('prefix')
@@ -244,8 +270,19 @@ EXTRA_CFLAGS = get_apxs_config('EXTRA_CFLAGS').split()
APR_CONFIG = get_apxs_config('APR_CONFIG')
APU_CONFIG = get_apxs_config('APU_CONFIG')
-APR_INCLUDES = get_apr_includes().split()
-APU_INCLUDES = get_apu_includes().split()
+# Make sure that 'apr-1-config' exists. If it doesn't we may be running
+# on MacOS X Sierra, which has decided to not provide either it or the
+# 'apu-1-config' script and otherwise completely broken 'apxs'. In that
+# case we manually set the locations of the Apache and APR header files.
+
+if (not os.path.exists(APR_CONFIG) and
+ os.path.exists('/Developer/SDKs/MacOSX10.6.sdk')):
+ INCLUDEDIR = '/Developer/SDKs/MacOSX10.6.sdk/usr/include/apache2'
+ APR_INCLUDES = ['-I/Developer/SDKs/MacOSX10.6.sdk/usr/include/apr-1']
+ APU_INCLUDES = []
+else:
+ APR_INCLUDES = get_apr_includes().split()
+ APU_INCLUDES = get_apu_includes().split()
# Write out apxs_config.py which caches various configuration related to
# Apache. For the case of using our own Apache build, this needs to
@@ -323,24 +360,39 @@ with open(os.path.join(os.path.dirname(__file__),
# Work out location of Python library and how to link it.
PYTHON_VERSION = get_python_config('VERSION')
-PYTHON_LDVERSION = get_python_config('LDVERSION') or PYTHON_VERSION
-PYTHON_LIBDIR = get_python_config('LIBDIR')
-PYTHON_CFGDIR = get_python_lib(plat_specific=1, standard_lib=1) + '/config'
+if os.name == 'nt':
+ if hasattr(sys, 'real_prefix'):
+ PYTHON_LIBDIR = sys.real_prefix
+ else:
+ PYTHON_LIBDIR = get_python_config('BINDIR')
-if PYTHON_LDVERSION and PYTHON_LDVERSION != PYTHON_VERSION:
- PYTHON_CFGDIR = '%s-%s' % (PYTHON_CFGDIR, PYTHON_LDVERSION)
+ PYTHON_LDFLAGS = []
+ PYTHON_LDLIBS = ['%s/libs/python%s.lib' % (PYTHON_LIBDIR, PYTHON_VERSION),
+ '%s/lib/libhttpd.lib' % WITH_WINDOWS_APACHE,
+ '%s/lib/libapr-1.lib' % WITH_WINDOWS_APACHE,
+ '%s/lib/libaprutil-1.lib' % WITH_WINDOWS_APACHE,
+ '%s/lib/libapriconv-1.lib' % WITH_WINDOWS_APACHE]
-PYTHON_LDFLAGS = ['-L%s' % PYTHON_LIBDIR, '-L%s' % PYTHON_CFGDIR]
-PYTHON_LDLIBS = ['-lpython%s' % PYTHON_LDVERSION]
+else:
+ PYTHON_LDVERSION = get_python_config('LDVERSION') or PYTHON_VERSION
-if os.path.exists(os.path.join(PYTHON_LIBDIR,
- 'libpython%s.a' % PYTHON_VERSION)):
- PYTHON_LDLIBS = ['-lpython%s' % PYTHON_VERSION]
+ PYTHON_LIBDIR = get_python_config('LIBDIR')
+ PYTHON_CFGDIR = get_python_lib(plat_specific=1, standard_lib=1) + '/config'
-if os.path.exists(os.path.join(PYTHON_CFGDIR,
- 'libpython%s.a' % PYTHON_VERSION)):
- PYTHON_LDLIBS = ['-lpython%s' % PYTHON_VERSION]
+ if PYTHON_LDVERSION and PYTHON_LDVERSION != PYTHON_VERSION:
+ PYTHON_CFGDIR = '%s-%s' % (PYTHON_CFGDIR, PYTHON_LDVERSION)
+
+ PYTHON_LDFLAGS = ['-L%s' % PYTHON_LIBDIR, '-L%s' % PYTHON_CFGDIR]
+ PYTHON_LDLIBS = ['-lpython%s' % PYTHON_LDVERSION]
+
+ if os.path.exists(os.path.join(PYTHON_LIBDIR,
+ 'libpython%s.a' % PYTHON_VERSION)):
+ PYTHON_LDLIBS = ['-lpython%s' % PYTHON_VERSION]
+
+ if os.path.exists(os.path.join(PYTHON_CFGDIR,
+ 'libpython%s.a' % PYTHON_VERSION)):
+ PYTHON_LDLIBS = ['-lpython%s' % PYTHON_VERSION]
# Create the final set of compilation flags to be used.
@@ -351,11 +403,12 @@ EXTRA_LINK_ARGS = PYTHON_LDFLAGS + PYTHON_LDLIBS
# Force adding of LD_RUN_PATH for platforms that may need it.
-LD_RUN_PATH = os.environ.get('LD_RUN_PATH', '')
-LD_RUN_PATH += ':%s:%s' % (PYTHON_LIBDIR, PYTHON_CFGDIR)
-LD_RUN_PATH = LD_RUN_PATH.lstrip(':')
+if os.name != 'nt':
+ LD_RUN_PATH = os.environ.get('LD_RUN_PATH', '')
+ LD_RUN_PATH += ':%s:%s' % (PYTHON_LIBDIR, PYTHON_CFGDIR)
+ LD_RUN_PATH = LD_RUN_PATH.lstrip(':')
-os.environ['LD_RUN_PATH'] = LD_RUN_PATH
+ os.environ['LD_RUN_PATH'] = LD_RUN_PATH
# On MacOS X, recent versions of Apple's Apache do not support compiling
# Apache modules with a target older than 10.8. This is because it
@@ -378,7 +431,10 @@ if sys.platform == 'darwin':
# Now add the definitions to build everything.
-extension_name = 'mod_wsgi.server.mod_wsgi-py%s%s' % sys.version_info[:2]
+if os.name == 'nt':
+ extension_name = 'mod_wsgi.server.mod_wsgi'
+else:
+ extension_name = 'mod_wsgi.server.mod_wsgi-py%s%s' % sys.version_info[:2]
extension = Extension(extension_name, source_files,
include_dirs=INCLUDE_DIRS, extra_compile_args=EXTRA_COMPILE_FLAGS,
@@ -417,25 +473,29 @@ 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)
+if os.name != 'nt':
+ if (not get_python_config('Py_ENABLE_SHARED') and
+ not get_python_config('PYTHONFRAMEWORK')):
+ print(SHARED_LIBRARY_WARNING)
# Now finally run distutils.
+long_description = open('README.rst').read()
+
setup(name = 'mod_wsgi',
version = _version(),
description = 'Installer for Apache/mod_wsgi.',
+ long_description = long_description,
author = 'Graham Dumpleton',
author_email = 'Graham.Dumpleton@gmail.com',
maintainer = 'Graham Dumpleton',
maintainer_email = 'Graham.Dumpleton@gmail.com',
url = 'http://www.modwsgi.org/',
- #bugtrack_url = 'https://github.com/GrahamDumpleton/mod_wsgi/issues',
+ bugtrack_url = 'https://github.com/GrahamDumpleton/mod_wsgi/issues',
license = 'Apache License, Version 2.0',
platforms = [],
download_url = None,
- classifiers= [
+ classifiers = [
'Development Status :: 6 - Mature',
'License :: OSI Approved :: Apache Software License',
'Operating System :: MacOS :: MacOS X',
@@ -449,9 +509,11 @@ setup(name = 'mod_wsgi',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
'Topic :: Internet :: WWW/HTTP :: WSGI',
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server'
],
+ keywords = 'mod_wsgi wsgi apache',
packages = ['mod_wsgi', 'mod_wsgi.server', 'mod_wsgi.server.management',
'mod_wsgi.server.management.commands', 'mod_wsgi.docs',
'mod_wsgi.images'],
diff --git a/src/server/__init__.py b/src/server/__init__.py
index 65ab8f9..83f99ed 100644
--- a/src/server/__init__.py
+++ b/src/server/__init__.py
@@ -10,8 +10,6 @@ import signal
import threading
import atexit
import imp
-import pwd
-import grp
import re
import pprint
import time
@@ -32,6 +30,7 @@ _py_soext = '.so'
try:
import imp
import sysconfig
+ import distutils.sysconfig
_py_soabi = sysconfig.get_config_var('SOABI')
_py_soext = sysconfig.get_config_var('SO')
@@ -46,24 +45,37 @@ if not os.path.exists(MOD_WSGI_SO) and _py_soabi:
MOD_WSGI_SO = 'mod_wsgi-py%s.%s%s' % (_py_version, _py_soabi, _py_soext)
MOD_WSGI_SO = os.path.join(os.path.dirname(__file__), MOD_WSGI_SO)
+if not os.path.exists(MOD_WSGI_SO) and os.name == 'nt':
+ MOD_WSGI_SO = 'mod_wsgi%s' % distutils.sysconfig.get_config_var('EXT_SUFFIX')
+ MOD_WSGI_SO = os.path.join(os.path.dirname(__file__), MOD_WSGI_SO)
+
def where():
return MOD_WSGI_SO
def default_run_user():
+ if os.name == 'nt':
+ return '#0'
+
try:
+ import pwd
uid = os.getuid()
return pwd.getpwuid(uid).pw_name
except KeyError:
return '#%d' % uid
def default_run_group():
+ if os.name == 'nt':
+ return '#0'
+
try:
+ import pwd
uid = os.getuid()
entry = pwd.getpwuid(uid)
except KeyError:
return '#%d' % uid
try:
+ import grp
gid = entry.pw_gid
return grp.getgrgid(gid).gr_name
except KeyError:
@@ -758,6 +770,12 @@ WSGIImportScript '%(server_root)s/handler.wsgi' \\
</IfDefine>
"""
+APACHE_IGNORE_ACTIVITY_CONFIG = """
+<Location '%(url)s'>
+WSGIIgnoreActivity On
+</Location>
+"""
+
APACHE_PROXY_PASS_MOUNT_POINT_CONFIG = """
ProxyPass '%(mount_point)s' '%(url)s'
ProxyPassReverse '%(mount_point)s' '%(url)s'
@@ -947,6 +965,10 @@ def generate_apache_config(options):
with open(options['httpd_conf'], 'w') as fp:
print(APACHE_GENERAL_CONFIG % options, file=fp)
+ if options['ignore_activity']:
+ for url in options['ignore_activity']:
+ print(APACHE_IGNORE_ACTIVITY_CONFIG % dict(url=url), file=fp)
+
if options['proxy_mount_points']:
for mount_point, url in options['proxy_mount_points']:
if mount_point.endswith('/'):
@@ -1698,7 +1720,7 @@ def generate_server_metrics_script(options):
print(SERVER_METRICS_SCRIPT % options, file=fp)
WSGI_CONTROL_SCRIPT = """
-#!/usr/bin/env bash
+#!/bin/sh
# %(sys_argv)s
@@ -2026,6 +2048,12 @@ option_list = (
'to pass before the worker process is shutdown and restarted '
'when the worker process has entered an idle state and is no '
'longer receiving new requests. Not enabled by default.'),
+ optparse.make_option('--ignore-activity', action='append',
+ dest='ignore_activity', metavar='URL-PATH', help='Specify '
+ 'the URL path for any location where activity should be '
+ 'ignored when the \'--activity-timeout\' option is used. '
+ 'This would be used on health check URLs so that health '
+ 'checks do not prevent process restarts due to inactivity.'),
optparse.make_option('--request-timeout', type='int', default=60,
metavar='SECONDS', help='Maximum number of seconds allowed '
@@ -2741,6 +2769,12 @@ def _cmd_setup_server(command, args, options):
try:
os.mkdir(options['python_eggs'])
+ if os.getuid() == 0:
+ import pwd
+ import grp
+ os.chown(options['python_eggs'],
+ pwd.getpwnam(options['user']).pw_uid,
+ grp.getgrnam(options['group']).gr_gid)
except Exception:
pass
@@ -3249,6 +3283,46 @@ def cmd_start_server(params):
executable = os.path.join(config['server_root'], 'apachectl')
os.execl(executable, executable, 'start', '-DFOREGROUND')
+def cmd_module_config(params):
+ formatter = optparse.IndentedHelpFormatter()
+ formatter.set_long_opt_delimiter(' ')
+
+ usage = '%prog module-config'
+ parser = optparse.OptionParser(usage=usage, formatter=formatter)
+
+ (options, args) = parser.parse_args(params)
+
+ if len(args) != 0:
+ parser.error('Incorrect number of arguments.')
+
+ if os.name == 'nt':
+ real_prefix = getattr(sys, 'real_prefix', None)
+ real_prefix = real_prefix or sys.prefix
+ library_name = 'python%s.dll' % sysconfig.get_config_var('VERSION')
+ library_path = os.path.join(real_prefix, library_name)
+ library_path = os.path.normpath(library_path)
+ library_path = library_path.replace('\\', '/')
+
+ module_path = where()
+ module_path = module_path.replace('\\', '/')
+
+ prefix = sys.prefix
+ prefix = os.path.normpath(prefix)
+ prefix = prefix.replace('\\', '/')
+
+ print('LoadFile "%s"' % library_path)
+ print('LoadModule wsgi_module "%s"' % module_path)
+ print('WSGIPythonHome "%s"' % prefix)
+
+ else:
+ module_path = where()
+
+ prefix = sys.prefix
+ prefix = os.path.normpath(prefix)
+
+ print('LoadModule wsgi_module "%s"' % module_path)
+ print('WSGIPythonHome "%s"' % prefix)
+
def cmd_install_module(params):
formatter = optparse.IndentedHelpFormatter()
formatter.set_long_opt_delimiter(' ')
@@ -3269,8 +3343,8 @@ def cmd_install_module(params):
shutil.copyfile(where(), target)
- print('LoadModule wsgi_module %s' % target)
- print('WSGIPythonHome %s' % os.path.normpath(sys.prefix))
+ print('LoadModule wsgi_module "%s"' % target)
+ print('WSGIPythonHome "%s"' % os.path.normpath(sys.prefix))
def cmd_module_location(params):
formatter = optparse.IndentedHelpFormatter()
@@ -3286,11 +3360,21 @@ def cmd_module_location(params):
print(where())
-main_usage="""
-%prog command [params]
+if os.name == 'nt':
+ main_usage="""
+ %prog command [params]
+
+Commands:
+ module-config
+ module-location
+"""
+else:
+ main_usage="""
+ %prog command [params]
Commands:
install-module
+ module-config
module-location
setup-server
start-server
@@ -3308,16 +3392,26 @@ def main():
args = [os.path.expandvars(arg) for arg in args]
- if command == 'install-module':
- cmd_install_module(args)
- elif command == 'module-location':
- cmd_module_location(args)
- elif command == 'setup-server':
- cmd_setup_server(args)
- elif command == 'start-server':
- cmd_start_server(args)
+ if os.name == 'nt':
+ if command == 'module-config':
+ cmd_module_config(args)
+ elif command == 'module-location':
+ cmd_module_location(args)
+ else:
+ parser.error('Invalid command was specified.')
else:
- parser.error('Invalid command was specified.')
+ if command == 'install-module':
+ cmd_install_module(args)
+ elif command == 'module-config':
+ cmd_module_config(args)
+ elif command == 'module-location':
+ cmd_module_location(args)
+ elif command == 'setup-server':
+ cmd_setup_server(args)
+ elif command == 'start-server':
+ cmd_start_server(args)
+ else:
+ parser.error('Invalid command was specified.')
def start(*args):
cmd_start_server(list(args))
diff --git a/src/server/mod_wsgi.c b/src/server/mod_wsgi.c
index 057f3a8..f49ac2c 100644
--- a/src/server/mod_wsgi.c
+++ b/src/server/mod_wsgi.c
@@ -187,6 +187,11 @@ static void *wsgi_merge_server_config(apr_pool_t *p, void *base_conf,
else
config->map_head_to_get = parent->map_head_to_get;
+ if (child->ignore_activity != -1)
+ config->ignore_activity = child->ignore_activity;
+ else
+ config->ignore_activity = parent->ignore_activity;
+
if (child->trusted_proxy_headers)
config->trusted_proxy_headers = child->trusted_proxy_headers;
else
@@ -231,6 +236,7 @@ typedef struct {
int error_override;
int chunked_request;
int map_head_to_get;
+ int ignore_activity;
apr_array_header_t *trusted_proxy_headers;
apr_array_header_t *trusted_proxies;
@@ -266,6 +272,7 @@ static WSGIDirectoryConfig *newWSGIDirectoryConfig(apr_pool_t *p)
object->error_override = -1;
object->chunked_request = -1;
object->map_head_to_get = -1;
+ object->ignore_activity = -1;
object->trusted_proxy_headers = NULL;
object->trusted_proxies = NULL;
@@ -357,6 +364,11 @@ static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
else
config->map_head_to_get = parent->map_head_to_get;
+ if (child->ignore_activity != -1)
+ config->ignore_activity = child->ignore_activity;
+ else
+ config->ignore_activity = parent->ignore_activity;
+
if (child->trusted_proxy_headers)
config->trusted_proxy_headers = child->trusted_proxy_headers;
else
@@ -426,6 +438,7 @@ typedef struct {
int error_override;
int chunked_request;
int map_head_to_get;
+ int ignore_activity;
apr_array_header_t *trusted_proxy_headers;
apr_array_header_t *trusted_proxies;
@@ -869,6 +882,14 @@ static WSGIRequestConfig *wsgi_create_req_config(apr_pool_t *p, request_rec *r)
config->map_head_to_get = 2;
}
+ config->ignore_activity = dconfig->ignore_activity;
+
+ if (config->ignore_activity < 0) {
+ config->ignore_activity = sconfig->ignore_activity;
+ if (config->ignore_activity < 0)
+ config->ignore_activity = 0;
+ }
+
config->trusted_proxy_headers = dconfig->trusted_proxy_headers;
if (!config->trusted_proxy_headers)
@@ -957,11 +978,12 @@ typedef struct {
apr_off_t bytes;
apr_off_t reads;
apr_time_t time;
+ int ignore_activity;
} InputObject;
static PyTypeObject Input_Type;
-static InputObject *newInputObject(request_rec *r)
+static InputObject *newInputObject(request_rec *r, int ignore_activity)
{
InputObject *self;
@@ -987,6 +1009,8 @@ static InputObject *newInputObject(request_rec *r)
self->reads = 0;
self->time = 0;
+ self->ignore_activity = ignore_activity;
+
return self;
}
@@ -1227,7 +1251,7 @@ static PyObject *Input_read(InputObject *self, PyObject *args)
return NULL;
#if defined(MOD_WSGI_WITH_DAEMONS)
- if (wsgi_idle_timeout) {
+ if (wsgi_idle_timeout && !self->ignore_activity) {
apr_thread_mutex_lock(wsgi_monitor_lock);
if (wsgi_idle_timeout) {
@@ -1985,7 +2009,7 @@ static AdapterObject *newAdapterObject(request_rec *r)
self->output_time = 0;
- self->input = newInputObject(r);
+ self->input = newInputObject(r, self->config->ignore_activity);
self->log_buffer = newLogBufferObject(r, APLOG_ERR, "wsgi.errors", 0);
self->log = newLogWrapperObject(self->log_buffer);
@@ -2112,7 +2136,7 @@ static int Adapter_output(AdapterObject *self, const char *data,
apr_time_t output_finish = 0;
#if defined(MOD_WSGI_WITH_DAEMONS)
- if (wsgi_idle_timeout) {
+ if (wsgi_idle_timeout && !self->config->ignore_activity) {
apr_thread_mutex_lock(wsgi_monitor_lock);
if (wsgi_idle_timeout) {
@@ -2444,6 +2468,8 @@ static int Adapter_output_file(AdapterObject *self, apr_file_t* tmpfile,
apr_status_t rv;
apr_bucket_brigade *bb;
+ apr_file_t* dupfile = NULL;
+
r = self->r;
if (r->connection->aborted) {
@@ -2457,17 +2483,27 @@ static int Adapter_output_file(AdapterObject *self, apr_file_t* tmpfile,
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ apr_file_dup(&dupfile, tmpfile, r->pool);
+
if (sizeof(apr_off_t) == sizeof(apr_size_t) || len < MAX_BUCKET_SIZE) {
/* Can use a single bucket to send file. */
+#if 0
b = apr_bucket_file_create(tmpfile, offset, (apr_size_t)len, r->pool,
r->connection->bucket_alloc);
+#endif
+ b = apr_bucket_file_create(dupfile, offset, (apr_size_t)len, r->pool,
+ r->connection->bucket_alloc);
}
else {
/* Need to create multiple buckets to send file. */
+#if 0
b = apr_bucket_file_create(tmpfile, offset, MAX_BUCKET_SIZE, r->pool,
r->connection->bucket_alloc);
+#endif
+ b = apr_bucket_file_create(dupfile, offset, MAX_BUCKET_SIZE, r->pool,
+ r->connection->bucket_alloc);
while (len > MAX_BUCKET_SIZE) {
apr_bucket *cb;
@@ -2945,7 +2981,7 @@ static int Adapter_run(AdapterObject *self, PyObject *object)
WSGIThreadCPUUsage end_usage;
#if defined(MOD_WSGI_WITH_DAEMONS)
- if (wsgi_idle_timeout) {
+ if (wsgi_idle_timeout && !self->config->ignore_activity) {
apr_thread_mutex_lock(wsgi_monitor_lock);
if (wsgi_idle_timeout) {
@@ -3780,6 +3816,7 @@ static int wsgi_execute_script(request_rec *r)
/* Setup startup timeout if first request and specified. */
+#if defined(MOD_WSGI_WITH_DAEMONS)
if (wsgi_daemon_process) {
if (wsgi_startup_shutdown_time == 0) {
if (wsgi_startup_timeout > 0) {
@@ -3790,6 +3827,7 @@ static int wsgi_execute_script(request_rec *r)
}
}
}
+#endif
/*
* Use a lock around the check to see if the module is
@@ -4063,7 +4101,9 @@ static int wsgi_execute_script(request_rec *r)
/* Clear startup timeout and prevent from running again. */
+#if defined(MOD_WSGI_WITH_DAEMONS)
wsgi_startup_shutdown_time = -1;
+#endif
}
else {
Py_BEGIN_ALLOW_THREADS
@@ -5305,6 +5345,36 @@ static const char *wsgi_set_map_head_to_get(cmd_parms *cmd, void *mconfig,
return NULL;
}
+static const char *wsgi_set_ignore_activity(cmd_parms *cmd, void *mconfig,
+ const char *f)
+{
+ if (cmd->path) {
+ WSGIDirectoryConfig *dconfig = NULL;
+ dconfig = (WSGIDirectoryConfig *)mconfig;
+
+ if (strcasecmp(f, "Off") == 0)
+ dconfig->ignore_activity = 0;
+ else if (strcasecmp(f, "On") == 0)
+ dconfig->ignore_activity = 1;
+ else
+ return "WSGIIgnoreActivity must be one of: Off | On";
+ }
+ else {
+ WSGIServerConfig *sconfig = NULL;
+ sconfig = ap_get_module_config(cmd->server->module_config,
+ &wsgi_module);
+
+ if (strcasecmp(f, "Off") == 0)
+ sconfig->ignore_activity = 0;
+ else if (strcasecmp(f, "On") == 0)
+ sconfig->ignore_activity = 1;
+ else
+ return "WSGIIgnoreActivity must be one of: Off | On";
+ }
+
+ return NULL;
+}
+
static char *wsgi_http2env(apr_pool_t *a, const char *w);
static const char *wsgi_set_trusted_proxy_headers(cmd_parms *cmd,
@@ -6081,6 +6151,8 @@ static void wsgi_build_environment(request_rec *r)
apr_table_setn(r->subprocess_env, "mod_wsgi.enable_sendfile",
apr_psprintf(r->pool, "%d", config->enable_sendfile));
+ apr_table_setn(r->subprocess_env, "mod_wsgi.ignore_activity",
+ apr_psprintf(r->pool, "%d", config->ignore_activity));
apr_table_setn(r->subprocess_env, "mod_wsgi.request_start",
apr_psprintf(r->pool, "%" APR_TIME_T_FMT, r->request_time));
@@ -12507,6 +12579,13 @@ static int wsgi_hook_daemon_handler(conn_rec *c)
else
config->enable_sendfile = 0;
+ item = apr_table_get(r->subprocess_env, "mod_wsgi.ignore_activity");
+
+ if (item && !strcasecmp(item, "1"))
+ config->ignore_activity = 1;
+ else
+ config->ignore_activity = 0;
+
config->daemon_connects = atoi(apr_table_get(r->subprocess_env,
"mod_wsgi.daemon_connects"));
config->daemon_restarts = atoi(apr_table_get(r->subprocess_env,
@@ -15592,6 +15671,8 @@ static const command_rec wsgi_commands[] =
NULL, OR_FILEINFO, "Enable/Disable support for chunked requests."),
AP_INIT_TAKE1("WSGIMapHEADToGET", wsgi_set_map_head_to_get,
NULL, OR_FILEINFO, "Enable/Disable mapping of HEAD to GET."),
+ AP_INIT_TAKE1("WSGIIgnoreActivity", wsgi_set_ignore_activity,
+ NULL, OR_FILEINFO, "Enable/Disable reset of inactvity timeout."),
AP_INIT_RAW_ARGS("WSGITrustedProxyHeaders", wsgi_set_trusted_proxy_headers,
NULL, OR_FILEINFO, "Specify a list of trusted proxy headers."),
@@ -15644,4 +15725,19 @@ module AP_MODULE_DECLARE_DATA wsgi_module = {
/* ------------------------------------------------------------------------- */
+#if defined(_WIN32)
+#if PY_MAJOR_VERSION < 3
+PyMODINIT_FUNC initmod_wsgi(void)
+{
+}
+#else
+PyMODINIT_FUNC PyInit_mod_wsgi(void)
+{
+ return NULL;
+}
+#endif
+#endif
+
+/* ------------------------------------------------------------------------- */
+
/* vi: set sw=4 expandtab : */
diff --git a/src/server/wsgi_apache.h b/src/server/wsgi_apache.h
index befed49..bcb6fed 100644
--- a/src/server/wsgi_apache.h
+++ b/src/server/wsgi_apache.h
@@ -35,6 +35,10 @@
#define CORE_PRIVATE 1
+#if defined(_WIN32)
+#include <ws2tcpip.h>
+#endif
+
#include "httpd.h"
#if !defined(HTTPD_ROOT)
diff --git a/src/server/wsgi_memory.c b/src/server/wsgi_memory.c
index 89e3ceb..28dad6e 100644
--- a/src/server/wsgi_memory.c
+++ b/src/server/wsgi_memory.c
@@ -6,6 +6,7 @@
*/
#if defined(_WIN32)
+#include <winsock2.h>
#include <windows.h>
#define PSAPI_VERSION 1
#include <psapi.h>
@@ -96,13 +97,21 @@ static size_t getCurrentRSS(void)
#elif defined(__APPLE__) && defined(__MACH__)
/* OSX ------------------------------------------------------ */
+#if defined(MACH_TASK_BASIC_INFO)
struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,
(task_info_t)&info, &infoCount ) != KERN_SUCCESS )
return (size_t)0L; /* Can't access? */
return (size_t)info.resident_size;
-
+#else
+ struct task_basic_info info;
+ mach_msg_type_number_t infoCount = TASK_BASIC_INFO_COUNT;
+ if ( task_info( mach_task_self( ), TASK_BASIC_INFO,
+ (task_info_t)&info, &infoCount ) != KERN_SUCCESS )
+ return (size_t)0L; /* Can't access? */
+ return (size_t)info.resident_size;
+#endif
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
/* Linux ---------------------------------------------------- */
long rss = 0L;
@@ -123,5 +132,7 @@ static size_t getCurrentRSS(void)
#endif
}
+#include "wsgi_memory.h"
+
size_t wsgi_get_peak_memory_RSS(void) { return getPeakRSS(); }
size_t wsgi_get_current_memory_RSS(void) { return getCurrentRSS(); }
diff --git a/src/server/wsgi_server.c b/src/server/wsgi_server.c
index e891d5e..2699b77 100644
--- a/src/server/wsgi_server.c
+++ b/src/server/wsgi_server.c
@@ -123,6 +123,7 @@ WSGIServerConfig *newWSGIServerConfig(apr_pool_t *p)
object->script_reloading = -1;
object->error_override = -1;
object->chunked_request = -1;
+ object->ignore_activity = -1;
object->enable_sendfile = -1;
diff --git a/src/server/wsgi_server.h b/src/server/wsgi_server.h
index 62d8861..464bd61 100644
--- a/src/server/wsgi_server.h
+++ b/src/server/wsgi_server.h
@@ -107,6 +107,7 @@ typedef struct {
int error_override;
int chunked_request;
int map_head_to_get;
+ int ignore_activity;
apr_array_header_t *trusted_proxy_headers;
apr_array_header_t *trusted_proxies;
diff --git a/src/server/wsgi_version.h b/src/server/wsgi_version.h
index 5d4b9e5..98d876d 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 5
-#define MOD_WSGI_MICROVERSION_NUMBER 7
-#define MOD_WSGI_VERSION_STRING "4.5.7"
+#define MOD_WSGI_MICROVERSION_NUMBER 8
+#define MOD_WSGI_VERSION_STRING "4.5.8"
/* ------------------------------------------------------------------------- */
diff --git a/tests/environ.wsgi b/tests/environ.wsgi
index 3c010f3..5be59cb 100644
--- a/tests/environ.wsgi
+++ b/tests/environ.wsgi
@@ -30,11 +30,12 @@ def application(environ, start_response):
input = environ['wsgi.input']
output = StringIO()
- print('PID: %s' % os.getpid(), file=output)
- print('UID: %s' % os.getuid(), file=output)
- print('GID: %s' % os.getgid(), file=output)
- print('CWD: %s' % os.getcwd(), file=output)
- print(file=output)
+ if os.name != 'nt':
+ print('PID: %s' % os.getpid(), file=output)
+ print('UID: %s' % os.getuid(), file=output)
+ print('GID: %s' % os.getgid(), file=output)
+ print('CWD: %s' % os.getcwd(), file=output)
+ print(file=output)
print('python.version: %r' % (sys.version,), file=output)
print('python.prefix: %r' % (sys.prefix,), file=output)