Aller au contenu

echo: module Echo de nginx

Installation

Vous pouvez installer ce module dans n'importe quelle distribution basée sur RHEL, y compris, mais sans s'y limiter :

  • RedHat Enterprise Linux 7, 8, 9 et 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 et Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-echo
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 nginx-module-echo

Activez le module en ajoutant ce qui suit en haut de /etc/nginx/nginx.conf :

load_module modules/ngx_http_echo_module.so;

Ce document décrit nginx-module-echo v0.64 publié le 30 octobre 2025.


   location /hello {
     echo "hello, world!";
   }
   location /hello {
     echo -n "hello, ";
     echo "world!";
   }
   location /timed_hello {
     echo_reset_timer;
     echo hello world;
     echo "'hello world' takes about $echo_timer_elapsed sec.";
     echo hiya igor;
     echo "'hiya igor' takes about $echo_timer_elapsed sec.";
   }
   location /echo_with_sleep {
     echo hello;
     echo_flush;  # ensure the client can see previous output immediately
     echo_sleep   2.5;  # in sec
     echo world;
   }
   # in the following example, accessing /echo yields
   #   hello
   #   world
   #   blah
   #   hiya
   #   igor
   location /echo {
       echo_before_body hello;
       echo_before_body world;
       proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
       echo_after_body hiya;
       echo_after_body igor;
   }
   location /echo/more {
       echo blah;
   }
   # the output of /main might be
   #   hello
   #   world
   #   took 0.000 sec for total.
   # and the whole request would take about 2 sec to complete.
   location /main {
       echo_reset_timer;

       # subrequests in parallel
       echo_location_async /sub1;
       echo_location_async /sub2;

       echo "took $echo_timer_elapsed sec for total.";
   }
   location /sub1 {
       echo_sleep 2;
       echo hello;
   }
   location /sub2 {
       echo_sleep 1;
       echo world;
   }
   # the output of /main might be
   #   hello
   #   world
   #   took 3.003 sec for total.
   # and the whole request would take about 3 sec to complete.
   location /main {
       echo_reset_timer;

       # subrequests in series (chained by CPS)
       echo_location /sub1;
       echo_location /sub2;

       echo "took $echo_timer_elapsed sec for total.";
   }
   location /sub1 {
       echo_sleep 2;
       echo hello;
   }
   location /sub2 {
       echo_sleep 1;
       echo world;
   }
   # Accessing /dup gives
   #   ------ END ------
   location /dup {
     echo_duplicate 3 "--";
     echo_duplicate 1 " END ";
     echo_duplicate 3 "--";
     echo;
   }
   # /bighello will generate 1000,000,000 hello's.
   location /bighello {
     echo_duplicate 1000_000_000 'hello';
   }
   # echo back the client request
   location /echoback {
     echo_duplicate 1 $echo_client_request_headers;
     echo "\r";

     echo_read_request_body;

     echo_request_body;
   }
   # GET /multi will yields
   #   querystring: foo=Foo
   #   method: POST
   #   body: hi
   #   content length: 2
   #   ///
   #   querystring: bar=Bar
   #   method: PUT
   #   body: hello
   #   content length: 5
   #   ///
   location /multi {
       echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
       echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
   }
   location /sub {
       echo "querystring: $query_string";
       echo "method: $echo_request_method";
       echo "body: $echo_request_body";
       echo "content length: $http_content_length";
       echo '///';
   }
   # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together
   location /merge {
       default_type 'text/javascript';
       echo_foreach_split '&' $query_string;
           echo "/* JS File $echo_it */";
           echo_location_async $echo_it;
           echo;
       echo_end;
   }
   # accessing /if?val=abc yields the "hit" output
   # while /if?val=bcd yields "miss":
   location ^~ /if {
       set $res miss;
       if ($arg_val ~* '^a') {
           set $res hit;
           echo $res;
       }
       echo $res;
   }

Description

Ce module enveloppe de nombreuses API internes de Nginx pour le streaming d'entrée et de sortie, les sous-requêtes parallèles/séquentielles, les minuteurs et le sommeil, ainsi que l'accès à diverses métadonnées.

Il fournit essentiellement diverses utilitaires qui aident à tester et déboguer d'autres modules en émule trivialement différents types de localisations de sous-requêtes fictives.

