diff options
| author | ianb <devnull@localhost> | 2005-05-14 05:50:19 +0000 |
|---|---|---|
| committer | ianb <devnull@localhost> | 2005-05-14 05:50:19 +0000 |
| commit | 9b267d3e23b5935b8be4fcea139186f570f6f23c (patch) | |
| tree | 46d874cdacad6d5b5653cfa8098d8752d8705476 /paste/servers | |
| parent | 426fea4d2bfa614e7247459ac80818b3c57de523 (diff) | |
| download | paste-9b267d3e23b5935b8be4fcea139186f570f6f23c.tar.gz | |
Starting work on making servers pluggable (have to commit as I rearrange files)
Diffstat (limited to 'paste/servers')
| -rw-r--r-- | paste/servers/__init__.py | 1 | ||||
| -rw-r--r-- | paste/servers/cgi/__init__.py | 1 | ||||
| -rw-r--r-- | paste/servers/cgi/cgiserver.py | 113 | ||||
| -rw-r--r-- | paste/servers/scgi/__init__.py | 1 | ||||
| -rw-r--r-- | paste/servers/scgi/scgiserver.py | 148 | ||||
| -rw-r--r-- | paste/servers/wsgiutils.py | 17 |
6 files changed, 281 insertions, 0 deletions
diff --git a/paste/servers/__init__.py b/paste/servers/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/servers/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/servers/cgi/__init__.py b/paste/servers/cgi/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/servers/cgi/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/servers/cgi/cgiserver.py b/paste/servers/cgi/cgiserver.py new file mode 100644 index 0000000..83d257c --- /dev/null +++ b/paste/servers/cgi/cgiserver.py @@ -0,0 +1,113 @@ +""" +cgi WSGI server +=============== + +Usage +----- + +The CGI script is the configuration and glue for this server. +Typically you will write a CGI script like:: + + #!/usr/bin/env python + from paste.cgiserver import run_with_cgi + # Assuming app is your WSGI application object... + from myapplication import app + run_with_cgi(app) + +""" + +import os, sys + +def run_with_cgi(application, + use_cgitb=True, + redirect_stdout=False): + stdout = sys.stdout + + if use_cgitb: + import cgitb + cgitb.enable() + + environ = dict(os.environ) + environ['wsgi.input'] = sys.stdin + environ['wsgi.errors'] = sys.stderr + environ['wsgi.version'] = (1, 0) + environ['wsgi.multithread'] = False + environ['wsgi.multiprocess'] = True + environ['wsgi.run_once'] = True + + if os.environ.get('HTTPS', 'off').lower() in ('on', '1'): + environ['wsgi.url_scheme'] = 'https' + else: + environ['wsgi.url_scheme'] = 'http' + + if redirect_stdout: + sys.stdout = sys.stderr + + headers_set = [] + headers_sent = [] + result = None + + def write(data): + assert headers_set, "write() before start_response()" + + if not headers_sent: + # Before the first output, send the stored headers + status, response_headers = headers_sent[:] = headers_set + + # See if Content-Length is given. + found = False + for name, value in response_headers: + if name.lower() == 'content-length': + found = True + break + + # If not given, try to deduce it if the iterator implements + # __len__ and is of length 1. (data will be result[0] in this + # case.) + if not found and result is not None: + try: + if len(result) == 1: + response_headers.append(('Content-Length', + str(len(data)))) + except: + pass + + stdout.write('Status: %s\r\n' % status) + for header in response_headers: + stdout.write('%s: %s\r\n' % header) + stdout.write('\r\n') + + stdout.write(data) + stdout.flush() + + 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 + else: + assert not headers_set, "Headers already set!" + + headers_set[:] = [status, response_headers] + return write + + result = application(environ, start_response) + try: + for data in result: + if data: # don't send headers until body appears + write(data) + if not headers_sent: + write('') # send headers now if body was empty + finally: + if hasattr(result, 'close'): + result.close() + +if __name__ == '__main__': + def myapp(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/plain')]) + return ['Hello World!\n'] + + run_with_cgi(myapp) diff --git a/paste/servers/scgi/__init__.py b/paste/servers/scgi/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/servers/scgi/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/servers/scgi/scgiserver.py b/paste/servers/scgi/scgiserver.py new file mode 100644 index 0000000..15fbef2 --- /dev/null +++ b/paste/servers/scgi/scgiserver.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +""" +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 +import os +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, "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):] + + 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): + class SCGIAppHandler(SWAP): + def __init__ (self, *args, **kwargs): + self.prefix = prefix + self.app_obj = application + SWAP.__init__(self, *args, **kwargs) + + scgi_server.SCGIServer(SCGIAppHandler, port=port).serve() diff --git a/paste/servers/wsgiutils.py b/paste/servers/wsgiutils.py new file mode 100644 index 0000000..2c63854 --- /dev/null +++ b/paste/servers/wsgiutils.py @@ -0,0 +1,17 @@ +def serve(conf, app): + thirdparty.add_package('wsgiutils') + from wsgiutils import wsgiServer + server = wsgiServer.WSGIServer( + (conf.get('host', 'localhost'), + int(conf.get('port', 8080))), {'': app}) + server.serve_forever() + +description = """\ +WSGIUtils is a small threaded server using Python's standard +SimpleHTTPServer. +""" + +options = { + 'port': 'Port to serve on (default: 8080)', + 'host': 'Host to serve from (default: localhost, which is only accessible from the local computer; use 0.0.0.0 to make your application public)', + } |
