Imported from SVN by Bitbucket
This commit is contained in:
171
Paste-1.7.5.1-py2.6.egg/paste/util/scgiserver.py
Executable file
171
Paste-1.7.5.1-py2.6.egg/paste/util/scgiserver.py
Executable file
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
SCGI-->WSGI application proxy, "SWAP".
|
||||
|
||||
(Originally written by Titus Brown.)
|
||||
|
||||
This lets an SCGI front-end like mod_scgi be used to execute WSGI
|
||||
application objects. To use it, subclass the SWAP class like so::
|
||||
|
||||
class TestAppHandler(swap.SWAP):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.prefix = '/canal'
|
||||
self.app_obj = TestAppClass
|
||||
swap.SWAP.__init__(self, *args, **kwargs)
|
||||
|
||||
where 'TestAppClass' is the application object from WSGI and '/canal'
|
||||
is the prefix for what is served by the SCGI Web-server-side process.
|
||||
|
||||
Then execute the SCGI handler "as usual" by doing something like this::
|
||||
|
||||
scgi_server.SCGIServer(TestAppHandler, port=4000).serve()
|
||||
|
||||
and point mod_scgi (or whatever your SCGI front end is) at port 4000.
|
||||
|
||||
Kudos to the WSGI folk for writing a nice PEP & the Quixote folk for
|
||||
writing a nice extensible SCGI server for Python!
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from scgi import scgi_server
|
||||
|
||||
def debug(msg):
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time()))
|
||||
sys.stderr.write("[%s] %s\n" % (timestamp, msg))
|
||||
|
||||
class SWAP(scgi_server.SCGIHandler):
|
||||
"""
|
||||
SCGI->WSGI application proxy: let an SCGI server execute WSGI
|
||||
application objects.
|
||||
"""
|
||||
app_obj = None
|
||||
prefix = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
assert self.app_obj, "must set app_obj"
|
||||
assert self.prefix is not None, "must set prefix"
|
||||
args = (self,) + args
|
||||
scgi_server.SCGIHandler.__init__(*args, **kwargs)
|
||||
|
||||
def handle_connection(self, conn):
|
||||
"""
|
||||
Handle an individual connection.
|
||||
"""
|
||||
input = conn.makefile("r")
|
||||
output = conn.makefile("w")
|
||||
|
||||
environ = self.read_env(input)
|
||||
environ['wsgi.input'] = input
|
||||
environ['wsgi.errors'] = sys.stderr
|
||||
environ['wsgi.version'] = (1, 0)
|
||||
environ['wsgi.multithread'] = False
|
||||
environ['wsgi.multiprocess'] = True
|
||||
environ['wsgi.run_once'] = False
|
||||
|
||||
# dunno how SCGI does HTTPS signalling; can't test it myself... @CTB
|
||||
if environ.get('HTTPS','off') in ('on','1'):
|
||||
environ['wsgi.url_scheme'] = 'https'
|
||||
else:
|
||||
environ['wsgi.url_scheme'] = 'http'
|
||||
|
||||
## SCGI does some weird environ manglement. We need to set
|
||||
## SCRIPT_NAME from 'prefix' and then set PATH_INFO from
|
||||
## REQUEST_URI.
|
||||
|
||||
prefix = self.prefix
|
||||
path = environ['REQUEST_URI'][len(prefix):].split('?', 1)[0]
|
||||
|
||||
environ['SCRIPT_NAME'] = prefix
|
||||
environ['PATH_INFO'] = path
|
||||
|
||||
headers_set = []
|
||||
headers_sent = []
|
||||
chunks = []
|
||||
def write(data):
|
||||
chunks.append(data)
|
||||
|
||||
def start_response(status, response_headers, exc_info=None):
|
||||
if exc_info:
|
||||
try:
|
||||
if headers_sent:
|
||||
# Re-raise original exception if headers sent
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
finally:
|
||||
exc_info = None # avoid dangling circular ref
|
||||
elif headers_set:
|
||||
raise AssertionError("Headers already set!")
|
||||
|
||||
headers_set[:] = [status, response_headers]
|
||||
return write
|
||||
|
||||
###
|
||||
|
||||
result = self.app_obj(environ, start_response)
|
||||
try:
|
||||
for data in result:
|
||||
chunks.append(data)
|
||||
|
||||
# Before the first output, send the stored headers
|
||||
if not headers_set:
|
||||
# Error -- the app never called start_response
|
||||
status = '500 Server Error'
|
||||
response_headers = [('Content-type', 'text/html')]
|
||||
chunks = ["XXX start_response never called"]
|
||||
else:
|
||||
status, response_headers = headers_sent[:] = headers_set
|
||||
|
||||
output.write('Status: %s\r\n' % status)
|
||||
for header in response_headers:
|
||||
output.write('%s: %s\r\n' % header)
|
||||
output.write('\r\n')
|
||||
|
||||
for data in chunks:
|
||||
output.write(data)
|
||||
finally:
|
||||
if hasattr(result,'close'):
|
||||
result.close()
|
||||
|
||||
# SCGI backends use connection closing to signal 'fini'.
|
||||
try:
|
||||
input.close()
|
||||
output.close()
|
||||
conn.close()
|
||||
except IOError, err:
|
||||
debug("IOError while closing connection ignored: %s" % err)
|
||||
|
||||
|
||||
def serve_application(application, prefix, port=None, host=None, max_children=None):
|
||||
"""
|
||||
Serve the specified WSGI application via SCGI proxy.
|
||||
|
||||
``application``
|
||||
The WSGI application to serve.
|
||||
|
||||
``prefix``
|
||||
The prefix for what is served by the SCGI Web-server-side process.
|
||||
|
||||
``port``
|
||||
Optional port to bind the SCGI proxy to. Defaults to SCGIServer's
|
||||
default port value.
|
||||
|
||||
``host``
|
||||
Optional host to bind the SCGI proxy to. Defaults to SCGIServer's
|
||||
default host value.
|
||||
|
||||
``host``
|
||||
Optional maximum number of child processes the SCGIServer will
|
||||
spawn. Defaults to SCGIServer's default max_children value.
|
||||
"""
|
||||
class SCGIAppHandler(SWAP):
|
||||
def __init__ (self, *args, **kwargs):
|
||||
self.prefix = prefix
|
||||
self.app_obj = application
|
||||
SWAP.__init__(self, *args, **kwargs)
|
||||
|
||||
kwargs = dict(handler_class=SCGIAppHandler)
|
||||
for kwarg in ('host', 'port', 'max_children'):
|
||||
if locals()[kwarg] is not None:
|
||||
kwargs[kwarg] = locals()[kwarg]
|
||||
|
||||
scgi_server.SCGIServer(**kwargs).serve()
|
||||
Reference in New Issue
Block a user