Les utilisateurs le trouveront également utile dans des applications réelles qui ont besoin de

  1. servir des contenus statiques directement depuis la mémoire (chargement à partir du fichier de configuration Nginx).
  2. envelopper la réponse en amont avec un en-tête et un pied de page personnalisés (un peu comme le module addition mais avec des contenus lus directement à partir du fichier de configuration et des variables Nginx).
  3. fusionner les contenus de diverses "localisations Nginx" (c'est-à-dire, des sous-requêtes) ensemble dans une seule requête principale (en utilisant echo_location et ses amis).

C'est un module à double rôle spécial qui peut paresseusement servir de gestionnaire de contenu ou s'enregistrer comme un filtre de sortie uniquement à la demande. Par défaut, ce module ne fait rien du tout.

Techniquement, ce module a également démontré les techniques suivantes qui pourraient être utiles pour les auteurs de modules :

  1. Émettre des sous-requêtes parallèles directement à partir du gestionnaire de contenu.
  2. Émettre des sous-requêtes chaînées directement à partir du gestionnaire de contenu, en passant la continuation le long de la chaîne de sous-requêtes.
  3. Émettre des sous-requêtes avec toutes les méthodes HTTP 1.1 et même un corps de requête HTTP fictif optionnel.
  4. Interagir avec le modèle d'événements Nginx directement depuis le gestionnaire de contenu en utilisant des événements et des minuteurs personnalisés, et reprendre le gestionnaire de contenu si nécessaire.
  5. Module à double rôle qui peut (paresseusement) servir de gestionnaire de contenu ou de filtre de sortie ou les deux.
  6. Création et interpolation de variables de fichier de configuration Nginx.
  7. Contrôle de sortie en streaming utilisant output_chain, flush et ses amis.
  8. Lire le corps de la requête client depuis le gestionnaire de contenu, et revenir (asynchrone) au gestionnaire de contenu après l'achèvement.
  9. Utiliser une suite de tests déclarative basée sur Perl test suite pour conduire le développement de modules C Nginx.

Directives du Gestionnaire de Contenu

L'utilisation des directives suivantes enregistre ce module dans la localisation Nginx actuelle en tant que gestionnaire de contenu. Si vous souhaitez utiliser un autre module, comme le module proxy standard, en tant que gestionnaire de contenu, utilisez les directives de filtre fournies par ce module.

Toutes les directives du gestionnaire de contenu peuvent être mélangées dans une seule localisation Nginx et elles sont censées s'exécuter séquentiellement tout comme dans le langage de script Bash.

Chaque directive de gestionnaire de contenu prend en charge l'interpolation de variables dans ses arguments (le cas échéant).

Le type MIME défini par la directive standard default_type est respecté par ce module, comme dans :

   location /hello {
     default_type text/plain;
     echo hello;
   }

Ensuite, du côté client :

   $ curl -I 'http://localhost/echo'
   HTTP/1.1 200 OK
   Server: nginx/0.8.20
   Date: Sat, 17 Oct 2009 03:40:19 GMT
   Content-Type: text/plain
   Connection: keep-alive

Depuis la version v0.22, toutes les directives sont autorisées dans le bloc if du module rewrite, par exemple :

 location ^~ /if {
     set $res miss;
     if ($arg_val ~* '^a') {
         set $res hit;
         echo $res;
     }
     echo $res;
 }

echo

syntax: echo [options] <string>...

default: non

context: location, location if

phase: content

Envoie des arguments séparés par des espaces, avec une nouvelle ligne à la fin, au client.

Notez que les données peuvent être mises en mémoire tampon par le tampon sous-jacent de Nginx. Pour forcer la sortie des données à être immédiatement envoyée, utilisez la commande echo_flush juste après echo, comme dans

    echo hello world;
    echo_flush;

Lorsque aucun argument n'est spécifié, echo émet uniquement la nouvelle ligne de fin, tout comme la commande echo dans le shell.

Les variables peuvent apparaître dans les arguments. Un exemple est

    echo The current request uri is $request_uri;

$request_uri est une variable exposée par le ngx_http_core_module.

Cette commande peut être utilisée plusieurs fois dans une seule configuration de localisation, comme dans

 location /echo {
     echo hello;
     echo world;
 }

La sortie du côté client ressemble à ceci

 $ curl 'http://localhost/echo'
 hello
 world

Les caractères spéciaux comme les nouvelles lignes (\n) et les tabulations (\t) peuvent être échappés en utilisant des séquences d'échappement de style C. Mais une exception notable est le signe dollar ($). Depuis Nginx 0.8.20, il n'y a toujours pas de moyen propre d'échapper ce caractère. (Une solution de contournement pourrait être d'utiliser une variable $echo_dollor qui est toujours évaluée au caractère constant $. Cette fonctionnalité sera probablement introduite dans une future version de ce module.)

Depuis la version echo v0.28, on peut supprimer le caractère de nouvelle ligne final dans la sortie en utilisant l'option -n, comme dans

 location /echo {
     echo -n "hello, ";
     echo "world";
 }

Accéder à /echo donne

 $ curl 'http://localhost/echo'
 hello, world

Les -n initiaux dans les valeurs de variable ne prendront pas effet et seront émis littéralement, comme dans

 location /echo {
     set $opt -n;
     echo $opt "hello,";
     echo "world";
 }

Cela donne la sortie suivante

 $ curl 'http://localhost/echo'
 -n hello,
 world

On peut émettre des littéraux -n initiaux et d'autres options en utilisant l'option spéciale -- comme ceci

 location /echo {
     echo -- -n is an option;
 }

ce qui donne

 $ curl 'http://localhost/echo'
 -n is an option

