events { worker_connections 1024; } http { upstream ubackend { server localhost:8020; } server { listen 8010; server_name router; location /test_dash_variable.html { proxy_pass http://$arg_some-var; echo_after_body "Hi! Value is '$arg_some-var'"; } } server { listen 8020; server_name backend; location /test_dash_variable.html { echo "In backend!"; } } }Видите, даже подсветке кода не нравится имя $arg_some-var. Что же произойдет, если мы направим запрос с аргументом some-var, равным ubackend? Мы ожидаем попасть в бэкенд, а вот что произойдет на самом деле:
$ curl 'http://localhost:8010/test_dash_variable.html?some-var=ubackend' <html> <head><title>502 Bad Gateway</title></head> <body bgcolor="white"> <center><h1>502 Bad Gateway</h1></center> <hr><center>nginx/1.0.8</center> </body> </html> Hi! Value is '-var'Получили 502 и какое-то неожиданное значение переменной $arg_some-var, равное -var. Загадка имеет простое объяснение: выражение справа от директивы proxy_pass обрабатывается внутренним парсером выражений nginx, для которого символ '-' является своего рода лексемой. Этот внутренний парсер разбирает выражение $arg_some-var как значение-переменной-$arg_some -- символ '-' -- строка 'var'. Переменная $arg_some не определена, соответственно значением данного выражения будет -var. Эта проблема поднималась раньше и был предложен простой патч (см. здесь), в котором символу '-' было разрешено быть частью имен переменных. Патч не был принят из опасения сломать обратную совместимость (я понял так).
Как можно обойти эту проблему? Правильный путь - воспользоваться директивой, которая не передает аргумент с переменной парсеру выражений, а берет имя переменной как есть. Директива if из модуля rewrite - отличный кандидат. Она рассматривает первый аргумент после открывающей круглой скобки как имя переменной. Мы проверим, как это работает, добавив директиву if в location /test_dash_variable.html сервера router:
location /test_dash_variable.html { proxy_pass http://$arg_some-var; echo_after_body "Hi! Value is '$arg_some-var'"; if ($arg_some-var = 'a') { echo "Bye! Value is '$arg_some-var'"; break; } }Теперь, если мы передадим значение a в аргументе some-var, мы должны будем попасть в обработчик внутри if, если if действительно работает так, как мы ожидаем:
$ curl 'http://localhost:8010/test_dash_variable.html?some-var=a' Bye! Value is '-var'Работает! Хотя echo по-прежнему настаивает, что значение $arg_some-var равно -var. Ничего удивительного, echo использует парсер выражений для получения значения своего аргумента. Ладно, if c одной стороны хорошо, но с другой стороны if - это зло! К тому же if не может отобразить бесконечное множество разных вариантов обработки переменных в конфигурации nginx. Нужно искать другие решения. Первое что приходит на ум - воспользоваться директивой set из того же модуля rewrite. После простого эксперимента выясняется, что set тоже пользуется парсером выражений (да и следовало ли ожидать другого?) и нам не подходит:
location /test_dash_variable.html { set $some_var $arg_some-var; echo "Hi! Variable 'some_var' is '$some_var'"; }Проверяем:
$ curl 'http://localhost:8010/test_dash_variable.html?some-var=a' Hi! Variable 'some_var' is '-var'Все то же самое. Можно еще воспользоваться директивой map, казалось бы это то, что нам нужно: берет одну переменную и мапит ее на другую. Имя второй переменной мы выбираем сами, а содержание маппинга мы, с помощью регулярных выражений, настроим таким образом, чтобы мапились любые значения первой переменной в значения второй переменной один к одному. Вот соответствующая конфигурация:
events { worker_connections 1024; } http { map $arg_some-var $map_arg_some_var { ~(?<match>.*) $match; } server { listen 8010; server_name router; location /test_dash_variable.html { set $some_var $arg_some-var; echo "Hi! Variable 'map_arg_some_var' is '$map_arg_some_var'"; } } }Проверяем:
$ curl 'http://localhost:8010/test_dash_variable.html?some-var=a' Hi! Variable 'map_arg_some_var' is '-var'Увы, ничего не получается. А все потому, что директива map мапит не переменную-в-переменную, а выражение-в-переменную. В роли выражения выступает наша переменная $arg_some-var, а что из этого следует, мы уже прекрасно знаем.
Итак, ни один использованный подход не смог решить нашу проблему. Поэтому для ее решения мы напишем наш собственный модуль, заодно потренируемся. В следующей части ...
Комментариев нет:
Отправить комментарий