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

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

Установка Debian/Ubuntu

Эти документы применимы к пакету APT nginx-module-echo, предоставляемому репозиторием GetPageSpeed Extras.

  1. Настройте репозиторий APT, как описано в настройке APT репозитория.
  2. Установите модуль:
sudo apt-get update
sudo apt-get install nginx-module-echo
Показать дистрибутивы и архитектуры
| Дистрибутив | Версия            | Компонент   | Архитектуры   |
|-------------|-------------------|-------------|-----------------|
| debian      | bookworm          | main        | amd64, arm64    |
| debian      | bookworm-mainline | main        | amd64, arm64    |
| debian      | trixie            | main        | amd64, arm64    |
| debian      | trixie-mainline   | main        | amd64, arm64    |
| ubuntu      | focal             | main        | amd64, arm64    |
| ubuntu      | focal-mainline    | main        | amd64, arm64    |
| ubuntu      | jammy             | main        | amd64, arm64    |
| ubuntu      | jammy-mainline    | main        | amd64, arm64    |
| ubuntu      | noble             | main        | amd64, arm64    |
| ubuntu      | noble-mainline    | main        | amd64, arm64    |

ngx_echo - добавляет "echo", "sleep", "time", "exec" и другие инструменты в стиле shell в конфигурационный файл Nginx.

Статус

Этот модуль готов к работе на продакшене.

Синопсис

   location /hello {
     echo "hello, world!";
   }
   location /hello {
     echo -n "hello, ";
     echo "world!";
   }
   location /timed_hello {
     echo_reset_timer;
     echo hello world;
     echo "'hello world' занимает около $echo_timer_elapsed сек.";
     echo hiya igor;
     echo "'hiya igor' занимает около $echo_timer_elapsed сек.";
   }
   location /echo_with_sleep {
     echo hello;
     echo_flush;  # убедитесь, что клиент сразу видит предыдущий вывод
     echo_sleep   2.5;  # в секундах
     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
   #   заняло 0.000 сек всего.
   # и весь запрос займет около 2 сек для завершения.
   location /main {
       echo_reset_timer;

       # параллельные подсrequests
       echo_location_async /sub1;
       echo_location_async /sub2;

       echo "заняло $echo_timer_elapsed сек всего.";
   }
   location /sub1 {
       echo_sleep 2;
       echo hello;
   }
   location /sub2 {
       echo_sleep 1;
       echo world;
   }
   # вывод /main может быть
   #   hello
   #   world
   #   заняло 3.003 сек всего.
   # и весь запрос займет около 3 сек для завершения.
   location /main {
       echo_reset_timer;

       # последовательные подсrequests (связываются через CPS)
       echo_location /sub1;
       echo_location /sub2;

       echo "заняло $echo_timer_elapsed сек всего.";
   }
   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 приветствий.
   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 Файл $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 для потоковой передачи ввода и вывода, параллельные/последовательные подсrequests, таймеры и задержки, а также доступ к различным метаданным.

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

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

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

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

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

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

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

Использование следующих директив регистрирует этот модуль в текущей локации 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, например:

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

echo

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

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

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

фаза: содержимое

Отправляет аргументы, объединенные пробелами, вместе с конечной новой строкой, клиенту.

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

    echo hello world;
    echo_flush;

Если аргумент не указан, echo выводит только конечную новую строку, как команда echo в shell.

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

    echo Текущий URI запроса - $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 является опцией;
 }

что yield

 $ curl 'http://localhost/echo'
 -n является опцией

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

echo_duplicate

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

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

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

фаза: содержимое

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

Например,

   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

фаза: содержимое

Принуждает данные, потенциально буферизованные фильтрами вывода 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.

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

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

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

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

   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

фаза: содержимое

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

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

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

Пример:

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

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

echo_blocking_sleep

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

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

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

фаза: содержимое

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

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

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

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

echo_reset_timer

синтаксис: echo_reset_timer

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

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

фаза: содержимое

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

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

   location /timed_sleep {
       echo_sleep 0.03;
       echo "$echo_timer_elapsed сек. прошло.";

       echo_reset_timer;

       echo_sleep 0.02;
       echo "$echo_timer_elapsed сек. прошло.";
   }

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

 $ curl 'http://localhost/timed_sleep'
 0.032 сек. прошло.
 0.020 сек. прошло.

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

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

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

echo_read_request_body

синтаксис: echo_read_request_body

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

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

фаза: содержимое

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

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

Эта директива сама не генерирует никакого вывода, так же как и 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

фаза: содержимое

Выдает GET подсrequest к указанной локации (первый аргумент) с необязательными аргументами 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 "заняло $echo_timer_elapsed сек всего.";
 }
 location /sub1 {
     echo_sleep 2; # спит 2 сек
     echo hello;
 }
 location /sub2 {
     echo_sleep 1; # спит 1 сек
     echo world;
 }

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

   $ time curl 'http://localhost/main'
   hello
   world
   заняло 0.000 сек всего.

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

Вы можете видеть, что основной обработчик /main не ждет завершения подсrequests /sub1 и /sub2 и быстро продолжает, отсюда результат времени "0.000 сек". Однако весь запрос занимает примерно 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 выдает один или несколько подсrequests непосредственно. Насколько мне известно, модуль 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

фаза: содержимое

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

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

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

 location /main {
     echo_reset_timer;
     echo_location /sub1;
     echo_location /sub2;
     echo "заняло $echo_timer_elapsed сек всего.";
 }
 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
   заняло 3.003 сек всего.

   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 подс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

фаза: содержимое

Инициирует асинхронный подсrequest, используя метод 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
   ///

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

 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 (или querystring) для подсrequest.

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

-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

фаза: содержимое

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

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

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

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

echo_foreach_split

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

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

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

фаза: содержимое

Разделяет второй аргумент 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 Файл $echo_it */";
           echo_location_async $echo_it;
           echo;
       echo_end;
   }

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

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

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

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

echo_end

синтаксис: echo_end

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

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

фаза: содержимое

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

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

echo_request_body

синтаксис: echo_request_body

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

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

фаза: содержимое

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

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

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

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

Это "нет операций", если еще не было прочитано ни одно тело запроса.

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

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

echo_exec

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

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

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

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

фаза: содержимое

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

   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

фаза: содержимое

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

Вот пример,

 location = /bad {
     echo_status 404;
     echo "Что-то отсутствует...";
 }

тогда мы получаем ответ, как этот:

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

Что-то отсутствует...

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

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

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

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

echo_before_body

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

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

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

фаза: фильтр вывода

Это фильтровая версия директивы 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

фаза: фильтр вывода

Это очень похоже на директиву 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 "заголовки:";
      echo $echo_client_request_headers;
   }

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

   $ curl 'http://localhost/echoback'
   заголовки:
   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 "основной пред: $echo_incr";
     echo_location_async /sub;
     echo_location_async /sub;
     echo "основной пост: $echo_incr";
 }
 location /sub {
     echo "под: $echo_incr";
 }

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

основной пред: 1
под: 3
под: 4
основной пост: 2

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

$echo_response_status

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

За кулисами это просто текстовое представление 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.

См. также