Pular para conteúdo

http2: A Implementação do Protocolo HTTP/2 (Lado do Cliente) para nginx-module-lua

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Em seguida, você pode prosseguir com os seguintes passos.

CentOS/RHEL 7 ou 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 com o NGINX, certifique-se de que o nginx-module-lua está instalado.

Este documento descreve o lua-resty-http2 v1.0 lançado em 20 de novembro 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, "falha ao conectar ", 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)
    -- Processar os cabeçalhos da resposta
end

local on_data_reach = function(ctx, data)
    -- Processar o corpo da resposta
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, "falha ao criar cliente HTTP/2: ", 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() falhou: ", err)
    return
end

sock:close()

Como um exemplo mais formal, por favor, leia o util/example.lua.

Descrição

Esta biblioteca Lua pura implementa o protocolo HTTP/2 do lado do cliente, mas nem todos os detalhes são cobertos, por exemplo, as dependências de stream são mantidas, mas nunca usadas.

Existem algumas limitações inerentes que não são resolvidas, no entanto.

Não pode ser usada sobre conexões com handshake SSL/TLS. O tcpsock:sslhandshake não suporta as extensões ALPN ou NPN, portanto, atualmente apenas as conexões simples podem ser usadas, a biblioteca iniciará a sessão HTTP/2 enviando o prefixo de conexão, ou seja, a string:

PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

Esta biblioteca fornece um patch para a Negociação de Protocolo de Camada de Aplicação. Basta usar isso se você precisar.

Apenas uma solicitação HTTP pode ser enviada. Atualmente, as APIs implementadas suportam o envio de apenas uma solicitação HTTP. PRs são bem-vindos para resolver isso.

Reutilização da sessão HTTP/2. O protocolo HTTP/2 é projetado para ser persistente, enquanto o objeto Cosocket está vinculado a uma solicitação HTTP específica. É necessário fechar o objeto Cosocket ou mantê-lo ativo antes que a solicitação termine, esse modelo entra em conflito com a reutilização da sessão HTTP/2, apenas uma solução alternativa pode resolver isso, veja client:keepalive para mais detalhes.

API Implementada

resty.http2

Para carregar este módulo, basta fazer isso:

local http2 = require "resty.http2"

http2.new

sintaxe: local client, err = http2.new(opts)

Cria um cliente HTTP/2 especificando as opções. Em caso de falha, nil e uma string de mensagem de erro serão retornados.

O único parâmetro opts, que é uma tabela Lua, contém alguns campos:

  • recv, uma função Lua que é usada para ler bytes;

  • send, uma função Lua que é usada para enviar bytes;

  • ctx, um dado opaco, atua como o contexto dos chamadores;

A função recv e send será chamada assim:

local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
  • preread_size, um número Lua que influencia o tamanho da janela de envio inicial do par (anunciado através do quadro SETTINGS), o padrão é 65535;

  • max_concurrent_stream, um número Lua que limita o número máximo de streams concorrentes em uma sessão HTTP/2, o padrão é 128;

  • max_frame_size, um número Lua que limita o tamanho máximo do quadro que o par pode enviar, o padrão é 16777215.

  • key, uma string Lua que representa qual sessão HTTP/2 em cache os chamadores desejam reutilizar, se não encontrada, uma nova sessão HTTP/2 será criada. Veja client:keepalive para mais detalhes.

client:acknowledge_settings

sintaxe: local ok, err = client:acknowledge_settings()

Reconhece o quadro SETTINGS do par, as configurações serão aplicadas automaticamente.

Em caso de falha, nil e uma string Lua descreverão a razão do erro.

client:request

sintaxe: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)

Envia uma solicitação HTTP para o par,

Em caso de falha, nil e uma string Lua descreverão a razão do erro.

Os headers devem ser uma tabela Lua semelhante a um array que representa os cabeçalhos da solicitação HTTP, cada entrada é como { name = "header1", value = "value1" }.