Utilisez cette forme lorsque vous souhaitez émettre quoi que ce soit commençant par un tiret (-).

echo_duplicate

syntax: echo_duplicate <count> <string>

default: non

context: location, location if

phase: content

Émet la duplication d'une chaîne indiquée par le deuxième argument, en utilisant le compte spécifié dans le premier argument.

Par exemple,

   location /dup {
       echo_duplicate 3 "abc";
   }

produira la sortie de "abcabcabc".

Les underscores sont autorisés dans le nombre de compte, tout comme en Perl. Par exemple, pour émettre 1000,000,000 instances de "hello, world" :

   location /many_hellos {
       echo_duplicate 1000_000_000 "hello, world";
   }

L'argument count pourrait être zéro, mais pas négatif. Le deuxième argument string pourrait également être une chaîne vide ("").

Contrairement à la directive echo, aucune nouvelle ligne finale n'est ajoutée au résultat. Il est donc possible de "mal utiliser" cette directive comme une version sans nouvelle ligne finale de echo en utilisant "count" 1, comme dans

   location /echo_art {
       echo_duplicate 2 '---';
       echo_duplicate 1 ' END ';  # nous ne voulons pas de nouvelle ligne finale ici
       echo_duplicate 2 '---';
       echo;  # nous voulons une nouvelle ligne finale ici...
   }

Vous obtenez

   ------ END ------

Mais l'utilisation de l'option -n dans echo est plus appropriée à cet effet.

Cette directive a été introduite pour la première fois dans la version 0.11.

echo_flush

syntax: echo_flush

default: non

context: location, location if

phase: content

Force les données potentiellement mises en mémoire tampon par les filtres de sortie sous-jacents de Nginx à être envoyées immédiatement au client via le socket.

Notez que techniquement, la commande émet simplement un objet ngx_buf_t avec le slot flush défini sur 1, donc certains modules de filtre de sortie tiers étranges pourraient encore le bloquer avant qu'il n'atteigne le dernier filtre d'écriture de Nginx.

Cette directive ne prend aucun argument.

Considérez l'exemple suivant :

   location /flush {
      echo hello;

      echo_flush;

      echo_sleep 1;
      echo world;
   }

Ensuite, du côté client, en utilisant curl pour accéder à /flush, vous verrez immédiatement la ligne "hello", mais seulement après 1 seconde, la dernière ligne "world". Sans appeler echo_flush dans l'exemple ci-dessus, vous ne verrez probablement aucune sortie jusqu'à ce qu'une seconde se soit écoulée en raison de la mise en mémoire tampon interne de Nginx.

Cette directive échouera à vider le tampon de sortie en cas de sous-requêtes impliquées. Considérez l'exemple suivant :

   location /main {
       echo_location_async /sub;
       echo hello;
       echo_flush;
   }
   location /sub {
       echo_sleep 1;
   }

Alors le client ne verra pas "hello" apparaître même si echo_flush a été exécuté avant que la sous-requête vers /sub ait réellement commencé à s'exécuter. Les sorties de /main qui sont envoyées après echo_location_async seront reportées et fermement mises en mémoire tampon.

Cela ne s'applique pas aux sorties envoyées avant que la sous-requête ne soit initiée. Pour une version modifiée de l'exemple donné ci-dessus :

   location /main {
       echo hello;
       echo_flush;
       echo_location_async /sub;
   }
   location /sub {
       echo_sleep 1;
   }

Le client verra immédiatement "hello" avant que /sub n'entre en sommeil.

Voir aussi echo, echo_sleep, et echo_location_async.

echo_sleep

syntax: echo_sleep <seconds>

default: non

context: location, location if

phase: content

S'endort pendant la période spécifiée par l'argument, qui est en secondes.

Cette opération est non-bloquante côté serveur, donc contrairement à la directive echo_blocking_sleep, elle ne bloquera pas tout le processus de travail Nginx.

La période peut prendre trois chiffres après la virgule et doit être supérieure à 0.001.

Un exemple est

    location /echo_after_sleep {
        echo_sleep 1.234;
        echo resumed!;
    }

En coulisses, elle configure un objet "sleep" ngx_event_t par requête, et ajoute un minuteur utilisant cet événement personnalisé au modèle d'événements Nginx et attend simplement un délai d'attente sur cet événement. Parce que l'événement "sleep" est par requête, cette directive peut fonctionner dans des sous-requêtes parallèles.

echo_blocking_sleep

syntax: echo_blocking_sleep <seconds>

default: non

context: location, location if

phase: content

C'est une version bloquante de la directive echo_sleep.

Voir la documentation de echo_sleep pour plus de détails.

En coulisses, elle appelle la macro ngx_msleep fournie par le noyau Nginx qui se mappe à usleep sur les systèmes conformes à POSIX.

Notez que cette directive bloquera complètement le processus de travail Nginx actuel pendant son exécution, donc ne l'utilisez jamais en environnement de production.

echo_reset_timer

