HTTP и WebSocket Формат сообщений ASGI¶
Версия: 2.1 (2019-03-20)
Спецификация HTTP+WebSocket ASGI описывает, как передавать HTTP/1.1, HTTP/2 и WebSocket соединения в ASGI.
Он намеренно задуман и разработан как надмножество формата WSGI и определяет, как осуществлять перевод между ними для набора запросов, которые могут быть обработаны WSGI.
Специальные версии¶
У этой спецификации было две версии:
2.0
: Первая версия спецификации, выпущенная вместе с ASGI 2.02.1
: Добавлен ключheaders
в ответ WebSocket Accept
Версии спецификаций позволяют понять, что понимает используемый вами сервер. Если сервер сообщает вам, что он поддерживает только версию 2.0
этой спецификации, то, например, отправка headers
с сообщением WebSocket Accept будет ошибкой.
Они отделены от версии HTTP или версии ASGI.
HTTP¶
Формат HTTP охватывает HTTP/1.0, HTTP/1.1 и HTTP/2, поскольку изменения в HTTP/2 в основном происходят на транспортном уровне. Сервер протокола должен предоставлять разные диапазоны для разных запросов в одном и том же соединении HTTP/2 и правильно мультиплексировать ответы обратно в тот же поток, в котором они пришли. Версия HTTP доступна в виде строки в области видимости.
Несколько полей заголовка с одним и тем же именем являются сложными в HTTP. RFC 7230 утверждает, что для любого поля заголовка, которое может появляться несколько раз, это точно эквивалентно отправке этого поля заголовка только один раз со всеми значениями, соединенными запятыми.
Однако RFC 7230 и RFC 6265 ясно дают понять, что это правило не распространяется на различные заголовки, используемые HTTP cookies (Cookie
и Set-Cookie
). Заголовок Cookie
должен быть отправлен агентом пользователя только один раз, но заголовок Set-Cookie
может появляться многократно и не может быть соединен запятыми. Дизайнерское решение ASGI состоит в том, чтобы передавать заголовки запроса и ответа в виде списков из двух элементов [name, value]
и сохранять заголовки именно в том виде, в котором они были предоставлены.
Протокол HTTP должен быть обозначен для приложений ASGI значением type
http
.
Область подключения¶
HTTP-соединения имеют область действия соединения с одним запросом - то есть, ваши приложения будут созданы в начале запроса и уничтожены в конце, даже если базовый сокет все еще открыт и обслуживает несколько запросов.
Если вы держите ответ открытым для длительного опроса или чего-то подобного, область видимости будет сохраняться до тех пор, пока ответ не закроется со стороны клиента или сервера.
Область подключения содержит:
type
(Unicode string) –"http"
.asgi["version"]
(Unicode string) – Версия спецификации ASGI.asgi["spec_version"]
(Unicode string) – Версия спецификации ASGI HTTP, которую понимает этот сервер; одна из"2.0"
или"2.1"
. Необязательно; если отсутствует, считайте, что2.0
.http_version
(Unicode string) – Одно из"1.0"
,"1.1"
или"2"
.method
(Unicode string) – Имя метода HTTP, в верхнем регистре.scheme
(Unicode string) – Часть схемы URL (вероятно,"http"
или"https"
). Необязательна (но не должна быть пустой); по умолчанию"http"
.path
(Unicode string) – Цель HTTP-запроса, исключающая любую строку запроса, с кодированными в процентах последовательностями и последовательностями байтов UTF-8, декодированными в символы.raw_path
(байтовая строка) – Оригинальный компонент пути HTTP, не измененный из байтов, которые были получены веб-сервером. Некоторые реализации веб-сервера могут быть не в состоянии обеспечить это. Необязательно; по умолчаниюNone
.query_string
(байтовая строка) – Часть URL после?
, закодированная в процентах.root_path
(Unicode string) – Корневой путь, по которому монтируется данное приложение; такой же, какSCRIPT_NAME
в WSGI. Необязательно; по умолчанию""
.headers
(Iterable[[byte string, byte string]]) – Итерабельность[name, value]
итерабельных элементов с двумя элементами, гдеname
- имя заголовка, аvalue
- его значение. Порядок значений заголовков должен быть сохранен из исходного HTTP-запроса; порядок имен заголовков не важен. Дубликаты возможны и должны быть сохранены в полученном сообщении. Имена заголовков должны быть в нижнем регистре. Псевдозаголовки (присутствующие в HTTP/2 и HTTP/3) должны быть удалены; если присутствует:authority
, его значение должно быть добавлено в начало итерабельной таблицы сhost
в качестве имени заголовка или заменить любой существующий заголовок хоста, который уже присутствует.client
(Iterable[Unicode string, int]) – Итерабельность из двух элементов[host, port]
, гдеhost
- IPv4 или IPv6 адрес удаленного хоста, аport
- удаленный порт в виде целого числа. Необязательно; по умолчаниюNone
.server
(Iterable[Unicode string, int]) – Итерабельность из двух элементов[host, port]
, гдеhost
- адрес прослушивания для данного сервера, аport
- целочисленный порт прослушивания. Необязательно; по умолчаниюNone
.
Серверы отвечают за обработку входящих и исходящих кодировок передачи данных. Запрос с кодированным телом chunked
должен быть автоматически де-чанкетирован сервером и представлен приложению как обычные байты тела; ответ, переданный серверу без << 1 >>>, может быть чанкетирован по усмотрению сервера.
Запрос¶
Отправляется для обозначения входящего запроса. Большая часть информации о запросе находится в области видимости соединения; сообщение body служит для передачи больших входящих HTTP-тела в виде кусков, а также как триггер для фактического выполнения кода запроса (поскольку вы не должны срабатывать только на открытие соединения).
Обратите внимание, что если запрос отправляется с использованием Transfer-Encoding: chunked
, сервер отвечает за обработку этой кодировки. Сообщения http.request
должны содержать только декодированное содержимое каждого чанка.
Ключи:
type
(Unicode string) –"http.request"
.body
(байтовая строка) – Тело запроса. Необязательно; по умолчаниюb""
. Если установлено значениеmore_body
, рассматривается как начало тела и конкатенируется на последующих фрагментах.more_body
(bool) – Указывает на наличие дополнительного содержимого (как часть сообщения Request). ЕслиTrue
, потребляющее приложение должно подождать, пока не получит чанк с этим значениемFalse
. ЕслиFalse
, запрос завершен и должен быть обработан. Необязательно; по умолчанию установлено значениеFalse
.
Начало ответа¶
Начинает отправку ответа клиенту. За ним должно следовать как минимум одно сообщение о содержимом ответа. Сервер протокола не должен начинать отправку ответа клиенту, пока не получит хотя бы одно событие Response Body.
Вы можете послать заголовок Transfer-Encoding
в этом сообщении, но сервер должен его проигнорировать. Серверы сами обрабатывают Transfer-Encoding
, и могут использовать Transfer-Encoding: chunked
, если приложение представляет ответ, в котором нет Content-Length
.
Обратите внимание, что это не то же самое, что Content-Encoding
, которое по-прежнему контролируется приложением, и которое является подходящим местом для установки gzip
или других флагов сжатия.
Ключи:
type
(Unicode string) –"http.response.start"
.status
(int) – Код состояния HTTP.headers
(Iterable[[byte string, byte string]]) – Итерабельная таблица[name, value]
из двух элементов, гдеname
- имя заголовка, аvalue
- значение заголовка. Порядок должен быть сохранен в ответе HTTP. Имена заголовков должны быть в нижнем регистре. Необязательно; по умолчанию используется пустой список. Псевдозаголовки (присутствующие в HTTP/2 и HTTP/3) не должны присутствовать.
Орган реагирования¶
Продолжает отправку ответа клиенту. Серверы протокола должны промыть все переданные им данные в буфер отправки перед возвращением из вызова отправки. Если more_body
установлено в False
, это приведет к закрытию соединения.
Ключи:
type
(Unicode string) –"http.response.body"
.body
(байтовая строка) – Содержимое тела HTTP. Конкатенируется с любыми предыдущими значениямиbody
, отправленными в этом диапазоне соединения. Необязательно; по умолчаниюb""
.more_body
(bool) – Указывает на наличие дополнительного содержимого (как часть сообщения Response Body). ЕслиFalse
, ответ будет считаться завершенным и закрытым, а все дальнейшие сообщения на канале будут игнорироваться. Необязательно; по умолчаниюFalse
.
Отключить¶
Отправляется в приложение при закрытии HTTP-соединения или при вызове receive
после отправки ответа. Это в основном полезно для длительного опроса, когда вы можете захотеть вызвать код очистки, если соединение закрывается раньше времени.
Ключи:
type
(Unicode string) –"http.disconnect"
.
WebSocket¶
WebSockets разделяют некоторые детали HTTP - у них есть путь и заголовки - но также имеют больше состояния. Опять же, большая часть этого состояния находится в области видимости, которая будет жить до тех пор, пока существует сокет.
Серверы протокола WebSocket должны сами обрабатывать сообщения PING/PONG и отправлять сообщения PING по мере необходимости, чтобы убедиться, что соединение живо.
Серверы протокола WebSocket должны сами обрабатывать фрагментацию сообщений и доставлять приложению полные сообщения.
Протокол WebSocket должен быть обозначен для приложений ASGI значением type
websocket
.
Область подключения¶
Область видимости соединений WebSocket живет столько же, сколько и сам сокет - если приложение умирает, сокет должен быть закрыт, и наоборот. Область видимости содержит начальные метаданные соединения (в основном из рукопожатия HTTP):
type
(Unicode string) –"websocket"
.asgi["version"]
(Unicode string) – Версия спецификации ASGI.asgi["spec_version"]
(Unicode string) – Версия спецификации ASGI HTTP, которую понимает этот сервер; одна из"2.0"
или"2.1"
. Необязательно; если отсутствует, считайте, что"2.0"
.http_version
(Unicode string) – Одно из"1.1"
или"2"
. Необязательно; по умолчанию"1.1"
.scheme
(Unicode string) – Часть схемы URL (вероятно,"ws"
или"wss"
). Необязательна (но не должна быть пустой); по умолчанию"ws"
.path
(Unicode string) – Цель HTTP-запроса, исключающая любую строку запроса, с кодированными в процентах последовательностями и последовательностями байтов UTF-8, декодированными в символы.raw_path
(байтовая строка) – Оригинальный компонент пути HTTP, не измененный из байтов, которые были получены веб-сервером. Некоторые реализации веб-сервера могут быть не в состоянии обеспечить это. Необязательно; по умолчаниюNone
.query_string
(байтовая строка) – Часть URL после?
. Необязательно; по умолчанию - пустая строка.root_path
(байтовая строка) – Корневой путь, по которому монтируется данное приложение; такой же, какSCRIPT_NAME
в WSGI. Необязательно; по умолчанию - пустая строка.headers
(Iterable[[byte string, byte string]]) – Итерабельность[name, value]
итерабельных элементов с двумя элементами, гдеname
- имя заголовка, аvalue
- его значение. Порядок должен быть сохранен от исходного HTTP-запроса; возможны дубликаты, которые должны быть сохранены в полученном сообщении. Имена заголовков должны быть в нижнем регистре. Псевдозаголовки (присутствующие в HTTP/2 и HTTP/3) должны быть удалены; если присутствует:authority
, его значение должно быть добавлено в начало итерабельной таблицы сhost
в качестве имени заголовка или заменить любой существующий заголовок хоста, который уже присутствует.client
(Iterable[Unicode string, int]) – Итерабельность из двух элементов[host, port]
, гдеhost
- IPv4 или IPv6 адрес удаленного хоста, аport
- удаленный порт. Необязательно; по умолчаниюNone
.server
(Iterable[Unicode string, int]) – Итерабельность из двух элементов[host, port]
, гдеhost
- адрес прослушивания для данного сервера, аport
- целочисленный порт прослушивания. Необязательно; по умолчаниюNone
.subprotocols
(Iterable[Unicode string]) – Подпротоколы, рекламируемые клиентом. Необязательно; по умолчанию - пустой список.
Подключение¶
Отправляется, когда клиент первоначально открывает соединение и собирается завершить квитирование WebSocket.
На это сообщение необходимо ответить либо сообщением Accept, либо сообщением Close, прежде чем сокет будет передавать сообщения websocket.receive
. Сервер протокола должен отправить это сообщение во время фазы рукопожатия WebSocket и не завершать рукопожатие до получения ответа, возвращая код состояния HTTP 403
, если соединение отклонено.
Ключи:
type
(Unicode string) –"websocket.connect"
.
Принять¶
Отправляется приложением, когда оно хочет принять входящее соединение.
type
(Unicode string) –"websocket.accept"
.subprotocol
(Unicode string) – Подпротокол, который сервер хочет принять. Необязательно; по умолчаниюNone
.headers
(Iterable[[byte string, byte string]]) – Итерабельная таблица[name, value]
из двух элементов, гдеname
- имя заголовка, аvalue
- его значение. Порядок должен быть сохранен в HTTP-ответе. Имена заголовков должны быть в нижнем регистре. Не должен включать заголовок с именемsec-websocket-protocol
; вместо него используйте ключsubprotocol
. Необязательно; по умолчанию - пустой список. Добавлено в спецификации версии 2.1. Псевдозаголовки (присутствующие в HTTP/2 и HTTP/3) не должны присутствовать.
Получить¶
Отправляется при получении сообщения данных от клиента.
Ключи:
type
(Unicode string) –"websocket.receive"
.bytes
(байтовая строка) – Содержание сообщения, если это был двоичный режим, илиNone
. Необязательно; если отсутствует, то эквивалентноNone
.text
(Unicode string) – Содержание сообщения, если это был текстовый режим, илиNone
. Необязательно; если отсутствует, то эквивалентноNone
.
Ровно один из bytes
или text
должен быть не None
. Однако один или оба ключа могут присутствовать.
Отправить¶
Отправляет сообщение данных клиенту.
Ключи:
type
(Unicode string) –"websocket.send"
.bytes
(байтовая строка) – Содержание двоичного сообщения, илиNone
.Необязательный; если отсутствует, то эквивалентен
None
.
text
(Unicode string) – Содержание текстового сообщения, илиNone
.Необязательный; если отсутствует, то эквивалентен
None
.
Ровно один из bytes
или text
должен быть не None
. Однако один или оба ключа могут присутствовать.
Отключение¶
Отправляется при потере соединения с клиентом, либо из-за закрытия соединения клиентом, либо из-за закрытия соединения сервером, либо из-за потери сокета.
Ключи:
type
(Unicode string) –"websocket.disconnect"
code
(int) – Код закрытия WebSocket, согласно спецификации WebSocket.
Закрыть¶
Сообщает серверу о необходимости закрыть соединение.
Если это сообщение отправлено до принятия сокета, сервер должен закрыть соединение с кодом ошибки HTTP 403 (Forbidden) и не завершить рукопожатие WebSocket; в некоторых браузерах это может выглядеть как другой код ошибки WebSocket (например, 1006, Abnormal Closure).
Если это сообщение отправлено после принятия сокета, сервер должен закрыть сокет с помощью кода закрытия, переданного в сообщении (или 1000, если он не указан).
type
(Unicode string) –"websocket.close"
.code
(int) – Код закрытия WebSocket, согласно спецификации WebSocket. Необязательно; по умолчанию1000
.
Совместимость с WSGI¶
Часть разработки HTTP-части этой спецификации заключается в том, чтобы убедиться, что она хорошо согласуется со спецификацией WSGI, чтобы обеспечить легкую адаптацию между обеими спецификациями и возможность продолжать использовать приложения WSGI с серверами ASGI.
Приложения WSGI, будучи синхронными, должны выполняться в пуле потоков для того, чтобы обслуживаться, но в противном случае их время выполнения совпадает с временем жизни диапазона HTTP-соединения.
Существует почти прямое отображение различных специальных ключей в переменной WSGI environ
на область видимости http
:
REQUEST_METHOD
- этоmethod
.SCRIPT_NAME
этоroot_path
PATH_INFO
может быть получено изpath
иroot_path
QUERY_STRING
этоquery_string
CONTENT_TYPE
можно извлечь изheaders
CONTENT_LENGTH
можно извлечь изheaders
SERVER_NAME
иSERVER_PORT
находятся вserver
.REMOTE_HOST
/REMOTE_ADDR
иREMOTE_PORT
находятся вclient
.SERVER_PROTOCOL
кодируется вhttp_version
wsgi.url_scheme
этоscheme
wsgi.input
- этоStringIO
, основанный на сообщенияхhttp.request
.wsgi.errors
направляется оберткой по мере необходимости
Вызываемый модуль start_response
отображается аналогично http.response.start
:
Аргумент
status
становитсяstatus
, при этом фраза причины опускается.response_headers
соответствуетheaders
Получение содержимого из приложения WSGI соответствует отправке сообщений http.response.body
.
Различия в кодировке WSGI¶
Спецификация WSGI (как определено в PEP 3333) определяет, что все строки, отправляемые на сервер или с сервера, должны быть типа str
, но содержать кодовые точки только в диапазоне ISO-8859-1 («latin-1»). Это связано с тем, что изначально он был разработан для Python 2 и его другого набора типов строк.
Спецификации ASGI HTTP и WebSocket вместо этого определяют каждый элемент диктанта scope
как строку байтов или строку Unicode. HTTP, будучи более старым протоколом, иногда несовершенен в указании кодировки, поэтому некоторые решения о том, что является Юникодом, а что байтами, могут быть неочевидны.
path
: URL-адреса могут иметь секции как с процентной кодировкой, так и с кодировкой UTF-8. Поскольку их декодирование часто выполняется базовым сервером (или иногда даже прокси-серверами в пути), это строка Unicode, полностью декодированная как из кодировки UTF-8, так и из кодировки процентов.headers
: Это байтовые строки точных последовательностей байтов, отправленных клиентом/ подлежащих отправке сервером. Хотя современные стандарты HTTP гласят, что заголовки должны быть ASCII, старые стандарты этого не делали и допускали более широкий диапазон символов. Фреймворки/приложения должны декодировать заголовки так, как они считают нужным.query_string
: В отличие отpath
, этот вариант не так подвержен вмешательству сервера и поэтому представлен в виде необработанной версии строки байтов, закодированной в процентах.root_path
: Строка Юникода для сопоставленияpath
.
2.0 (2017-11-28): Первоначальная спецификация ASGI на основе неканального уровня
Этот документ стал общественным достоянием.