Перейти к содержанию

echo: модуль Echo для nginx

Установка

Вы можете установить этот модуль в любой дистрибутив на базе RHEL, включая, но не ограничиваясь:

  • RedHat Enterprise Linux 7, 8, 9 и 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 и 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

Активируйте модуль, добавив следующее в начало файла /etc/nginx/nginx.conf:

load_module modules/ngx_http_echo_module.so;

Этот документ описывает nginx-module-echo v0.64, выпущенный 30 октября 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;
   }
   # в следующем примере доступ к /echo дает
   #   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;
   }
   # вывод /main может быть
   #   hello
   #   world
   #   took 0.000 sec for total.
   # и весь запрос займет около 2 секунд для завершения.
   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;
       echo hello;
   }
   location /sub2 {
       echo_sleep 1;
       echo world;
   }
   # вывод /main может быть
   #   hello
   #   world
   #   took 3.003 sec for total.
   # и весь запрос займет около 3 секунд для завершения.
   location /main {
       echo_reset_timer;

       # последовательные подзапросы (связанные 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;
   }
   # Доступ к /dup дает
   #   ------ END ------
   location /dup {
     echo_duplicate 3 "--";
     echo_duplicate 1 " END ";
     echo_duplicate 3 "--";
     echo;
   }
   # /bighello сгенерирует 1000,000,000 hello.
   location /bighello {
     echo_duplicate 1000_000_000 'hello';
   }
   # echo возвращает клиентский запрос
   location /echoback {
     echo_duplicate 1 $echo_client_request_headers;
     echo "\r";

     echo_read_request_body;

     echo_request_body;
   }
   # GET /multi даст
   #   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 объединит .js ресурсы
   location /merge {
       default_type 'text/javascript';
       echo_foreach_split '&' $query_string;
           echo "/* JS File $echo_it */";
           echo_location_async $echo_it;
           echo;
       echo_end;
   }
   # доступ к /if?val=abc дает вывод "hit"
   # в то время как /if?val=bcd дает "miss":
   location ^~ /if {
       set $res miss;
       if ($arg_val ~* '^a') {
           set $res hit;
           echo $res;
       }
       echo $res;
   }

Описание

Этот модуль оборачивает множество внутренних API Nginx для потоковой передачи ввода и вывода, параллельных/последовательных подзапросов, таймеров и задержек, а также различных метаданных.

По сути, он предоставляет различные утилиты, которые помогают тестированию и отладке других модулей, тривиально эмулируя различные виды подзапросов.

Люди также найдут его полезным в реальных приложениях, которые требуют

  1. обслуживать статическое содержимое непосредственно из памяти (загрузка из конфигурационного файла Nginx).
  2. оборачивать ответ от upstream в пользовательские заголовки и подвал (что-то вроде addition module, но с содержимым, читаемым непосредственно из конфигурационного файла и переменных Nginx).
  3. объединять содержимое различных "локаций Nginx" (т.е. подзапросов) вместе в одном основном запросе (используя echo_location и его друзей).

Это специальный модуль с двойной ролью, который может лениво служить в качестве обработчика содержимого или регистрировать себя в качестве фильтра вывода только по запросу. По умолчанию этот модуль вообще ничего не делает.

Технически этот модуль также продемонстрировал следующие техники, которые могут быть полезны для разработчиков модулей:

  1. Выпускать параллельные подзапросы непосредственно из обработчика содержимого.
  2. Выпускать цепочечные подзапросы непосредственно из обработчика содержимого, передавая продолжение вдоль цепочки подзапросов.
  3. Выпускать подзапросы со всеми методами HTTP 1.1 и даже с необязательным поддельным телом HTTP-запроса.
  4. Взаимодействовать с моделью событий Nginx непосредственно из обработчика содержимого, используя пользовательские события и таймеры, и возобновлять обработчик содержимого при необходимости.
  5. Модуль с двойной ролью, который может (лениво) служить в качестве обработчика содержимого или фильтра вывода, или обоих.
  6. Создание и интерполяция переменных конфигурационного файла Nginx.
  7. Управление потоковым выводом с использованием output_chain, flush и его друзей.
  8. Чтение тела клиентского запроса из обработчика содержимого и асинхронное возвращение обратно в обработчик содержимого после завершения.
  9. Использование декларативного тестового набора на основе Perl для управления разработкой модулей C для Nginx.

