codekingpro/portable-devtools
114k
1"""2Application Dispatcher3======================4 5This middleware creates a single WSGI application that dispatches to6multiple other WSGI applications mounted at different URL paths.7 8A common example is writing a Single Page Application, where you have a9backend API and a frontend written in JavaScript that does the routing10in the browser rather than requesting different pages from the server.11The frontend is a single HTML and JS file that should be served for any12path besides "/api".13 14This example dispatches to an API app under "/api", an admin app15under "/admin", and an app that serves frontend files for all other16requests::17 18 app = DispatcherMiddleware(serve_frontend, {19 '/api': api_app,20 '/admin': admin_app,21 })22 23In production, you might instead handle this at the HTTP server level,24serving files or proxying to application servers based on location. The25API and admin apps would each be deployed with a separate WSGI server,26and the static files would be served directly by the HTTP server.27 28.. autoclass:: DispatcherMiddleware29 30:copyright: 2007 Pallets31:license: BSD-3-Clause32"""33 34from __future__ import annotations35 36import typing as t37 38if t.TYPE_CHECKING:39 from _typeshed.wsgi import StartResponse40 from _typeshed.wsgi import WSGIApplication41 from _typeshed.wsgi import WSGIEnvironment42 43 44class DispatcherMiddleware:45 """Combine multiple applications as a single WSGI application.46 Requests are dispatched to an application based on the path it is47 mounted under.48 49 :param app: The WSGI application to dispatch to if the request50 doesn't match a mounted path.51 :param mounts: Maps path prefixes to applications for dispatching.52 """53 54 def __init__(55 self,56 app: WSGIApplication,57 mounts: dict[str, WSGIApplication] | None = None,58 ) -> None:59 self.app = app60 self.mounts = mounts or {}61 62 def __call__(63 self, environ: WSGIEnvironment, start_response: StartResponse64 ) -> t.Iterable[bytes]:65 script = environ.get("PATH_INFO", "")66 path_info = ""67 68 while "/" in script:69 if script in self.mounts:70 app = self.mounts[script]71 break72 73 script, last_item = script.rsplit("/", 1)74 path_info = f"/{last_item}{path_info}"75 else:76 app = self.mounts.get(script, self.app)77 78 original_script_name = environ.get("SCRIPT_NAME", "")79 environ["SCRIPT_NAME"] = original_script_name + script80 environ["PATH_INFO"] = path_info81 return app(environ, start_response)82 