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.65 выпущенный 7 июня 2026 года.
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';
}
# эхо обратно клиентский запрос
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 для потоковой передачи ввода и вывода, параллельных/последовательных подзапросов, таймеров и задержек, а также доступа к различным метаданным.
В основном он предоставляет различные утилиты, которые помогают тестированию и отладке других модулей, тривиально эмулируя различные виды фальшивых подзапросов.
Люди также найдут его полезным в реальных приложениях, которые нуждаются в
- обслуживании статического контента непосредственно из памяти (загрузка из конфигурационного файла Nginx).
- обертывании ответа от upstream с пользовательскими заголовками и подвалами (что-то вроде addition module, но с содержимым, читаемым непосредственно из конфигурационного файла и переменных Nginx).
- объединении содержимого различных "локаций Nginx" (т.е. подзапросов) вместе в одном основном запросе (с использованием echo_location и его друзей).
Это специальный модуль с двойной ролью, который может лениво служить как обработчик контента или регистрировать себя как фильтр вывода только по запросу. По умолчанию этот модуль вообще ничего не делает.
Технически этот модуль также продемонстрировал следующие техники, которые могут быть полезны для авторов модулей:
- Выпуск параллельных подзапросов непосредственно из обработчика контента.
- Выпуск цепочечных подзапросов непосредственно из обработчика контента, передавая продолжение вдоль цепочки подзапросов.
- Выпуск подзапросов со всеми методами HTTP 1.1 и даже с необязательным фальшивым телом HTTP-запроса.
- Взаимодействие с моделью событий Nginx непосредственно из обработчика контента с использованием пользовательских событий и таймеров, и возобновление обработчика контента, если это необходимо.
- Модуль с двойной ролью, который может (лениво) служить как обработчик контента или фильтр вывода, или оба.
- Создание и интерполяция переменных конфигурационного файла Nginx.
- Управление потоковым выводом с использованием output_chain, flush и его друзей.
- Чтение тела клиентского запроса из обработчика контента и возврат (асинхронно) к обработчику контента после завершения.
- Использование декларативного тестового набора на основе 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 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 для объединения указанных в query string .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:
Тестовый набор
Этот модуль поставляется с тестовым набором на основе 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.
См. также
- Оригинальный блог-пост о первоначальной разработке этого модуля.
- Стандартный модуль фильтра добавления.
- Стандартный прокси-модуль.
- Пакет OpenResty.
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-echo.