HTTP и WebSocket Формат сообщений ASGI

Версия: 2.1 (2019-03-20)

Спецификация HTTP+WebSocket ASGI описывает, как передавать HTTP/1.1, HTTP/2 и WebSocket соединения в ASGI.

Он намеренно задуман и разработан как надмножество формата WSGI и определяет, как осуществлять перевод между ними для набора запросов, которые могут быть обработаны WSGI.

Специальные версии

У этой спецификации было две версии:

  • 2.0: Первая версия спецификации, выпущенная вместе с ASGI 2.0

  • 2.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 на основе неканального уровня

Этот документ стал общественным достоянием.

Вернуться на верх