Директивы обработчика содержимого

Использование следующих директив регистрирует этот модуль в текущей локации Nginx в качестве обработчика содержимого. Если вы хотите использовать другой модуль, например, стандартный прокси-модуль, в качестве обработчика содержимого, используйте фильтровые директивы, предоставленные этим модулем.

Все директивы обработчика содержимого могут быть смешаны в одной локации Nginx и должны выполняться последовательно, как в языке сценариев Bash.

Каждая директива обработчика содержимого поддерживает интерполяцию переменных в своих аргументах (если таковые имеются).

MIME-тип, установленный стандартной директивой default_type, учитывается этим модулем, как в:

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

Затем на стороне клиента:

   $ 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

С момента выпуска v0.22 все директивы разрешены в блоке директивы if rewrite module, например:

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

echo

синтаксис: echo [options] <string>...

по умолчанию: нет

контекст: location, location if

фаза: content

Отправляет аргументы, разделенные пробелами, вместе с завершающим переводом строки, клиенту.

Обратите внимание, что данные могут быть буферизованы внутренним буфером Nginx. Чтобы принудительно сбросить выходные данные немедленно, используйте команду echo_flush сразу после echo, как в

    echo hello world;
    echo_flush;

Когда аргумент не указан, echo выдает только завершающий перевод строки, как команда echo в оболочке.

Переменные могут появляться в аргументах. Пример:

    echo The current request uri is $request_uri;

где $request_uri — это переменная, предоставляемая ngx_http_core_module.

Эта команда может использоваться несколько раз в одной конфигурации локации, как в

 location /echo {
     echo hello;
     echo world;
 }

Вывод на стороне клиента будет выглядеть так

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

Специальные символы, такие как переводы строк (\n) и табуляции (\t), могут быть экранированы с использованием последовательностей экранирования в стиле C. Но заметным исключением является знак доллара ($). На момент Nginx 0.8.20 все еще нет чистого способа экранировать этот символ. (Обходным решением может быть использование переменной $echo_dollor, которая всегда оценивается как константа $. Эта функция, возможно, будет введена в будущей версии этого модуля.)

С момента выпуска echo v0.28 можно подавить завершающий символ новой строки в выводе, используя опцию -n, как в

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

Доступ к /echo дает

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

Предваряющий -n в значениях переменных не подействует и будет выведен буквально, как в

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

Это дает следующий вывод

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

Можно вывести предваряющие литералы -n и другие опции, используя специальную опцию --, как это

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

что дает

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

Используйте эту форму, когда хотите вывести что-либо, начинающееся с дефиса (-).

echo_duplicate

синтаксис: echo_duplicate <count> <string>

по умолчанию: нет

контекст: location, location if

фаза: content

Выводит дублирование строки, указанной вторым аргументом, с использованием количества, указанного в первом аргументе.

Например,

   location /dup {
       echo_duplicate 3 "abc";
   }

приведет к выводу "abcabcabc".

Подчеркивания разрешены в числе счетчика, так же как и в Perl. Например, чтобы вывести 1000,000,000 экземпляров "hello, world":

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

Аргумент count может быть равен нулю, но не отрицательным. Второй аргумент string также может быть пустой строкой ("").

В отличие от директивы echo, к результату не добавляется завершающий перевод строки. Поэтому возможно "злоупотребить" этой директивой как версией echo без завершающего перевода строки, используя "count" 1, как в

   location /echo_art {
       echo_duplicate 2 '---';
       echo_duplicate 1 ' END ';  # мы не хотим завершающего перевода строки здесь
       echo_duplicate 2 '---';
       echo;  # мы хотим завершающий перевод строки здесь...
   }

Вы получите

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

Но использование опции -n в echo более уместно для этой цели.

Эта директива была впервые введена в версии 0.11.

echo_flush

синтаксис: echo_flush

по умолчанию: нет

контекст: location, location if

фаза: content

Принудительно отправляет данные, потенциально буферизованные внутренними фильтрами вывода Nginx, немедленно на клиентскую сторону через сокет.

