Re: Проблема с двойным кодированием заголовка при проксировании
Maxim Dounin
mdounin на mdounin.ru
Ср Авг 31 17:58:47 UTC 2016
Hello!
On Wed, Aug 31, 2016 at 06:54:08PM +0300, Alexander Gerasiov wrote:
> Использую nginx в качестве forward proxy к apache с mod_svn.
>
> Известный трюк: для работы методов COPY MV требуется переписывать
> заголок Destination, чтобы поменять схему с https на http
>
> set $fixed_destination $http_destination;
> if ($http_destination ~* ^https(.*)$)
> {
> set $fixed_destination http$1;
> }
> location / {
> proxy_pass http://svn:80/;
> proxy_set_header Destination $fixed_destination;
> proxy_set_header Host $host;
>
> }
>
>
> Это работает, но есть нюанс: если перемещаемый файл содержит
> кириллицу в имени, то он оказывается дважды перекодирован:
>
> Приходит в nginx он в виде:
>
> Destination: https://host/test/!svn/txr/1-1/dir2/%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9
>
> Дальше выделяется при помощи регулярного выражения, конструируется
> новый заголовок, оперируя всё еще закодированной строкой.
>
> И дальше nginx _еще_раз_ кодирует этот заголовок при отправке, так
> что в apache уже приходит
>
> Destination: http://host/test/!svn/txr/1-1/dir2/%25D1%2580%25D1%2583%25D1%2581%25D1%2581%25D0%25BA%25D0%25B8%25D0%25B9
>
>
> В результате имя файла при копировании/перемещении бьется.
>
> Вопрос: что с этим делать? Как сказать nginx, чтобы он не кодировал заголовок
> при помощи urlencode? Или может как декодировать строку перед передачей
> ее в proxy_set_header?
Проблема в том, что "set" при использовании переменных $1 .. $9
делает ровно то же, что и rewrite, а именно - экранирует
специальные символы, предполагая, что исходной строкой был URI
запроса без экранировки. При этом nginx знает, были ли в URI
запроса специальные символы, и если их не было - то экранирование
не делается.
Это, очевидно, баг, и когда нибудь его исправят, но пока этого не
случилось (в первую очередь потому, что там очень легко что-нибудь
сломать неосторожным движением).
Простейшее решение на текущий момент - вместо $1 использовать
именованные выделения, т.е. так:
if ($http_destination ~* ^https(?<foo>.*)$)
{
set $fixed_destination http$foo;
}
Тогда экранирование делаться не будет в любом случае, и всё будет
работать как ожидается.
--
Maxim Dounin
http://nginx.org/
Подробная информация о списке рассылки nginx-ru