syntax: echo_reset_timer

default: non

context: location, location if

phase: content

Réinitialise le temps de début du minuteur à maintenant, c'est-à-dire, le temps lorsque cette commande est exécutée pendant la requête.

Le temps de début du minuteur est par défaut le temps de début de la requête actuelle et peut être remplacé par cette directive, potentiellement plusieurs fois dans une seule localisation. Par exemple :

   location /timed_sleep {
       echo_sleep 0.03;
       echo "$echo_timer_elapsed sec elapsed.";

       echo_reset_timer;

       echo_sleep 0.02;
       echo "$echo_timer_elapsed sec elapsed.";
   }

La sortie du côté client pourrait être

 $ curl 'http://localhost/timed_sleep'
 0.032 sec elapsed.
 0.020 sec elapsed.

Les chiffres réels que vous obtenez de votre côté peuvent varier un peu en raison des activités actuelles de votre système.

L'invocation de cette directive forcera le minuteur sous-jacent de Nginx à se mettre à jour à l'heure système actuelle (indépendamment de la résolution du minuteur spécifiée ailleurs dans le fichier de configuration). De plus, les références de la variable $echo_timer_elapsed déclencheront également une mise à jour du minuteur de manière forcée.

Voir aussi echo_sleep et $echo_timer_elapsed.

echo_read_request_body

syntax: echo_read_request_body

default: non

context: location, location if

phase: content

Lit explicitement le corps de la requête afin que la variable $request_body ait toujours des valeurs non vides (sauf si le corps est si volumineux qu'il a été enregistré par Nginx dans un fichier temporaire local).

Notez que cela pourrait ne pas être le corps de requête client d'origine car la requête actuelle pourrait être une sous-requête avec un corps "artificiel" spécifié par son parent.

Cette directive ne génère aucune sortie elle-même, tout comme echo_sleep.

Voici un exemple pour renvoyer la requête HTTP d'origine du client (les en-têtes et le corps sont inclus) :

   location /echoback {
     echo_duplicate 1 $echo_client_request_headers;
     echo "\r";
     echo_read_request_body;
     echo $request_body;
   }

Le contenu de /echoback ressemble à ceci de mon côté (j'utilisais l'utilitaire LWP de Perl pour accéder à cette localisation sur le serveur) :

   $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback'
   POST /echoback HTTP/1.1
   TE: deflate,gzip;q=0.3
   Connection: TE, close
   Host: localhost
   User-Agent: lwp-request/5.818 libwww-perl/5.820
   Content-Length: 12
   Content-Type: application/x-www-form-urlencoded

   hello
   world

Parce que /echoback est la requête principale, $request_body contient le corps de requête d'origine du client.

Avant Nginx 0.7.56, il n'avait aucun sens d'utiliser cette directive car $request_body a été introduit pour la première fois dans Nginx 0.7.58.

Cette directive elle-même a été introduite pour la première fois dans la version v0.14 du module echo.

echo_location_async

syntax: echo_location_async <location> [<url_args>]

default: non

context: location, location if

phase: content

Émet une sous-requête GET à la localisation spécifiée (premier argument) avec des arguments d'URL optionnels spécifiés dans le deuxième argument.

Depuis Nginx 0.8.20, l'argument location ne prend pas en charge les localisations nommées, en raison d'une limitation dans la fonction ngx_http_subrequest. Il en va de même pour son frère, la directive echo_location.

Un exemple très simple est

 location /main {
     echo_location_async /sub;
     echo world;
 }
 location /sub {
     echo hello;
 }

Accéder à /main donne

   hello
   world

Appeler plusieurs localisations en parallèle est également possible :

 location /main {
     echo_reset_timer;
     echo_location_async /sub1;
     echo_location_async /sub2;
     echo "took $echo_timer_elapsed sec for total.";
 }
 location /sub1 {
     echo_sleep 2; # sleeps 2 sec
     echo hello;
 }
 location /sub2 {
     echo_sleep 1; # sleeps 1 sec
     echo world;
 }

Accéder à /main donne

   $ time curl 'http://localhost/main'
   hello
   world
   took 0.000 sec for total.

   real  0m2.006s
   user  0m0.000s
   sys   0m0.004s

Vous pouvez voir que le gestionnaire principal /main ne attend pas que les sous-requêtes /sub1 et /sub2 se terminent et continue rapidement, d'où le résultat de temps "0.000 sec". Cependant, la requête entière prend environ 2 secondes au total pour se terminer car /sub1 et /sub2 s'exécutent en parallèle (ou "concurremment" pour être plus précis).

Si vous utilisez echo_blocking_sleep dans l'exemple précédent à la place, vous obtiendrez la même sortie, mais avec un temps de réponse total de 3 secondes, car "le sommeil bloquant" bloque tout le processus de travail Nginx.

Les localisations peuvent également prendre un argument de chaîne de requête optionnel, par exemple

 location /main {
     echo_location_async /sub 'foo=Foo&bar=Bar';
 }
 location /sub {
     echo $arg_foo $arg_bar;
 }

Accéder à /main donne

   $ curl 'http://localhost/main'
   Foo Bar

Les chaînes de requête ne peuvent pas être concaténées à l'argument location avec "?" directement, par exemple, /sub?foo=Foo&bar=Bar est une localisation invalide et ne doit pas être fournie comme premier argument à cette directive.

Techniquement parlant, cette directive est un exemple que le gestionnaire de contenu Nginx émet une ou plusieurs sous-requêtes directement. A ma connaissance, le module fancyindex fait également ce genre de choses ;)

