comparison piecrust/serving/middlewares.py @ 553:cc6f3dbe3048

serve: Extract some of the server's functionality into WSGI middlewares.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 08 Aug 2015 22:01:47 -0700
parents
children 93b656f0af54
comparison
equal deleted inserted replaced
552:9612cfc6455a 553:cc6f3dbe3048
1 import os.path
2 from werkzeug.wrappers import Request, Response
3 from werkzeug.wsgi import ClosingIterator
4 from piecrust import RESOURCES_DIR, CACHE_DIR
5 from piecrust.serving.util import make_wrapped_file_response
6
7
8 class StaticResourcesMiddleware(object):
9 """ WSGI middleware that serves static files from the `resources/server`
10 directory in the PieCrust package.
11 """
12 def __init__(self, app):
13 self.app = app
14
15 def __call__(self, environ, start_response):
16 static_mount = '/__piecrust_static/'
17
18 request = Request(environ)
19 if request.path.startswith(static_mount):
20 rel_req_path = request.path[len(static_mount):]
21 mount = os.path.join(RESOURCES_DIR, 'server')
22 full_path = os.path.join(mount, rel_req_path)
23 try:
24 response = make_wrapped_file_response(
25 environ, request, full_path)
26 return response(environ, start_response)
27 except OSError:
28 pass
29
30 return self.app(environ, start_response)
31
32
33 class PieCrustDebugMiddleware(object):
34 """ WSGI middleware that handles debugging of PieCrust stuff.
35 """
36 def __init__(self, app, root_dir, debug=False,
37 sub_cache_dir=None, run_sse_check=None):
38 self.app = app
39 self.root_dir = root_dir
40 self.debug = debug
41 self.run_sse_check = run_sse_check
42 self._proc_loop = None
43 self._out_dir = os.path.join(root_dir, CACHE_DIR, 'server')
44 if sub_cache_dir:
45 self._out_dir = os.path.join(sub_cache_dir, 'server')
46 self._handlers = {
47 'werkzeug_shutdown': self._shutdownWerkzeug,
48 'pipeline_status': self._startSSEProvider}
49
50 if not self.run_sse_check or self.run_sse_check():
51 # When using a server with code reloading, some implementations
52 # use process forking and we end up going here twice. We only want
53 # to start the pipeline loop in the inner process most of the
54 # time so we let the implementation tell us if this is OK.
55 from piecrust.serving.procloop import ProcessingLoop
56 self._proc_loop = ProcessingLoop(root_dir, self._out_dir,
57 sub_cache_dir=sub_cache_dir,
58 debug=debug)
59 self._proc_loop.start()
60
61 def __call__(self, environ, start_response):
62 debug_mount = '/__piecrust_debug/'
63
64 request = Request(environ)
65 if request.path.startswith(debug_mount):
66 rel_req_path = request.path[len(debug_mount):]
67 handler = self._handlers.get(rel_req_path)
68 if handler is not None:
69 return handler(request, start_response)
70
71 return self.app(environ, start_response)
72
73 def _shutdownWerkzeug(self, request, start_response):
74 shutdown_func = request.environ.get('werkzeug.server.shutdown')
75 if shutdown_func is None:
76 raise RuntimeError('Not running with the Werkzeug Server')
77 shutdown_func()
78 response = Response("Server shutting down...")
79 return response(request.environ, start_response)
80
81 def _startSSEProvider(self, request, start_response):
82 from piecrust.serving.procloop import (
83 PipelineStatusServerSentEventProducer)
84 provider = PipelineStatusServerSentEventProducer(
85 self._proc_loop)
86 it = provider.run()
87 response = Response(it, mimetype='text/event-stream')
88 response.headers['Cache-Control'] = 'no-cache'
89 response.headers['Last-Event-ID'] = \
90 self._proc_loop.last_status_id
91 return ClosingIterator(
92 response(request.environ, start_response),
93 [provider.close])
94