HTTP & WebSocket ASGI Message Format¶
Version: 2.1 (2019-03-20)
The HTTP+WebSocket ASGI sub-specification outlines how to transport HTTP/1.1, HTTP/2 and WebSocket connections within ASGI.
It is deliberately intended and designed to be a superset of the WSGI format and specifies how to translate between the two for the set of requests that are able to be handled by WSGI.
Spec Versions¶
This spec has had two versions:
2.0
: The first version of the spec, released with ASGI 2.02.1
: Added theheaders
key to the WebSocket Accept response
Spec versions let you understand what the server you are using understands. If
a server tells you it only supports version 2.0
of this spec, then
sending headers
with a WebSocket Accept message is an error, for example.
They are separate from the HTTP version or the ASGI version.
HTTP¶
The HTTP format covers HTTP/1.0, HTTP/1.1 and HTTP/2, as the changes in HTTP/2 are largely on the transport level. A protocol server should give different scopes to different requests on the same HTTP/2 connection, and correctly multiplex the responses back to the same stream in which they came. The HTTP version is available as a string in the scope.
Multiple header fields with the same name are complex in HTTP. RFC 7230 states that for any header field that can appear multiple times, it is exactly equivalent to sending that header field only once with all the values joined by commas.
However, RFC 7230 and RFC 6265 make it clear that this rule does not apply to
the various headers used by HTTP cookies (Cookie
and Set-Cookie
). The
Cookie
header must only be sent once by a user-agent, but the
Set-Cookie
header may appear repeatedly and cannot be joined by commas.
The ASGI design decision is to transport both request and response headers as
lists of 2-element [name, value]
lists and preserve headers exactly as they
were provided.
The HTTP protocol should be signified to ASGI applications with a type
value of http
.
Connection Scope¶
HTTP connections have a single-request connection scope - that is, your applications will be instantiated at the start of the request, and destroyed at the end, even if the underlying socket is still open and serving multiple requests.
If you hold a response open for long-polling or similar, the scope will persist until the response closes from either the client or server side.
The connection scope contains:
type
(Unicode string) –"http"
.asgi["version"]
(Unicode string) – Version of the ASGI spec.asgi["spec_version"]
(Unicode string) – Version of the ASGI HTTP spec this server understands; one of"2.0"
or"2.1"
. Optional; if missing assume2.0
.http_version
(Unicode string) – One of"1.0"
,"1.1"
or"2"
.method
(Unicode string) – The HTTP method name, uppercased.scheme
(Unicode string) – URL scheme portion (likely"http"
or"https"
). Optional (but must not be empty); default is"http"
.path
(Unicode string) – HTTP request target excluding any query string, with percent-encoded sequences and UTF-8 byte sequences decoded into characters.raw_path
(byte string) – The original HTTP path component unmodified from the bytes that were received by the web server. Some web server implementations may be unable to provide this. Optional; defaults toNone
.query_string
(byte string) – URL portion after the?
, percent-encoded.root_path
(Unicode string) – The root path this application is mounted at; same asSCRIPT_NAME
in WSGI. Optional; defaults to""
.headers
(Iterable[[byte string, byte string]]) – An iterable of[name, value]
two-item iterables, wherename
is the header name, andvalue
is the header value. Order of header values must be preserved from the original HTTP request; order of header names is not important. Duplicates are possible and must be preserved in the message as received. Header names must be lowercased. Pseudo headers (present in HTTP/2 and HTTP/3) must be removed; if:authority
is present its value must be added to the start of the iterable withhost
as the header name or replace any existing host header already present.client
(Iterable[Unicode string, int]) – A two-item iterable of[host, port]
, wherehost
the remote host’s IPv4 or IPv6 address, andport
is the remote port as an integer. Optional; defaults toNone
.server
(Iterable[Unicode string, int]) – A two-item iterable of[host, port]
, wherehost
is the listening address for this server, andport
is the integer listening port. Optional; defaults toNone
.
Servers are responsible for handling inbound and outbound chunked transfer
encodings. A request with a chunked
encoded body should be automatically
de-chunked by the server and presented to the application as plain body bytes;
a response that is given to the server with no Content-Length
may be chunked
as the server sees fit.
Request¶
Sent to indicate an incoming request. Most of the request information is in the connection scope; the body message serves as a way to stream large incoming HTTP bodies in chunks, and as a trigger to actually run request code (as you should not trigger on a connection opening alone).
Note that if the request is being sent using Transfer-Encoding: chunked
,
the server is responsible for handling this encoding. The http.request
messages should contain just the decoded contents of each chunk.
Keys:
type
(Unicode string) –"http.request"
.body
(byte string) – Body of the request. Optional; defaults tob""
. Ifmore_body
is set, treat as start of body and concatenate on further chunks.more_body
(bool) – Signifies if there is additional content to come (as part of a Request message). IfTrue
, the consuming application should wait until it gets a chunk with this set toFalse
. IfFalse
, the request is complete and should be processed. Optional; defaults toFalse
.
Response Start¶
Starts sending a response to the client. Needs to be followed by at least one response content message. The protocol server must not start sending the response to the client until it has received at least one Response Body event.
You may send a Transfer-Encoding
header in this message, but the server
must ignore it. Servers handle Transfer-Encoding
themselves, and may opt
to use Transfer-Encoding: chunked
if the application presents a response
that has no Content-Length
set.
Note that this is not the same as Content-Encoding
, which the application
still controls, and which is the appropriate place to set gzip
or other
compression flags.
Keys:
type
(Unicode string) –"http.response.start"
.status
(int) – HTTP status code.headers
(Iterable[[byte string, byte string]]) – A iterable of[name, value]
two-item iterables, wherename
is the header name, andvalue
is the header value. Order must be preserved in the HTTP response. Header names must be lowercased. Optional; defaults to an empty list. Pseudo headers (present in HTTP/2 and HTTP/3) must not be present.
Response Body¶
Continues sending a response to the client. Protocol servers must
flush any data passed to them into the send buffer before returning from a
send call. If more_body
is set to False
this will
close the connection.
Keys:
type
(Unicode string) –"http.response.body"
.body
(byte string) – HTTP body content. Concatenated onto any previousbody
values sent in this connection scope. Optional; defaults tob""
.more_body
(bool) – Signifies if there is additional content to come (as part of a Response Body message). IfFalse
, response will be taken as complete and closed, and any further messages on the channel will be ignored. Optional; defaults toFalse
.
Disconnect¶
Sent to the application when a HTTP connection is closed or if receive
is called after a response has been sent. This is mainly useful for
long-polling, where you may want to trigger cleanup code if the
connection closes early.
Keys:
type
(Unicode string) –"http.disconnect"
.
WebSocket¶
WebSockets share some HTTP details - they have a path and headers - but also have more state. Again, most of that state is in the scope, which will live as long as the socket does.
WebSocket protocol servers should handle PING/PONG messages themselves, and send PING messages as necessary to ensure the connection is alive.
WebSocket protocol servers should handle message fragmentation themselves, and deliver complete messages to the application.
The WebSocket protocol should be signified to ASGI applications with
a type
value of websocket
.
Connection Scope¶
WebSocket connections’ scope lives as long as the socket itself - if the application dies the socket should be closed, and vice-versa. The scope contains the initial connection metadata (mostly from the HTTP handshake):
type
(Unicode string) –"websocket"
.asgi["version"]
(Unicode string) – The version of the ASGI spec.asgi["spec_version"]
(Unicode string) – Version of the ASGI HTTP spec this server understands; one of"2.0"
or"2.1"
. Optional; if missing assume"2.0"
.http_version
(Unicode string) – One of"1.1"
or"2"
. Optional; default is"1.1"
.scheme
(Unicode string) – URL scheme portion (likely"ws"
or"wss"
). Optional (but must not be empty); default is"ws"
.path
(Unicode string) – HTTP request target excluding any query string, with percent-encoded sequences and UTF-8 byte sequences decoded into characters.raw_path
(byte string) – The original HTTP path component unmodified from the bytes that were received by the web server. Some web server implementations may be unable to provide this. Optional; defaults toNone
.query_string
(byte string) – URL portion after the?
. Optional; default is empty string.root_path
(byte string) – The root path this application is mounted at; same asSCRIPT_NAME
in WSGI. Optional; defaults to empty string.headers
(Iterable[[byte string, byte string]]) – An iterable of[name, value]
two-item iterables, wherename
is the header name andvalue
is the header value. Order should be preserved from the original HTTP request; duplicates are possible and must be preserved in the message as received. Header names must be lowercased. Pseudo headers (present in HTTP/2 and HTTP/3) must be removed; if:authority
is present its value must be added to the start of the iterable withhost
as the header name or replace any existing host header already present.client
(Iterable[Unicode string, int]) – A two-item iterable of[host, port]
, wherehost
is the remote host’s IPv4 or IPv6 address, andport
is the remote port. Optional; defaults toNone
.server
(Iterable[Unicode string, int]) – A two-item iterable of[host, port]
, wherehost
is the listening address for this server, andport
is the integer listening port. Optional; defaults toNone
.subprotocols
(Iterable[Unicode string]) – Subprotocols the client advertised. Optional; defaults to empty list.
Connection¶
Sent when the client initially opens a connection and is about to finish the WebSocket handshake.
This message must be responded to with either an Accept message
or a Close message before the socket will pass websocket.receive
messages. The protocol server must send this message
during the handshake phase of the WebSocket and not complete the handshake
until it gets a reply, returning HTTP status code 403
if the connection is
denied.
Keys:
type
(Unicode string) –"websocket.connect"
.
Accept¶
Sent by the application when it wishes to accept an incoming connection.
type
(Unicode string) –"websocket.accept"
.subprotocol
(Unicode string) – The subprotocol the server wishes to accept. Optional; defaults toNone
.headers
(Iterable[[byte string, byte string]]) – An iterable of[name, value]
two-item iterables, wherename
is the header name, andvalue
is the header value. Order must be preserved in the HTTP response. Header names must be lowercased. Must not include a header namedsec-websocket-protocol
; use thesubprotocol
key instead. Optional; defaults to an empty list. Added in spec version 2.1. Pseudo headers (present in HTTP/2 and HTTP/3) must not be present.
Receive¶
Sent when a data message is received from the client.
Keys:
type
(Unicode string) –"websocket.receive"
.bytes
(byte string) – The message content, if it was binary mode, orNone
. Optional; if missing, it is equivalent toNone
.text
(Unicode string) – The message content, if it was text mode, orNone
. Optional; if missing, it is equivalent toNone
.
Exactly one of bytes
or text
must be non-None
. One or both
keys may be present, however.
Send¶
Sends a data message to the client.
Keys:
type
(Unicode string) –"websocket.send"
.bytes
(byte string) – Binary message content, orNone
.Optional; if missing, it is equivalent to
None
.
text
(Unicode string) – Text message content, orNone
.Optional; if missing, it is equivalent to
None
.
Exactly one of bytes
or text
must be non-None
. One or both
keys may be present, however.
Disconnection¶
Sent when either connection to the client is lost, either from the client closing the connection, the server closing the connection, or loss of the socket.
Keys:
type
(Unicode string) –"websocket.disconnect"
code
(int) – The WebSocket close code, as per the WebSocket spec.
Close¶
Tells the server to close the connection.
If this is sent before the socket is accepted, the server must close the connection with a HTTP 403 error code (Forbidden), and not complete the WebSocket handshake; this may present on some browsers as a different WebSocket error code (such as 1006, Abnormal Closure).
If this is sent after the socket is accepted, the server must close the socket with the close code passed in the message (or 1000 if none is specified).
type
(Unicode string) –"websocket.close"
.code
(int) – The WebSocket close code, as per the WebSocket spec. Optional; defaults to1000
.
WSGI Compatibility¶
Part of the design of the HTTP portion of this spec is to make sure it aligns well with the WSGI specification, to ensure easy adaptability between both specifications and the ability to keep using WSGI applications with ASGI servers.
WSGI applications, being synchronous, must be run in a threadpool in order to be served, but otherwise their runtime maps onto the HTTP connection scope’s lifetime.
There is an almost direct mapping for the various special keys in
WSGI’s environ
variable to the http
scope:
REQUEST_METHOD
is themethod
SCRIPT_NAME
isroot_path
PATH_INFO
can be derived frompath
androot_path
QUERY_STRING
isquery_string
CONTENT_TYPE
can be extracted fromheaders
CONTENT_LENGTH
can be extracted fromheaders
SERVER_NAME
andSERVER_PORT
are inserver
REMOTE_HOST
/REMOTE_ADDR
andREMOTE_PORT
are inclient
SERVER_PROTOCOL
is encoded inhttp_version
wsgi.url_scheme
isscheme
wsgi.input
is aStringIO
based around thehttp.request
messageswsgi.errors
is directed by the wrapper as needed
The start_response
callable maps similarly to http.response.start
:
The
status
argument becomesstatus
, with the reason phrase dropped.response_headers
maps toheaders
Yielding content from the WSGI application maps to sending
http.response.body
messages.
WSGI encoding differences¶
The WSGI specification (as defined in PEP 3333) specifies that all strings
sent to or from the server must be of the str
type but only contain
codepoints in the ISO-8859-1 (“latin-1”) range. This was due to it originally
being designed for Python 2 and its different set of string types.
The ASGI HTTP and WebSocket specifications instead specify each entry of the
scope
dict as either a byte string or a Unicode string. HTTP, being an
older protocol, is sometimes imperfect at specifying encoding, so some
decisions of what is Unicode versus bytes may not be obvious.
path
: URLs can have both percent-encoded and UTF-8 encoded sections. Because decoding these is often done by the underlying server (or sometimes even proxies in the path), this is a Unicode string, fully decoded from both UTF-8 encoding and percent encodings.headers
: These are byte strings of the exact byte sequences sent by the client/to be sent by the server. While modern HTTP standards say that headers should be ASCII, older ones did not and allowed a wider range of characters. Frameworks/applications should decode headers as they deem appropriate.query_string
: Unlike thepath
, this is not as subject to server interference and so is presented as its raw byte string version, percent-encoded.root_path
: Unicode string to matchpath
.
2.0 (2017-11-28): Initial non-channel-layer based ASGI spec
This document has been placed in the public domain.