Aller au contenu

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.18.0 publié le 6 juillet 2026.


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 simple alternative 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)
  • Propagation du contexte de trace W3C via l'en-tête traceparent (>= v0.18.0)

API

Obsolète

Ces méthodes peuvent être supprimées dans de futures versions.

Utilisation

Il existe deux modes de fonctionnement de base :

  1. Requêtes simples à usage unique qui ne nécessitent aucune gestion manuelle de la connexion mais qui mettent en mémoire tampon l'intégralité de la réponse et laissent la connexion soit fermée, soit de retour dans le pool de connexions.

  2. 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 potentiellement de grands corps de réponse 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, établissez 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, envoyez 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 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 attente : ", 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, renvoie `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 intégrant les activités suivantes :

- Connexion TCP
- Négociation SSL
- Configuration du proxy HTTP

Ce faisant, il créera un nom de pool de connexion 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) [syntax de connexion uniquement TCP](#TCP-only-connect).

La table d'options a les champs suivants :

* `scheme` : schéma à utiliser, ou nil pour un socket de domaine unix
* `host` : hôte cible, ou chemin vers un socket de domaine unix
* `port` : port sur l'hôte cible, par défaut à `80` ou `443` selon le schéma
* `pool` : nom de pool de connexion personnalisé. Option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect), 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 de la connexion. En cas de doute, laissez-le vide !
* `pool_debug` : défini sur `true` pour enregistrer le nom du pool de connexion au niveau `DEBUG`, désactivé par défaut
* `pool_size` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `backlog` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `proxy_opts` : sous-table, par défaut aux options de proxy globales définies, voir [set\_proxy\_options](#set_proxy_options).
* `ssl_reused_session` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_verify` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake), sauf qu'elle est par défaut à `true`.
* `ssl_server_name` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_send_status_req` : option selon [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_client_cert` : sera passé à `tcpsock:setclientcert`. Nécessite `ngx_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](#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 (connexion, envoi, réception et itérateurs renvoyé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 ceci au lieu de [close](#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, renvoie `1`. En cas d'erreurs, renvoie `nil, err`. Dans le cas où la connexion est fermée conditionnellement comme décrit ci-dessus, renvoie `2` et la chaîne d'erreur `la connexion doit être fermée`, afin de distinguer des erreurs inattendues.

Voir [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive) 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 HTTP
* `http_proxy_authorization` : une valeur d'en-tête `Proxy-Authorization` par défaut à utiliser avec `http_proxy`, par exemple `Basic ZGVtbzp0ZXN0`, qui sera remplacée si l'en-tête de requête `Proxy-Authorization` est présent.
* `https_proxy` : une URI vers un serveur proxy à utiliser avec les requêtes HTTPS
* `https_proxy_authorization` : comme `http_proxy_authorization` mais à utiliser avec `https_proxy` (puisque pour 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ête `Proxy-Authorization`).
* `no_proxy` : une liste séparée par des virgules d'hôtes qui ne doivent pas être proxy.

Notez que cette méthode n'a aucun effet lors de l'utilisation de la syntaxe de connexion obsolète [TCP uniquement](#TCP-only-connect).

## get\_reused\_times

`syntax: times, err = httpc:get_reused_times()`

Voir [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockgetreusedtimes).

## close

`syntax: ok, err = httpc:close()`

Voir [la documentation OpenResty](https://github.com/openresty/lua-nginx-module#tcpsockclose).

## Demande

## request

`syntax: res, err = httpc:request(params)`

Envoie une requête HTTP sur une connexion déjà établie. Renvoie 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éfaut à `1.1`.
* `method` : La chaîne de méthode HTTP. Par défaut à `GET`.
* `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 d'itérateur produisant des chaînes jusqu'à nil lorsqu'épuisé. Notez que vous devez spécifier un `Content-Length` pour le corps de la requête, ou spécifier `Transfer-Encoding: chunked` et faire en sorte que votre fonction implémente l'encodage. Voir aussi : [get\_client\_body\_reader](#get_client_body_reader).

Lorsque la requête est réussie, `res` contiendra les champs suivants :

* `status` : Le code d'état.
* `reason` : La phrase de raison d'état.
* `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 d'itérateur pour lire le corps de manière continue.
* `read_body` : Une méthode pour lire l'intégralité du corps dans une chaîne.
* `read_trailers` : Une méthode pour fusionner les trailers sous la table `res.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](#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](#connect) et [request](#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 sur `false` pour désactiver les keepalives et fermer immédiatement la connexion. Par défaut à `true`.
* `keepalive_timeout` : Le délai d'attente maximal d'inactivité (ms). Par défaut à `lua_socket_keepalive_timeout`.
* `keepalive_pool` : Le nombre maximal de connexions dans le pool. Par défaut à `lua_socket_pool_size`.

Si la requête est réussie, `res` contiendra les champs suivants :

* `status` : Le code d'état.
* `headers` : Une table d'en-têtes.
* `body` : L'intégralité 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](#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 renvoyé sous forme de table de gestionnaires de réponse. Par exemple :

```lua
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'intégralité du corps (et tous les trailers si vous en avez) avant d'essayer de lire la réponse suivante.

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'intégralité du corps est streamée avant d'essayer d'accéder à la réponse suivante.

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 du 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 l'itérateur est appelé sans arguments, le comportement dépend du type de connexion. Si la réponse est encodée en morceaux, alors l'itérateur renverra les morceaux au fur et à mesure de leur arrivée. Sinon, il renverra simplement l'intégralité du corps.

Notez que la taille fournie est en fait une taille maximale. Donc dans le cas de transfert en morceaux, vous pouvez obtenir des tampons plus petits que la taille que vous demandez, comme reste des morceaux réellement encodés.

res:read_body

syntax: body, err = res:read_body()

Lit l'intégralité 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.

Depuis 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?)

Renvoie une fonction d'itérateur qui peut être utilisée pour lire le corps de la requête du client en aval de manière continue. Vous pouvez également spécifier une taille de morceau par défaut optionnelle (la valeur par défaut est 65536), ou un socket déjà établi à la place de la requête du 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 en amont proxy.

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 à utiliser en cas de connexions SSL et/ou Proxy. Spécifiez votre propre pool ou, mieux encore, ne pas utiliser ces signatures.

connect_proxy

syntax: ok, err = httpc:connect_proxy(proxy_uri, scheme, host, port, proxy_authorization)

L'appel de cette méthode manuellement n'est plus nécessaire car elle est intégrée dans connect. Elle est conservée pour l'instant pour la compatibilité avec les utilisateurs de l'ancienne syntaxe TCP uniquement connect.

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 exemple http://proxy.example.com:3128/). Remarque : seul le protocole http est pris en charge.
  • scheme - Le protocole à utiliser entre le serveur proxy et l'hôte distant (http ou https). Si https est spécifié comme schéma, connect_proxy() effectue une requête CONNECT pour é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 d'en-tête Proxy-Authorization envoyée au serveur proxy via CONNECT lorsque le scheme est https.

Si une erreur se produit lors de la tentative de connexion, cette méthode renvoie nil avec une chaîne décrivant l'erreur. Si la connexion a été établie avec succès, la méthode renvoie 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éthode ssl_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 utilisez send_request(), le path doit être une URI absolue vers la ressource (par exemple http://example.com/index.html au lieu de simplement /index.html).

ssl_handshake

syntax: session, err = httpc:ssl_handshake(session, host, verify)

L'appel de cette méthode manuellement n'est plus nécessaire car elle est intégrée dans connect. Elle est conservée pour l'instant pour la compatibilité avec les utilisateurs de l'ancienne syntaxe TCP uniquement connect.

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 pourraient ê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 requête du client actuel, 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 de la res donnée. 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.