What is ASGI and why do we need it?
ASGI stands for Asynchronous Server Gateway Interface. It extends the capabilities of WSGI (Web Server Gateway Interface), which is a standard way of communication between the web server and the web applications in most of the python web frameworks like Django.
Both ASGI and WSGI are the specifications to provide a standard interface between the Python web servers, applications/framework, but to understand why do we need to introduce a completely new specification (ASGI) especially when most of the python web applications and frameworks successfully using WSGI as a standard, we need to first understand what are the limitations with WSGI and how a new specification like ASGI can handle them.
What are the limitations of WSGI?
At the time when WSGI was in the process of development, the only concern designers had was to make a protocol which provides a common ground for web development, such that, users can easily switch between many web frameworks without worrying about the details of how the new framework interacts with the server. And actually WSGI did a pretty good job in dealing with these issues appropriately, but when the relatively new protocols other than standard HTTP (HyperText Transfer Protocol) especially WebSocket started gaining popularity within the web developers community, WSGI failed to provide a way to develop applications which can handle these protocols.
As the WSGI applications can only take requests from the server and return responses to the client/server, WSGI is inherently suited for handling HTTP protocol only.
WSGI applications are single, synchronous callable that takes a request as an input and returns a response due to which:
Connections are short lived which is only preferable for HTTP and not for long-polling HTTP and WebSocket whose connections are relatively long lived.
Requests have only a single path to follow in applications, so protocols that have more than one incoming event (for example receiving WebSocket frames) can’t get handled by a WSGI application.
How does ASGI handle these limitations?
ASGI have 2 different component with different responsibilities:
A protocol server, which terminates sockets and maps them into connections and per-connection event messages.
An application, which runs inside a protocol server, is instantiated once per connection, and handles event messages as they happen.
Like WSGI, the server hosts and runs the application inside it, and passes incoming requests to the application in a standardized format. Unlike WSGI, however, applications are objects that accept events instead of simple callables, and must run as coroutines which are capable of handling asynchronous I/O operations (on the main thread; they can also use threading for this to maintain synchronous code).
Unlike WSGI, there are 2 different parts of a connection:
A connection scope, which represents a protocol connection to a user and lasts the lifetime of that connection.
Events, which are sent to the application when anything happens on that connection.
Applications are instantiated by passing a connection scope, and then application is run in an event loop where they handle events and send data back to the client as events.
There is a mapping of a single incoming socket/connection to an application instance that lasts the lifetime of that connection or maybe a little longer if there is some cleanup that needs to be done.
ASGI application is a single, asynchronous callable. It accepts scope, which contains the information about the incoming request, send, an awaitable that lets you send events to the client, and receive, an awaitable which lets you receive events from the client.
Due to this reordering of the structure of application, ASGI application can now accept multiple incoming and outgoing events which removes the limitation of WSGI application having a single path for incoming requests. Not only this, ASGI application can also allow for a background coroutine so the application can also do other things in the background, not just handling requests (say listening for events that need external triggers, like a Redis queue).
A very simple ASGI application can be defined as following:
Every event that you send or receive in an ASGI application is a Python dict, with a predefined format. It’s these predefined formats of events which give ASGI applications to be easily switched from different web servers.
These events always have a defined type root level key, which can be useful in deducing the event’s structure. The sample event for sending the start of a HTTP response that you may receive from receive:
And the event you might pass to send to send an outgoing WebSocket message may look like following:
As the typical structure of WSGI Application looks like following:
Where application is a WSGI application which accepts 2 parameters environ which contains the information about the incoming requests and start_response is the callable using which application returns the HTTP header.
To make ASGI backward compatible with the WSGI applications, We need to make ASGI able to run WSGI synchronous applications inside the asynchronous coroutine, also ASGI gets incoming request information through scope, whereas WSGI accepts environ. So we need to map environ to scope.
Synchronous WSGI applications must be run inside the threadpool so that they can be served, but otherwise their runtime maps onto the HTTP connection scope’s lifetime.
The WSGI’s environ variable is almost map to the ASGI http scope:
REQUEST_METHOD is the method
SCRIPT_NAME is root_path
PATH_INFO can be derived from path and root_path
QUERY_STRING -> query_string
headers[‘content-type’] -> CONTENT_TYPE.
Headers[‘content-length’] -> CONTENT_LENGTH
SERVER_NAME and SERVER_PORT can be get from server
REMOTE_HOST/REMOTE_ADDR and REMOTE_PORT are available in client
SERVER_PROTOCOL is extracted from http_version
wsgi.url_scheme -> scheme
wsgi.input is a StringIO based around the http.request messages
wsgi.errors is directed by the wrapper as needed
Similarly, The start_response callable can also be mapped to http.response.start.
ASGI specification have also provided a python package asgiref to provide utility methods to wrap WSGI application.
ASGI applications are capable of both long lived connection and short lived connection which are defined by the connection scope of the instantiated application object which depends on the socket on which that protocol runs.
ASGI Applications have more than one path for incoming requests, which makes them handle protocols which have more than one incoming events during the lifetime of connections.
ASGI applications are asynchronous coroutines which can also listen to the external trigger.
ASGI also decouples the application from the web protocol by abstracting the incoming requests into scope and events which makes them able to run different types of protocols instead of handling only HTTP requests and responses.
ASGI applications are also backward compatible for WSGI applications. So ASGI is the superset of WSGI.