Vale a pena notar que esta biblioteca não cuida da semântica dos cabeçalhos HTTP, portanto, é responsabilidade dos chamadores fornecer isso, e os chamadores devem implementar quaisquer conversões necessárias, por exemplo, Host deve ser convertido para :authority. Além disso, os seguintes cabeçalhos serão ignorados, pois são específicos de CONEXÃO.

  • Connection
  • Keep-Alive
  • Proxy-Connection
  • Upgrade
  • Transfer-Encoding

O body pode ser uma string Lua que representa o corpo da solicitação HTTP. Também pode ser uma função Lua para implementar o upload em fluxo. Quando body é uma função Lua, será chamada assim:

local part_data, last, err = body(size)

Em caso de falha, body deve fornecer o 3º valor de retorno err para informar a esta biblioteca que ocorreram alguns erros fatais, então este método será abortado imediatamente, e um quadro GOAWAY será enviado ao par com o código de erro INTERNAL_ERROR.

Quando todos os dados forem gerados, o 2º valor de retorno last deve ser fornecido, e seu valor deve ser true.

on_headers_reach deve ser uma função Lua, como um callback que será chamada quando os cabeçalhos completos da resposta HTTP forem recebidos, será chamada assim:

local abort = on_headers_reach(ctx, headers)

O 2º parâmetro headers é uma tabela Lua semelhante a um hash que representa os cabeçalhos da resposta HTTP recebidos do par.

on_headers_reach pode decidir se aborta a sessão HTTP/2 retornando um valor booleano abort para a biblioteca, a sessão HTTP/2 será abortada se on_headers_reach retornar um valor verdadeiro.

O último parâmetro, on_data_reach, é uma função Lua, atua como o callback que será chamado sempre que o corpo da resposta for recebido, será chamada assim:

local abort = on_data_reach(ctx, data)

O 2º parâmetro data é uma string Lua que representa o corpo da resposta HTTP recebido desta vez.

O significado do valor de retorno é o mesmo que o de on_headers_reach.

Após este método retornar, a sessão HTTP/2 ainda está ativa, pode-se decidir fechar esta sessão chamando client:close ou prosseguir para fazer algo.

client:send_request

sintaxe: local stream, err = client:send_request(headers, body?)

Envia os cabeçalhos e o corpo (se houver) para o par.

Os significados de headers e body são os mesmos que os de client:request.

O objeto de stream correspondente criado será fornecido quando este método retornar.

Em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

client:read_headers

sintaxe: local headers, err = client:read_headers(stream)

Lê os cabeçalhos da resposta do par, o parâmetro stream é o criado por client:send_request.

Os cabeçalhos retornados são uma tabela Lua semelhante a um hash que contém todos os cabeçalhos da resposta HTTP, que podem conter alguns cabeçalhos pseudo como ":status", os chamadores devem fazer algumas transformações se necessário.

Em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

client:read_body

sintaxe: local body, err = client:read_body(stream)

Lê um quadro DATA do par, o parâmetro stream é o criado por client:send_request.

Os dados retornados são uma string Lua que representa um pedaço do corpo da resposta. Uma string vazia será fornecida se todo o corpo tiver sido lido.

Em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

client:close

sintaxe: local ok, err = client:close(code)

Fecha a sessão HTTP/2 atual com o código de erro code.

Veja resty.http2.error para aprender sobre os códigos de erro.

Em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

client:keepalive

sintaxe: client:keepalive(key)

Armazena em cache a sessão HTTP/2 atual para reutilização, note que sessões HTTP/2 malformadas nunca serão armazenadas em cache. A sessão HTTP/2 será destacada da conexão, precisamente, do objeto Cosocket atual.

A sessão HTTP/2 destacada será salva em uma tabela Lua interna semelhante a um hash, o parâmetro único key será usado para indexar esta sessão quando os chamadores desejarem reutilizá-la.