Les localisations nommées Nginx comme @foo ne sont pas prises en charge ici.

Cette directive est logiquement équivalente à la version GET de echo_subrequest_async. Par exemple,

   echo_location_async /foo 'bar=Bar';

est logiquement équivalent à

   echo_subrequest_async GET /foo -q 'bar=Bar';

Mais appeler cette directive est légèrement plus rapide que d'appeler echo_subrequest_async en utilisant GET car nous n'avons pas à analyser les noms de méthodes HTTP comme GET et les options comme -q.

Cette directive a été introduite pour la première fois dans la version 0.09 de ce module et nécessite au moins Nginx 0.7.46.

echo_location

syntax: echo_location <location> [<url_args>]

default: non

context: location, location if

phase: content

Tout comme la directive echo_location_async, mais echo_location émet des sous-requêtes en série plutôt qu'en parallèle. C'est-à-dire que les directives du gestionnaire de contenu suivant cette directive ne seront pas exécutées tant que la sous-requête émise par cette directive n'est pas terminée.

Le corps de réponse final est presque toujours équivalent au cas où echo_location_async est utilisé à la place, seulement si des variables de temps sont utilisées dans les sorties.

Considérez l'exemple suivant :

 location /main {
     echo_reset_timer;
     echo_location /sub1;
     echo_location /sub2;
     echo "took $echo_timer_elapsed sec for total.";
 }
 location /sub1 {
     echo_sleep 2;
     echo hello;
 }
 location /sub2 {
     echo_sleep 1;
     echo world;
 }

La localisation /main ci-dessus prendra au total 3 secondes pour se terminer (comparé à 2 secondes si echo_location_async est utilisé à la place ici). Voici le résultat en action sur ma machine :

   $ curl 'http://localhost/main'
   hello
   world
   took 3.003 sec for total.

   real  0m3.027s
   user  0m0.020s
   sys   0m0.004s

Cette directive est logiquement équivalente à la version GET de echo_subrequest. Par exemple,

   echo_location /foo 'bar=Bar';

est logiquement équivalent à

   echo_subrequest GET /foo -q 'bar=Bar';

Mais appeler cette directive est légèrement plus rapide que d'appeler echo_subrequest en utilisant GET car nous n'avons pas à analyser les noms de méthodes HTTP comme GET et les options comme -q.

En coulisses, elle crée un objet ngx_http_post_subrequest_t comme une continuation et le passe dans l'appel de fonction ngx_http_subrequest. Nginx rouvrira plus tard cette "continuation" dans l'appel de fonction ngx_http_finalize_request de la sous-requête. Nous reprenons l'exécution du gestionnaire de contenu de la requête parente et commençons à exécuter la directive suivante (commande) s'il y en a une.

Les localisations nommées Nginx comme @foo ne sont pas prises en charge ici.

Cette directive a été introduite pour la première fois dans la version v0.12.

Voir aussi echo_location_async pour plus de détails sur la signification des arguments.

echo_subrequest_async

syntax: echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]

default: non

context: location, location if

phase: content

Initie une sous-requête asynchrone en utilisant la méthode HTTP, des arguments d'URL optionnels (ou chaîne de requête) et un corps de requête optionnel qui peut être défini comme une chaîne ou comme un chemin vers un fichier contenant le corps.

Cette directive est très similaire à une version généralisée de la directive echo_location_async.

Voici un petit exemple démontrant son utilisation :

 location /multi {
     # body defined as string
     echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
     # body defined as path to a file, relative to nginx prefix path if not absolute
     echo_subrequest_async PUT '/sub' -q 'bar=Bar' -f '/tmp/hello.txt';
 }
 location /sub {
     echo "querystring: $query_string";
     echo "method: $echo_request_method";
     echo "body: $echo_request_body";
     echo "content length: $http_content_length";
     echo '///';
 }

Ensuite, du côté client :

   $ echo -n hello > /tmp/hello.txt
   $ curl 'http://localhost/multi'
   querystring: foo=Foo
   method: POST
   body: hi
   content length: 2
   ///
   querystring: bar=Bar
   method: PUT
   body: hello
   content length: 5
   ///

