http2: L'implémentation du protocole HTTP/2 (côté client) pour nginx-module-lua
Installation
Si vous n'avez pas configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.
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
Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.
Ce document décrit lua-resty-http2 v1.0 publié le 20 novembre 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, "échec de la connexion ", 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)
-- Traitez les en-têtes de réponse
end
local on_data_reach = function(ctx, data)
-- Traitez le corps de la réponse
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, "échec de la création du client 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() a échoué: ", err)
return
end
sock:close()
Pour un exemple plus formel, veuillez lire util/example.lua.
Description
Cette bibliothèque Lua pure implémente le protocole HTTP/2 côté client, mais tous les détails ne sont pas couverts, par exemple, les dépendances de flux sont maintenues mais jamais utilisées.
Il existe certaines limitations inhérentes qui ne sont pas résolues, cependant.
Ne peut pas être utilisé sur des connexions SSL/TLS établies. Le tcpsock:sslhandshake ne prend pas en charge les extensions ALPN ou NPN, donc actuellement seules les connexions simples peuvent être utilisées, la bibliothèque commencera une session HTTP/2 en envoyant le préfixe de connexion, c'est-à-dire la chaîne :
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
Cette bibliothèque fournit un correctif pour la négociation de protocole de couche applicative. Utilisez simplement ceci si vous en avez besoin.
Seule une requête HTTP peut être soumise. Actuellement, les API implémentées ne prennent en charge que la soumission d'une seule requête HTTP. Les PRs sont les bienvenues pour résoudre cela.
Réutilisation de session HTTP/2. Le protocole HTTP/2 est conçu comme persistant, tandis que l'objet Cosocket est lié à une requête HTTP spécifique. Il faut fermer l'objet Cosocket ou le garder actif avant que la requête ne soit terminée, ce modèle est en conflit avec la réutilisation de la session HTTP/2, seule une solution de contournement peut résoudre cela, voir client:keepalive pour plus de détails.
API Implémentée
resty.http2
Pour charger ce module, il suffit de faire ceci :
local http2 = require "resty.http2"
http2.new
syntax: local client, err = http2.new(opts)
Crée un client HTTP/2 en spécifiant les options. En cas d'échec, nil et une chaîne de message d'erreur seront retournés.
Le seul paramètre opts, qui est une table Lua, contient certains champs :
-
recv, une fonction Lua utilisée pour lire des octets ; -
send, une fonction Lua utilisée pour envoyer des octets ; -
ctx, des données opaques, agissant comme le contexte des appelants ;
Les fonctions recv et send seront appelées comme suit :
local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
-
preread_size, un nombre Lua qui influence la taille de la fenêtre d'envoi initiale du pair (annoncée via le cadre SETTINGS), par défaut65535; -
max_concurrent_stream, un nombre Lua qui limite le nombre maximum de flux concurrents dans une session HTTP/2, par défaut128; -
max_frame_size, un nombre Lua qui limite la taille maximale du cadre que le pair peut envoyer, par défaut16777215. -
key, une chaîne Lua qui représente quelle session HTTP/2 mise en cache les appelants souhaitent réutiliser, si non trouvée, une nouvelle session HTTP/2 sera créée. Voir client:keepalive pour plus de détails.
client:acknowledge_settings
syntax: local ok, err = client:acknowledge_settings()
Reconnaît le cadre SETTINGS du pair, les paramètres seront appliqués automatiquement.
En cas d'échec, nil et une chaîne Lua décrivant la raison de l'erreur seront données.
client:request
syntax: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)
Envoie une requête HTTP au pair,
En cas d'échec, nil et une chaîne Lua décrivant la raison de l'erreur seront données.
Les headers doivent être une table Lua de type tableau représentant les en-têtes de la requête HTTP, chaque entrée est comme { name = "header1", value = "value1" }.
Il convient de noter que cette bibliothèque ne prend pas en charge la sémantique des en-têtes HTTP, donc il est de la responsabilité des appelants de fournir cela, et les appelants doivent mettre en œuvre toutes les conversions nécessaires, par exemple, Host doit être converti en :authority. De plus, les en-têtes suivants seront ignorés car ils sont spécifiques à la CONNEXION.
ConnectionKeep-AliveProxy-ConnectionUpgradeTransfer-Encoding
Le body peut être une chaîne Lua représentant le corps de la requête HTTP. Il peut également s'agir d'une fonction Lua pour implémenter le téléchargement en mode flux. Lorsque body est une fonction Lua, elle sera appelée comme suit :
local part_data, last, err = body(size)
En cas d'échec, body doit fournir la 3ème valeur de retour err pour indiquer à cette bibliothèque qu'une erreur fatale s'est produite, puis cette méthode sera immédiatement abandonnée, et un cadre GOAWAY sera envoyé au pair avec le code d'erreur INTERNAL_ERROR.
Lorsque toutes les données ont été générées, la 2ème valeur de retour last doit être fournie, et sa valeur doit être true.
on_headers_reach, doit être une fonction Lua, en tant que rappel qui sera appelée lorsque les en-têtes de réponse HTTP complets sont reçus, elle sera appelée comme suit :
local abort = on_headers_reach(ctx, headers)
Le 2ème paramètre headers est une table Lua de type hash qui représente les en-têtes de réponse HTTP reçus du pair.
on_headers_reach peut décider d'abandonner la session HTTP/2 en retournant une valeur booléenne abort à la bibliothèque, la session HTTP/2 sera abandonnée si on_headers_reach retourne une valeur vraie.
Le dernier paramètre, on_data_reach, est une fonction Lua, agissant comme le rappel qui sera appelé chaque fois que le corps de la réponse est reçu, elle sera appelée comme suit :
local abort = on_data_reach(ctx, data)
Le 2ème paramètre data est une chaîne Lua représentant le corps de la réponse HTTP reçu cette fois.
La signification de la valeur de retour est la même que celle de on_headers_reach.
Après que cette méthode retourne, la session HTTP/2 est toujours active, on peut décider de fermer cette session en appelant client:close ou de continuer à faire quelque chose.
client:send_request
syntax: local stream, err = client:send_request(headers, body?)
Envoie les en-têtes et le corps (le cas échéant) au pair.
Les significations de headers et body sont les mêmes que dans client:request.
L'objet de flux créé correspondant sera donné lorsque cette méthode retourne.
En cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
client:read_headers
syntax: local headers, err = client:read_headers(stream)
Lit les en-têtes de réponse du pair, le paramètre stream est celui créé par client:send_request.
Les en-têtes de retour sont une table Lua de type hash qui contient tous les en-têtes de réponse HTTP, qui peuvent contenir certains en-têtes pseudo comme ":status", les appelants doivent effectuer certaines transformations si nécessaire.
En cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
client:read_body
syntax: local body, err = client:read_body(stream)
Lit un cadre DATA du pair, le paramètre stream est celui créé par client:send_request.
Les données de retour sont une chaîne Lua qui représente un morceau du corps de réponse. Une chaîne vide sera donnée si l'ensemble du corps a été lu.
En cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
client:close
syntax: local ok, err = client:close(code)
Ferme la session HTTP/2 actuelle avec le code d'erreur code.
Voir resty.http2.error pour connaître les codes d'erreur.
En cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
client:keepalive
syntax: client:keepalive(key)
Met en cache la session HTTP/2 actuelle pour la réutilisation, notez qu'une session HTTP/2 mal formée ne sera jamais mise en cache. La session HTTP/2 sera détachée de la connexion, précisément, l'objet Cosocket actuel.
La session HTTP/2 détachée sera enregistrée dans une table Lua interne de type hash, le paramètre unique key sera utilisé pour indexer cette session lorsque les appelants souhaitent la réutiliser.
Après avoir défini cette session comme active, les appelants doivent également définir l'objet Cosocket comme keepalive.
Il existe une limitation inhérente entre le mappage de la session HTTP/2 et la connexion sous-jacente. Une session HTTP/2 ne peut être utilisée que dans une connexion TCP car elle est état, si les appelants stockent la connexion dans un pool qui met en cache plusieurs connexions, les relations de liaison sont perdues, puisque quelle connexion est choisie pour l'objet Cosocket n'est pas sûr, par conséquent, quelle session HTTP/2 doit être associée est également inconnue.
Il n'y a pas de solution élégante à cela, à moins que le modèle Cosocket puisse attribuer un identifiant à la connexion sous-jacente. Maintenant, ce que les appelants peuvent faire est d'utiliser le pool de connexions de taille unique pour contourner cette limitation, par exemple :
...
sock:connect(host, port, { pool = "h2" })
...
sock:setkeepalive(75, 1)
client:keepalive("test")
resty.http2.protocol
Ce module implémente certaines API pertinentes au protocole de bas niveau.
Pour charger ce module, il suffit de faire ceci :
local protocol = require "resty.http2.protocol"
protocol.session
syntax: local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)
Crée une nouvelle session HTTP/2, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
La signification de chaque paramètre est la même que celle décrite dans http2.new.
Le cadre SETTINGS initial et le cadre WINDOW_UPDATE seront envoyés avant que cette fonction ne retourne.
session:adjust_window
syntax: local ok = session:adjust_window(delta)
Ajuste la taille de la fenêtre d'envoi de chaque flux, le flux sera réinitialisé si la taille de la fenêtre d'envoi modifiée dépasse MAX_WINDOW_SIZE, dans ce cas, ok sera nil.
session:frame_queue
syntax: session:frame_queue(frame)
Ajoute frame à la file de sortie de la session actuelle.
session:flush_queue
syntax: local ok, err = session:flush_queue()
Emballage et vidage des cadres en file d'attente, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
session:submit_request
syntax: local ok, err = session:submit_request(headers, no_body, priority?, pad?)
Soumet une requête HTTP à la session HTTP/2 actuelle, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
Signification de chaque paramètre :
-
headers, doit être une table Lua de type hash représentant les en-têtes de la requête HTTP, il convient de noter que cette bibliothèque ne prend pas en charge la sémantique des en-têtes HTTP, donc il est de la responsabilité des appelants de fournir cela, et les appelants doivent transformer tous les en-têtes pseudo nécessaires. Par exemple,:authoritydoit être passé plutôt queHost; -
no_body, une valeur booléenne, indique si cette requête a un corps. Lorsqu'elle est vraie, le cadre HEADERS généré contiendra le drapeau END_HEADERS ; -
priority, une table Lua de type hash, qui est utilisée pour définir des dépendances de flux personnalisées : priority.sidreprésente l'identifiant du flux dépendant ;priority.excl, si le nouveau flux devient la seule dépendance du flux indiqué parpriority.sid;-
priority.weightdéfinit le poids du nouveau flux ; -
pad, les données de remplissage.
session:submit_window_update
syntax: local ok, err = session:submit_window_update(incr)
Soumet un cadre WINDOW_UPDATE pour l'ensemble de la session HTTP/2 avec un incrément incr, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
session:recv_frame
syntax: local frame, err = session:recv_frame()
Reçoit un cadre HTTP/2, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
L'action correspondante sera prise automatiquement, par exemple, un cadre GOAWAY sera envoyé si le pair viole les conventions du protocole HTTP/2 ; un cadre WINDOW_UPDATE sera envoyé si la fenêtre d'envoi du pair devient trop petite.
session:close
syntax: session:close(code?, debug_data?)
Génère un cadre GOAWAY avec le code d'erreur code et les données de débogage debug_data, le code d'erreur par défaut est NO_ERROR et les données de débogage sont nil.
Notez que cette fonction ne fait que mettre en file d'attente le cadre GOAWAY dans la file de sortie, les appelants doivent appeler session:flush_queue pour réellement envoyer les cadres.
session:detach
syntax: session:detach()
Détache la session HTTP/2 actuelle de l'objet Cosocket.
session:attach
syntax: local ok, err = session:attach(recv, send, ctx)
Attache la session HTTP/2 actuelle à un objet Cosocket, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
Les significations de recv, send et ctx sont les mêmes que celles décrites dans http.new.
resty.http2.stream
Ce module implémente certaines API pertinentes au flux de bas niveau.
Pour charger ce module, il suffit de faire ceci :
local h2_stream = require "resty.http2.stream"
h2_stream.new
syntax: local stream = h2_stream.new(sid, weight, session)
Crée un nouveau flux avec l'identifiant sid, le poids weight et la session HTTP/2 à laquelle il appartient.
h2_stream.new_root
syntax: local root_stream = h2_stream.new_root(session)
Crée le flux racine avec sa session.
L'identifiant du flux racine est 0x0 et est en réalité un flux virtuel utilisé pour manipuler l'ensemble de la session HTTP/2.
stream:submit_headers
syntax: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)
Soumet certains en-têtes HTTP au flux.
Le premier paramètre headers, doit être une table Lua de type hash représentant les en-têtes de la requête HTTP, il convient de noter que cette bibliothèque ne prend pas en charge la sémantique des en-têtes HTTP, donc il est de la responsabilité des appelants de fournir cela, et les appelants doivent transformer tous les en-têtes pseudo nécessaires. Par exemple, :authority doit être passé plutôt que Host ;
Le paramètre end_stream doit être une valeur booléenne et est utilisé pour contrôler si le cadre HEADERS doit prendre le drapeau END_STREAM, fondamentalement, les appelants peuvent le définir sur vrai s'il n'y a pas de corps de requête à envoyer.
priority doit être une table Lua de type hash (le cas échéant), qui est utilisée pour définir des dépendances de flux personnalisées :
* priority.sid représente l'identifiant du flux dépendant ;
* priority.excl, si le nouveau flux devient la seule dépendance du flux indiqué par priority.sid ;
* priority.weight définit le poids du nouveau flux ;
Le dernier paramètre pad, représente les données de remplissage.
En cas d'échec, nil et une chaîne Lua qui décrit l'erreur correspondante seront données.
stream:submit_data
syntax: local ok, err = stream:submit_data(data, pad, last)
Soumet un corps de requête au flux, data doit être une chaîne Lua, avec des données de remplissage optionnelles.
Le dernier paramètre last indique si c'est la dernière soumission, le cadre DATA actuel attachera le drapeau END_STREAM si last est vrai.
En cas d'échec, nil et une chaîne Lua qui décrit l'erreur correspondante seront données.
stream:submit_window_update
syntax: local ok, err = session:submit_window_update(incr)
Soumet un cadre WINDOW_UPDATE pour le flux avec un incrément incr, en cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
stream:set_dependency
syntax: stream:set_dependency(depend, excl)
Définit les dépendances du flux actuel à un flux avec l'identifiant depend.
Le deuxième paramètre excl, indique si le flux actuel sera le seul enfant de depend.
Lorsque depend est absent, le flux cible sera la racine et excl sera traité comme false.
stream:rst
syntax: stream:rst(code)
Génère un cadre RST_STREAM avec le code d'erreur code. Dans le cas où code est absent, le code NO_ERROR sera sélectionné.
Notez que cette méthode ne génère qu'un cadre RST_STREAM plutôt que de l'envoyer, l'appelant doit envoyer ce cadre en appelant session:flush_queue.
resty.http2.frame
Ce module implémente certaines API pertinentes aux cadres de bas niveau.
Pour charger ce module, il suffit de faire ceci :
local h2_frame = require "resty.http2.frame"
h2_frame.header.new
syntax: local hd = h2_frame.header.new(length, typ, flags, id)
Crée un en-tête de cadre, avec la longueur de charge utile length, le type de cadre type et prend flags comme les drapeaux du cadre, qui appartient au flux id.
h2_frame.header.pack
syntax: h2_frame.header.pack(hd, dst)
Sérialise l'en-tête de cadre hd vers la destination dst. Le dst doit être une table Lua de type tableau.
h2_frame.header.unpack
syntax: h2_frame.header.unpack(src)
Désérialise un en-tête de cadre à partir d'une chaîne Lua src, la longueur de src doit être d'au moins 9 octets.
h2_frame.priority.pack
syntax: h2_frame.priority.pack(pf, dst)
Sérialise un cadre PRIORITY vers la destination dst. Le dst doit être une table Lua de type tableau.
Le pf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;depend, l'identifiant du flux dépendant ;excl, spécifie si le flux actuel où ce cadre PRIORITY se trouve devient le seul enfant du flux identifié pardepend;weight, attribue un nouveau poidsweightau flux actuel ;
h2_frame.priority.unpack
syntax: local ok, err = h2_frame.priority.unpack(pf, src, stream)
Désérialise un cadre PRIORITY à partir d'une chaîne Lua src, la longueur de src doit être d'au moins la taille spécifiée dans pf.header.length.
Le pf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre PRIORITY actuel, c'est-à-dire pf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre PRIORITY actuel.
Les actions correspondantes seront prises automatiquement à l'intérieur de cette méthode, comme la construction des nouvelles dépendances.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.rst_stream.pack
syntax: h2_frame.rst_stream.pack(rf, dst)
Sérialise un cadre RST_STREAM vers la destination dst. Le dst doit être une table Lua de type tableau.
Le rf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;error_code, le code d'erreur ;
h2_frame.rst_stream.unpack
syntax: local ok, err = h2_frame.rst_stream.unpack(rf, src, stream)
Désérialise un cadre RST_STREAM à partir d'une chaîne Lua src. La longueur de src doit être d'au moins la taille spécifiée dans rf.header.length.
Le rf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre RST_STREAM actuel, c'est-à-dire rf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre RST_STREAM actuel.
Les actions correspondantes seront prises automatiquement à l'intérieur de cette méthode, comme le changement de l'état du flux.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.rst_stream.new
syntax: local rf = h2_frame.rst_stream.new(error_code, sid)
Crée un cadre RST_STREAM avec le code d'erreur error_code, qui appartient au flux sid.
h2_frame.settings.pack
syntax: h2_frame.settings.pack(sf, dst)
Sérialise un cadre SETTINGS vers la destination dst. Le dst doit être une table Lua de type tableau.
Le sf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;item, les paramètres spécifiques, qui doivent être une table Lua de type tableau, chaque élément doit être une table Lua de type hash :id, l'identifiant du paramètre, peut être :- SETTINGS_ENABLE_PUSH (0x2)
- SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
- SETTINGS_INITIAL_WINDOW_SIZE (0x4)
- SETTINGS_MAX_FRAME_SIZE (0x5)
value, la valeur du paramètre correspondant ;
h2_frame.settings.unpack
syntax: local ok, err = h2_frame.settings.unpack(sf, src, stream)
Désérialise un cadre SETTINGS à partir d'une chaîne Lua src. La longueur de src doit être d'au moins la taille spécifiée dans sf.header.length.
Le sf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre SETTINGS actuel, c'est-à-dire sf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre SETTINGS actuel (doit être le flux racine).
Les actions correspondantes seront prises automatiquement à l'intérieur de cette méthode, comme la mise à jour de la valeur des paramètres de session HTTP/2.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.settings.new
syntax: local sf = h2_frame.settings.new(flags, payload)
Crée un cadre SETTINGS avec les drapeaux flags et l'élément de charge utile payload.
Le payload doit être une table Lua de type tableau, chaque élément doit être une table Lua de type hash :
* id, l'identifiant du paramètre, peut être :
* SETTINGS_ENABLE_PUSH (0x2)
* SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
* SETTINGS_INITIAL_WINDOW_SIZE (0x4)
* SETTINGS_MAX_FRAME_SIZE (0x5)
* value, la valeur du paramètre correspondant ;
h2_frame.ping.pack
syntax: h2_frame.ping.pack(pf, dst)
Sérialise un cadre PING vers la destination dst. Le dst doit être une table Lua de type tableau.
Le pf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;opaque_data_hi, la valeur des 32 bits les plus élevés des données de ping correspondantes ;opaque_data_lo, la valeur des 32 bits les plus bas des données de ping correspondantes ;
h2_frame.ping.unpack
syntax: local ok, err = h2_frame.ping.unpack(pf, src, stream)
Désérialise un cadre PING à partir d'une chaîne Lua src. La longueur de src doit être d'au moins la taille spécifiée dans sf.header.length.
Le pf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre PING actuel, c'est-à-dire pf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre PING actuel (doit être le flux racine).
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.goaway.pack
syntax: h2_frame.goaway.pack(gf, dst)
Sérialise un cadre GOAWAY vers la destination dst. Le dst doit être une table Lua de type tableau.
Le gf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;last_stream_id, l'identifiant du dernier flux initialisé par le pair ;error_code, le code d'erreur ;debug_data, les données de débogage ;
h2_frame.goaway.unpack
syntax: local ok, err = h2_frame.goaway.unpack(gf, src, stream)
Désérialise un cadre GOAWAY à partir d'une chaîne Lua src. La longueur de src doit être d'au moins la taille spécifiée dans gf.header.length.
Le gf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre GOAWAY actuel, c'est-à-dire gf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre GOAWAY actuel (doit être le flux racine).
En cas d'échec, nil et une chaîne Lua qui décrit la raison de l'erreur seront données.
h2_frame.goaway.new
syntax: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)
Crée un cadre GOAWAY avec l'identifiant du dernier flux initialisé par le pair last_sid, et le code d'erreur error_code. Optionnellement, avec les données de débogage debug_data.
h2_frame.window_update.pack
syntax: h2_frame.window_update.pack(wf, dst)
Sérialise un cadre WINDOW_UPDATE vers la destination dst. Le dst doit être une table Lua de type tableau.
Le wf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;window_size_increment, l'incrément de la taille de la fenêtre ;
h2_frame.window_update.unpack
syntax: local ok, err = h2_frame.window_update.unpack(wf, src, stream)
Désérialise un cadre WINDOW_UPDATE à partir d'une chaîne Lua src. La longueur de src doit être d'au moins la taille spécifiée dans wf.header.length.
Le wf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre WINDOW_UPDATE actuel, c'est-à-dire wf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre WINDOW_UPDATE actuel.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.window_update.new
syntax: local wf = h2_frame.window_update.new(sid, window)
Crée un cadre WINDOW_UPDATE avec l'identifiant du flux sid, et agrandit la taille de la fenêtre spécifiée par window.
h2_frame.headers.pack
syntax: h2_frame.headers.pack(hf, dst)
Sérialise un cadre HEADERS vers la destination dst. Le dst doit être une table Lua de type tableau.
Le hf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;pad, les données de remplissage ;depend, l'identifiant du flux dépendant ;excl, spécifie si le flux auquel appartient le cadre HEADERS actuel deviendra le seul enfant du fluxdepend;weight, spécifie le poids du flux auquel appartient le cadre HEADERS actuel.block_frags, les en-têtes HTTP bruts (après compression hpack) ;
h2_frame.headers.unpack
syntax: local ok,err = h2_frame.headers.unpack(hf, src, stream)
Désérialise un cadre HEADERS à partir de la chaîne Lua src, la longueur de src doit être d'au moins la taille spécifiée dans hf.header.length.
Le hf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre HEADERS actuel, c'est-à-dire hf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre HEADERS actuel.
L'action correspondante sera prise, par exemple, la transition d'état du flux se produira.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.headers.new
syntax: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)
Crée un cadre HEADERS qui prend les fragments de bloc frags.
Le paramètre pri peut être utilisé pour spécifier les dépendances de flux, pri doit être une table Lua de type hash, qui contient :
sid, l'identifiant du flux dépendant ;excl, si le fluxsidsera le seul enfant du flux dépendant ;weight, définit le poids du flux actuel (spécifié parsid) ;
Le pad spécifie les données de remplissage, qui sont optionnelles.
Lorsque end_stream est vrai, le cadre HEADERS actuel prendra le drapeau END_STREAM, de même, lorsque end_headers est vrai, le cadre HEADERS actuel prendra le drapeau END_HEADERS.
Il faut faire attention si le cadre HEADERS actuel ne contient pas tous les en-têtes, alors un ou plusieurs cadres CONTINUATION doivent suivre conformément au protocole HTTP/2.
h2_frame.continuation.pack
syntax: h2_frame.continuation.pack(cf, dst)
Sérialise un cadre CONTINUATION vers la destination dst. Le dst doit être une table Lua de type tableau.
Le cf doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;block_frags, les en-têtes HTTP bruts (après compression hpack) ;
h2_frame.continuation.unpack
syntax: local ok, err = h2_frame.continuation.unpack(cf, src, stream)
Désérialise un cadre CONTINUATION à partir de la chaîne Lua src, la longueur de src doit être d'au moins la taille spécifiée dans cf.header.length.
Le cf doit être une table Lua de type hash qui contient déjà l'en-tête du cadre CONTINUATION actuel, c'est-à-dire cf.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre CONTINUATION actuel.
L'action correspondante sera prise, par exemple, la transition d'état du flux se produira.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.continuation.new
syntax: local cf = h2_frame.continuation.new(frags, end_headers, sid)
Crée un cadre CONTINUATION qui prend les fragments de bloc frags.
Lorsque end_headers est vrai, le cadre CONTINUATION actuel prendra le drapeau END_HEADERS.
Il faut faire attention si le cadre CONTINUATION actuel ne contient pas tous les en-têtes, alors un ou plusieurs cadres CONTINUATION doivent suivre conformément au protocole HTTP/2.
Le sid spécifie le flux auquel appartient le cadre CONTINUATION actuel.
h2_frame.data.pack
syntax: h2_frame.data.pack(df, dst)
Sérialise un cadre DATA vers la destination dst. Le dst doit être une table Lua de type tableau.
Le df doit être une table Lua de type hash qui contient :
header, l'en-tête du cadre ;payload, le corps de la requête/réponse HTTP ;
h2_frame.data.unpack
syntax: local ok, err = h2_frame.data.unpack(df, src, stream)
Désérialise un cadre DATA à partir de la chaîne Lua src, la longueur de src doit être d'au moins la taille spécifiée dans df.header.length.
Le df doit être une table Lua de type hash qui contient déjà l'en-tête du cadre DATA actuel, c'est-à-dire df.header.
Le dernier paramètre stream spécifie le flux auquel appartient le cadre DATA actuel.
L'action correspondante sera prise, par exemple, la transition d'état du flux se produira.
En cas d'échec, nil et un code d'erreur seront donnés.
h2_frame.data.new
syntax: local df = h2_frame.data.new(payload, pad, last, sid)
Crée un cadre DATA qui prend la charge utile payload.
Le pad spécifie les données de remplissage, qui sont optionnelles.
Lorsque last est vrai, le cadre DATA actuel prendra le drapeau END_STREAM.
Le sid spécifie le flux auquel appartient le cadre DATA actuel.
h2_frame.push_promise.unpack
syntax: local df = h2_frame.data.new(payload, pad, last, sid)
Actuellement, tout cadre PUSH_PROMISE entrant sera rejeté.
Cette méthode retourne toujours nil et l'erreur PROTOCOL_ERROR.
resty.http2.hpack
Ce module implémente certaines API HPACK de bas niveau.
Pour charger ce module, il suffit de faire ceci :
local hpack = require "resty.http2.hpack"
hpack.encode
syntax: hpack.encode(src, dst, lower)
Encode la chaîne Lua src vers la destination dst, le dst doit être une table Lua de type tableau. Les codes de Huffman seront d'abord essayés.
Le lower spécifie si l'opération d'encodage actuelle est insensible à la casse, par défaut false.
hpack.indexed
syntax: local v = hpack.indexed(index)
Retourne l'index après avoir utilisé la représentation de champ d'en-tête indexé.
hpack.incr_indexed
syntax: local v = hpack.indexed(index)
Retourne l'index après avoir utilisé le champ d'en-tête littéral avec indexation incrémentale.
hpack.new
syntax: local h = hpack.new(size)
Crée une instance hpack, puisque le décodage HPACK est état.
La size représente la taille maximale de la table hpack, par défaut 4096 octets.
La valeur de retour h, représente l'instance HPACK. Un des membres de h est important, c'est-à-dire h.cached, qui sauvegarde tous les fragments de bloc d'en-tête, et h:decode analysera les données à l'intérieur de h.cached.
Actuellement, les h2_frame.headers.unpack et h2_frame.continuation.unpack pousseront les fragments de bloc d'en-tête dans h.cached, une fois le bloc complet, le décodage sera exécuté.
h:insert_entry
syntax: local ok = h:insert_entry(header_name, header_value)
Tente d'insérer une entrée d'en-tête avec le nom header_name et la valeur header_value dans la table dynamique HPACK.
L'insertion peut échouer si cette entrée est trop grande. Une éviction d'entrée nécessaire se produira si l'espace n'est pas suffisant.
Cette méthode retournera true si l'insertion est réussie ou false sinon.
h:resize
syntax: local ok = h:resize(new_size)
Ajuste la taille de la table dynamique à new_size, actuellement la new_size ne peut pas dépasser 4096, sinon l'opération de redimensionnement échouera.
Lorsque la taille de la table dynamique est réduite, certaines entrées seront évincées selon la règle HPACK.
Cette méthode retournera true si l'opération de redimensionnement est réussie ou false sinon.
h:decode
syntax: local ok, err = h:decode(dst)
Décode les fragments de bloc d'en-tête à l'intérieur de h.cached, les en-têtes décodés seront sauvegardés dans dst, une table Lua de type hash.
En cas d'échec, nil et un code d'erreur seront donnés.
h:get_indexed_header
syntax: local entry = h:get_indexed_header(index)
Retourne l'entrée d'en-tête selon l'index index.
La valeur de retour sera nil si l'index est invalide, sinon, l'entry sera une table Lua de type hash avec deux éléments :
entry.name, le nom de l'en-tête ;entry.value, la valeur de l'en-tête ;
resty.http2.error
Ce module implémente certaines API pertinentes aux erreurs de bas niveau.
Il existe de nombreux codes d'erreur définis, qui sont essentiellement cohérents avec le protocole 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
Et trois codes d'erreur personnalisés :
h2_error.STREAM_PROTOCOL_ERROR, erreur de protocole au niveau du flux ;h2_error.STREAM_FLOW_CONTROL_ERROR, erreur de contrôle de flux au niveau du flux ;h2_error.STREAM_FRAME_SIZE_ERROR, erreur de taille de cadre au niveau du flux ;
Les erreurs au niveau du flux n'influenceront pas la connexion entière mais réinitialiseront le flux actuel.
Pour charger ce module, il suffit de faire ceci :
h2_error.strerror
syntax: local msg = h2_error.strerror(code)
Retourne une chaîne Lua qui décrit le code d'erreur code, "erreur inconnue" sera donnée si le code d'erreur est inconnu.
h2_error.is_stream_error
syntax: local ok = h2_error.is_stream_error(code)
Juge si le code d'erreur code est une erreur au niveau du flux.
Voir Aussi
- 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
Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-http2.