Após definir esta sessão como ativa, os chamadores também devem definir o objeto Cosocket como keepalive.

Há uma limitação inerente entre o mapeamento da sessão HTTP/2 e a conexão subjacente. Uma sessão HTTP/2 só pode ser usada em uma conexão TCP porque é stateful, se os chamadores armazenarem a conexão em um pool que armazena várias conexões, a relação de vinculação é perdida, uma vez que não se sabe qual conexão é escolhida para o objeto Cosocket, portanto, qual sessão HTTP/2 deve ser correspondida também é desconhecido.

Não há uma maneira elegante de resolver isso, a menos que o modelo Cosocket possa atribuir um identificador à conexão subjacente. Agora, o que os chamadores podem fazer é usar o pool de conexões de tamanho único para contornar essa limitação, por exemplo:

...

sock:connect(host, port, { pool = "h2" })

...

sock:setkeepalive(75, 1)
client:keepalive("test")

resty.http2.protocol

Este módulo implementa algumas APIs de baixo nível relevantes ao protocolo.

Para carregar este módulo, basta fazer isso:

local protocol = require "resty.http2.protocol"

protocol.session

sintaxe: local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)

Cria uma nova sessão HTTP/2, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

O significado de cada parâmetro é o mesmo que os descritos em http2.new.

O quadro SETTINGS inicial e o quadro WINDOW_UPDATE serão enviados antes que esta função retorne.

session:adjust_window

sintaxe: local ok = session:adjust_window(delta)

Ajusta o tamanho da janela de envio de cada stream, o stream será redefinido se o tamanho da janela de envio alterado exceder MAX_WINDOW_SIZE, nesse caso, ok será nil.

session:frame_queue

sintaxe: session:frame_queue(frame)

Anexa frame à fila de saída da sessão atual.

session:flush_queue

sintaxe: local ok, err = session:flush_queue()

Empacota e esvazia os quadros em fila, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

session:submit_request

sintaxe: local ok, err = session:submit_request(headers, no_body, priority?, pad?)

Submete uma solicitação HTTP à sessão HTTP/2 atual, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

Significado de cada parâmetro:

  • headers, deve ser uma tabela Lua semelhante a um hash que representa os cabeçalhos da solicitação HTTP, vale a pena notar que esta biblioteca não cuida da semântica dos cabeçalhos HTTP, portanto, é responsabilidade dos chamadores fornecer isso, e os chamadores devem transformar quaisquer cabeçalhos pseudo necessários. Por exemplo, :authority deve ser passado em vez de Host;

  • no_body, um valor booleano, indica se esta solicitação tem corpo. Quando for verdadeiro, o quadro HEADERS gerado conterá a flag END_HEADERS;

  • priority, uma tabela Lua semelhante a um hash, que é usada para definir dependências de stream personalizadas:

  • priority.sid representa o identificador do stream dependente;
  • priority.excl, se o novo stream se torna a única dependência do stream indicado por priority.sid;
  • priority.weight define o peso do novo stream;

  • pad, os dados de preenchimento.

session:submit_window_update

sintaxe: local ok, err = session:submit_window_update(incr)

Submete um quadro WINDOW_UPDATE para toda a sessão HTTP/2 com um incremento incr, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

session:recv_frame

sintaxe: local frame, err = session:recv_frame()

Recebe um quadro HTTP/2, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

A ação correspondente será tomada automaticamente, por exemplo, um quadro GOAWAY será enviado se o par violar as convenções do protocolo HTTP/2; um quadro WINDOW_UPDATE será enviado se a janela de envio do par se tornar muito pequena.

session:close

sintaxe: session:close(code?, debug_data?)

Gera um quadro GOAWAY com o código de erro code e dados de depuração debug_data, o código de erro padrão é NO_ERROR e os dados de depuração são nil.

Note que esta função apenas coloca o quadro GOAWAY na fila de saída, os chamadores devem chamar session:flush_queue para realmente enviar os quadros.