Обратите внимание, что технически команда просто выдает объект ngx_buf_t с установленным слотом flush в 1, поэтому некоторые странные сторонние модули фильтров вывода все еще могут блокировать его, прежде чем он достигнет (последнего) фильтра записи Nginx.

Эта директива не принимает никаких аргументов.

Рассмотрим следующий пример:

   location /flush {
      echo hello;

      echo_flush;

      echo_sleep 1;
      echo world;
   }

Затем на стороне клиента, используя curl для доступа к /flush, вы увидите строку "hello" немедленно, но только через 1 секунду появится последняя строка "world". Без вызова echo_flush в приведенном выше примере вы, скорее всего, не увидите никакого вывода до истечения 1 секунды из-за внутреннего буферизования Nginx.

Эта директива не сможет сбросить буфер вывода в случае вовлечения подзапросов. Рассмотрим следующий пример:

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

Тогда клиент не увидит "hello", даже если echo_flush был выполнен до того, как подзапрос к /sub фактически начнет выполняться. Выводы /main, которые отправляются после echo_location_async, будут отложены и надежно буферизованы.

Это не относится к выводам, отправленным до инициации подзапроса. Для измененной версии приведенного выше примера:

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

Клиент немедленно увидит "hello", прежде чем /sub войдет в состояние ожидания.

См. также echo, echo_sleep и echo_location_async.

echo_sleep

синтаксис: echo_sleep <seconds>

по умолчанию: нет

контекст: location, location if

фаза: content

Задерживает выполнение на период времени, указанный аргументом, который выражается в секундах.

Эта операция не блокирует серверную сторону, поэтому, в отличие от директивы echo_blocking_sleep, она не блокирует весь рабочий процесс Nginx.

Период может содержать три цифры после десятичной точки и должен быть больше 0.001.

Пример:

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

За кулисами она настраивает объект "sleep" ngx_event_t на уровне каждого запроса и добавляет таймер, используя это пользовательское событие в модель событий Nginx, и просто ждет тайм-аута на этом событии. Поскольку событие "sleep" относится к каждому запросу, эта директива может работать в параллельных подзапросах.

echo_blocking_sleep

синтаксис: echo_blocking_sleep <seconds>

по умолчанию: нет

контекст: location, location if

фаза: content

Это блокирующая версия директивы echo_sleep.

Смотрите документацию echo_sleep для получения более подробной информации.

За кулисами она вызывает макрос ngx_msleep, предоставленный ядром Nginx, который соответствует usleep на системах, совместимых с POSIX.

Обратите внимание, что эта директива полностью блокирует текущий рабочий процесс Nginx во время выполнения, поэтому никогда не используйте ее в производственной среде.

echo_reset_timer

синтаксис: echo_reset_timer

по умолчанию: нет

контекст: location, location if

фаза: content

Сбрасывает начальное время таймера на сейчас, т.е. время, когда эта команда выполняется во время запроса.

Начальное время таймера по умолчанию соответствует времени начала текущего запроса и может быть переопределено этой директивой, потенциально несколько раз в одной локации. Например:

   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.";
   }

Вывод на стороне клиента может быть следующим:

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

Фактические цифры, которые вы получите на своей стороне, могут немного варьироваться в зависимости от текущей активности вашей системы.

Вызов этой директивы заставит внутренний таймер Nginx обновиться до текущего системного времени (независимо от разрешения таймера, указанного в других местах конфигурационного файла). Более того, ссылки на переменную $echo_timer_elapsed также будут принудительно обновлять таймер.

См. также echo_sleep и $echo_timer_elapsed.

echo_read_request_body

синтаксис: echo_read_request_body

по умолчанию: нет

контекст: location, location if

фаза: content

Явно читает тело запроса, чтобы переменная $request_body всегда имела непустые значения (если только тело не слишком велико и не было сохранено Nginx во временный файл).

Обратите внимание, что это может не быть оригинальным телом клиентского запроса, потому что текущий запрос может быть подзапросом с "искусственным" телом, указанным его родителем.

Эта директива сама по себе не генерирует никакого вывода, как и echo_sleep.

Вот пример для возврата оригинального HTTP клиентского запроса (включая заголовки и тело):

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

