From: W. Trevor King Date: Mon, 9 Dec 2013 05:21:21 +0000 (-0800) Subject: gallery.py: Convert '--scgi' to '--mode=scgi' and add a 'wsgi' mode X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=cd6398bd449d9d2d3381c2af450255c0f57b571d;p=blog.git gallery.py: Convert '--scgi' to '--mode=scgi' and add a 'wsgi' mode This is useful for serving a one-off gallery when you don't want to bother setting up a real frontend for the CGI servers. We use the ProcessingComplete exception to pass out the header OrderedDict for start_response. --- diff --git a/posts/gallery/gallery.py b/posts/gallery/gallery.py index ac62c36..110f383 100755 --- a/posts/gallery/gallery.py +++ b/posts/gallery/gallery.py @@ -15,8 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -""" -CGI gallery server for a picture directory organized along:: +"""Gallery server for a picture directory organized along:: pics |-- some_directory @@ -36,10 +35,12 @@ With:: Note that you can store a caption for ```` as plain text in ``.txt``. -See RFC 3875 for more details on the the Common Gateway Interface. +See RFC 3875 for more details on the the Common Gateway Interface (CGI). + +Besides the CGI interface, this script can also be run as: -This script can also be run as a Simple Common Gateway Interface -(SCGI) with the ``--scgi`` option. +* a Simple Common Gateway Interface (SCGI) with the ``--mode=scgi`` option +* a stand-alone server with the ``--mode=wsgi`` option """ import collections as _collections @@ -105,7 +106,8 @@ class HTTPError(Exception): class ProcessingComplete(Exception): - pass + def __init__(self, headers=None): + self.headers = headers def invoke(args, stdin=None, stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, @@ -153,11 +155,13 @@ class CGIGalleryServer (object): def __init__(self, base_path='.', base_url='/', cache_path='/tmp/gallery-cache/', - serve_originals=True): + serve_originals=True, + write_http_headers=True): self._base_path = _os_path.abspath(base_path) self._base_url = base_url self._cache_path = cache_path self._serve_originals = serve_originals + self._write_http_headers = write_http_headers self._text_charset = 'UTF-8' self._url_regexp = _re.compile('^[a-zA-Z0-9._/-]*$') self._rows = 3 @@ -197,7 +201,7 @@ class CGIGalleryServer (object): if charset: content = content.encode(charset) stream.write(content) - raise ProcessingComplete() + raise ProcessingComplete(headers=headers) def _response_stream(self, headers=None, content=None, stream=None, chunk_size=1024): @@ -216,7 +220,7 @@ class CGIGalleryServer (object): if not chunk: break stream.write(chunk) - raise ProcessingComplete() + raise ProcessingComplete(headers=headers) def _error(self, status=404, content=None, stream=None): headers = self._http_headers(status=status) @@ -264,8 +268,8 @@ class CGIGalleryServer (object): except HTTPError as e: LOG.error(e.message) self._error(e.status, content=e.content, stream=stream) - except ProcessingComplete: - pass + except ProcessingComplete as e: + return e def page_from_query(self, query=None, query_string=None): """Extract the requested page from a query string @@ -776,6 +780,39 @@ def serve_scgi(server, host='localhost', port=4000): LOG.info('serving SCGI on {}:{}'.format(host, port)) s.serve() +def serve_wsgi(server, host='localhost', port=4000): + import io + import wsgiref.simple_server + + server._write_http_headers = False + + def app(environ, start_response): + url = environ.get('PATH_INFO', None) + page = server.page_from_query( + query_string=environ.get('QUERY_STRING', '')) + status = '200 OK' + headers = {} + stream = io.BytesIO() + try: + try: + url = server.relative_url(url=url) + except HTTPError as e: + LOG.error(e.message) + server._error(e.status, content=e.content, stream=stream) + except ProcessingComplete as e: + headers = e.headers + else: + e = server.serve(url=url, page=page, stream=stream) + headers = e.headers + output = stream.getvalue() + status = headers.pop('Status') + start_response(status, list(headers.items())) + return [output] + + wsgi = wsgiref.simple_server.make_server(host=host, port=port, app=app) + LOG.info('serving WSGI on {}:{}'.format(host, port)) + wsgi.serve_forever() + if __name__ == '__main__': import argparse as _argparse @@ -784,8 +821,8 @@ if __name__ == '__main__': description=__doc__, version=__version__, formatter_class=_argparse.RawDescriptionHelpFormatter) parser.add_argument( - '--scgi', default=False, action='store_const', const=True, - help='Run as a SCGI server (vs. serving a single CGI call)') + '--mode', default='cgi', choices=['cgi', 'scgi', 'wsgi'], + help='Server mode (defaults to CGI)') parser.add_argument( '--port', default=4000, type=int, help='Port to listen to (if runing as a SCGI server)') @@ -813,7 +850,9 @@ if __name__ == '__main__': s.header = [open(_os_path.join(shared, 'header.shtml'), 'r').read()] s.footer = [open(_os_path.join(shared, 'footer.shtml'), 'r').read()] - if args.scgi: + if args.mode == 'scgi': serve_scgi(server=s, port=args.port) + if args.mode == 'wsgi': + serve_wsgi(server=s, port=args.port) else: serve_cgi(server=s)