session:detach

sintaxe: session:detach()

Destaca a sessão HTTP/2 atual do objeto Cosocket.

session:attach

sintaxe: local ok, err = session:attach(recv, send, ctx)

Anexa a sessão HTTP/2 atual a um objeto Cosocket, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

Os significados de recv, send e ctx são os mesmos que os descritos em http.new.

resty.http2.stream

Este módulo implementa algumas APIs de baixo nível relevantes a streams.

Para carregar este módulo, basta fazer isso:

local h2_stream = require "resty.http2.stream"

h2_stream.new

sintaxe: local stream = h2_stream.new(sid, weight, session)

Cria um novo stream com o identificador sid, peso weight e a sessão HTTP/2 à qual pertence.

h2_stream.new_root

sintaxe: local root_stream = h2_stream.new_root(session)

Cria o stream raiz com sua sessão.

O identificador do stream raiz é 0x0 e é realmente um stream virtual que é usado para manipular toda a sessão HTTP/2.

stream:submit_headers

sintaxe: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)

Submete alguns cabeçalhos HTTP ao stream.

O primeiro parâmetro headers, deve ser uma tabela Lua semelhante a um hash que representa os cabeçalhos da solicitação HTTP, vale a pena notar que esta biblioteca não cuida da semântica dos cabeçalhos HTTP, portanto, é responsabilidade dos chamadores fornecer isso, e os chamadores devem transformar quaisquer cabeçalhos pseudo necessários. Por exemplo, :authority deve ser passado em vez de Host;

O parâmetro end_stream deve ser um valor booleano e é usado para controlar se o quadro HEADERS deve ter a flag END_STREAM, basicamente os chamadores podem defini-lo como verdadeiro se não houver corpo da solicitação a ser enviado.

priority deve ser uma tabela Lua semelhante a um hash (se houver), que é usada para definir dependências de stream personalizadas: * priority.sid representa o identificador do stream dependente; * priority.excl, se o novo stream se torna a única dependência do stream indicado por priority.sid; * priority.weight define o peso do novo stream;

O último parâmetro pad representa os dados de preenchimento.

Em caso de falha, nil e uma string Lua que descreve o erro correspondente serão fornecidos.

stream:submit_data

sintaxe: local ok, err = stream:submit_data(data, pad, last)

Submete algum corpo da solicitação ao stream, data deve ser uma string Lua, com dados de preenchimento opcionais.

O último parâmetro last indica se esta é a última submissão, o quadro DATA atual anexará a flag END_STREAM se last for verdadeiro.

Em caso de falha, nil e uma string Lua que descreve o erro correspondente serão fornecidos.

stream:submit_window_update

sintaxe: local ok, err = session:submit_window_update(incr)

Submete um quadro WINDOW_UPDATE para o stream com um incremento incr, em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

stream:set_dependency

sintaxe: stream:set_dependency(depend, excl)

Define as dependências do stream atual para um stream com o identificador depend.

O segundo parâmetro excl indica se o stream atual será o único filho de depend.

Quando depend está ausente, o stream alvo será a raiz e excl será tratado como false.

stream:rst

sintaxe: stream:rst(code)

Gera um quadro RST_STREAM com o código de erro code. No caso de code estar ausente, o código NO_ERROR será selecionado.

Note que este método apenas gera um quadro RST_STREAM em vez de enviá-lo, o chamador deve enviar este quadro chamando session:flush_queue.

resty.http2.frame

Este módulo implementa algumas APIs de baixo nível relevantes a quadros.

Para carregar este módulo, basta fazer isso:

local h2_frame = require "resty.http2.frame"

h2_frame.header.new

sintaxe: local hd = h2_frame.header.new(length, typ, flags, id)

Cria um cabeçalho de quadro, com o comprimento do payload length, tipo de quadro type e usa flags como as flags do quadro, que pertence ao stream id.

