Saltar a contenido

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 es 65535;

  • max_concurrent_stream, un número Lua que limita los flujos concurrentes máximos en una sesión HTTP/2, el valor predeterminado es 128;

  • max_frame_size, un número Lua que limita el tamaño máximo del marco que el par puede enviar, el valor predeterminado es 16777215.

  • 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.

  • Connection
  • Keep-Alive
  • Proxy-Connection
  • Upgrade
  • Transfer-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, :authority debe ser pasado en lugar de Host;

  • 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.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;

  • 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 por depend;
  • weight, asigna un nuevo peso weight al 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 flujo depend;
  • 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 flujo sid será el único hijo del flujo dependiente;
  • weight, define el peso del flujo actual (especificado por sid);

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_ERROR
  • h2_error.PROTOCOL
  • h2_error.INTERNAL_ERROR
  • h2_error.FLOW_CONTROL_ERROR
  • h2_error.SETTINGS_TIMEOUT
  • h2_error.STREAM_CLOSED
  • h2_error.FRAME_SIZE_ERROR
  • h2_error.REFUSED_STREAM
  • h2_error.CANCEL
  • h2_error.COMPRESSION_ERROR
  • h2_error.CONNECT_ERROR
  • h2_error.ENHANCE_YOUR_CALM
  • h2_error.INADEQUATE_SECURITY
  • h2_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

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-http2.