websocket: Поддержка WebSocket для модуля 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-websocket
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-websocket
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-websocket v0.14, выпущенный 7 июня 2026 года.
Эта библиотека Lua реализует сервер WebSocket и клиентские библиотеки на основе модуля ngx_lua.
Эта библиотека Lua использует API cosocket модуля ngx_lua, который обеспечивает 100% неблокирующее поведение.
Обратите внимание, что поддерживается только RFC 6455. Более ранние версии протокола, такие как "hybi-10", "hybi-07" и "hybi-00", не поддерживаются и не будут рассматриваться.
Синопсис
local server = require "resty.websocket.server"
local wb, err = server:new{
timeout = 5000, -- в миллисекундах
max_payload_len = 65535,
}
if not wb then
ngx.log(ngx.ERR, "не удалось создать websocket: ", err)
return ngx.exit(444)
end
local data, typ, err = wb:recv_frame()
if not data then
if not string.find(err, "timeout", 1, true) then
ngx.log(ngx.ERR, "не удалось получить кадр: ", err)
return ngx.exit(444)
end
end
if typ == "close" then
-- для типа "close" err содержит код состояния
local code = err
-- отправить кадр закрытия обратно:
local bytes, err = wb:send_close(1000, "достаточно, достаточно!")
if not bytes then
ngx.log(ngx.ERR, "не удалось отправить кадр закрытия: ", err)
return
end
ngx.log(ngx.INFO, "закрытие с кодом состояния ", code, " и сообщением ", data)
return
end
if typ == "ping" then
-- отправить кадр pong обратно:
local bytes, err = wb:send_pong(data)
if not bytes then
ngx.log(ngx.ERR, "не удалось отправить кадр: ", err)
return
end
elseif typ == "pong" then
-- просто игнорировать входящий кадр pong
else
ngx.log(ngx.INFO, "получен кадр типа ", typ, " и полезная нагрузка ", data)
end
wb:set_timeout(1000) -- изменить сетевой тайм-аут на 1 секунду
bytes, err = wb:send_text("Hello world")
if not bytes then
ngx.log(ngx.ERR, "не удалось отправить текстовый кадр: ", err)
return ngx.exit(444)
end
bytes, err = wb:send_binary("blah blah blah...")
if not bytes then
ngx.log(ngx.ERR, "не удалось отправить бинарный кадр: ", err)
return ngx.exit(444)
end
local bytes, err = wb:send_close(1000, "достаточно, достаточно!")
if not bytes then
ngx.log(ngx.ERR, "не удалось отправить кадр закрытия: ", err)
return
end
Модули
resty.websocket.server
Чтобы загрузить этот модуль, просто выполните
local server = require "resty.websocket.server"
Методы
new
синтаксис: wb, err = server:new()
синтаксис: wb, err = server:new(opts)
Выполняет процесс рукопожатия websocket на стороне сервера и возвращает объект WebSocket сервера.
В случае ошибки возвращает nil и строку, описывающую ошибку.
Можно указать необязательную таблицу параметров. Следующие параметры следующие:
-
max_payload_lenУказывает максимальную длину полезной нагрузки, разрешенную при отправке и получении кадров WebSocket. По умолчанию
65535. *max_recv_lenУказывает максимальную длину полезной нагрузки, разрешенную при получении кадров WebSocket. По умолчанию равна значению
max_payload_len. *max_send_lenУказывает максимальную длину полезной нагрузки, разрешенную при отправке кадров WebSocket. По умолчанию равна значению
max_payload_len. *send_maskedУказывает, следует ли отправлять замаскированные кадры WebSocket. Если это
true, замаскированные кадры всегда отправляются. По умолчаниюfalse. *timeoutУказывает порог сетевого тайм-аута в миллисекундах. Вы можете изменить эту настройку позже с помощью вызова метода
set_timeout. Обратите внимание, что эта настройка тайм-аута не влияет на процесс отправки заголовка HTTP-ответа для рукопожатия websocket; вам нужно одновременно настроить директиву send_timeout.
set_timeout
синтаксис: wb:set_timeout(ms)
Устанавливает задержку тайм-аута (в миллисекундах) для операций, связанных с сетью.
send_text
синтаксис: bytes, err = wb:send_text(text)
Отправляет аргумент text как неразделенный кадр данных типа text. Возвращает количество байт, которые фактически были отправлены на уровне TCP.
В случае ошибок возвращает nil и строку, описывающую ошибку.
send_binary
синтаксис: bytes, err = wb:send_binary(data)
Отправляет аргумент data как неразделенный кадр данных типа binary. Возвращает количество байт, которые фактически были отправлены на уровне TCP.
В случае ошибок возвращает nil и строку, описывающую ошибку.
send_ping
синтаксис: bytes, err = wb:send_ping()
синтаксис: bytes, err = wb:send_ping(msg)
Отправляет кадр ping с необязательным сообщением, указанным аргументом msg. Возвращает количество байт, которые фактически были отправлены на уровне TCP.
В случае ошибок возвращает nil и строку, описывающую ошибку.
Обратите внимание, что этот метод не ждет кадра pong от удаленной стороны.
send_pong
синтаксис: bytes, err = wb:send_pong()
синтаксис: bytes, err = wb:send_pong(msg)
Отправляет кадр pong с необязательным сообщением, указанным аргументом msg. Возвращает количество байт, которые фактически были отправлены на уровне TCP.
В случае ошибок возвращает nil и строку, описывающую ошибку.
send_close
синтаксис: bytes, err = wb:send_close()
синтаксис: bytes, err = wb:send_close(code, msg)
Отправляет кадр close с необязательным кодом состояния и сообщением.
В случае ошибок возвращает nil и строку, описывающую ошибку.
Для списка допустимых кодов состояния смотрите следующий документ:
http://tools.ietf.org/html/rfc6455#section-7.4.1
Обратите внимание, что этот метод не ждет кадра close от удаленной стороны.
send_frame
синтаксис: bytes, err = wb:send_frame(fin, opcode, payload)
Отправляет необработанный кадр websocket, указывая поле fin (логическое значение), код операции и полезную нагрузку.
Для списка допустимых кодов операции смотрите
http://tools.ietf.org/html/rfc6455#section-5.2
В случае ошибок возвращает nil и строку, описывающую ошибку.
Чтобы контролировать максимальную длину полезной нагрузки, разрешенной, вы можете передать параметр max_payload_len в конструктор new.
Чтобы контролировать, следует ли отправлять замаскированные кадры, вы можете передать true параметру send_masked в методе конструктора new. По умолчанию отправляются незамаскированные кадры.
recv_frame
синтаксис: data, typ, err = wb:recv_frame()
Получает кадр WebSocket из сети.
В случае ошибки возвращает два значения nil и строку, описывающую ошибку.
Второе возвращаемое значение всегда является типом кадра, который может быть одним из continuation, text, binary, close, ping, pong или nil (для неизвестных типов).
Для кадров close возвращает 3 значения: дополнительное сообщение состояния (которое может быть пустой строкой), строку "close" и число Lua для кода состояния (если есть). Для возможных кодов закрытия смотрите
http://tools.ietf.org/html/rfc6455#section-7.4.1
Для других типов кадров просто возвращает полезную нагрузку и тип.
Для фрагментированных кадров значение err возвращается как строка Lua "again".
resty.websocket.client
Чтобы загрузить этот модуль, просто выполните
local client = require "resty.websocket.client"
Простой пример для демонстрации использования:
local client = require "resty.websocket.client"
local wb, err = client:new()
local uri = "ws://127.0.0.1:" .. ngx.var.server_port .. "/s"
local ok, err, res = wb:connect(uri)
if not ok then
ngx.say("не удалось подключиться: " .. err)
return
end
local data, typ, err = wb:recv_frame()
if not data then
ngx.say("не удалось получить кадр: ", err)
return
end
ngx.say("получено: ", data, " (", typ, "): ", err)
local bytes, err = wb:send_text("copy: " .. data)
if not bytes then
ngx.say("не удалось отправить кадр: ", err)
return
end
local bytes, err = wb:send_close()
if not bytes then
ngx.say("не удалось отправить кадр: ", err)
return
end
Методы
client:new
синтаксис: wb, err = client:new()
синтаксис: wb, err = client:new(opts)
Создает объект клиента WebSocket.
В случае ошибки возвращает nil и строку, описывающую ошибку.
Можно указать необязательную таблицу параметров. Следующие параметры следующие:
-
max_payload_lenУказывает максимальную длину полезной нагрузки, разрешенную при отправке и получении кадров WebSocket. По умолчанию
65536. *max_recv_lenУказывает максимальную длину полезной нагрузки, разрешенную при получении кадров WebSocket. По умолчанию равна значению
max_payload_len. *max_send_lenУказывает максимальную длину полезной нагрузки, разрешенную при отправке кадров WebSocket. По умолчанию равна значению
max_payload_len. *send_unmaskedУказывает, следует ли отправлять незамаскированные кадры WebSocket. Если это
true, незамаскированные кадры всегда отправляются. По умолчаниюfalse. Однако RFC 6455 требует, чтобы клиент ДОЛЖЕН отправлять замаскированные кадры на сервер, поэтому никогда не устанавливайте эту опцию вtrue, если вы не знаете, что делаете. *timeoutУказывает порог сетевого тайм-аута по умолчанию в миллисекундах. Вы можете изменить эту настройку позже с помощью вызова метода
set_timeout.
client:connect
синтаксис: ok, err, res = wb:connect("ws://<host>:<port>/<path>")
синтаксис: ok, err, res = wb:connect("wss://<host>:<port>/<path>")
синтаксис: ok, err, res = wb:connect("ws://<host>:<port>/<path>", options)
синтаксис: ok, err, res = wb:connect("wss://<host>:<port>/<path>", options)
Подключается к удаленному порту службы WebSocket и выполняет процесс рукопожатия websocket на стороне клиента.
Перед фактическим разрешением имени хоста и подключением к удаленному бэкенду этот метод всегда будет искать в пуле соединений совпадающие неактивные соединения, созданные предыдущими вызовами этого метода.
Третье возвращаемое значение этого метода содержит необработанный, простой текстовый ответ (строка состояния и заголовки) на запрос рукопожатия. Это позволяет вызывающему выполнить дополнительную проверку и/или извлечь заголовки ответа. Когда соединение повторно используется и запрос рукопожатия не отправляется, строка "connection reused" возвращается вместо ответа.
Можно указать необязательную таблицу Lua в качестве последнего аргумента этого метода для указания различных параметров подключения:
-
protocolsУказывает все подпроtocolы, используемые для текущей сессии WebSocket. Это может быть таблица Lua, содержащая все имена подпроtocolов, или просто одна строка Lua. *
originУказывает значение заголовка запроса
Origin. *poolУказывает пользовательское имя для используемого пула соединений. Если не указано, имя пула соединений будет сгенерировано из строкового шаблона
<host>:<port>. *pool_size
указывает размер пула соединений. Если не указано и не
была предоставлена опция backlog, пул не будет создан. Если не указано,
но backlog была предоставлена, пул будет создан с размером по умолчанию,
равным значению директивы lua_socket_pool_size.
Пул соединений удерживает до pool_size активных соединений,
готовых к повторному использованию последующими вызовами к connect, но
обратите внимание, что нет верхнего предела для общего числа открытых соединений
вне пула. Если вам нужно ограничить общее количество открытых
соединений, укажите опцию backlog.
Когда пул соединений превысит свой лимит по размеру, будет закрыто
наименее недавно использованное (поддерживаемое) соединение, уже находящееся в пуле, чтобы освободить место для
текущего соединения.
Обратите внимание, что пул соединений cosocket относится к каждому рабочему процессу Nginx, а не к каждому экземпляру сервера Nginx, поэтому указанный здесь лимит размера также применяется
к каждому отдельному рабочему процессу Nginx. Также обратите внимание, что размер пула соединений не может быть изменен после его создания.
Эта опция была впервые представлена в релизе v0.10.14.
backlog
если указано, этот модуль ограничит общее количество открытых соединений
для этого пула. Не более pool_size соединений могут быть открыты
для этого пула в любое время. Если пул соединений полон, последующие
операции подключения будут помещены в очередь, равную значению этой опции
(очередь "backlog").
Если количество ожидающих операций подключения равно backlog,
последующие операции подключения завершатся неудачей и вернут nil плюс
строку ошибки "слишком много ожидающих операций подключения".
Ожидающие операции подключения будут возобновлены, как только количество соединений
в пуле станет меньше pool_size.
Ожидающая операция подключения будет прервана, как только она будет находиться в очереди более
connect_timeout, контролируемого
settimeouts, и вернет nil плюс
строку ошибки "тайм-аут".
Эта опция была впервые представлена в релизе v0.10.14.
* ssl_verify
Указывает, следует ли выполнять проверку SSL-сертификата во время
SSL-рукопожатия, если используется схема wss://.
-
headersУказывает пользовательские заголовки, которые будут отправлены в запросе рукопожатия. Ожидается, что таблица будет содержать строки в формате
{"a-header: a header value", "another-header: another header value"}. -
client_certУказывает объект цепочки сертификатов клиента, который будет использоваться во время TLS-рукопожатия с удаленным сервером. Эти объекты можно создать с помощью ngx.ssl.parse_pem_cert функции, предоставленной lua-resty-core. Обратите внимание, что указание опции
client_certтребует также предоставления соответствующегоclient_priv_key. Смотрите ниже. -
client_priv_keyУказывает закрытый ключ, соответствующий опции
client_certвыше. Эти объекты можно создать с помощью ngx.ssl.parse_pem_priv_key функции, предоставленной lua-resty-core. -
hostУказывает значение заголовка
Host, отправляемого в запросе рукопожатия. Если не указано, заголовокHostбудет выведен из имени хоста/адреса и порта в URI подключения. -
server_nameУказывает имя сервера (SNI), которое будет использоваться при выполнении TLS-рукопожатия с сервером. Если не указано, будет использовано значение
hostили<host/addr>:<port>из URI подключения. -
keyУказывает значение заголовка
Sec-WebSocket-Keyв запросе рукопожатия. Значение должно быть строкой длиной 16 байт, закодированной в base64, соответствующей требованиям рукопожатия клиента WebSocket RFC. Если не указано, ключ генерируется случайным образом.
Режим SSL-соединения (wss://) требует как минимум ngx_lua 0.9.11 или OpenResty 1.7.4.1.
client:close
синтаксис: ok, err = wb:close()
Закрывает текущее соединение WebSocket. Если еще не был отправлен кадр close, то кадр close будет автоматически отправлен.
client:set_keepalive
синтаксис: ok, err = wb:set_keepalive(max_idle_timeout, pool_size)
Немедленно помещает текущее соединение WebSocket в пул соединений ngx_lua.
Вы можете указать максимальный тайм-аут бездействия (в мс), когда соединение находится в пуле, и максимальный размер пула для каждого рабочего процесса nginx.
В случае успеха возвращает 1. В случае ошибок возвращает nil с строкой, описывающей ошибку.
Вызывайте этот метод только в том месте, где вы бы вызвали метод close. Вызов этого метода немедленно переведет текущий объект WebSocket в состояние closed. Любые последующие операции, кроме connect(), на текущем объекте вернут ошибку closed.
client:set_timeout
синтаксис: wb:set_timeout(ms)
Идентичен методу set_timeout объектов resty.websocket.server.
client:send_text
синтаксис: bytes, err = wb:send_text(text)
Идентичен методу send_text объектов resty.websocket.server.
client:send_binary
синтаксис: bytes, err = wb:send_binary(data)
Идентичен методу send_binary объектов resty.websocket.server.
client:send_ping
синтаксис: bytes, err = wb:send_ping()
синтаксис: bytes, err = wb:send_ping(msg)
Идентичен методу send_ping объектов resty.websocket.server.
client:send_pong
синтаксис: bytes, err = wb:send_pong()
синтаксис: bytes, err = wb:send_pong(msg)
Идентичен методу send_pong объектов resty.websocket.server.
client:send_close
синтаксис: bytes, err = wb:send_close()
синтаксис: bytes, err = wb:send_close(code, msg)
Идентичен методу send_close объектов resty.websocket.server.
client:send_frame
синтаксис: bytes, err = wb:send_frame(fin, opcode, payload)
Идентичен методу send_frame объектов resty.websocket.server.
Чтобы контролировать, следует ли отправлять незамаскированные кадры, вы можете передать true параметру send_unmasked в методе конструктора new. По умолчанию отправляются замаскированные кадры.
client:recv_frame
синтаксис: data, typ, err = wb:recv_frame()
Идентичен методу recv_frame объектов resty.websocket.server.
resty.websocket.protocol
Чтобы загрузить этот модуль, просто выполните
local protocol = require "resty.websocket.protocol"
Методы
protocol.recv_frame
синтаксис: data, typ, err = protocol.recv_frame(socket, max_payload_len, force_masking)
Получает кадр WebSocket из сети.
protocol.build_frame
синтаксис: frame = protocol.build_frame(fin, opcode, payload_len, payload, masking)
Создает необработанный кадр WebSocket.
protocol.send_frame
синтаксис: bytes, err = protocol.send_frame(socket, fin, opcode, payload, max_payload_len, masking)
Отправляет необработанный кадр WebSocket.
Автоматическая регистрация ошибок
По умолчанию основной модуль ngx_lua выполняет регистрацию ошибок, когда происходят ошибки сокета. Если вы уже выполняете правильную обработку ошибок в своем собственном коде Lua, рекомендуется отключить эту автоматическую регистрацию ошибок, отключив директиву lua_socket_log_errors модуля ngx_lua, то есть,
lua_socket_log_errors off;
Ограничения
- Эта библиотека не может использоваться в контекстах кода, таких как init_by_lua, set_by_lua, log_by_lua, и header_filter_by_lua, где API cosocket ngx_lua недоступен.
- Экземпляр объекта
resty.websocketне может храниться в переменной Lua на уровне модуля Lua, поскольку он будет разделяться всеми параллельными запросами, обрабатываемыми одним и тем же рабочим процессом nginx (см. http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker) и приведет к плохим условиям гонки, когда параллельные запросы пытаются использовать один и тот же экземплярresty.websocket. Вы всегда должны инициализировать объектыresty.websocketв локальных переменных функции или в таблицеngx.ctx. Эти места имеют свои собственные копии данных для каждого запроса.
См. также
- Пост в блоге WebSockets с OpenResty от Аапо Талвенсаари.
- модуль ngx_lua: http://wiki.nginx.org/HttpLuaModule
- протокол websocket: http://tools.ietf.org/html/rfc6455
- библиотека lua-resty-upload
- библиотека lua-resty-redis
- библиотека lua-resty-memcached
- библиотека lua-resty-mysql
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-websocket.