summaryrefslogtreecommitdiff
path: root/paste/servers
diff options
context:
space:
mode:
authorianb <devnull@localhost>2005-05-14 05:50:19 +0000
committerianb <devnull@localhost>2005-05-14 05:50:19 +0000
commit9b267d3e23b5935b8be4fcea139186f570f6f23c (patch)
tree46d874cdacad6d5b5653cfa8098d8752d8705476 /paste/servers
parent426fea4d2bfa614e7247459ac80818b3c57de523 (diff)
downloadpaste-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__.py1
-rw-r--r--paste/servers/cgi/__init__.py1
-rw-r--r--paste/servers/cgi/cgiserver.py113
-rw-r--r--paste/servers/scgi/__init__.py1
-rw-r--r--paste/servers/scgi/scgiserver.py148
-rw-r--r--paste/servers/wsgiutils.py17
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)',
+ }