|
|
Integration With Karrigell
Note: This will most likely not work with Karrigell 3.0.
Note: This is not intended as a basic tutorial on how to setup mod_wsgi. It is recommended you first read more introductory material for mod_wsgi. Start by reading through various documents linked off Installation Instructions.
The design of the Karrigell framework presents a number of problems in relation to using it in conjunction with Apache. These problems stem from Karrigell not accessing resources by absolute pathnames, and instead relying on being able to change the current working directory for each request. And due to Karrigell overriding sys.stdout for each request so that the Python 'print' statement can be used as a means of generating a response.
In short, Karrigell is not safe to use in a multithreaded web server. It would therefore not generally be possible to run Karrigell using traditional solutions for hosting Python applications with Apache, such as mod_python, when using the Apache worker MPM on UNIX systems or on Windows. The only Apache configuration for which it would be safe to use Karrigell in conjunction with mod_python or embedded mode of mod_wsgi would be the prefork MPM on UNIX systems.
Even then, it would be recommended that the Karrigell application be the only application hosted by that Apache server. This is because outside of the actual bounds of the request handler, the Karrigell infrastructure also relies on the current working directory being the location of the Karrigell installation. If another application were also being hosted, it could change the working directory and subsequently cause havoc with the Karrigell application.
When using mod_wsgi, all these problems are avoided by delegating each Karrigell application instance to its own set of daemon processes, where each daemon process only runs one thread. In effect, this acts like the Apache prefork MPM, but where distinct daemon processes managed by mod_wsgi are used to run the application, rather than it being run within the Apache child processes.
Because mod_wsgi manages these daemon processes and controls the number of threads used in each, it is even possible to run with this configuration when the main Apache web server is using the worker MPM rather than the prefork MPM.
An appropriate Apache configuration for mod_wsgi to allow Karrigell to be run would thus be:
WSGIDaemonProcess karrigell processes=5 threads=1 WSGIScriptAlias / /usr/local/karrigell/Karrigell-2.3.5/apache/modwsgi.py <Directory /usr/local/karrigell/Karrigell-2.3.5/apache> WSGIProcessGroup karrigell Order deny,allow Allow from all </Directory>
Note though that when debugging a Karrigell application, you should run with only one daemon process. This is because debugging features can rely on subsequent requests being handled by the same process.
If you need to run the Karrigell application as a user different to that which Apache would normally run Apache child processes as, use the 'user' and 'group' options to the WSGIDaemonProcess directive.
Also note though that as the 'daemon' mode of mod_wsgi is being used to make running Karrigell safe, it can only be used on Apache 2.X on UNIX systems. It is not possible to use the WSGI adapter for Karrigell on Windows.
The final problem to be solved is that Karrigell doesn't provide support for the WSGI interface. To remove this limitation, code for a WSGI adapter for Karrigell is supplied below. This code should be placed in the file 'apache/modwsgi.py' within the Karrigell installation. The file should be placed in its own directory in this way so that no other source code is potentially exposed by the Apache web server.
import os
import sys
here = os.path.dirname(__file__)
root = os.path.normpath(os.path.join(here, '..'))
os.chdir(root)
if not root in sys.path:
sys.path.append(root)
import cgi
import CGIHTTPServer
import KarrigellRequestHandler
class RequestHandler(KarrigellRequestHandler.KarrigellRequestHandler,
CGIHTTPServer.CGIHTTPRequestHandler):
def __init__(self, environ, start_response):
self.server = "wsgi"
self.server_version = environ["SERVER_SOFTWARE"]
self.sys_version = sys.version
self.request = environ['wsgi.input']
self.client_address = (environ["REMOTE_ADDR"],
int(environ["REMOTE_PORT"]))
self.headers = {}
for k in environ:
if k.startswith("HTTP_"):
header = k[5:].replace('_', '-')
self.headers[header] = environ[k]
self.path = environ["REQUEST_URI"]
self.request_version = environ["SERVER_PROTOCOL"]
self.protocol_version = environ["SERVER_PROTOCOL"]
self.command = environ["REQUEST_METHOD"]
self.requestline = "%s %s %s" %(environ["REQUEST_METHOD"],
environ["REQUEST_URI"],environ["SERVER_PROTOCOL"])
if self.command == "POST":
self.body = cgi.FieldStorage(fp=environ['wsgi.input'],
environ=environ, keep_blank_values=1)
self.__status = 0
self.__message = None
self.__headers = {}
self.__output = None
self.__start = start_response
class output: pass
self.wfile = output()
self.wfile.write = self.write_content
def send_response(self, code, message=None):
self.__status = code
if message is None:
if code in self.responses:
message = self.responses[code][0]
else:
message = str(code)
self.__message = message
def send_header(self, key, value):
self.__headers[key] = str(value)
def end_headers(self):
self.write_content(None)
def write_content(self, data):
if not self.__output:
status_line = "%d %s" % (self.__status, self.__message)
self.__output = self.__start(status_line, self.__headers.items())
if data:
self.__output(data)
def flush_response(self):
self.write_content(None)
warning = """
CONFIGURATION ERROR
Note: Karrigell is not safe to run in a multithread server process.
It is also not recommended that Karrigell be run within the actual Apache
child processes, as the fact that Karrigell keeps changing the current
working directory could interfere with other hosted Apache applications.
Correspondingly, other Apache applications could interfere with Karrigell
if they also change the current working directory. The safest way to run
Karrigell is with mod_wsgi in 'daemon' mode. This can be enabled using
the additional configuration:
WSGIDaemonProcess karrigell processes=5 threads=1
WSGIProcessGroup karrigell
The WSGIDaemonProcess directive should appear at global scope within the
Apache configuration, and the WSGIProcessGroup directive with the virtual
host or directory container pertaining to the Karrigell application.
"""
def application(environ, start_response):
if environ['wsgi.multithread'] or not environ['mod_wsgi.process_group']:
headers = [
('Content-Length', str(len(warning))),
('Content-Type', 'text/plain'),
]
start_response('500 Internal Server Error', headers)
return [warning]
handler = RequestHandler(environ, start_response)
handler.handle_data()
handler.flush_response()
return []