Voici un exemple plus amusant utilisant le module proxy standard proxy module pour gérer la sous-requête :

 location /main {
     echo_subrequest_async POST /sub -b 'hello, world';
 }
 location /sub {
     proxy_pass $scheme://127.0.0.1:$server_port/proxied;
 }
 location /proxied {
     echo "method: $echo_request_method.";

     # nous devons lire le corps ici explicitement...ou $echo_request_body
     #   sera évalué à vide ("")
     echo_read_request_body;

     echo "body: $echo_request_body.";
 }

Ensuite, du côté client, nous pouvons voir que

   $ curl 'http://localhost/main'
   method: POST.
   body: hello, world.

Les localisations nommées Nginx comme @foo ne sont pas prises en charge ici.

Cette directive prend plusieurs options :

-q <url_args>        Spécifiez les arguments d'URL (ou chaîne de requête) pour la sous-requête.

-f <path>            Spécifiez le chemin pour le fichier dont le contenu sera utilisé comme le
                     corps de la requête de la sous-requête.

-b <data>            Spécifiez les données du corps de la requête.

Cette directive a été introduite pour la première fois dans la version v0.15.

L'option -f pour définir un chemin de fichier pour le corps a été introduite dans la version v0.35.

Voir aussi les directives echo_subrequest et echo_location_async.

echo_subrequest

syntax: echo_subrequest <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]

default: non

context: location, location if

phase: content

C'est la version synchrone de la directive echo_subrequest_async. Et tout comme echo_location, elle ne bloque pas le processus de travail Nginx (tandis que echo_blocking_sleep le fait), mais utilise plutôt une continuation pour passer le contrôle le long de la chaîne de sous-requêtes.

Voir echo_subrequest_async pour plus de détails.

Les localisations nommées Nginx comme @foo ne sont pas prises en charge ici.

Cette directive a été introduite pour la première fois dans la version v0.15.

echo_foreach_split

syntax: echo_foreach_split <delimiter> <string>

default: non

context: location, location if

phase: content

Divise le deuxième argument string en utilisant le délimiteur spécifié dans le premier argument, puis itère à travers les éléments résultants. Par exemple :

   location /loop {
     echo_foreach_split ',' $arg_list;
       echo "item: $echo_it";
     echo_end;
   }

Accéder à /main donne

   $ curl 'http://localhost/loop?list=cat,dog,mouse'
   item: cat
   item: dog
   item: mouse

Comme vu dans l'exemple précédent, cette directive doit toujours être accompagnée d'une directive echo_end.

Les boucles echo_foreach_split parallèles sont autorisées, mais les imbriquées sont actuellement interdites.

L'argument delimiter pourrait contenir plusieurs caractères arbitraires, comme

   # cela produit "cat\ndog\nmouse\n"
   echo_foreach_split -- '-a-' 'cat-a-dog-a-mouse';
     echo $echo_it;
   echo_end;