h2_frame.header.pack

sintaxe: h2_frame.header.pack(hd, dst)

Serializa o cabeçalho do quadro hd para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

h2_frame.header.unpack

sintaxe: h2_frame.header.unpack(src)

Desserializa um cabeçalho de quadro de uma string Lua src, o comprimento de src deve ser pelo menos 9 octetos.

h2_frame.priority.pack

sintaxe: h2_frame.priority.pack(pf, dst)

Serializa um quadro PRIORITY para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O pf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • depend, o identificador do stream dependente;
  • excl, especifica se o stream atual onde este quadro PRIORITY reside se torna o único filho do stream identificado por depend;
  • weight, atribui um novo peso weight ao stream atual;

h2_frame.priority.unpack

sintaxe: local ok, err = h2_frame.priority.unpack(pf, src, stream)

Desserializa um quadro PRIORITY de uma string Lua src, o comprimento de src deve ser pelo menos o tamanho especificado em pf.header.length.

O pf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro PRIORITY atual, ou seja, pf.header.

O último parâmetro stream especifica o stream ao qual o quadro PRIORITY atual pertence.

Ações correspondentes serão tomadas automaticamente dentro deste método, como construir as novas dependências.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.rst_stream.pack

sintaxe: h2_frame.rst_stream.pack(rf, dst)

Serializa um quadro RST_STREAM para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O rf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • error_code, o código de erro;

h2_frame.rst_stream.unpack

sintaxe: local ok, err = h2_frame.rst_stream.unpack(rf, src, stream)

Desserializa um quadro RST_STREAM de uma string Lua src. O comprimento de src deve ser pelo menos o tamanho especificado em rf.header.length.

O rf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro RST_STREAM atual, ou seja, rf.header.

O último parâmetro stream especifica o stream ao qual o quadro RST_STREAM atual pertence.

Ações correspondentes serão tomadas automaticamente dentro deste método, como mudar o estado do stream.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.rst_stream.new

sintaxe: local rf = h2_frame.rst_stream.new(error_code, sid)

Cria um quadro RST_STREAM com o código de erro error_code, que pertence ao stream sid.

h2_frame.settings.pack

sintaxe: h2_frame.settings.pack(sf, dst)

Serializa um quadro SETTINGS para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O sf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • item, as configurações específicas, que devem ser uma tabela Lua semelhante a um array, cada elemento deve ser uma tabela Lua semelhante a um hash:
  • id, o identificador da configuração, pode ser:
    • SETTINGS_ENABLE_PUSH (0x2)
    • SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
    • SETTINGS_INITIAL_WINDOW_SIZE (0x4)
    • SETTINGS_MAX_FRAME_SIZE (0x5)
  • value, o valor da configuração correspondente;

h2_frame.settings.unpack

sintaxe: local ok, err = h2_frame.settings.unpack(sf, src, stream)

Desserializa um quadro SETTINGS de uma string Lua src. O comprimento de src deve ser pelo menos o tamanho especificado em sf.header.length.

O sf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro SETTINGS atual, ou seja, sf.header.

O último parâmetro stream especifica o stream ao qual o quadro SETTINGS atual pertence (deve ser o stream raiz).

Ações correspondentes serão tomadas automaticamente dentro deste método, como atualizar o valor das configurações da sessão HTTP/2.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.settings.new

sintaxe: local sf = h2_frame.settings.new(flags, payload)

Cria um quadro SETTINGS com as flags flags e o item de payload payload.

O payload deve ser uma tabela Lua semelhante a um array, cada elemento deve ser uma tabela Lua semelhante a um hash: * id, o identificador da configuração, pode ser: * SETTINGS_ENABLE_PUSH (0x2) * SETTINGS_MAX_CONCURRENT_STREAMS (0x3) * SETTINGS_INITIAL_WINDOW_SIZE (0x4) * SETTINGS_MAX_FRAME_SIZE (0x5) * value, o valor da configuração correspondente;

