http2: La implementación del protocolo HTTP/2 (lado del cliente) para nginx-module-lua
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-http2 v1.0 lanzado el 20 de noviembre de 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)
-- Procesar los encabezados de respuesta
end
local on_data_reach = function(ctx, data)
-- Procesar el cuerpo de respuesta
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()
Como un ejemplo más formal, por favor lee util/example.lua.
Descripción
Esta biblioteca pura de Lua implementa el protocolo HTTP/2 del lado del cliente, pero no se cubren todos los detalles, por ejemplo, las dependencias de flujo se mantienen pero nunca se utilizan.
Hay algunas limitaciones inherentes que no se resuelven, sin embargo.
No se puede usar sobre conexiones con apretón de manos SSL/TLS. El tcpsock:sslhandshake no soporta las extensiones ALPN o NPN, por lo que actualmente solo se pueden usar conexiones simples, la biblioteca comenzará la sesión HTTP/2 enviando el prefacio de conexión, es decir, la cadena:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
Esta biblioteca proporciona un parche para la Negociación de Protocolo de Capa de Aplicación. Solo usa esto si lo necesitas.
Solo se puede enviar una solicitud HTTP. Actualmente, las API implementadas solo soportan el envío de una sola solicitud HTTP. Se aceptan PR para resolver esto.
Reutilización de sesión HTTP/2. El protocolo HTTP/2 está diseñado como persistente, mientras que el objeto Cosocket está vinculado a una solicitud HTTP específica. Uno debe cerrar el objeto Cosocket o mantenerlo vivo antes de que la solicitud termine, este modelo entra en conflicto con la reutilización de la sesión HTTP/2, solo una solución alternativa puede resolver esto, consulta client:keepalive para más detalles.
API Implementada
resty.http2
Para cargar este módulo, simplemente haz esto:
local http2 = require "resty.http2"
http2.new
sintaxis: local client, err = http2.new(opts)
Crea un cliente HTTP/2 especificando las opciones. En caso de fallo, se devolverán nil y una cadena de mensaje de error.
El único parámetro opts, que es una tabla Lua, contiene algunos campos:
-
recv, una función Lua que se utiliza para leer bytes; -
send, una función Lua que se utiliza para enviar bytes; -
ctx, un dato opaco, actúa como el contexto de los llamadores;
La función recv y send se llamarán así:
local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
-
preread_size, un número Lua que influye en el tamaño de la ventana de envío inicial del par (anunciado a través del marco SETTINGS), el valor predeterminado es65535; -
max_concurrent_stream, un número Lua que limita los flujos concurrentes máximos en una sesión HTTP/2, el valor predeterminado es128; -
max_frame_size, un número Lua que limita el tamaño máximo del marco que el par puede enviar, el valor predeterminado es16777215. -
key, una cadena Lua que representa qué sesión HTTP/2 en caché quieren reutilizar los llamadores, si no se encuentra, se creará una nueva sesión HTTP/2. Consulta client:keepalive para más detalles.
client:acknowledge_settings
sintaxis: local ok, err = client:acknowledge_settings()
Reconoce el marco SETTINGS del par, los ajustes se aplicarán automáticamente.
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
client:request
sintaxis: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)
Envía una solicitud HTTP al par,
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
Los headers deben ser una tabla Lua similar a un array que representa los encabezados de la solicitud HTTP, cada entrada es como { name = "header1", value = "value1" }.
Cabe destacar que esta biblioteca no se encarga de la semántica de los encabezados HTTP, por lo que es responsabilidad de los llamadores proporcionar esto, y los llamadores deben implementar cualquier conversión necesaria, por ejemplo, Host debe convertirse en :authority. Además, los siguientes encabezados se ignorarán ya que son específicos de la CONEXIÓN.
ConnectionKeep-AliveProxy-ConnectionUpgradeTransfer-Encoding
El body puede ser una cadena Lua que representa el cuerpo de la solicitud HTTP. También puede ser una función Lua para implementar la carga por flujo. Cuando body es una función Lua, se llamará así:
local part_data, last, err = body(size)
En caso de fallo, body debe proporcionar el tercer valor de retorno err para informar a esta biblioteca que ocurrieron algunos errores fatales, entonces este método se abortará inmediatamente, y se enviará un marco GOAWAY al par con el código de error INTERNAL_ERROR.
Cuando todos los datos han sido generados, el segundo valor de retorno last debe ser proporcionado, y su valor debe ser true.
on_headers_reach debe ser una función Lua, como un callback que se llamará cuando se reciban todos los encabezados de respuesta HTTP, se llamará así:
local abort = on_headers_reach(ctx, headers)
El segundo parámetro headers es una tabla Lua similar a un hash que representa los encabezados de respuesta HTTP recibidos del par.
on_headers_reach puede decidir si aborta la sesión HTTP/2 devolviendo un valor booleano abort a la biblioteca, la sesión HTTP/2 se abortará si on_headers_reach devuelve un valor verdadero.
El último parámetro, on_data_reach, es una función Lua, actúa como el callback que se llamará cada vez que se reciban los cuerpos de respuesta, se llamará así:
local abort = on_data_reach(ctx, data)
El segundo parámetro data es una cadena Lua que representa el cuerpo de respuesta HTTP recibido esta vez.
El significado del valor de retorno es el mismo que el de on_headers_reach.
Después de que este método devuelva, la sesión HTTP/2 seguirá viva, uno puede decidir cerrar esta sesión llamando a client:close o continuar haciendo algo.
client:send_request
sintaxis: local stream, err = client:send_request(headers, body?)
Envía los encabezados y el cuerpo (si los hay) al par.
Los significados de headers y body son los mismos que en client:request.
El objeto de flujo creado correspondiente se dará cuando este método devuelva.
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
client:read_headers
sintaxis: local headers, err = client:read_headers(stream)
Lee los encabezados de respuesta del par, el parámetro stream es el creado por client:send_request.
Los encabezados devueltos son una tabla Lua similar a un hash que contiene todos los encabezados de respuesta HTTP, que pueden contener algunos pseudo-encabezados como ":status", los llamadores deben hacer algunas transformaciones si es necesario.
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
client:read_body
sintaxis: local body, err = client:read_body(stream)
Lee un marco DATA del par, el parámetro stream es el creado por client:send_request.
Los datos devueltos son una cadena Lua que representa una parte del cuerpo de respuesta. Se devolverá una cadena vacía si se ha leído todo el cuerpo.
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
client:close
sintaxis: local ok, err = client:close(code)
Cierra la sesión HTTP/2 actual con el código de error code.
Consulta resty.http2.error para aprender sobre los códigos de error.
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
client:keepalive
sintaxis: client:keepalive(key)
Caché la sesión HTTP/2 actual para su reutilización, ten en cuenta que las sesiones HTTP/2 mal formadas nunca se almacenarán en caché. La sesión HTTP/2 se separará de la conexión, precisamente, del objeto Cosocket actual.
La sesión HTTP/2 separada se guardará en una tabla Lua interna similar a un hash, el parámetro único key se utilizará para indexar esta sesión cuando los llamadores quieran reutilizarla.
Después de establecer esta sesión como viva, los llamadores también deben establecer el objeto Cosocket como keepalive.
Hay una limitación inherente entre el mapeo de la sesión HTTP/2 y la conexión subyacente. Una sesión HTTP/2 solo se puede usar en una conexión TCP porque es estatal, si los llamadores almacenan la conexión en un grupo que almacena múltiples conexiones, la relación de vinculación se pierde, ya que no se sabe qué conexión se selecciona para el objeto Cosocket, por lo tanto, qué sesión HTTP/2 debe coincidir también es desconocido.
No hay una forma elegante de resolver esto, a menos que el modelo Cosocket pueda asignar un identificador a la conexión subyacente. Ahora lo que los llamadores pueden hacer es usar el grupo de conexiones de tamaño único para eludir esta limitación, por ejemplo:
...
sock:connect(host, port, { pool = "h2" })
...
sock:setkeepalive(75, 1)
client:keepalive("test")
resty.http2.protocol
Este módulo implementa algunas API de bajo nivel relevantes al protocolo.
Para cargar este módulo, simplemente haz esto:
local protocol = require "resty.http2.protocol"
protocol.session
sintaxis: local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)
Crea una nueva sesión HTTP/2, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
El significado de cada parámetro es el mismo que el descrito en http2.new.
El marco SETTINGS inicial y el marco WINDOW_UPDATE se enviarán antes de que esta función devuelva.
session:adjust_window
sintaxis: local ok = session:adjust_window(delta)
Ajusta el tamaño de la ventana de envío de cada flujo, el flujo se restablecerá si el tamaño de la ventana de envío alterado excede MAX_WINDOW_SIZE, en este caso, ok será nil.
session:frame_queue
sintaxis: session:frame_queue(frame)
Agrega frame a la cola de salida de la sesión actual.
session:flush_queue
sintaxis: local ok, err = session:flush_queue()
Empaqueta y vacía los marcos en cola, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
session:submit_request
sintaxis: local ok, err = session:submit_request(headers, no_body, priority?, pad?)
Envía una solicitud HTTP a la sesión HTTP/2 actual, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
El significado de cada parámetro:
-
headers, debe ser una tabla Lua similar a un hash que representa los encabezados de la solicitud HTTP, cabe destacar que esta biblioteca no se encarga de la semántica de los encabezados HTTP, por lo que es responsabilidad de los llamadores proporcionar esto, y los llamadores deben transformar cualquier encabezado pseudo necesario. Por ejemplo,:authoritydebe ser pasado en lugar deHost; -
no_body, un valor booleano, indica si esta solicitud tiene cuerpo. Cuando es verdadero, el marco HEADERS generado contendrá la bandera END_HEADERS; -
priority, una tabla Lua similar a un hash, que se utiliza para definir dependencias de flujo personalizadas: priority.sidrepresenta el identificador del flujo dependiente;priority.excl, si el nuevo flujo se convierte en la única dependencia del flujo indicado porpriority.sid;-
priority.weightdefine el peso del nuevo flujo; -
pad, los datos de relleno.
session:submit_window_update
sintaxis: local ok, err = session:submit_window_update(incr)
Envía un marco WINDOW_UPDATE para toda la sesión HTTP/2 con un incremento incr, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
session:recv_frame
sintaxis: local frame, err = session:recv_frame()
Recibe un marco HTTP/2, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
La acción correspondiente se tomará automáticamente, por ejemplo, se enviará un marco GOAWAY si el par viola las convenciones del protocolo HTTP/2; se enviará un marco WINDOW_UPDATE si la ventana de envío del par se vuelve demasiado pequeña.
session:close
sintaxis: session:close(code?, debug_data?)
Genera un marco GOAWAY con el código de error code y datos de depuración debug_data, el código de error predeterminado es NO_ERROR y los datos de depuración son nil.
Ten en cuenta que esta función solo pone en cola el marco GOAWAY en la cola de salida, los llamadores deben llamar a session:flush_queue para realmente enviar los marcos.
session:detach
sintaxis: session:detach()
Desacopla la sesión HTTP/2 actual del objeto Cosocket.
session:attach
sintaxis: local ok, err = session:attach(recv, send, ctx)
Acopla la sesión HTTP/2 actual a un objeto Cosocket, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
Los significados de recv, send y ctx son los mismos que los descritos en http.new.
resty.http2.stream
Este módulo implementa algunas API de bajo nivel relevantes a los flujos.
Para cargar este módulo, simplemente haz esto:
local h2_stream = require "resty.http2.stream"
h2_stream.new
sintaxis: local stream = h2_stream.new(sid, weight, session)
Crea un nuevo flujo con el identificador sid, peso weight y la sesión HTTP/2 a la que pertenece.
h2_stream.new_root
sintaxis: local root_stream = h2_stream.new_root(session)
Crea el flujo raíz con su sesión.
El identificador del flujo raíz es 0x0 y es realmente un flujo virtual que se utiliza para manipular toda la sesión HTTP/2.
stream:submit_headers
sintaxis: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)
Envía algunos encabezados HTTP al flujo.
El primer parámetro headers, debe ser una tabla Lua similar a un hash que representa los encabezados de la solicitud HTTP, cabe destacar que esta biblioteca no se encarga de la semántica de los encabezados HTTP, por lo que es responsabilidad de los llamadores proporcionar esto, y los llamadores deben transformar cualquier encabezado pseudo necesario. Por ejemplo, :authority debe ser pasado en lugar de Host;
El parámetro end_stream debe ser un valor booleano y se utiliza para controlar si el marco HEADERS debe llevar la bandera END_STREAM, básicamente los llamadores pueden configurarlo como verdadero si no hay cuerpo de solicitud que enviar.
priority debe ser una tabla Lua similar a un hash (si la hay), que se utiliza para definir dependencias de flujo personalizadas:
* priority.sid representa el identificador del flujo dependiente;
* priority.excl, si el nuevo flujo se convierte en la única dependencia del flujo indicado por priority.sid;
* priority.weight define el peso del nuevo flujo;
El último parámetro pad, representa los datos de relleno.
En caso de fallo, se devolverán nil y una cadena Lua que describe el error correspondiente.
stream:submit_data
sintaxis: local ok, err = stream:submit_data(data, pad, last)
Envía algunos datos del cuerpo de la solicitud al flujo, data debe ser una cadena Lua, con datos de relleno opcionales.
El último parámetro last indica si esta es la última presentación, el marco DATA actual llevará la bandera END_STREAM si last es verdadero.
En caso de fallo, se devolverán nil y una cadena Lua que describe el error correspondiente.
stream:submit_window_update
sintaxis: local ok, err = session:submit_window_update(incr)
Envía un marco WINDOW_UPDATE para el flujo con un incremento incr, en caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
stream:set_dependency
sintaxis: stream:set_dependency(depend, excl)
Establece las dependencias del flujo actual a un flujo con el identificador depend.
El segundo parámetro excl, indica si el flujo actual será el único hijo de depend.
Cuando depend está ausente, el flujo objetivo será el raíz y excl se tratará como false.
stream:rst
sintaxis: stream:rst(code)
Genera un marco RST_STREAM con el código de error code. En el caso de que code esté ausente, se seleccionará el código NO_ERROR.
Ten en cuenta que este método solo genera un marco RST_STREAM en lugar de enviarlo, el llamador debe enviar este marco llamando a session:flush_queue.
resty.http2.frame
Este módulo implementa algunas API de bajo nivel relevantes a los marcos.
Para cargar este módulo, simplemente haz esto:
local h2_frame = require "resty.http2.frame"
h2_frame.header.new
sintaxis: local hd = h2_frame.header.new(length, typ, flags, id)
Crea un encabezado de marco, con la longitud de carga útil length, tipo de marco typ y toma flags como las banderas del marco, que pertenece al flujo id.
h2_frame.header.pack
sintaxis: h2_frame.header.pack(hd, dst)
Serializa el encabezado del marco hd al destino dst. El dst debe ser una tabla Lua similar a un array.
h2_frame.header.unpack
sintaxis: h2_frame.header.unpack(src)
Deserializa un encabezado de marco de una cadena Lua src, la longitud de src debe ser al menos de 9 octetos.
h2_frame.priority.pack
sintaxis: h2_frame.priority.pack(pf, dst)
Serializa un marco PRIORITY al destino dst. El dst debe ser una tabla Lua similar a un array.
El pf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;depend, el identificador del flujo dependiente;excl, especifica si el flujo actual donde se encuentra este marco PRIORITY se convierte en el único hijo del flujo identificado pordepend;weight, asigna un nuevo pesoweightal flujo actual;
h2_frame.priority.unpack
sintaxis: local ok, err = h2_frame.priority.unpack(pf, src, stream)
Deserializa un marco PRIORITY de una cadena Lua src, la longitud de src debe ser al menos del tamaño especificado en pf.header.length.
El pf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco PRIORITY actual, es decir, pf.header.
El último parámetro stream especifica el flujo al que pertenece el marco PRIORITY actual.
Las acciones correspondientes se tomarán automáticamente dentro de este método, como construir las nuevas dependencias.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.rst_stream.pack
sintaxis: h2_frame.rst_stream.pack(rf, dst)
Serializa un marco RST_STREAM al destino dst. El dst debe ser una tabla Lua similar a un array.
El rf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;error_code, el código de error;
h2_frame.rst_stream.unpack
sintaxis: local ok, err = h2_frame.rst_stream.unpack(rf, src, stream)
Deserializa un marco RST_STREAM de una cadena Lua src. La longitud de src debe ser al menos del tamaño especificado en rf.header.length.
El rf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco RST_STREAM actual, es decir, rf.header.
El último parámetro stream especifica el flujo al que pertenece el marco RST_STREAM actual.
Las acciones correspondientes se tomarán automáticamente dentro de este método, como cambiar el estado del flujo.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.rst_stream.new
sintaxis: local rf = h2_frame.rst_stream.new(error_code, sid)
Crea un marco RST_STREAM con el código de error error_code, que pertenece al flujo sid.
h2_frame.settings.pack
sintaxis: h2_frame.settings.pack(sf, dst)
Serializa un marco SETTINGS al destino dst. El dst debe ser una tabla Lua similar a un array.
El sf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;item, la configuración específica, que debe ser una tabla Lua similar a un array, cada elemento debe ser una tabla Lua similar a un hash:id, el identificador de la configuración, puede ser:- SETTINGS_ENABLE_PUSH (0x2)
- SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
- SETTINGS_INITIAL_WINDOW_SIZE (0x4)
- SETTINGS_MAX_FRAME_SIZE (0x5)
value, el valor correspondiente de la configuración;
h2_frame.settings.unpack
sintaxis: local ok, err = h2_frame.settings.unpack(sf, src, stream)
Deserializa un marco SETTINGS de una cadena Lua src. La longitud de src debe ser al menos del tamaño especificado en sf.header.length.
El sf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco SETTINGS actual, es decir, sf.header.
El último parámetro stream especifica el flujo al que pertenece el marco SETTINGS actual (debe ser el flujo raíz).
Las acciones correspondientes se tomarán automáticamente dentro de este método, como actualizar el valor de configuración de la sesión HTTP/2.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.settings.new
sintaxis: local sf = h2_frame.settings.new(flags, payload)
Crea un marco SETTINGS con las banderas flags y el elemento de carga payload.
El payload debe ser una tabla Lua similar a un array, cada elemento debe ser una tabla Lua similar a un hash:
* id, el identificador de la configuración, puede ser:
* SETTINGS_ENABLE_PUSH (0x2)
* SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
* SETTINGS_INITIAL_WINDOW_SIZE (0x4)
* SETTINGS_MAX_FRAME_SIZE (0x5)
* value, el valor correspondiente de la configuración;
h2_frame.ping.pack
sintaxis: h2_frame.ping.pack(pf, dst)
Serializa un marco PING al destino dst. El dst debe ser una tabla Lua similar a un array.
El pf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;opaque_data_hi, el valor más alto de 32 bits de los datos de ping correspondientes;opaque_data_lo, el valor más bajo de 32 bits de los datos de ping correspondientes;
h2_frame.ping.unpack
sintaxis: local ok, err = h2_frame.ping.unpack(pf, src, stream)
Deserializa un marco PING de una cadena Lua src. La longitud de src debe ser al menos del tamaño especificado en sf.header.length.
El pf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco PING actual, es decir, pf.header.
El último parámetro stream especifica el flujo al que pertenece el marco PING actual (debe ser el flujo raíz).
En caso de fallo, se devolverán nil y un código de error.
h2_frame.goaway.pack
sintaxis: h2_frame.goaway.pack(gf, dst)
Serializa un marco GOAWAY al destino dst. El dst debe ser una tabla Lua similar a un array.
El gf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;last_stream_id, el último identificador de flujo inicializado por el par;error_code, el código de error;debug_data, los datos de depuración;
h2_frame.goaway.unpack
sintaxis: local ok, err = h2_frame.goaway.unpack(gf, src, stream)
Deserializa un marco GOAWAY de una cadena Lua src. La longitud de src debe ser al menos del tamaño especificado en gf.header.length.
El gf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco GOAWAY actual, es decir, gf.header.
El último parámetro stream especifica el flujo al que pertenece el marco GOAWAY actual (debe ser el flujo raíz).
En caso de fallo, se devolverán nil y una cadena Lua que describe la razón del error.
h2_frame.goaway.new
sintaxis: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)
Crea un marco GOAWAY con el último identificador de flujo inicializado por el par last_sid, y el código de error error_code. Opcionalmente, con los datos de depuración debug_data.
h2_frame.window_update.pack
sintaxis: h2_frame.window_update.pack(wf, dst)
Serializa un marco WINDOW_UPDATE al destino dst. El dst debe ser una tabla Lua similar a un array.
El wf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;window_size_increment, el incremento del tamaño de la ventana;
h2_frame.window_update.unpack
sintaxis: local ok, err = h2_frame.window_update.unpack(wf, src, stream)
Deserializa un marco WINDOW_UPDATE de una cadena Lua src. La longitud de src debe ser al menos del tamaño especificado en wf.header.length.
El wf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco WINDOW_UPDATE actual, es decir, wf.header.
El último parámetro stream especifica el flujo al que pertenece el marco WINDOW_UPDATE actual.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.window_update.new
sintaxis: local wf = h2_frame.window_update.new(sid, window)
Crea un marco WINDOW_UPDATE con el identificador de flujo sid, y aumenta el tamaño de la ventana especificado por window.
h2_frame.headers.pack
sintaxis: h2_frame.headers.pack(hf, dst)
Serializa un marco HEADERS al destino dst. El dst debe ser una tabla Lua similar a un array.
El hf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;pad, los datos de relleno;depend, el identificador del flujo dependiente;excl, especifica si el flujo al que pertenece el marco HEADERS actual se convertirá en el único hijo del flujodepend;weight, especifica el peso del flujo al que pertenece el marco HEADERS actual.block_frags, los encabezados HTTP en texto plano (después de la compresión hpack);
h2_frame.headers.unpack
sintaxis: local ok, err = h2_frame.headers.unpack(hf, src, stream)
Deserializa un marco HEADERS de la cadena Lua src, la longitud de src debe ser al menos del tamaño especificado en hf.header.length.
El hf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco HEADERS actual, es decir, hf.header.
El último parámetro stream especifica el flujo al que pertenece el marco HEADERS actual.
Se tomarán las acciones correspondientes, por ejemplo, la transición del estado del flujo ocurrirá.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.headers.new
sintaxis: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)
Crea un marco HEADERS que toma los fragmentos de bloque frags.
El parámetro pri puede ser utilizado para especificar las dependencias del flujo, pri debe ser una tabla Lua similar a un hash, que contiene:
sid, el identificador del flujo dependiente;excl, si el flujosidserá el único hijo del flujo dependiente;weight, define el peso del flujo actual (especificado porsid);
El pad especifica los datos de relleno, que son opcionales.
Cuando end_stream es verdadero, el marco HEADERS actual llevará la bandera END_STREAM, de manera similar, cuando end_headers es verdadero, el marco HEADERS actual llevará la bandera END_HEADERS.
Se debe tener cuidado de que si el marco HEADERS actual no contiene todos los encabezados, entonces uno o más marcos CONTINUATION deben seguirse de acuerdo con el protocolo HTTP/2.
h2_frame.continuation.pack
sintaxis: h2_frame.continuation.pack(cf, dst)
Serializa un marco CONTINUATION al destino dst. El dst debe ser una tabla Lua similar a un array.
El cf debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;block_frags, los encabezados HTTP en texto plano (después de la compresión hpack);
h2_frame.continuation.unpack
sintaxis: local ok, err = h2_frame.continuation.unpack(cf, src, stream)
Deserializa un marco CONTINUATION de la cadena Lua src, la longitud de src debe ser al menos del tamaño especificado en cf.header.length.
El cf debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco CONTINUATION actual, es decir, cf.header.
El último parámetro stream especifica el flujo al que pertenece el marco CONTINUATION actual.
Se tomarán las acciones correspondientes, por ejemplo, la transición del estado del flujo ocurrirá.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.continuation.new
sintaxis: local cf = h2_frame.continuation.new(frags, end_headers, sid)
Crea un marco CONTINUATION que toma los fragmentos de bloque frags.
Cuando end_headers es verdadero, el marco CONTINUATION actual llevará la bandera END_HEADERS.
Se debe tener cuidado de que si el marco CONTINUATION actual no contiene todos los encabezados, entonces uno o más marcos CONTINUATION deben seguirse de acuerdo con el protocolo HTTP/2.
El sid especifica el flujo al que pertenece el marco CONTINUATION actual.
h2_frame.data.pack
sintaxis: h2_frame.data.pack(df, dst)
Serializa un marco DATA al destino dst. El dst debe ser una tabla Lua similar a un array.
El df debe ser una tabla Lua similar a un hash que contiene:
header, el encabezado del marco;payload, el cuerpo de la solicitud/respuesta HTTP;
h2_frame.data.unpack
sintaxis: local ok, err = h2_frame.data.unpack(df, src, stream)
Deserializa un marco DATA de la cadena Lua src, la longitud de src debe ser al menos del tamaño especificado en df.header.length.
El df debe ser una tabla Lua similar a un hash que ya contiene el encabezado del marco DATA actual, es decir, df.header.
El último parámetro stream especifica el flujo al que pertenece el marco DATA actual.
Se tomarán las acciones correspondientes, por ejemplo, la transición del estado del flujo ocurrirá.
En caso de fallo, se devolverán nil y un código de error.
h2_frame.data.new
sintaxis: local df = h2_frame.data.new(payload, pad, last, sid)
Crea un marco DATA que toma la carga útil payload.
El pad especifica los datos de relleno, que son opcionales.
Cuando last es verdadero, el marco DATA actual llevará la bandera END_STREAM.
El sid especifica el flujo al que pertenece el marco DATA actual.
h2_frame.push_promise.unpack
sintaxis: local df = h2_frame.data.new(payload, pad, last, sid)
Actualmente, cualquier marco PUSH_PROMISE entrante será rechazado.
Este método siempre devuelve nil y el error PROTOCOL_ERROR.
resty.http2.hpack
Este módulo implementa algunas API de bajo nivel de HPACK.
Para cargar este módulo, simplemente haz esto:
local hpack = require "resty.http2.hpack"
hpack.encode
sintaxis: hpack.encode(src, dst, lower)
Codifica la cadena Lua src al destino dst, el dst debe ser una tabla Lua similar a un array. Se intentarán primero los códigos de Huffman.
El lower especifica si la operación de codificación actual es insensible a mayúsculas y minúsculas, el valor predeterminado es false.
hpack.indexed
sintaxis: local v = hpack.indexed(index)
Devuelve el índice después de usar la Representación de Campo de Encabezado Indexado.
hpack.incr_indexed
sintaxis: local v = hpack.indexed(index)
Devuelve el índice después de usar el Campo de Encabezado Literal con Indexación Incremental.
hpack.new
sintaxis: local h = hpack.new(size)
Crea una instancia de hpack, ya que la decodificación de HPACK es estatal.
El size representa el tamaño máximo de la tabla hpack, el valor predeterminado es 4096 bytes.
El valor de retorno h representa la instancia de HPACK. Uno de los miembros de h es importante, es decir, h.cached, que guarda todos los fragmentos de bloque de encabezados, y h:decode analizará los datos dentro de h.cached.
Actualmente, h2_frame.headers.unpack y h2_frame.continuation.unpack empujarán los fragmentos de bloque de encabezados a h.cached, una vez que el bloque esté completo, se ejecutará la decodificación.
h:insert_entry
sintaxis: local ok = h:insert_entry(header_name, header_value)
Intenta insertar una entrada de encabezado con el nombre header_name y el valor header_value en la tabla dinámica de HPACK.
La inserción puede fallar si esta entrada es demasiado grande. Se producirá la expulsión de entradas necesarias si no hay suficiente espacio.
Este método devolverá true si la inserción es exitosa o false si no.
h:resize
sintaxis: local ok = h:resize(new_size)
Ajusta el tamaño de la tabla dinámica a new_size, actualmente el new_size no puede exceder 4096, de lo contrario, la operación de cambio de tamaño fallará.
Cuando el tamaño de la tabla dinámica se reduce, algunas entradas se expulsarán de acuerdo con la regla de HPACK.
Este método devolverá true si la operación de cambio de tamaño es exitosa o false si no.
h:decode
sintaxis: local ok, err = h:decode(dst)
Decodifica los fragmentos de bloque de encabezados dentro de h.cached, los encabezados decodificados se guardarán en dst, una tabla Lua similar a un hash.
En caso de fallo, se devolverán nil y un código de error.
h:get_indexed_header
sintaxis: local entry = h:get_indexed_header(index)
Devuelve la entrada de encabezado de acuerdo con el índice index.
El valor de retorno será nil si el índice es inválido, de lo contrario, la entry será una tabla Lua similar a un hash con dos elementos:
entry.name, el nombre del encabezado;entry.value, el valor del encabezado;
resty.http2.error
Este módulo implementa algunas API de bajo nivel relevantes a los errores.
Hay muchos códigos de error definidos, básicamente son consistentes con el protocolo 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
Y tres códigos de error personalizados:
h2_error.STREAM_PROTOCOL_ERROR, error de protocolo a nivel de flujo;h2_error.STREAM_FLOW_CONTROL_ERROR, error de control de flujo a nivel de flujo;h2_error.STREAM_FRAME_SIZE_ERROR, error de tamaño de marco a nivel de flujo;
Los errores a nivel de flujo no influirán en toda la conexión, sino que restablecerán el flujo actual.
Para cargar este módulo, simplemente haz esto:
h2_error.strerror
sintaxis: local msg = h2_error.strerror(code)
Devuelve una cadena Lua que describe el código de error code, se devolverá "unknown error" si el código de error es desconocido.
h2_error.is_stream_error
sintaxis: local ok = h2_error.is_stream_error(code)
Juzga si el código de error code es un error a nivel de flujo.
Ver También
- 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
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-http2.