http: pilote cosocket HTTP Lua 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-http
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-http
Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.
Ce document décrit lua-resty-http v0.17.2 publié le 29 février 2024.
Pilote cosocket HTTP Lua pour OpenResty / ngx_lua.
Fonctionnalités
- HTTP 1.0 et 1.1
- SSL
- Interface de streaming pour le corps de la réponse, pour une utilisation mémoire prévisible
- Interface alternative simple pour des requêtes à usage unique sans étape de connexion manuelle
- Encodages de transfert en morceaux et non en morceaux
- Connexions keepalives
- Pipelining des requêtes
- Trailers
- Connexions proxy HTTP
- mTLS (nécessite
ngx_lua_http_module>= v0.10.23)
API
- new
- connect
- set_proxy_options
- set_timeout
- set_timeouts
- set_keepalive
- get_reused_times
- close
- request
- request_uri
- request_pipeline
- parse_uri
- get_client_body_reader
- Response
Obsolète
Ces méthodes peuvent être supprimées dans de futures versions.
Utilisation
Il existe deux modes de fonctionnement de base :
-
Requêtes simples à usage unique qui ne nécessitent aucune gestion manuelle de la connexion mais qui mettent en mémoire tampon l'ensemble de la réponse et laissent la connexion soit fermée, soit de retour dans le pool de connexions.
-
Requêtes en streaming où la connexion est établie séparément, puis la requête est envoyée, le flux du corps est lu par morceaux, et enfin la connexion est fermée manuellement ou maintenue active. Cette technique nécessite un peu plus de code mais permet de rejeter des corps de réponse potentiellement volumineux du côté Lua, ainsi que de pipeliner plusieurs requêtes sur une seule connexion.
Requête à usage unique
local httpc = require("resty.http").new()
-- Les requêtes à usage unique utilisent l'interface `request_uri`.
local res, err = httpc:request_uri("http://example.com/helloworld", {
method = "POST",
body = "a=1&b=2",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
},
})
if not res then
ngx.log(ngx.ERR, "échec de la requête : ", err)
return
end
-- À ce stade, l'ensemble de la requête / réponse est complet et la connexion
-- sera fermée ou de retour dans le pool de connexions.
-- La table `res` contient les champs `status`, `headers` et `body` attendus.
local status = res.status
local length = res.headers["Content-Length"]
local body = res.body
Requête en streaming
local httpc = require("resty.http").new()
-- D'abord établir une connexion
local ok, err, ssl_session = httpc:connect({
scheme = "https",
host = "127.0.0.1",
port = 8080,
})
if not ok then
ngx.log(ngx.ERR, "échec de la connexion : ", err)
return
end
-- Ensuite, envoyer en utilisant `request`, en fournissant un chemin et un
-- en-tête `Host` au lieu d'une URI complète.
local res, err = httpc:request({
path = "/helloworld",
headers = {
["Host"] = "example.com",
},
})
if not res then
ngx.log(ngx.ERR, "échec de la requête : ", err)
return
end
-- À ce stade, le statut et les en-têtes seront disponibles pour être utilisés dans la table `res`,
-- mais le corps et les trailers seront encore sur le fil.
-- Nous pouvons utiliser l'itérateur `body_reader`, pour streamer le corps selon notre
-- taille de tampon souhaitée.
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- traiter
end
until not buffer
local ok, err = httpc:set_keepalive()
if not ok then
ngx.say("échec de la mise en keepalive : ", err)
return
end
-- À ce stade, la connexion sera soit de retour en toute sécurité dans le pool, soit fermée.
Connexion
new
syntax: httpc, err = http.new()
Crée l'objet de connexion HTTP. En cas d'échec, retourne nil et une chaîne décrivant l'erreur.
connect
syntax: ok, err, ssl_session = httpc:connect(options)
Tente de se connecter au serveur web tout en incorporant les activités suivantes :
- Connexion TCP
- Négociation SSL
- Configuration du proxy HTTP
Ce faisant, il créera un nom de pool de connexions distinct qui est sûr à utiliser avec des connexions basées sur SSL et / ou proxy, et par conséquent cette syntaxe est fortement recommandée par rapport à l'originale (maintenant obsolète) syntaxe de connexion uniquement TCP.
La table d'options a les champs suivants :
scheme: schéma à utiliser, ou nil pour un socket de domaine unixhost: hôte cible, ou chemin vers un socket de domaine unixport: port sur l'hôte cible, par défaut80ou443selon le schémapool: nom de pool de connexion personnalisé. Option selon la documentation OpenResty, sauf que le défaut deviendra un nom de pool construit à l'aide des propriétés SSL / proxy, ce qui est important pour une réutilisation sûre des connexions. En cas de doute, laissez-le vide !pool_size: option selon la documentation OpenRestybacklog: option selon la documentation OpenRestyproxy_opts: sous-table, par défaut aux options proxy globales définies, voir set_proxy_options.ssl_reused_session: option selon la documentation OpenRestyssl_verify: option selon la documentation OpenResty, sauf qu'elle est par défauttrue.ssl_server_name: option selon la documentation OpenRestyssl_send_status_req: option selon la documentation OpenRestyssl_client_cert: sera passé àtcpsock:setclientcert. Nécessitengx_lua_http_module>= v0.10.23.ssl_client_priv_key: comme ci-dessus.
set_timeout
syntax: httpc:set_timeout(time)
Définit le délai d'attente du socket (en ms) pour les opérations suivantes. Voir set_timeouts ci-dessous pour une approche plus déclarative.
set_timeouts
syntax: httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)
Définit le seuil de délai d'attente de connexion, le seuil de délai d'attente d'envoi et le seuil de délai d'attente de lecture, respectivement, en millisecondes, pour les opérations de socket suivantes (connecter, envoyer, recevoir, et itérateurs retournés par receiveuntil).
set_keepalive
syntax: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)
Place soit la connexion actuelle dans le pool pour une réutilisation future, soit ferme la connexion. Appeler cela au lieu de close est "sûr" en ce sens qu'il fermera conditionnellement en fonction du type de requête. En particulier, une requête 1.0 sans Connection: Keep-Alive sera fermée, tout comme une requête 1.1 avec Connection: Close.
En cas de succès, retourne 1. En cas d'erreurs, retourne nil, err. Dans le cas où la connexion est conditionnellement fermée comme décrit ci-dessus, retourne 2 et la chaîne d'erreur la connexion doit être fermée, afin de distinguer des erreurs inattendues.
Voir la documentation OpenResty pour la documentation des paramètres.
set_proxy_options
syntax: httpc:set_proxy_options(opts)
Configure un proxy HTTP à utiliser avec cette instance de client. La table opts attend les champs suivants :
http_proxy: une URI vers un serveur proxy à utiliser avec les requêtes HTTPhttp_proxy_authorization: une valeur d'en-tête par défautProxy-Authorizationà utiliser avechttp_proxy, par exempleBasic ZGVtbzp0ZXN0, qui sera remplacée si l'en-tête de requêteProxy-Authorizationest présent.https_proxy: une URI vers un serveur proxy à utiliser avec les requêtes HTTPShttps_proxy_authorization: commehttp_proxy_authorizationmais à utiliser avechttps_proxy(puisque avec HTTPS l'autorisation est effectuée lors de la connexion, celle-ci ne peut pas être remplacée en passant l'en-tête de requêteProxy-Authorization).no_proxy: une liste séparée par des virgules d'hôtes qui ne doivent pas être proxyfiés.
Notez que cette méthode n'a aucun effet lors de l'utilisation de la syntaxe de connexion obsolète TCP uniquement.
get_reused_times
syntax: times, err = httpc:get_reused_times()
Voir la documentation OpenResty.
close
syntax: ok, err = httpc:close()
Voir la documentation OpenResty.
Demande
request
syntax: res, err = httpc:request(params)
Envoie une requête HTTP sur une connexion déjà établie. Retourne une table res ou nil et un message d'erreur.
La table params attend les champs suivants :
version: Le numéro de version HTTP. Par défaut1.1.method: La chaîne de méthode HTTP. Par défautGET.path: La chaîne de chemin. Par défaut/.query: La chaîne de requête, présentée soit comme une chaîne littérale soit comme une table Lua.headers: Une table d'en-têtes de requête.body: Le corps de la requête sous forme de chaîne, de table de chaînes, ou d'une fonction itératrice produisant des chaînes jusqu'à nil lorsqu'elle est épuisée. Notez que vous devez spécifier unContent-Lengthpour le corps de la requête, ou spécifierTransfer-Encoding: chunkedet faire en sorte que votre fonction implémente l'encodage. Voir aussi : get_client_body_reader.
Lorsque la requête est réussie, res contiendra les champs suivants :
status: Le code de statut.reason: La phrase de raison du statut.headers: Une table d'en-têtes. Plusieurs en-têtes avec le même nom de champ seront présentés sous forme de table de valeurs.has_body: Un indicateur booléen indiquant s'il y a un corps à lire.body_reader: Une fonction itératrice pour lire le corps de manière continue.read_body: Une méthode pour lire l'ensemble du corps dans une chaîne.read_trailers: Une méthode pour fusionner tous les trailers sous la tableres.headers, après avoir lu le corps.
Si la réponse a un corps, alors avant que la même connexion puisse être utilisée pour une autre requête, vous devez lire le corps en utilisant read_body ou body_reader.
request_uri
syntax: res, err = httpc:request_uri(uri, params)
L'interface à usage unique (voir utilisation). Puisque cette méthode effectue une requête complète de bout en bout, les options spécifiées dans params peuvent inclure tout ce qui se trouve dans connect et request documentées ci-dessus. Notez également que les champs path, et query, dans params remplaceront les composants pertinents de l'uri si spécifiés (scheme, host, et port seront toujours pris de l'uri).
Il y a 3 paramètres supplémentaires pour contrôler les keepalives :
keepalive: Défini surfalsepour désactiver les keepalives et fermer immédiatement la connexion. Par défauttrue.keepalive_timeout: Le délai d'attente maximal d'inactivité (ms). Par défautlua_socket_keepalive_timeout.keepalive_pool: Le nombre maximal de connexions dans le pool. Par défautlua_socket_pool_size.
Si la requête est réussie, res contiendra les champs suivants :
status: Le code de statut.headers: Une table d'en-têtes.body: L'ensemble du corps de la réponse sous forme de chaîne.
request_pipeline
syntax: responses, err = httpc:request_pipeline(params)
Cette méthode fonctionne comme la méthode request ci-dessus, mais params est plutôt une table imbriquée de tables de paramètres. Chaque requête est envoyée dans l'ordre, et responses est retourné sous forme de table de gestionnaires de réponse. Par exemple :
local responses = httpc:request_pipeline({
{ path = "/b" },
{ path = "/c" },
{ path = "/d" },
})
for _, r in ipairs(responses) do
if not r.status then
ngx.log(ngx.ERR, "erreur de lecture du socket")
break
end
ngx.say(r.status)
ngx.say(r:read_body())
end
En raison de la nature du pipelining, aucune réponse n'est réellement lue jusqu'à ce que vous tentiez d'utiliser les champs de réponse (statut / en-têtes, etc.). Et puisque les réponses sont lues dans l'ordre, vous devez lire l'ensemble du corps (et tous les trailers si vous en avez) avant de tenter de lire la prochaine réponse.
Notez que cela n'exclut pas l'utilisation de l'itérateur de corps de réponse en streaming. Les réponses peuvent toujours être streamées, tant que l'ensemble du corps est streamé avant de tenter d'accéder à la prochaine réponse.
Assurez-vous de tester au moins un champ (tel que le statut) avant d'essayer d'utiliser les autres, au cas où une erreur de lecture de socket se serait produite.
Réponse
res.body_reader
L'itérateur body_reader peut être utilisé pour streamer le corps de la réponse en tailles de morceaux de votre choix, comme suit :
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- traiter
end
until not buffer
Si le lecteur est appelé sans arguments, le comportement dépend du type de connexion. Si la réponse est encodée comme chunked, alors l'itérateur renverra les morceaux au fur et à mesure de leur arrivée. Sinon, il renverra simplement l'ensemble du corps.
Notez que la taille fournie est en fait une taille maximale. Donc dans le cas du transfert en morceaux, vous pouvez obtenir des tampons plus petits que la taille demandée, en raison du reste des morceaux réellement encodés.
res:read_body
syntax: body, err = res:read_body()
Lit l'ensemble du corps dans une chaîne locale.
res:read_trailers
syntax: res:read_trailers()
Cela fusionne tous les trailers sous la table res.headers elle-même. Doit être appelé après avoir lu le corps.
Utilitaire
parse_uri
syntax: local scheme, host, port, path, query? = unpack(httpc:parse_uri(uri, query_in_path?))
C'est une fonction de commodité permettant d'utiliser plus facilement l'interface générique, lorsque les données d'entrée sont une URI.
À partir de la version 0.10, le paramètre optionnel query_in_path a été ajouté, qui spécifie si la chaîne de requête doit être incluse dans la valeur de retour path, ou séparément comme sa propre valeur de retour. Cela est par défaut true afin de maintenir la compatibilité ascendante. Lorsqu'il est défini sur false, path n'inclura que le chemin, et query contiendra les arguments de l'URI, sans inclure le délimiteur ?.
get_client_body_reader
syntax: reader, err = httpc:get_client_body_reader(chunksize?, sock?)
Retourne une fonction itératrice qui peut être utilisée pour lire le corps de la requête client en aval de manière continue. Vous pouvez également spécifier une taille de morceau par défaut optionnelle (par défaut 65536), ou un socket déjà établi à la place de la requête client.
Exemple :
local req_reader = httpc:get_client_body_reader()
local buffer_size = 8192
repeat
local buffer, err = req_reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- traiter
end
until not buffer
Cet itérateur peut également être utilisé comme valeur pour le champ body dans les paramètres de requête, permettant de streamer le corps de la requête dans une requête proxy en amont.
local client_body_reader, err = httpc:get_client_body_reader()
local res, err = httpc:request({
path = "/helloworld",
body = client_body_reader,
})
Obsolète
Ces fonctionnalités restent pour la compatibilité ascendante, mais peuvent être supprimées dans de futures versions.
Connexion uniquement TCP
Les versions suivantes de la signature de méthode connect sont obsolètes en faveur de l'argument unique table documenté ci-dessus.
syntax: ok, err = httpc:connect(host, port, options_table?)
syntax: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)
REMARQUE : le nom de pool par défaut n'incorporera que des informations IP et port, il est donc dangereux de l'utiliser en cas de connexions SSL et/ou Proxy. Spécifiez votre propre pool ou, mieux encore, n'utilisez pas ces signatures.
connect_proxy
syntax: ok, err = httpc:connect_proxy(proxy_uri, scheme, host, port, proxy_authorization)
Appeler cette méthode manuellement n'est plus nécessaire puisqu'elle est incorporée dans connect. Elle est conservée pour l'instant pour la compatibilité avec les utilisateurs de l'ancienne syntaxe connexion uniquement TCP.
Tente de se connecter au serveur web via le serveur proxy donné. La méthode accepte les arguments suivants :
proxy_uri- URI complète du serveur proxy à utiliser (par exemplehttp://proxy.example.com:3128/). Remarque : seul le protocolehttpest pris en charge.scheme- Le protocole à utiliser entre le serveur proxy et l'hôte distant (httpouhttps). Sihttpsest spécifié comme schéma,connect_proxy()effectue une requêteCONNECTpour établir un tunnel TCP vers l'hôte distant via le serveur proxy.host- Le nom d'hôte de l'hôte distant auquel se connecter.port- Le port de l'hôte distant auquel se connecter.proxy_authorization- La valeur de l'en-têteProxy-Authorizationenvoyée au serveur proxy viaCONNECTlorsque leschemeesthttps.
S'il y a une erreur lors de la tentative de connexion, cette méthode retourne nil avec une chaîne décrivant l'erreur. Si la connexion a été établie avec succès, la méthode retourne 1.
Il y a quelques points clés à garder à l'esprit lors de l'utilisation de cette API :
- Si le schéma est
https, vous devez effectuer la négociation TLS avec le serveur distant manuellement en utilisant la méthodessl_handshake()avant d'envoyer des requêtes via le tunnel proxy. - Si le schéma est
http, vous devez vous assurer que les requêtes que vous envoyez via les connexions respectent RFC 7230 et en particulier Section 5.3.2. qui stipule que la cible de la requête doit être sous forme absolue. En pratique, cela signifie que lorsque vous utilisezsend_request(), lepathdoit être une URI absolue vers la ressource (par exemplehttp://example.com/index.htmlau lieu de simplement/index.html).
ssl_handshake
syntax: session, err = httpc:ssl_handshake(session, host, verify)
Appeler cette méthode manuellement n'est plus nécessaire puisqu'elle est incorporée dans connect. Elle est conservée pour l'instant pour la compatibilité avec les utilisateurs de l'ancienne syntaxe connexion uniquement TCP.
Voir la documentation OpenResty.
proxy_request / proxy_response
Ces deux méthodes de commodité étaient simplement destinées à démontrer un cas d'utilisation courant de la mise en œuvre du proxy inverse, et l'auteur regrette leur inclusion dans le module. Les utilisateurs sont encouragés à créer leurs propres solutions plutôt que de dépendre de ces fonctions, qui peuvent être supprimées dans une version ultérieure.
proxy_request
syntax: local res, err = httpc:proxy_request(request_body_chunk_size?)
Effectue une requête en utilisant les arguments de la requête client actuelle, effectuant effectivement un proxy vers l'amont connecté. Le corps de la requête sera lu de manière continue, selon request_body_chunk_size (voir documentation sur le lecteur de corps client ci-dessous).
proxy_response
syntax: httpc:proxy_response(res, chunksize?)
Définit la réponse actuelle en fonction du res donné. S'assure que les en-têtes hop-by-hop ne sont pas envoyés en aval, et lira la réponse selon chunksize (voir documentation sur le lecteur de corps ci-dessus).
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-http.