ASGI HTTP Request and Response Message Events

The Asynchronous Server Gateway Interface (ASGI) defines how asynchronous Python web servers and applications communicate using a standard message-passing protocol. For HTTP transactions, the lifecycle is managed by an initial connection scope and a sequence of predefined inbound and outbound message events passed via receive and send callables. This article outlines the specific message event types that govern HTTP request ingestion and response delivery under the ASGI specification.

The Connection Scope

Before any message events are exchanged, the ASGI server initializes the connection by passing a scope dictionary of type http to the application. This dictionary establishes the context of the request and includes metadata such as:

Inbound Request Events

Once the scope is established, the application listens for incoming data using the receive awaitable callable. The HTTP lifecycle dictates two primary inbound message types:

1. http.request

Sent by the server to deliver the HTTP request body to the application. If the body is large or streamed, multiple http.request events are sent sequentially.

2. http.disconnect

Sent by the server if the client closes the connection before the response is fully generated or sent. It carries no additional payload beyond type: "http.disconnect". Applications can listen for this event to cancel expensive background tasks early.

Outbound Response Events

To return data to the client, the application transmits structured dictionaries to the server using the send awaitable callable. The lifecycle strictly requires a response to begin before body data can be delivered.

1. http.response.start

This event must be sent first to initialize the HTTP response. Sending any other event before this raises an error.

2. http.response.body

Sent after http.response.start to transmit the response payload. Multiple http.response.body events can be chained to stream data.

Standard Lifecycle Execution Order

  1. Initialization: The server calls the application: app(scope, receive, send).
  2. Body Consumption: The application calls await receive() to consume one or more http.request events until more_body is False.
  3. Response Header Transmission: The application calls await send({"type": "http.response.start", "status": ..., "headers": ...}).
  4. Response Body Transmission: The application calls await send({"type": "http.response.body", "body": ..., "more_body": ...}) until all body chunks are sent with more_body=False.
  5. Termination: The application completes its execution loop, and the server closes or reuses the underlying connection.