Содержимое /echoback выглядит так на моей стороне (я использовал утилиту Perl's LWP для доступа к этому местоположению на сервере):

   $ (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

Поскольку /echoback является основным запросом, $request_body содержит оригинальное тело клиентского запроса.

До Nginx 0.7.56 не имело смысла использовать эту директиву, потому что $request_body была впервые введена в Nginx 0.7.58.

Эта директива была впервые введена в релизе v0.14 модуля echo.

echo_location_async

синтаксис: echo_location_async <location> [<url_args>]

по умолчанию: нет

контекст: location, location if

фаза: content

Инициирует GET подзапрос к указанной локации (первый аргумент) с необязательными URL-аргументами, указанными во втором аргументе.

На момент Nginx 0.8.20 аргумент location не поддерживает именованные локации из-за ограничения в функции ngx_http_subrequest. То же самое относится и к его брату, директиве echo_location.

Очень простой пример:

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

Доступ к /main дает

   hello
   world

Также возможно вызывать несколько локаций параллельно:

 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; # спит 2 сек
     echo hello;
 }
 location /sub2 {
     echo_sleep 1; # спит 1 сек
     echo world;
 }

Доступ к /main дает

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

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

Вы можете видеть, что основной обработчик /main не ждет завершения подзапросов /sub1 и /sub2 и быстро продолжает, поэтому результат времени "0.000 sec". Однако весь запрос занимает примерно 2 секунды для завершения, потому что /sub1 и /sub2 выполняются параллельно (или "одновременно", чтобы быть более точным).

Если вы используете echo_blocking_sleep в предыдущем примере, то вы получите тот же вывод, но с общим временем отклика в 3 секунды, потому что "блокирующий сон" блокирует весь рабочий процесс Nginx.

Локации также могут принимать необязательный аргумент querystring, например

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

Доступ к /main дает

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

Querystrings не разрешается конкатенировать с аргументом location с помощью "?" напрямую, например, /sub?foo=Foo&bar=Bar является недопустимой локацией и не должна передаваться как первый аргумент этой директивы.

Технически эта директива является примером того, как обработчик содержимого Nginx инициирует один или несколько подзапросов напрямую. Насколько мне известно, модуль fancyindex также делает подобные вещи ;)

Именованные локации Nginx, такие как @foo, не поддерживаются здесь.

Эта директива логически эквивалентна GET-версии echo_subrequest_async. Например,

   echo_location_async /foo 'bar=Bar';

логически эквивалентно

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

Но вызов этой директивы немного быстрее, чем вызов echo_subrequest_async с использованием GET, потому что нам не нужно разбирать имена HTTP-методов, такие как GET, и опции, такие как -q.

Эта директива была впервые введена в версии 0.09 этого модуля и требует как минимум Nginx 0.7.46.

echo_location

синтаксис: echo_location <location> [<url_args>]

по умолчанию: нет

контекст: location, location if

фаза: content

Как и директива echo_location_async, но echo_location инициирует подзапросы последовательно, а не параллельно. То есть, директивы обработчика содержимого, следующие за этой директивой, не будут выполняться, пока подзапрос, инициированный этой директивой, не завершится.

Финальное тело ответа почти всегда эквивалентно случаю, когда вместо этого используется echo_location_async, только если в выводах используются временные переменные.

Рассмотрим следующий пример:

 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;
 }

Локация /main выше займет в общей сложности 3 секунды для завершения (по сравнению с 2 секундами, если вместо этого используется echo_location_async). Вот результат в действии на моем компьютере:

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

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

Эта директива логически эквивалентна GET-версии echo_subrequest. Например,

   echo_location /foo 'bar=Bar';

логически эквивалентно

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

Но вызов этой директивы немного быстрее, чем вызов echo_subrequest с использованием GET, потому что нам не нужно разбирать имена HTTP-методов, такие как GET, и опции, такие как -q.

За кулисами она создает объект ngx_http_post_subrequest_t в качестве продолжения и передает его в вызов функции ngx_http_subrequest. Nginx позже повторно откроет это "продолжение" в вызове функции ngx_http_finalize_request подзапроса. Мы возобновляем выполнение обработчика содержимого родительского запроса и начинаем выполнять следующую директиву (команду), если такая имеется.

Именованные локации Nginx, такие как @foo, не поддерживаются здесь.

Эта директива была впервые введена в релизе v0.12.

