http2: Реализация протокола HTTP/2 (Клиентская сторона) для nginx-module-lua
Установка
Если вы еще не настроили подписку на репозиторий RPM, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.
CentOS/RHEL 7 или Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-http2
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-http2
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-http2 v1.0, выпущенную 20 ноября 2019 года.
local http2 = require "resty.http2"
local host = "127.0.0.1"
local port = 8080
local sock = ngx.socket.tcp()
local ok, err = sock:connect(host, port)
if not ok then
ngx.log(ngx.ERR, "failed to connect ", host, ":", port, ": ", err)
return
end
local headers = {
{ name = ":authority", value = "test.com" },
{ name = ":method", value = "GET" },
{ name = ":path", value = "/index.html" },
{ name = ":scheme", value = "http" },
{ name = "accept-encoding", value = "gzip" },
{ name = "user-agent", value = "example/client" },
}
local on_headers_reach = function(ctx, headers)
-- Обработка заголовков ответа
end
local on_data_reach = function(ctx, data)
-- Обработка тела ответа
end
local opts = {
ctx = sock,
recv = sock.receive,
send = sock.send,
}
local client, err = http2.new(opts)
if not client then
ngx.log(ngx.ERR, "failed to create HTTP/2 client: ", err)
return
end
local ok, err = client:request(headers, nil, on_headers_reach, on_data_reach)
if not ok then
ngx.log(ngx.ERR, "client:process() failed: ", err)
return
end
sock:close()
В качестве более формального примера, пожалуйста, ознакомьтесь с util/example.lua.
Описание
Эта чистая библиотека Lua реализует клиентскую часть протокола HTTP/2, но не все детали охвачены, например, зависимости потоков поддерживаются, но никогда не используются.
Существуют некоторые врожденные ограничения, которые не решены.
Не может использоваться через соединения с рукопожатием SSL/TLS. tcpsock:sslhandshake не поддерживает расширения ALPN или NPN, поэтому в настоящее время можно использовать только обычные соединения, библиотека начнет сессию HTTP/2, отправив префикс соединения, т.е. строку:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
Эта библиотека предоставляет патч для согласования протоколов прикладного уровня. Просто используйте это, если это необходимо.
Можно отправить только один HTTP-запрос. В настоящее время реализованные API поддерживают отправку только одного HTTP-запроса. PR приветствуются для решения этой проблемы.
Повторное использование сессии HTTP/2. Протокол HTTP/2 разработан как постоянный, в то время как объект Cosocket связан с конкретным HTTP-запросом. Необходимо закрыть объект Cosocket или установить его в состояние alive до завершения запроса, эта модель конфликтует с повторным использованием сессии HTTP/2, только обходной путь может решить эту проблему, смотрите client:keepalive для получения подробностей.
Реализованный API
resty.http2
Чтобы загрузить этот модуль, просто выполните следующее:
local http2 = require "resty.http2"
http2.new
синтаксис: local client, err = http2.new(opts)
Создает клиент HTTP/2, указывая параметры. В случае неудачи будет возвращено nil и строка с сообщением об ошибке.
Единственный параметр opts, который является таблицей Lua, содержит некоторые поля:
-
recv, функция Lua, которая используется для чтения байтов; -
send, функция Lua, которая используется для отправки байтов; -
ctx, непрозрачные данные, действующие как контекст вызывающего;
Функции recv и send будут вызываться следующим образом:
local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
-
preread_size, число Lua, которое влияет на начальный размер окна отправки партнера (рекламируется через фрейм SETTINGS), по умолчанию65535; -
max_concurrent_stream, число Lua, которое ограничивает максимальное количество одновременно открытых потоков в сессии HTTP/2, по умолчанию128; -
max_frame_size, число Lua, которое ограничивает максимальный размер фрейма, который может отправить партнер, по умолчанию16777215. -
key, строка Lua, которая представляет кэшированную сессию HTTP/2, которую вызывающий хочет повторно использовать, если не найдено, будет создана новая сессия HTTP/2. Смотрите client:keepalive для получения дополнительных сведений.
client:acknowledge_settings
синтаксис: local ok, err = client:acknowledge_settings()
Подтверждает фрейм SETTINGS партнера, настройки будут применены автоматически.
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
client:request
синтаксис: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)
Отправляет HTTP-запрос партнеру,
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
headers должен быть массивоподобной таблицей Lua, представляющей заголовки HTTP-запроса, каждая запись выглядит как { name = "header1", value = "value1" }.
Стоит отметить, что эта библиотека не заботится о семантике заголовков HTTP, поэтому ответственность за это лежит на вызывающих, и вызывающие должны реализовать любые необходимые преобразования, например, Host должен быть преобразован в :authority. Также следующие заголовки будут игнорироваться, так как они специфичны для соединения.
ConnectionKeep-AliveProxy-ConnectionUpgradeTransfer-Encoding
body может быть строкой Lua, представляющей тело HTTP-запроса. Это также может быть функцией Lua для реализации потоковой загрузки. Когда body является функцией Lua, она будет вызываться следующим образом:
local part_data, last, err = body(size)
В случае неудачи body должна предоставить 3-й возвращаемый параметр err, чтобы сообщить этой библиотеке о возникновении фатальных ошибок, тогда этот метод будет немедленно прерван, и фрейм GOAWAY будет отправлен партнеру с кодом ошибки INTERNAL_ERROR.
Когда все данные были сгенерированы, 2-й возвращаемый параметр last должен быть предоставлен, и его значение должно быть true.
on_headers_reach должна быть функцией Lua, как обратный вызов, которая будет вызвана, когда будут получены полные заголовки HTTP-ответа, она будет вызываться следующим образом:
local abort = on_headers_reach(ctx, headers)
2-й параметр headers — это хешоподобная таблица Lua, представляющая заголовки HTTP-ответа, полученные от партнера.
on_headers_reach может решить, прерывать ли сессию HTTP/2, вернув логическое значение abort библиотеке, сессия HTTP/2 будет прервана, если on_headers_reach вернет истинное значение.
Последний параметр, on_data_reach, является функцией Lua, действующей как обратный вызов, которая будет вызвана каждый раз, когда будет получено тело ответа, она будет вызываться следующим образом:
local abort = on_data_reach(ctx, data)
2-й параметр data — это строка Lua, представляющая тело HTTP-ответа, полученное в этот раз.
Значение возвращаемого значения такое же, как и у on_headers_reach.
После того как этот метод вернется, сессия HTTP/2 все еще будет активна, можно решить закрыть эту сессию, вызвав client:close или продолжить делать что-то еще.
client:send_request
синтаксис: local stream, err = client:send_request(headers, body?)
Отправляет заголовки и тело (если есть) партнеру.
Значения headers и body такие же, как в client:request.
Соответствующий созданный объект потока будет возвращен, когда этот метод завершится.
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
client:read_headers
синтаксис: local headers, err = client:read_headers(stream)
Читает заголовки ответа от партнера, параметр stream — это тот, который был создан с помощью client:send_request.
Возвращаемые заголовки — это хешоподобная таблица Lua, которая содержит все заголовки HTTP-ответа, которые могут содержать некоторые псевдозаголовки, такие как ":status", вызывающие должны выполнять некоторые преобразования, если это необходимо.
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
client:read_body
синтаксис: local body, err = client:read_body(stream)
Читает фрейм DATA от партнера, параметр stream — это тот, который был создан с помощью client:send_request.
Возвращаемые данные — это строка Lua, представляющая часть тела ответа. Пустая строка будет возвращена, если все тело было прочитано.
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
client:close
синтаксис: local ok, err = client:close(code)
Закрывает текущую сессию HTTP/2 с кодом ошибки code.
Смотрите resty.http2.error, чтобы узнать о кодах ошибок.
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
client:keepalive
синтаксис: client:keepalive(key)
Кэширует текущую сессию HTTP/2 для повторного использования, обратите внимание, что неправильно сформированные сессии HTTP/2 никогда не будут кэшироваться. Сессия HTTP/2 будет отсоединена от соединения, точнее, от текущего объекта Cosocket.
Отсоединенная сессия HTTP/2 будет сохранена во внутренней хешоподобной таблице Lua, уникальный параметр key будет использоваться для индексации этой сессии, когда вызывающие захотят ее повторно использовать.
После того как эта сессия будет установлена как активная, вызывающие также должны установить объект Cosocket как keepalive.
Существует врожденное ограничение между сопоставлением сессии HTTP/2 и основным соединением. Сессия HTTP/2 может использоваться только в TCP-соединении, потому что она имеет состояние; если вызывающие хранят соединение в пуле, который кэширует несколько соединений, связь теряется, поскольку неясно, какое соединение выбрано для объекта Cosocket, следовательно, какое HTTP/2 сессия должна быть сопоставлена, также неизвестно.
Нет элегантного способа решить эту проблему, если только модель Cosocket не может назначить идентификатор основному соединению. Сейчас вызывающие могут использовать пул соединений единственного размера, чтобы обойти это ограничение, например:
...
sock:connect(host, port, { pool = "h2" })
...
sock:setkeepalive(75, 1)
client:keepalive("test")
resty.http2.protocol
Этот модуль реализует некоторые низкоуровневые API, относящиеся к протоколу.
Чтобы загрузить этот модуль, просто выполните следующее:
local protocol = require "resty.http2.protocol"
protocol.session
синтаксис: local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)
Создает новую сессию HTTP/2, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
Значение каждого параметра такое же, как описано в http2.new.
Начальный фрейм SETTINGS и фрейм WINDOW_UPDATE будут отправлены перед тем, как эта функция вернется.
session:adjust_window
синтаксис: local ok = session:adjust_window(delta)
Регулирует размер окна отправки каждого потока, поток будет сброшен, если измененный размер окна отправки превышает MAX_WINDOW_SIZE, в этом случае ok будет nil.
session:frame_queue
синтаксис: session:frame_queue(frame)
Добавляет frame в текущую очередь вывода сессии.
session:flush_queue
синтаксис: local ok, err = session:flush_queue()
Упаковывает и очищает очередь фреймов, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
session:submit_request
синтаксис: local ok, err = session:submit_request(headers, no_body, priority?, pad?)
Отправляет HTTP-запрос в текущую сессию HTTP/2, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
Значение каждого параметра:
-
headers, должна быть хешоподобной таблицей Lua, представляющей заголовки HTTP-запроса, стоит отметить, что эта библиотека не заботится о семантике заголовков HTTP, поэтому ответственность за это лежит на вызывающих, и вызывающие должны преобразовать любые необходимые псевдозаголовки. Например,:authorityдолжен быть передан вместоHost; -
no_body, логическое значение, указывающее, есть ли у этого запроса тело. Когда оно истинно, сгенерированный фрейм HEADERS будет содержать флаг END_HEADERS; -
priority, хешоподобная таблица Lua, которая используется для определения пользовательских зависимостей потоков: priority.sidпредставляет идентификатор зависимого потока;priority.excl, указывает, станет ли новый поток единственной зависимостью потока, указанногоpriority.sid;-
priority.weightопределяет вес нового потока; -
pad, данные для дополнения.
session:submit_window_update
синтаксис: local ok, err = session:submit_window_update(incr)
Отправляет фрейм WINDOW_UPDATE для всей сессии HTTP/2 с увеличением incr, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
session:recv_frame
синтаксис: local frame, err = session:recv_frame()
Принимает фрейм HTTP/2, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
Соответствующее действие будет выполнено автоматически, например, фрейм GOAWAY будет отправлен, если партнер нарушает конвенции протокола HTTP/2; фрейм WINDOW_UPDATE будет отправлен, если окно отправки партнера становится слишком маленьким.
session:close
синтаксис: session:close(code?, debug_data?)
Генерирует фрейм GOAWAY с кодом ошибки code и отладочными данными debug_data, код ошибки по умолчанию — NO_ERROR, а отладочные данные — nil.
Обратите внимание, что эта функция просто ставит фрейм GOAWAY в очередь на вывод, вызывающие должны вызвать session:flush_queue, чтобы действительно отправить фреймы.
session:detach
синтаксис: session:detach()
Отсоединяет текущую сессию HTTP/2 от объекта Cosocket.
session:attach
синтаксис: local ok, err = session:attach(recv, send, ctx)
Присоединяет текущую сессию HTTP/2 к объекту Cosocket, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
Значения recv, send и ctx такие же, как описано в http.new.
resty.http2.stream
Этот модуль реализует некоторые низкоуровневые API, относящиеся к потокам.
Чтобы загрузить этот модуль, просто выполните следующее:
local h2_stream = require "resty.http2.stream"
h2_stream.new
синтаксис: local stream = h2_stream.new(sid, weight, session)
Создает новый поток с идентификатором sid, весом weight и сессией HTTP/2, к которой он принадлежит.
h2_stream.new_root
синтаксис: local root_stream = h2_stream.new_root(session)
Создает корневой поток с его сессией.
Идентификатор корневого потока — 0x0, и он действительно является виртуальным потоком, который используется для управления всей сессией HTTP/2.
stream:submit_headers
синтаксис: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)
Отправляет некоторые HTTP-заголовки в поток.
Первый параметр headers должен быть хешоподобной таблицей Lua, представляющей заголовки HTTP-запроса, стоит отметить, что эта библиотека не заботится о семантике заголовков HTTP, поэтому ответственность за это лежит на вызывающих, и вызывающие должны преобразовать любые необходимые псевдозаголовки. Например, :authority должен быть передан вместо Host;
Параметр end_stream должен быть логическим значением и используется для управления тем, должен ли фрейм HEADERS принимать флаг END_STREAM, в основном вызывающие могут установить его в истинное значение, если нет тела запроса, которое нужно отправить.
priority должна быть хешоподобной таблицей Lua (если есть), которая используется для определения пользовательских зависимостей потоков:
* priority.sid представляет идентификатор зависимого потока;
* priority.excl, указывает, станет ли новый поток единственной зависимостью потока, указанного priority.sid;
* priority.weight определяет вес нового потока;
Последний параметр pad представляет данные для дополнения.
В случае неудачи будет возвращено nil и строка Lua, описывающая соответствующую ошибку.
stream:submit_data
синтаксис: local ok, err = stream:submit_data(data, pad, last)
Отправляет некоторое тело запроса в поток, data должна быть строкой Lua, с необязательными данными для дополнения.
Последний параметр last указывает, является ли это последней отправкой, текущий фрейм DATA будет прикреплен с флагом END_STREAM, если last истинно.
В случае неудачи будет возвращено nil и строка Lua, описывающая соответствующую ошибку.
stream:submit_window_update
синтаксис: local ok, err = session:submit_window_update(incr)
Отправляет фрейм WINDOW_UPDATE для потока с увеличением incr, в случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
stream:set_dependency
синтаксис: stream:set_dependency(depend, excl)
Устанавливает зависимости текущего потока к потоку с идентификатором depend.
Второй параметр excl указывает, станет ли текущий поток единственным дочерним потоком depend.
Когда depend отсутствует, целевой поток будет корневым, а excl будет считаться false.
stream:rst
синтаксис: stream:rst(code)
Генерирует фрейм RST_STREAM с кодом ошибки code. В случае отсутствия code будет выбран код NO_ERROR.
Обратите внимание, что этот метод просто генерирует фрейм RST_STREAM, а не отправляет его, вызывающий должен отправить этот фрейм, вызвав session:flush_queue.
resty.http2.frame
Этот модуль реализует некоторые низкоуровневые API, относящиеся к фреймам.
Чтобы загрузить этот модуль, просто выполните следующее:
local h2_frame = require "resty.http2.frame"
h2_frame.header.new
синтаксис: local hd = h2_frame.header.new(length, typ, flags, id)
Создает заголовок фрейма с длиной полезной нагрузки length, типом фрейма type и принимает flags как флаги фрейма, который принадлежит потоку id.
h2_frame.header.pack
синтаксис: h2_frame.header.pack(hd, dst)
Сериализует заголовок фрейма hd в назначение dst. dst должен быть массивоподобной таблицей Lua.
h2_frame.header.unpack
синтаксис: h2_frame.header.unpack(src)
Десериализует заголовок фрейма из строки Lua src, длина src должна быть не менее 9 октетов.
h2_frame.priority.pack
синтаксис: h2_frame.priority.pack(pf, dst)
Сериализует фрейм PRIORITY в назначение dst. dst должен быть массивоподобной таблицей Lua.
pf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;depend, идентификатор зависимого потока;excl, указывает, станет ли текущий поток, где находится этот фрейм PRIORITY, единственным дочерним потоком потока, идентифицированногоdepend;weight, назначает новый весweightтекущему потоку;
h2_frame.priority.unpack
синтаксис: local ok, err = h2_frame.priority.unpack(pf, src, stream)
Десериализует фрейм PRIORITY из строки Lua src, длина src должна быть не менее размера, указанного в pf.header.length.
pf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма PRIORITY, т.е. pf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм PRIORITY.
Соответствующие действия будут выполнены автоматически внутри этого метода, например, создание новых зависимостей.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.rst_stream.pack
синтаксис: h2_frame.rst_stream.pack(rf, dst)
Сериализует фрейм RST_STREAM в назначение dst. dst должен быть массивоподобной таблицей Lua.
rf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;error_code, код ошибки;
h2_frame.rst_stream.unpack
синтаксис: local ok, err = h2_frame.rst_stream.unpack(rf, src, stream)
Десериализует фрейм RST_STREAM из строки Lua src. Длина src должна быть не менее размера, указанного в rf.header.length.
rf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма RST_STREAM, т.е. rf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм RST_STREAM.
Соответствующие действия будут выполнены автоматически внутри этого метода, например, изменение состояния потока.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.rst_stream.new
синтаксис: local rf = h2_frame.rst_stream.new(error_code, sid)
Создает фрейм RST_STREAM с кодом ошибки error_code, который принадлежит потоку sid.
h2_frame.settings.pack
синтаксис: h2_frame.settings.pack(sf, dst)
Сериализует фрейм SETTINGS в назначение dst. dst должен быть массивоподобной таблицей Lua.
sf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;item, конкретные настройки, которые должны быть массивоподобной таблицей Lua, каждый элемент должен быть хешоподобной таблицей Lua:id, идентификатор настройки, может быть:- SETTINGS_ENABLE_PUSH (0x2)
- SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
- SETTINGS_INITIAL_WINDOW_SIZE (0x4)
- SETTINGS_MAX_FRAME_SIZE (0x5)
value, соответствующее значение настройки;
h2_frame.settings.unpack
синтаксис: local ok, err = h2_frame.settings.unpack(sf, src, stream)
Десериализует фрейм SETTINGS из строки Lua src. Длина src должна быть не менее размера, указанного в sf.header.length.
sf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма SETTINGS, т.е. sf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм SETTINGS (должен быть корневым потоком).
Соответствующие действия будут выполнены автоматически внутри этого метода, например, обновление значений настроек сессии HTTP/2.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.settings.new
синтаксис: local sf = h2_frame.settings.new(flags, payload)
Создает фрейм SETTINGS с флагами flags и полезной нагрузкой payload.
payload должна быть массивоподобной таблицей Lua, каждый элемент должен быть хешоподобной таблицей Lua:
* id, идентификатор настройки, может быть:
* SETTINGS_ENABLE_PUSH (0x2)
* SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
* SETTINGS_INITIAL_WINDOW_SIZE (0x4)
* SETTINGS_MAX_FRAME_SIZE (0x5)
* value, соответствующее значение настройки;
h2_frame.ping.pack
синтаксис: h2_frame.ping.pack(pf, dst)
Сериализует фрейм PING в назначение dst. dst должен быть массивоподобной таблицей Lua.
pf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;opaque_data_hi, старшие 32 бита соответствующих данных пинга;opaque_data_lo, младшие 32 бита соответствующих данных пинга;
h2_frame.ping.unpack
синтаксис: local ok, err = h2_frame.ping.unpack(pf, src, stream)
Десериализует фрейм PING из строки Lua src. Длина src должна быть не менее размера, указанного в sf.header.length.
pf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма PING, т.е. pf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм PING (должен быть корневым потоком).
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.goaway.pack
синтаксис: h2_frame.goaway.pack(gf, dst)
Сериализует фрейм GOAWAY в назначение dst. dst должен быть массивоподобной таблицей Lua.
gf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;last_stream_id, последний инициализированный партнером идентификатор потока;error_code, код ошибки;debug_data, отладочные данные;
h2_frame.goaway.unpack
синтаксис: local ok, err = h2_frame.goaway.unpack(gf, src, stream)
Десериализует фрейм GOAWAY из строки Lua src. Длина src должна быть не менее размера, указанного в gf.header.length.
gf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма GOAWAY, т.е. gf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм GOAWAY (должен быть корневым потоком).
В случае неудачи будет возвращено nil и строка Lua, описывающая причину ошибки.
h2_frame.goaway.new
синтаксис: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)
Создает фрейм GOAWAY с последним инициализированным партнером идентификатором потока last_sid и кодом ошибки error_code. Опционально, с отладочными данными debug_data.
h2_frame.window_update.pack
синтаксис: h2_frame.window_update.pack(wf, dst)
Сериализует фрейм WINDOW_UPDATE в назначение dst. dst должен быть массивоподобной таблицей Lua.
wf должен быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;window_size_increment, увеличение размера окна;
h2_frame.window_update.unpack
синтаксис: local ok, err = h2_frame.window_update.unpack(wf, src, stream)
Десериализует фрейм WINDOW_UPDATE из строки Lua src. Длина src должна быть не менее размера, указанного в wf.header.length.
wf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма WINDOW_UPDATE, т.е. wf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм WINDOW_UPDATE.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.window_update.new
синтаксис: local wf = h2_frame.window_update.new(sid, window)
Создает фрейм WINDOW_UPDATE с идентификатором потока sid и увеличивает размер окна, указанный window.
h2_frame.headers.pack
синтаксис: h2_frame.headers.pack(hf, dst)
Сериализует фрейм HEADERS в назначение dst. dst должен быть массивоподобной таблицей Lua.
hf должна быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;pad, данные для дополнения;depend, идентификатор зависимого потока;excl, указывает, станет ли поток, которому принадлежит текущий фрейм HEADERS, единственным дочерним потоком потокаdepend;weight, указывает вес потока, которому принадлежит текущий фрейм HEADERS;block_frags, обычные HTTP-заголовки (после сжатия hpack);
h2_frame.headers.unpack
синтаксис: local ok,err = h2_frame.headers.unpack(hf, src, stream)
Десериализует фрейм HEADERS из строки Lua src, длина src должна быть не менее размера, указанного в hf.header.length.
hf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма HEADERS, т.е. hf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм HEADERS.
Соответствующее действие будет выполнено, например, произойдет переход состояния потока.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.headers.new
синтаксис: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)
Создает фрейм HEADERS, который принимает блок фрагментов frags.
Параметр pri может быть использован для указания зависимостей потока, pri должна быть хешоподобной таблицей Lua, которая содержит:
sid, идентификатор зависимого потока;excl, указывает, станет ли потокsidединственным дочерним потоком зависимого потока;weight, определяет вес текущего потока (указанногоsid);
Параметр pad указывает данные для дополнения, которые являются необязательными.
Когда end_stream истинно, текущий фрейм HEADERS будет принимать флаг END_STREAM, аналогично, когда end_headers истинно, текущий фрейм HEADERS будет принимать флаг END_HEADERS.
Необходимо учитывать, что если текущий фрейм HEADERS не содержит все заголовки, то один или несколько фреймов CONTINUATION должны следовать в соответствии с протоколом HTTP/2.
h2_frame.continuation.pack
синтаксис: h2_frame.continuation.pack(cf, dst)
Сериализует фрейм CONTINUATION в назначение dst. dst должен быть массивоподобной таблицей Lua.
cf должна быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;block_frags, обычные HTTP-заголовки (после сжатия hpack);
h2_frame.continuation.unpack
синтаксис: local ok, err = h2_frame.continuation.unpack(cf, src, stream)
Десериализует фрейм CONTINUATION из строки Lua src, длина src должна быть не менее размера, указанного в cf.header.length.
cf должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма CONTINUATION, т.е. cf.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм CONTINUATION.
Соответствующее действие будет выполнено, например, произойдет переход состояния потока.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.continuation.new
синтаксис: local cf = h2_frame.continuation.new(frags, end_headers, sid)
Создает фрейм CONTINUATION, который принимает блок фрагментов frags.
Когда end_headers истинно, текущий фрейм CONTINUATION будет принимать флаг END_HEADERS.
Необходимо учитывать, что если текущий фрейм CONTINUATION не содержит все заголовки, то один или несколько фреймов CONTINUATION должны следовать в соответствии с протоколом HTTP/2.
Параметр sid указывает поток, которому принадлежит текущий фрейм CONTINUATION.
h2_frame.data.pack
синтаксис: h2_frame.data.pack(df, dst)
Сериализует фрейм DATA в назначение dst. dst должен быть массивоподобной таблицей Lua.
df должна быть хешоподобной таблицей Lua, которая содержит:
header, заголовок фрейма;payload, тело HTTP-запроса/ответа;
h2_frame.data.unpack
синтаксис: local ok, err = h2_frame.data.unpack(df, src, stream)
Десериализует фрейм DATA из строки Lua src, длина src должна быть не менее размера, указанного в df.header.length.
df должна быть хешоподобной таблицей Lua, которая уже содержит заголовок текущего фрейма DATA, т.е. df.header.
Последний параметр stream указывает поток, которому принадлежит текущий фрейм DATA.
Соответствующее действие будет выполнено, например, произойдет переход состояния потока.
В случае неудачи будет возвращено nil и код ошибки.
h2_frame.data.new
синтаксис: local df = h2_frame.data.new(payload, pad, last, sid)
Создает фрейм DATA, который принимает полезную нагрузку payload.
Параметр pad указывает данные для дополнения, которые являются необязательными.
Когда last истинно, текущий фрейм DATA будет принимать флаг END_STREAM.
Параметр sid указывает поток, которому принадлежит текущий фрейм DATA.
h2_frame.push_promise.unpack
синтаксис: local df = h2_frame.data.new(payload, pad, last, sid)
В настоящее время любой входящий фрейм PUSH_PROMISE будет отклонен.
Этот метод всегда возвращает nil и ошибку PROTOCOL_ERROR.
resty.http2.hpack
Этот модуль реализует некоторые низкоуровневые API HPACK.
Чтобы загрузить этот модуль, просто выполните следующее:
local hpack = require "resty.http2.hpack"
hpack.encode
синтаксис: hpack.encode(src, dst, lower)
Кодирует строку Lua src в назначение dst, dst должен быть массивоподобной таблицей Lua. Сначала будут пробоваться коды Хаффмана.
Параметр lower указывает, является ли текущая операция кодирования нечувствительной к регистру, по умолчанию false.
hpack.indexed
синтаксис: local v = hpack.indexed(index)
Возвращает индекс после использования представления индексированного заголовка.
hpack.incr_indexed
синтаксис: local v = hpack.indexed(index)
Возвращает индекс после использования литерального заголовка с инкрементной индексацией.
hpack.new
синтаксис: local h = hpack.new(size)
Создает экземпляр hpack, так как декодирование HPACK имеет состояние.
Параметр size представляет максимальный размер таблицы hpack, по умолчанию 4096 байт.
Возвращаемое значение h представляет экземпляр HPACK. Один из членов h важен, т.е. h.cached, который сохраняет все фрагменты блоков заголовков, и h:decode будет анализировать данные внутри h.cached.
В настоящее время h2_frame.headers.unpack и h2_frame.continuation.unpack будут добавлять фрагменты блоков заголовков в h.cached, как только блок будет завершен, декодирование будет выполнено.
h:insert_entry
синтаксис: local ok = h:insert_entry(header_name, header_value)
Пытается вставить запись заголовка с именем header_name и значением header_value в динамическую таблицу HPACK.
Вставка может не удаться, если эта запись слишком велика. Необходимая эвакуация записей произойдет, если места будет недостаточно.
Этот метод вернет true, если вставка успешна, или false, если нет.
h:resize
синтаксис: local ok = h:resize(new_size)
Регулирует размер динамической таблицы до new_size, в настоящее время new_size не может превышать 4096, иначе операция изменения размера завершится неудачей.
Когда размер динамической таблицы уменьшается, некоторые записи будут эвакуированы в соответствии с правилами HPACK.
Этот метод вернет true, если операция изменения размера успешна, или false, если нет.
h:decode
синтаксис: local ok, err = h:decode(dst)
Декодирует фрагменты блоков заголовков внутри h.cached, декодированные заголовки будут сохранены в dst, хешоподобной таблице Lua.
В случае неудачи будет возвращено nil и код ошибки.
h:get_indexed_header
синтаксис: local entry = h:get_indexed_header(index)
Возвращает запись заголовка в соответствии с индексом index.
Возвращаемое значение будет nil, если индекс недействителен, в противном случае entry будет хешоподобной таблицей Lua с двумя элементами:
entry.name, имя заголовка;entry.value, значение заголовка;
resty.http2.error
Этот модуль реализует некоторые низкоуровневые API, относящиеся к ошибкам.
Существует множество определенных кодов ошибок, которые в основном соответствуют протоколу HTTP/2:
h2_error.NO_ERRORh2_error.PROTOCOLh2_error.INTERNAL_ERRORh2_error.FLOW_CONTROL_ERRORh2_error.SETTINGS_TIMEOUTh2_error.STREAM_CLOSEDh2_error.FRAME_SIZE_ERRORh2_error.REFUSED_STREAMh2_error.CANCELh2_error.COMPRESSION_ERRORh2_error.CONNECT_ERRORh2_error.ENHANCE_YOUR_CALMh2_error.INADEQUATE_SECURITYh2_error.HTTP_1_1_REQUIRED
И три пользовательских кода ошибки:
h2_error.STREAM_PROTOCOL_ERROR, ошибка протокола на уровне потока;h2_error.STREAM_FLOW_CONTROL_ERROR, ошибка управления потоком на уровне потока;h2_error.STREAM_FRAME_SIZE_ERROR, ошибка размера фрейма на уровне потока;
Ошибки на уровне потока не повлияют на все соединение, но сбросят текущий поток.
Чтобы загрузить этот модуль, просто выполните следующее:
h2_error.strerror
синтаксис: local msg = h2_error.strerror(code)
Возвращает строку Lua, которая описывает код ошибки code, будет возвращено "unknown error", если код ошибки неизвестен.
h2_error.is_stream_error
синтаксис: local ok = h2_error.is_stream_error(code)
Определяет, является ли код ошибки code ошибкой на уровне потока.
См. также
- upyun-resty: https://github.com/upyun/upyun-resty
- lua-resty-httpipe: https://github.com/timebug/lua-resty-httpipe
- lua-resty-requests: https://github.com/tokers/lua-resty-requests
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-http2.