82 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			82 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
Application Dispatcher
 | 
						|
======================
 | 
						|
 | 
						|
This middleware creates a single WSGI application that dispatches to
 | 
						|
multiple other WSGI applications mounted at different URL paths.
 | 
						|
 | 
						|
A common example is writing a Single Page Application, where you have a
 | 
						|
backend API and a frontend written in JavaScript that does the routing
 | 
						|
in the browser rather than requesting different pages from the server.
 | 
						|
The frontend is a single HTML and JS file that should be served for any
 | 
						|
path besides "/api".
 | 
						|
 | 
						|
This example dispatches to an API app under "/api", an admin app
 | 
						|
under "/admin", and an app that serves frontend files for all other
 | 
						|
requests::
 | 
						|
 | 
						|
    app = DispatcherMiddleware(serve_frontend, {
 | 
						|
        '/api': api_app,
 | 
						|
        '/admin': admin_app,
 | 
						|
    })
 | 
						|
 | 
						|
In production, you might instead handle this at the HTTP server level,
 | 
						|
serving files or proxying to application servers based on location. The
 | 
						|
API and admin apps would each be deployed with a separate WSGI server,
 | 
						|
and the static files would be served directly by the HTTP server.
 | 
						|
 | 
						|
.. autoclass:: DispatcherMiddleware
 | 
						|
 | 
						|
:copyright: 2007 Pallets
 | 
						|
:license: BSD-3-Clause
 | 
						|
"""
 | 
						|
 | 
						|
from __future__ import annotations
 | 
						|
 | 
						|
import typing as t
 | 
						|
 | 
						|
if t.TYPE_CHECKING:
 | 
						|
    from _typeshed.wsgi import StartResponse
 | 
						|
    from _typeshed.wsgi import WSGIApplication
 | 
						|
    from _typeshed.wsgi import WSGIEnvironment
 | 
						|
 | 
						|
 | 
						|
class DispatcherMiddleware:
 | 
						|
    """Combine multiple applications as a single WSGI application.
 | 
						|
    Requests are dispatched to an application based on the path it is
 | 
						|
    mounted under.
 | 
						|
 | 
						|
    :param app: The WSGI application to dispatch to if the request
 | 
						|
        doesn't match a mounted path.
 | 
						|
    :param mounts: Maps path prefixes to applications for dispatching.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        app: WSGIApplication,
 | 
						|
        mounts: dict[str, WSGIApplication] | None = None,
 | 
						|
    ) -> None:
 | 
						|
        self.app = app
 | 
						|
        self.mounts = mounts or {}
 | 
						|
 | 
						|
    def __call__(
 | 
						|
        self, environ: WSGIEnvironment, start_response: StartResponse
 | 
						|
    ) -> t.Iterable[bytes]:
 | 
						|
        script = environ.get("PATH_INFO", "")
 | 
						|
        path_info = ""
 | 
						|
 | 
						|
        while "/" in script:
 | 
						|
            if script in self.mounts:
 | 
						|
                app = self.mounts[script]
 | 
						|
                break
 | 
						|
 | 
						|
            script, last_item = script.rsplit("/", 1)
 | 
						|
            path_info = f"/{last_item}{path_info}"
 | 
						|
        else:
 | 
						|
            app = self.mounts.get(script, self.app)
 | 
						|
 | 
						|
        original_script_name = environ.get("SCRIPT_NAME", "")
 | 
						|
        environ["SCRIPT_NAME"] = original_script_name + script
 | 
						|
        environ["PATH_INFO"] = path_info
 | 
						|
        return app(environ, start_response)
 |