См. также echo_location_async для получения более подробной информации о значении аргументов.

echo_subrequest_async

синтаксис: echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]

по умолчанию: нет

контекст: location, location if

фаза: content

Инициирует асинхронный подзапрос с использованием HTTP-метода, необязательных URL-аргументов (или querystring) и необязательного тела запроса, которое может быть определено как строка или как путь к файлу, содержащему тело.

Эта директива очень похожа на обобщенную версию директивы echo_location_async.

Вот небольшой пример, демонстрирующий ее использование:

 location /multi {
     # тело определено как строка
     echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
     # тело определено как путь к файлу, относительно префикса nginx, если не абсолютный
     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 '///';
 }

Затем на стороне клиента:

   $ 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
   ///

Вот еще один интересный пример, использующий стандартный прокси-модуль для обработки подзапроса:

 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.";

     # нам нужно явно прочитать тело здесь...или $echo_request_body
     #   будет оцениваться как пустое ("")
     echo_read_request_body;

     echo "body: $echo_request_body.";
 }

Затем на стороне клиента мы можем увидеть, что

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

Именованные локации Nginx, такие как @foo, не поддерживаются здесь.

Эта директива принимает несколько опций:

-q <url_args>        Укажите URL-аргументы (или URL querystring) для подзапроса.

-f <path>            Укажите путь к файлу, содержимое которого будет использоваться в качестве тела запроса подзапроса.

-b <data>            Укажите данные тела запроса.

Эта директива была впервые введена в релизе v0.15.

Опция -f для определения пути к файлу для тела была введена в релизе v0.35.

См. также директивы echo_subrequest и echo_location_async.

echo_subrequest

синтаксис: echo_subrequest <HTTP_method> <location> [-q <url_args>] [-b <request_body>] [-f <request_body_path>]

по умолчанию: нет

контекст: location, location if

фаза: content

Это синхронная версия директивы echo_subrequest_async. И, как и echo_location, она не блокирует рабочий процесс Nginx (в то время как echo_blocking_sleep делает это), а использует продолжение для передачи управления вдоль цепочки подзапросов.

Смотрите echo_subrequest_async для получения более подробной информации.

Именованные локации Nginx, такие как @foo, не поддерживаются здесь.

Эта директива была впервые введена в релизе v0.15.

echo_foreach_split

синтаксис: echo_foreach_split <delimiter> <string>

по умолчанию: нет

контекст: location, location if

фаза: content

Разделяет второй аргумент string, используя указанный в первом аргументе разделитель, а затем итерирует по полученным элементам. Например:

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

Доступ к /main дает

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

Как видно из предыдущего примера, эта директива всегда должна сопровождаться директивой echo_end.

Параллельные циклы echo_foreach_split разрешены, но вложенные в настоящее время запрещены.

Аргумент delimiter может содержать несколько произвольных символов, например

   # это выводит "cat\ndog\nmouse\n"
   echo_foreach_split -- '-a-' 'cat-a-dog-a-mouse';
     echo $echo_it;
   echo_end;

Логически говоря, эта структура цикла просто является циклом foreach, объединенным с вызовом функции split в Perl (используя предыдущий пример):

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