h2_frame.ping.pack

sintaxe: h2_frame.ping.pack(pf, dst)

Serializa um quadro PING para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O pf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • opaque_data_hi, o valor mais alto de 32 bits dos dados de ping correspondentes;
  • opaque_data_lo, o valor mais baixo de 32 bits dos dados de ping correspondentes;

h2_frame.ping.unpack

sintaxe: local ok, err = h2_frame.ping.unpack(pf, src, stream)

Desserializa um quadro PING de uma string Lua src. O comprimento de src deve ser pelo menos o tamanho especificado em sf.header.length.

O pf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro PING atual, ou seja, pf.header.

O último parâmetro stream especifica o stream ao qual o quadro PING atual pertence (deve ser o stream raiz).

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.goaway.pack

sintaxe: h2_frame.goaway.pack(gf, dst)

Serializa um quadro GOAWAY para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O gf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • last_stream_id, o último identificador de stream inicializado pelo par;
  • error_code, o código de erro;
  • debug_data, os dados de depuração;

h2_frame.goaway.unpack

sintaxe: local ok, err = h2_frame.goaway.unpack(gf, src, stream)

Desserializa um quadro GOAWAY de uma string Lua src. O comprimento de src deve ser pelo menos o tamanho especificado em gf.header.length.

O gf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro GOAWAY atual, ou seja, gf.header.

O último parâmetro stream especifica o stream ao qual o quadro GOAWAY atual pertence (deve ser o stream raiz).

Em caso de falha, nil e uma string Lua que descreve a razão do erro serão fornecidos.

h2_frame.goaway.new

sintaxe: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)

Cria um quadro GOAWAY com o último identificador de stream inicializado pelo par last_sid, e código de erro error_code. Opcionalmente, com os dados de depuração debug_data.

h2_frame.window_update.pack

sintaxe: h2_frame.window_update.pack(wf, dst)

Serializa um quadro WINDOW_UPDATE para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O wf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • window_size_increment, o incremento do tamanho da janela;

h2_frame.window_update.unpack

sintaxe: local ok, err = h2_frame.window_update.unpack(wf, src, stream)

Desserializa um quadro WINDOW_UPDATE de uma string Lua src. O comprimento de src deve ser pelo menos o tamanho especificado em wf.header.length.

O wf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro WINDOW_UPDATE atual, ou seja, wf.header.

O último parâmetro stream especifica o stream ao qual o quadro WINDOW_UPDATE atual pertence.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.window_update.new

sintaxe: local wf = h2_frame.window_update.new(sid, window)

Cria um quadro WINDOW_UPDATE com o identificador do stream sid, e aumenta o tamanho da janela especificado por window.

h2_frame.headers.pack

sintaxe: h2_frame.headers.pack(hf, dst)

Serializa um quadro HEADERS para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O hf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • pad, os dados de preenchimento;
  • depend, o identificador do stream dependente;
  • excl, especifica se o stream ao qual o quadro HEADERS atual pertence se tornará o único filho do stream depend;
  • weight, especifica o peso do stream ao qual o quadro HEADERS atual pertence;
  • block_frags, os cabeçalhos HTTP simples (após a compressão hpack);

h2_frame.headers.unpack

sintaxe: local ok,err = h2_frame.headers.unpack(hf, src, stream)

Desserializa um quadro HEADERS de uma string Lua src, o comprimento de src deve ser pelo menos o tamanho especificado em hf.header.length.

O hf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro HEADERS atual, ou seja, hf.header.

O último parâmetro stream especifica o stream ao qual o quadro HEADERS atual pertence.

A ação correspondente será tomada, por exemplo, a transição de estado do stream ocorrerá.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.headers.new

sintaxe: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)

Cria um quadro HEADERS que leva os fragmentos de bloco frags.

