r"""Makes a WSGI-compatible function runnable on Tornado's HTTP server. .. warning:: WSGI is a *synchronous* interface, while Tornado's concurrency model is based on single-threaded asynchronous execution. This means that running a WSGI app with Tornado's `WSGIContainer`
| 54 | |
| 55 | |
| 56 | class WSGIContainer(object): |
| 57 | r"""Makes a WSGI-compatible function runnable on Tornado's HTTP server. |
| 58 | |
| 59 | .. warning:: |
| 60 | |
| 61 | WSGI is a *synchronous* interface, while Tornado's concurrency model |
| 62 | is based on single-threaded asynchronous execution. This means that |
| 63 | running a WSGI app with Tornado's `WSGIContainer` is *less scalable* |
| 64 | than running the same app in a multi-threaded WSGI server like |
| 65 | ``gunicorn`` or ``uwsgi``. Use `WSGIContainer` only when there are |
| 66 | benefits to combining Tornado and WSGI in the same process that |
| 67 | outweigh the reduced scalability. |
| 68 | |
| 69 | Wrap a WSGI function in a `WSGIContainer` and pass it to `.HTTPServer` to |
| 70 | run it. For example:: |
| 71 | |
| 72 | def simple_app(environ, start_response): |
| 73 | status = "200 OK" |
| 74 | response_headers = [("Content-type", "text/plain")] |
| 75 | start_response(status, response_headers) |
| 76 | return [b"Hello world!\n"] |
| 77 | |
| 78 | async def main(): |
| 79 | container = tornado.wsgi.WSGIContainer(simple_app) |
| 80 | http_server = tornado.httpserver.HTTPServer(container) |
| 81 | http_server.listen(8888) |
| 82 | await asyncio.Event().wait() |
| 83 | |
| 84 | asyncio.run(main()) |
| 85 | |
| 86 | This class is intended to let other frameworks (Django, web.py, etc) |
| 87 | run on the Tornado HTTP server and I/O loop. |
| 88 | |
| 89 | The `tornado.web.FallbackHandler` class is often useful for mixing |
| 90 | Tornado and WSGI apps in the same server. See |
| 91 | https://github.com/bdarnell/django-tornado-demo for a complete example. |
| 92 | """ |
| 93 | |
| 94 | def __init__(self, wsgi_application: "WSGIAppType") -> None: |
| 95 | self.wsgi_application = wsgi_application |
| 96 | |
| 97 | def __call__(self, request: httputil.HTTPServerRequest) -> None: |
| 98 | data = {} # type: Dict[str, Any] |
| 99 | response = [] # type: List[bytes] |
| 100 | |
| 101 | def start_response( |
| 102 | status: str, |
| 103 | headers: List[Tuple[str, str]], |
| 104 | exc_info: Optional[ |
| 105 | Tuple[ |
| 106 | "Optional[Type[BaseException]]", |
| 107 | Optional[BaseException], |
| 108 | Optional[TracebackType], |
| 109 | ] |
| 110 | ] = None, |
| 111 | ) -> Callable[[bytes], Any]: |
| 112 | data["status"] = status |
| 113 | data["headers"] = headers |