Logiquement parlant, cette structure de boucle est juste la boucle foreach combinée avec un appel de fonction split en Perl (en utilisant l'exemple précédent) :

    foreach (split ',', $arg_list) {
        print "item $_\n";
    }

Les utilisateurs le trouveront également utile pour fusionner plusieurs ressources .js ou .css en un tout. Voici un exemple :

   location /merge {
       default_type 'text/javascript';

       echo_foreach_split '&' $query_string;
           echo "/* JS File $echo_it */";
           echo_location_async $echo_it;
           echo;
       echo_end;
   }

Ensuite, accéder à /merge pour fusionner les ressources .js spécifiées dans la chaîne de requête :

   $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js'

On peut également utiliser un module de cache Nginx tiers pour mettre en cache la réponse fusionnée générée par la localisation /merge dans l'exemple précédent.

Cette directive a été introduite pour la première fois dans la version v0.17.

echo_end

syntax: echo_end

default: non

context: location, location if

phase: content

Cette directive est utilisée pour terminer le corps des structures de contrôle de boucle et conditionnelles comme echo_foreach_split.

Cette directive a été introduite pour la première fois dans la version v0.17.

echo_request_body

syntax: echo_request_body

default: non

context: location, location if

phase: content

Émet le contenu du corps de la requête précédemment lu.

En coulisses, il est implémenté à peu près comme ceci :

   if (r->request_body && r->request_body->bufs) {
       return ngx_http_output_filter(r, r->request_body->bufs);
   }

Contrairement aux variables $echo_request_body et $request_body, cette directive affichera l'ensemble du corps de la requête même si certaines parties ou toutes les parties ont été enregistrées dans des fichiers temporaires sur le disque.

C'est une "no-op" si aucun corps de requête n'a encore été lu.

Cette directive a été introduite pour la première fois dans la version v0.18.

Voir aussi echo_read_request_body et le module chunkin.

echo_exec

syntax: echo_exec <location> [<query_string>]

syntax: echo_exec <named_location>

default: non

context: location, location if

phase: content

Effectue une redirection interne vers la localisation spécifiée. Une chaîne de requête optionnelle peut être spécifiée pour les localisations normales, comme dans

   location /foo {
       echo_exec /bar weight=5;
   }
   location /bar {
       echo $arg_weight;
   }

Ou équivalemment

   location /foo {
       echo_exec /bar?weight=5;
   }
   location /bar {
       echo $arg_weight;
   }

Les localisations nommées sont également prises en charge. Voici un exemple :

   location /foo {
       echo_exec @bar;
   }
   location @bar {
       # vous obtiendrez /foo plutôt que @bar
       #  en raison d'un bug potentiel dans nginx.
       echo $echo_request_uri;
   }

Mais la chaîne de requête (le cas échéant) sera toujours ignorée pour les redirections de localisation nommée en raison d'une limitation dans la fonction ngx_http_named_location.

N'essayez jamais d'émettre des choses avant la directive echo_exec sinon vous ne verrez pas la réponse appropriée de la localisation vers laquelle vous souhaitez rediriger. Parce que toute émission provoquera le gestionnaire de localisation d'origine à envoyer des en-têtes HTTP avant que la redirection ne se produise.

Techniquement parlant, cette directive expose les fonctions API internes de Nginx ngx_http_internal_redirect et ngx_http_named_location.

Cette directive a été introduite pour la première fois dans la version v0.21 release.

echo_status

syntax: echo_status <status-num>

default: echo_status 200

context: location, location if

phase: content

Spécifie le code de statut de réponse par défaut. Par défaut à 200. Cette directive est déclarative et l'ordre relatif avec d'autres directives de type echo n'est pas important.

Voici un exemple,

 location = /bad {
     echo_status 404;
     echo "Something is missing...";
 }

alors nous obtenons une réponse comme ceci :

HTTP/1.1 404 Not Found
Server: nginx/1.2.1
Date: Sun, 24 Jun 2012 03:58:18 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive

Something is missing...

Cette directive a été introduite pour la première fois dans la version v0.40.

Directives de Filtre

L'utilisation des directives suivantes déclenche l'enregistrement du filtre de ce module. Par défaut, aucun filtre ne sera enregistré par ce module.

Chaque directive de filtre prend en charge l'interpolation de variables dans ses arguments (le cas échéant).

echo_before_body

syntax: echo_before_body [options] [argument]...

default: non

context: location, location if

phase: output filter

C'est la version filtre de la directive echo, et préfixe sa sortie au début des sorties originales générées par le gestionnaire de contenu sous-jacent.

Un exemple est

 location /echo {
     echo_before_body hello;
     proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
 }
 location /echo/more {
     echo world
 }

Accéder à /echo depuis le côté client donne

   hello
   world

Dans l'exemple précédent, nous empruntons le module proxy standard pour servir de gestionnaire de contenu sous-jacent qui génère les "contenus principaux".

Plusieurs instances de cette directive de filtre sont également autorisées, comme dans :

 location /echo {
     echo_before_body hello;
     echo_before_body world;
     echo !;
 }

Du côté client, la sortie est comme

   $ curl 'http://localhost/echo'
   hello
   world
   !

Dans cet exemple, nous utilisons également les directives du gestionnaire de contenu fournies par ce module comme gestionnaire de contenu sous-jacent.

Cette directive prend également en charge les options -n et -- comme la directive echo.

Cette directive peut être mélangée avec sa directive sœur echo_after_body.

echo_after_body

syntax: echo_after_body [argument]...

default: non

context: location, location if

phase: output filter

C'est très similaire à la directive echo_before_body, mais ajoute sa sortie à la fin des sorties originales générées par le gestionnaire de contenu sous-jacent.

Voici un exemple simple :

 location /echo {
     echo_after_body hello;
     proxy_pass http://127.0.0.1:$server_port$request_uri/more;
 }
 location /echo/more {
     echo world
 }

Accéder à /echo depuis le côté client donne

  world
  hello

Plusieurs instances sont autorisées, comme dans :

 location /echo {
     echo_after_body hello;
     echo_after_body world;
     echo i;
     echo say;
 }

La sortie du côté client lors de l'accès à la localisation /echo ressemble à

  i
  say
  hello
  world

Cette directive prend également en charge les options -n et -- comme la directive echo.

Cette directive peut être mélangée avec sa directive sœur echo_before_body.

Variables

$echo_it

C'est une "variable de sujet" utilisée par echo_foreach_split, tout comme la variable $_ en Perl.

$echo_timer_elapsed

Cette variable contient les secondes écoulées depuis le début de la requête actuelle (peut être une sous-requête cependant) ou la dernière invocation de la commande echo_reset_timer.

Le résultat du chronométrage prend trois chiffres après la virgule.

Les références de cette variable forceront le minuteur sous-jacent de Nginx à se mettre à jour à l'heure système actuelle, indépendamment des paramètres de résolution du minuteur ailleurs dans le fichier de configuration, tout comme la directive echo_reset_timer.

$echo_request_body

Évalue à l'actuel corps de la requête (sous) précédemment lu si aucune partie du corps n'a été sauvegardée dans un fichier temporaire. Pour toujours afficher le corps de la requête même s'il est très volumineux, utilisez la directive echo_request_body.

$echo_request_method

Évalue à la méthode de requête HTTP de la requête actuelle (cela peut être une sous-requête).

En coulisses, elle prend simplement les données de chaîne stockées dans r->method_name.

Comparez-la à la variable $echo_client_request_method.

Au moins pour Nginx 0.8.20 et plus anciens, la variable $request_method fournie par le module de base http fait en fait ce que notre $echo_client_request_method fait.

Cette variable a été introduite pour la première fois dans notre version v0.15.

$echo_client_request_method

Évalue toujours à la méthode HTTP de la requête principale même si la requête actuelle est une sous-requête.

En coulisses, elle prend simplement les données de chaîne stockées dans r->main->method_name.

Comparez-la à la variable $echo_request_method.

Cette variable a été introduite pour la première fois dans notre version v0.15.

$echo_client_request_headers

Évalue aux en-têtes de la requête d'origine du client.

Comme le nom l'indique, elle prendra toujours la requête principale (ou la requête client) même si elle est actuellement exécutée dans une sous-requête.

Un exemple simple est ci-dessous :

   location /echoback {
      echo "headers are:"
      echo $echo_client_request_headers;
   }

Accéder à /echoback donne

   $ curl 'http://localhost/echoback'
   headers are
   GET /echoback HTTP/1.1
   User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g
   Host: localhost:1984
   Accept: */*

En coulisses, elle récupère r->main->header_in (ou les grands tampons d'en-tête, le cas échéant) au niveau C et ne construit pas les en-têtes elle-même en parcourant les résultats analysés dans l'objet de requête.

Cette variable est toujours évaluée à une valeur vide dans les requêtes HTTP/2 pour l'instant en raison de l'implémentation actuelle.

Cette variable a été introduite pour la première fois dans la version 0.15.

$echo_cacheable_request_uri

Évalue à la forme analysée de l'URI (généralement précédée de /) de la requête (sous-)actuelle. Contrairement à la variable $echo_request_uri, elle est cacheable.

Voir $echo_request_uri pour plus de détails.

Cette variable a été introduite pour la première fois dans la version 0.17.

$echo_request_uri

Évalue à la forme analysée de l'URI (généralement précédée de /) de la requête (sous-)actuelle. Contrairement à la variable $echo_cacheable_request_uri, elle n'est pas cacheable.

C'est assez différent de la variable $request_uri exportée par le ngx_http_core_module, car $request_uri est la forme non analysée de l'URI de la requête actuelle.

Cette variable a été introduite dans version 0.17.

$echo_incr

C'est un compteur qui génère toujours le numéro de comptage actuel, commençant à 1. Le compteur est toujours associé à la requête principale même s'il est accédé dans une sous-requête.

Considérez l'exemple suivant

 location /main {
     echo "main pre: $echo_incr";
     echo_location_async /sub;
     echo_location_async /sub;
     echo "main post: $echo_incr";
 }
 location /sub {
     echo "sub: $echo_incr";
 }

Accéder à /main donne

main pre: 1
sub: 3
sub: 4
main post: 2

Cette directive a été introduite pour la première fois dans la version v0.18.

$echo_response_status

Évalue au code de statut de la requête (sous-)actuelle, nul s'il n'y en a pas.

En coulisses, c'est simplement la représentation textuelle de r->headers_out->status.

Cette directive a été introduite pour la première fois dans la version v0.23.

Modules qui utilisent ce module pour les tests

Les modules suivants tirent parti de ce module echo dans leur suite de tests :

  • Le module memc qui prend en charge presque l'ensemble du protocole TCP de memcached.
  • Le module chunkin qui ajoute le support d'entrée chunked HTTP 1.1 à Nginx.
  • Le module headers_more qui vous permet d'ajouter, de définir et de supprimer des en-têtes d'entrée et de sortie sous les conditions que vous spécifiez.
  • Le module echo lui-même.

Veuillez m'envoyer d'autres modules qui utilisent echo sous n'importe quelle forme et je les ajouterai à la liste ci-dessus :)

Changements

Les changements de chaque version de ce module peuvent être obtenus à partir des journaux de modifications du bundle OpenResty :

http://openresty.org/#Changes

Suite de Tests

Ce module est livré avec une suite de tests pilotée par Perl. Les cas de test sont également déclaratifs. Merci au module Test::Nginx dans le monde Perl.

Pour l'exécuter de votre côté :

 $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t

Vous devez terminer tous les processus Nginx avant d'exécuter la suite de tests si vous avez changé le binaire du serveur Nginx.

Parce qu'un seul serveur nginx (par défaut, localhost:1984) est utilisé à travers tous les scripts de test (.t fichiers), il est inutile d'exécuter la suite de tests en parallèle en spécifiant -jN lors de l'invocation de l'utilitaire prove.

Certaines parties de la suite de tests nécessitent également que les modules standard proxy, rewrite et SSI soient activés lors de la construction de Nginx.

Voir Aussi

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