O parâmetro pri pode ser usado para especificar as dependências do stream, pri deve ser uma tabela Lua semelhante a um hash, que contém:

  • sid, o identificador do stream dependente;
  • excl, se o stream sid será o único filho do stream dependente;
  • weight, define o peso do stream atual (especificado por sid);

O pad especifica os dados de preenchimento, que são opcionais.

Quando end_stream é verdadeiro, o quadro HEADERS atual terá a flag END_STREAM, da mesma forma, quando end_headers é verdadeiro, o quadro HEADERS atual terá a flag END_HEADERS.

Deve-se ter cuidado que se o quadro HEADERS atual não contiver todos os cabeçalhos, então um ou mais quadros CONTINUATION devem ser seguidos de acordo com o protocolo HTTP/2.

h2_frame.continuation.pack

sintaxe: h2_frame.continuation.pack(cf, dst)

Serializa um quadro CONTINUATION para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O cf deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • block_frags, os cabeçalhos HTTP simples (após a compressão hpack);

h2_frame.continuation.unpack

sintaxe: local ok, err = h2_frame.continuation.unpack(cf, src, stream)

Desserializa um quadro CONTINUATION de uma string Lua src, o comprimento de src deve ser pelo menos o tamanho especificado em cf.header.length.

O cf deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro CONTINUATION atual, ou seja, cf.header.

O último parâmetro stream especifica o stream ao qual o quadro CONTINUATION atual pertence.

A ação correspondente será tomada, por exemplo, a transição de estado do stream ocorrerá.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.continuation.new

sintaxe: local cf = h2_frame.continuation.new(frags, end_headers, sid)

Cria um quadro CONTINUATION que leva os fragmentos de bloco frags.

Quando end_headers é verdadeiro, o quadro CONTINUATION atual terá a flag END_HEADERS.

Deve-se ter cuidado que se o quadro CONTINUATION atual não contiver todos os cabeçalhos, então um ou mais quadros CONTINUATION devem ser seguidos de acordo com o protocolo HTTP/2.

O sid especifica o stream ao qual o quadro CONTINUATION atual pertence.

h2_frame.data.pack

sintaxe: h2_frame.data.pack(df, dst)

Serializa um quadro DATA para o destino dst. O dst deve ser uma tabela Lua semelhante a um array.

O df deve ser uma tabela Lua semelhante a um hash que contém:

  • header, o cabeçalho do quadro;
  • payload, o corpo da solicitação/resposta HTTP;

h2_frame.data.unpack

sintaxe: local ok, err = h2_frame.data.unpack(df, src, stream)

Desserializa um quadro DATA de uma string Lua src, o comprimento de src deve ser pelo menos o tamanho especificado em df.header.length.

O df deve ser uma tabela Lua semelhante a um hash que já contém o cabeçalho do quadro DATA atual, ou seja, df.header.

O último parâmetro stream especifica o stream ao qual o quadro DATA atual pertence.

A ação correspondente será tomada, por exemplo, a transição de estado do stream ocorrerá.

Em caso de falha, nil e um código de erro serão fornecidos.

h2_frame.data.new

sintaxe: local df = h2_frame.data.new(payload, pad, last, sid)

Cria um quadro DATA que leva o payload payload.

O pad especifica os dados de preenchimento, que são opcionais.

Quando last é verdadeiro, o quadro DATA atual terá a flag END_STREAM.

O sid especifica o stream ao qual o quadro DATA atual pertence.

h2_frame.push_promise.unpack

sintaxe: local df = h2_frame.data.new(payload, pad, last, sid)

Atualmente, qualquer quadro PUSH_PROMISE recebido será rejeitado.

Este método sempre retorna nil e o erro PROTOCOL_ERROR.

resty.http2.hpack

Este módulo implementa algumas APIs de baixo nível HPACK.

Para carregar este módulo, basta fazer isso:

local hpack = require "resty.http2.hpack"

hpack.encode