Люди также найдут это полезным для объединения нескольких ресурсов .js или .css в одно целое. Вот пример:

   location /merge {
       default_type 'text/javascript';

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

Затем доступ к /merge для объединения ресурсов .js, указанных в строке запроса:

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

Также можно использовать сторонний модуль кэширования Nginx для кэширования объединенного ответа, сгенерированного локацией /merge в предыдущем примере.

Эта директива была впервые введена в релизе v0.17.

echo_end

синтаксис: echo_end

по умолчанию: нет

контекст: location, location if

фаза: content

Эта директива используется для завершения тела циклов и условных управляющих структур, таких как echo_foreach_split.

Эта директива была впервые введена в релизе v0.17.

echo_request_body

синтаксис: echo_request_body

по умолчанию: нет

контекст: location, location if

фаза: content

Выводит содержимое ранее прочитанного тела запроса.

За кулисами это реализовано примерно так:

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

В отличие от переменных $echo_request_body и $request_body, эта директива покажет все тело запроса, даже если некоторые части или все части были сохранены во временных файлах на диске.

Это "no-op", если еще не было прочитано никакого тела запроса.

Эта директива была впервые введена в релизе v0.18.

См. также echo_read_request_body и модуль chunkin.

echo_exec

синтаксис: echo_exec <location> [<query_string>]

синтаксис: echo_exec <named_location>

по умолчанию: нет

контекст: location, location if

фаза: content

Выполняет внутренний редирект к указанной локации. Необязательная строка запроса может быть указана для обычных локаций, как в

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

Или эквивалентно

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

Именованные локации также поддерживаются. Вот пример:

   location /foo {
       echo_exec @bar;
   }
   location @bar {
       # вы получите /foo, а не @bar
       #  из-за потенциальной ошибки в nginx.
       echo $echo_request_uri;
   }

Но строка запроса (если есть) всегда будет игнорироваться для редиректов именованных локаций из-за ограничения в функции ngx_http_named_location.

Никогда не пытайтесь выводить что-либо перед директивой echo_exec, иначе вы не увидите правильный ответ от локации, на которую хотите перенаправить. Потому что любое вывод будет заставлять обработчик оригинальной локации отправлять HTTP-заголовки до того, как произойдет редирект.

Технически эта директива открывает внутренние API-функции Nginx ngx_http_internal_redirect и ngx_http_named_location.

Эта директива была впервые введена в релизе v0.21.

echo_status

синтаксис: echo_status <status-num>

по умолчанию: echo_status 200

контекст: location, location if

фаза: content

Указывает код состояния ответа по умолчанию. По умолчанию 200. Эта директива декларативная, и относительный порядок с другими директивами, подобными echo, не важен.

Вот пример:

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

тогда мы получим ответ, похожий на этот:

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

Эта директива была впервые введена в v0.40.

Фильтровые директивы

Использование следующих директив вызывает регистрацию фильтра этого модуля. По умолчанию никакой фильтр не будет зарегистрирован этим модулем.

Каждая фильтровая директива поддерживает интерполяцию переменных в своих аргументах (если таковые имеются).

echo_before_body

синтаксис: echo_before_body [options] [argument]...

по умолчанию: нет

контекст: location, location if

фаза: output filter

Это фильтровая версия директивы echo, которая добавляет свой вывод в начало оригинальных выводов, сгенерированных подлежащим обработчиком содержимого.

Пример:

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

Доступ к /echo со стороны клиента дает

   hello
   world

В предыдущем примере мы используем стандартный прокси-модуль в качестве подлежащего обработчика содержимого, который генерирует "основное содержимое".

Несколько экземпляров этой фильтровой директивы также разрешены, как в:

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

На стороне клиента вывод будет выглядеть так

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

В этом примере мы также используем директивы обработчика содержимого, предоставленные этим модулем, в качестве подлежащего обработчика содержимого.

Эта директива также поддерживает опции -n и --, как и директива echo.

Эта директива может быть смешана с ее братом echo_after_body.

echo_after_body

синтаксис: echo_after_body [argument]...

по умолчанию: нет

контекст: location, location if

фаза: output filter

Это очень похоже на директиву echo_before_body, но добавляет свой вывод в конец оригинальных выводов, сгенерированных подлежащим обработчиком содержимого.

Вот простой пример:

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

Доступ к /echo со стороны клиента дает

  world
  hello

Несколько экземпляров разрешены, как в:

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

Вывод на стороне клиента при доступе к локации /echo выглядит так

  i
  say
  hello
  world

Эта директива также поддерживает опции -n и --, как и директива echo.

Эта директива может быть смешана с ее братом echo_before_body.

Переменные

$echo_it

Это "тематическая переменная", используемая в echo_foreach_split, как переменная $_ в Perl.

$echo_timer_elapsed

Эта переменная хранит количество секунд, прошедших с начала текущего запроса (возможно, это подзапрос) или последнего вызова команды echo_reset_timer.

Результат времени занимает три цифры после десятичной точки.

Ссылки на эту переменную будут принудительно обновлять внутренний таймер Nginx до текущего системного времени, независимо от настроек разрешения таймера в других местах конфигурационного файла, так же как и директива echo_reset_timer.

$echo_request_body

Оценивается как тело текущего (под)запроса, ранее прочитанное, если ни одна часть тела не была сохранена во временный файл. Чтобы всегда показывать тело запроса, даже если оно очень велико, используйте директиву echo_request_body.

$echo_request_method

Оценивается как метод HTTP запроса текущего запроса (это может быть подзапрос).

За кулисами она просто берет строковые данные, хранящиеся в r->method_name.

Сравните это с переменной $echo_client_request_method.

По крайней мере для Nginx 0.8.20 и более ранних версий переменная $request_method, предоставляемая http core module, фактически выполняет то, что делает наша переменная $echo_client_request_method.

Эта переменная была впервые введена в нашем релизе v0.15.

$echo_client_request_method

Всегда оценивается как HTTP метод основного запроса, даже если текущий запрос является подзапросом.

За кулисами она просто берет строковые данные, хранящиеся в r->main->method_name.

Сравните это с переменной $echo_request_method.

Эта переменная была впервые введена в нашем релизе v0.15.

$echo_client_request_headers

Оценивается как заголовки оригинального клиентского запроса.

Как и следует из названия, она всегда берет основной запрос (или клиентский запрос), даже если в данный момент выполняется в подзапросе.

Простой пример ниже:

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

Доступ к /echoback дает

   $ 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: */*

За кулисами она восстанавливает r->main->header_in (или большие буферы заголовков, если они есть) на уровне C и не конструирует заголовки самостоятельно, проходя по разобранным результатам в объекте запроса.

Эта переменная всегда оценивается как пустое значение в HTTP/2 запросах на данный момент из-за текущей реализации.

Эта переменная была впервые введена в версии 0.15.

$echo_cacheable_request_uri

Оценивается как разобранная форма URI (обычно начинающаяся с /) текущего (под-)запроса. В отличие от переменной $echo_request_uri, она кэшируема.

Смотрите $echo_request_uri для получения более подробной информации.

Эта переменная была впервые введена в версии 0.17.

$echo_request_uri

Оценивается как разобранная форма URI (обычно начинающаяся с /) текущего (под-)запроса. В отличие от переменной $echo_cacheable_request_uri, она не кэшируема.

Это довольно отличается от переменной $request_uri, экспортируемой ngx_http_core_module, потому что $request_uri является неразобранной формой URI текущего запроса.

Эта переменная была впервые введена в версии 0.17.

$echo_incr

Это счетчик, который всегда генерирует текущее число счета, начиная с 1. Счетчик всегда ассоциирован с основным запросом, даже если он доступен в подзапросе.

Рассмотрим следующий пример

 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";
 }

Доступ к /main дает

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

Эта директива была впервые введена в релизе v0.18.

$echo_response_status

Оценивается как код состояния текущего (под)запроса, null, если такового нет.

За кулисами это просто текстовое представление r->headers_out->status.

Эта директива была впервые введена в релизе v0.23.

Модули, которые используют этот модуль для тестирования

Следующие модули используют этот модуль echo в своих тестовых наборах:

  • Модуль memc, который поддерживает почти весь протокол TCP memcached.
  • Модуль chunkin, который добавляет поддержку HTTP 1.1 с чанкованным вводом в Nginx.
  • Модуль headers_more, который позволяет добавлять, устанавливать и очищать входные и выходные заголовки при условиях, которые вы указываете.
  • Сам модуль echo.

Пожалуйста, напишите мне о других модулях, которые используют echo в любой форме, и я добавлю их в список выше :)

Изменения

Изменения каждой версии этого модуля можно получить из журналов изменений пакета OpenResty:

http://openresty.org/#Changes

Тестовый набор

Этот модуль поставляется с тестовым набором на основе Perl. Тестовые случаи также являются декларативными. Спасибо модулю Test::Nginx в мире Perl.

Чтобы запустить его на своей стороне:

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

Вам нужно завершить все процессы Nginx перед запуском тестового набора, если вы изменили двоичный файл сервера Nginx.

Поскольку один сервер nginx (по умолчанию, localhost:1984) используется во всех тестовых скриптах (.t файлах), не имеет смысла запускать тестовый набор параллельно, указывая -jN, когда вы вызываете утилиту prove.

Некоторые части тестового набора требуют, чтобы стандартные модули proxy, rewrite и SSI были включены при сборке Nginx.

См. также

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-echo.