sintaxe: hpack.encode(src, dst, lower)

Codifica a string Lua src para o destino dst, o dst deve ser uma tabela Lua semelhante a um array. Códigos Huffman serão tentados primeiro.

O lower especifica se a operação de codificação atual é insensível a maiúsculas e minúsculas, o padrão é false.

hpack.indexed

sintaxe: local v = hpack.indexed(index)

Retorna o índice após usar a Representação de Campo de Cabeçalho Indexado.

hpack.incr_indexed

sintaxe: local v = hpack.indexed(index)

Retorna o índice após usar o Campo de Cabeçalho Literal com Indexação Incremental.

hpack.new

sintaxe: local h = hpack.new(size)

Cria uma instância hpack, uma vez que a decodificação HPACK é stateful.

O size representa o tamanho máximo da tabela hpack, o padrão é 4096 bytes.

O valor de retorno h representa a instância HPACK. Um dos membros de h é importante, ou seja, h.cached, que salva todos os fragmentos de bloco de cabeçalho, e h:decode analisará os dados dentro do h.cached.

Atualmente, o h2_frame.headers.unpack e h2_frame.continuation.unpack empurrarão fragmentos de bloco de cabeçalho para h.cached, uma vez que o bloco esteja completo, a decodificação será executada.

h:insert_entry

sintaxe: local ok = h:insert_entry(header_name, header_value)

Tenta inserir uma entrada de cabeçalho com o nome header_name e valor header_value na tabela dinâmica HPACK.

A inserção pode falhar se esta entrada for muito grande. A evacuação necessária de entradas ocorrerá se o espaço não for suficiente.

Este método retornará true se a inserção for bem-sucedida ou false se não for.

h:resize

sintaxe: local ok = h:resize(new_size)

Ajusta o tamanho da tabela dinâmica para new_size, atualmente o new_size não pode exceder 4096, caso contrário, a operação de redimensionamento falhará.

Quando o tamanho da tabela dinâmica é reduzido, algumas entradas serão evacuadas de acordo com a regra do HPACK.

Este método retornará true se a operação de redimensionamento for bem-sucedida ou false se não for.

h:decode

sintaxe: local ok, err = h:decode(dst)

Decodifica os fragmentos de bloco de cabeçalho dentro de h.cached, os cabeçalhos decodificados serão salvos em dst, uma tabela Lua semelhante a um hash.

Em caso de falha, nil e um código de erro serão fornecidos.

h:get_indexed_header

sintaxe: local entry = h:get_indexed_header(index)

Retorna a entrada de cabeçalho de acordo com o índice index.

O valor de retorno será nil se o índice for inválido, caso contrário, a entry será uma tabela Lua semelhante a um hash com dois itens:

  • entry.name, o nome do cabeçalho;
  • entry.value, o valor do cabeçalho;

resty.http2.error

Este módulo implementa algumas APIs de baixo nível relevantes a erros.

Existem muitos códigos de erro definidos, basicamente eles são consistentes com o 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

E três códigos de erro personalizados:

  • h2_error.STREAM_PROTOCOL_ERROR, erro de protocolo em nível de stream;
  • h2_error.STREAM_FLOW_CONTROL_ERROR, erro de controle de fluxo em nível de stream;
  • h2_error.STREAM_FRAME_SIZE_ERROR, erro de tamanho de quadro em nível de stream;

Erros em nível de stream não influenciarão a conexão inteira, mas redefinirão o stream atual.

Para carregar este módulo, basta fazer isso:

h2_error.strerror

sintaxe: local msg = h2_error.strerror(code)

Retorna uma string Lua que descreve o código de erro code, "erro desconhecido" será fornecido se o código de erro for desconhecido.

h2_error.is_stream_error

sintaxe: local ok = h2_error.is_stream_error(code)

Julga se o código de erro code é um erro em nível de stream.

Veja Também

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub do nginx-module-http2.