Re: proxy cache key и fastcgi cache key
Gena Makhomed
gmm at csdoc.com
Sat Jan 11 10:34:35 UTC 2014
On 11.01.2014 1:51, Валентин Бартенев wrote:
>>>> с RFC то как раз все в порядке: "network location of the URI
>>>> (authority) MUST be transmitted in a Host header field",
>>>> только вот nginx не соответствует этим требованиям...
>>>>
>>>> http://tools.ietf.org/search/rfc2616#section-5.1.2
>> >
>>> Если почитать внимательнее, то приведенные требования относятся к клиенту.
>>> Понятно, что сервер в принципе не может влиять на то, что transmitted в
>>> запросе.
>>
>> nginx выступает в роли сервера только в том случае,
>> когда он самостоятельно обслуживает клиентский запос.
>>
>> В тот момент, когда nginx делает http запрос к удаленному
>> серверу он выступает в роли клиента. поэтому я и цитировал 5.1.2
>
> С точки зрения удаленного сервера да. И при этом поступает совершенно
> корректно, а именно передает "network location of the URI (authority)"
> в заголовки Host.
В том-то и дело, что в настройке по-умолчанию он этого не делает:
server { server_name example.com; proxy_pass http://127.0.0.1/; }
Хотя nginx и определил, что имя хоста example.com,
на backend в заголовке Host: запроса он отправляет
совсем другое значение, в данном случае: 127.0.0.1
Очень наивно отправлять в заголовке Host: 127.0.0.1
клиентского запроса к своему upstream`у и ожидиать,
что в ответ придет контент для хоста example.com
То же самое касается и работы по протоколу FastCGI.
nginx верно определил, что имя виртуального хоста good-site.com
но на backend отправил в запросе совсем другу инфу bad-site.com
>> например, если исходный запрос от клиента к nginx был
>>
>> GET http://good-site.com/pub/WWW/TheProject.html HTTP/1.1
>> Host: bad-site.com
>>
>> содержимое заголовка Host: согласно 5.2.1 должно игнорироваться,
>> адрес хоста в этом случае: good-site.com
>>
>> а согласно требований 5.1.2 - network location of the URI (authority)
>> MUST be transmitted in a Host header field, то есть исходящий запрос
>> должен быть
>>
>> GET /pub/WWW/TheProject.html HTTP/1.1
>> Host: good-site.com
>>
>> nginx же в настройке по-умолчанию не соответсвует RFC,
>> и вместо требуемого значения пишет в заголовок Host:
>> значение переменной $proxy_host
>
> Поздравляю. Это самое необычное толкование RFC 2616, которое я когда либо
> встречал.
>
> К счастью оно разбивается об определение терминов client и server из него же:
>
> client
> A program that establishes connections for the purpose of sending
> requests.
>
> server
> An application program that accepts connections in order to
> service requests by sending back responses. Any given program may
> be capable of being both a client and a server; our use of these
> terms refers only to the role being performed by the program for a
> particular connection, rather than to the program's capabilities
> in general. Likewise, any server may act as an origin server,
> proxy, gateway, or tunnel, switching behavior based on the nature
> of each request.
>
> ключевой момент тут: "refers only to the role being performed by the program
> for a particular connection".
> Соединение между клиент->nginx и соединение nginx->upstream - это два
> разных соединения, и в каждом из них nginx играет исключительно одну
> единственную роль, применимую только к конкретному соединению.
Все верно. В particular connection между браузером и nginx
- nginx выступает в роли сервера, и поэтому там требования из 5.2
а в particular connection между nginx и backend`ом - nginx выступает
в роли клиента и поэтому здесь он обязан выполнять требования 5.1.2
> В соединении "клиент->nginx" не играет роль клиента и клиентские требования
> RFC 2616 в данном соединении к нему не применимы.
Клиентские требования из п.5.1.2 RFC 2616
применимы к nginx в своединении nginx->upstream
Внутри какого-то server { ... } - nginx определил,
имя виртуального хоста (authority) соответственно
именно это имя виртуального хоста он MUST передать
в заголовке Host: при запросе к upstream серверу.
Только таким способом, с помощью заголовка Host:
upstream сможет отличить один виртуальный сервер
от другого и дать своему клиенту верный ответ.
Это же очевидно, если кто-то хочет получить в ответ
страницу с сайта гугла, то в заголовке Host: своего
запроса он обязан отправить именно "google.com".
А если отправить там "microsoft.com" - то ничего
работать не будет и так делать нельзя, см. 5.1.2
Тут у нас точно такая же ситуация,
только в роли клиента выступает nginx.
>> Теперь рассмотрим вариант связи с backend`ом по протоколу FastCGI,
>> но поскольку у нас большой и сложный сайт сделаем два фронтенда:
>>
>> 1) основной nginx frontend
>> 2) nginx frontend на хосте backend`а
>> 3) backend, работающий по протоколу FastCGI.
>>
>> запрос от клиента проходит цепочку (1)->(2)->(3).
>> поскольку между (1) и (2) используется протокол http,
>> то согласно требований 5.1.2 и 5.2.1 на (2) запрос приходит
>> в виде
>>
>> GET /pub/WWW/TheProject.html HTTP/1.1
>> Host: good-site.com
>>
>> не смотря на то, что в исходном запросе
>> от клиента был игнорируемый заголовок Host: bad-site.com
>>
>> а дальше - все просто. В соответствии с требованиями
>> спецификации протокола FastCGI - nginx записывает в переменную
>> HTTP_HOST значение good-site.com.
>>
>> точнее, он ДОЛЖЕН так делать, согласно требований HTTP протокола
>> прямо из коробки, без какой-либо дополнительной настройки.
> В спецификации нет требования, согласно котором сервер из коробки
> должен быть клиентом и отправлять запросы куда-то дальше.
Такого требования действительно нет. И запросы клиентов к статике
никуда дальше nginx не уходят, он их обрабатывает самостоятельно.
Кстати, именно по этой причине nginx и называется веб-сервером.
Но в том случае, когда nginx, как http клиент делает запросы к своему
upstream серверу по протоколу HTTP/1.1 - он обязан выполнять требования
RFC 2616 в частности http://tools.ietf.org/search/rfc2616#section-5.1.2
> Так что дополнительная настройка все равно потребуется, и я так
> понимаю, исходя из описания, она была какой-то такой:
>
> location {
> proxy_pass http://good-site.com;
> }
Что именно и как именно писать в конфигах nginx - это его личное дело,
требования к оформлению конфигов изложены только в документации к nginx.
Мы сейчас о другом аспекте говорим. О том, как работает протокол HTTP.
Если nginx хочет получит от backend`а ответ для виртуального хоста
good-site.com - тогда он обязан в заголовке Host отправить именно
good-site.com и он не имеет права отправлять там 127.0.0.1 и т.п.
Это то, что касается работы name-based virtual host`ов.
Если же upstream у nginx не name-based virtual host,
а IP-based virtual host - тогда можно и не отправлять
заголовок Host: или писать там все что угодно, backend
всеравно проигнорирует этот заголовок и даст ответ для good-site.com
даже если в заголовке Host: запроса было 127.0.0.1
>> если в случае отсутствия промежуточных серверов nginx ведет себя
>> не так, - то это BUG, ибо в случае, когда на nginx frontend
>> приходит запрос в виде absoluteURI, - тогда "Any Host header
>> field value in the request MUST be ignored".
>> nginx этого по каким-то причинам не делает.
>
> Почему не делает? Процитированная фраза относится к rules из
> фразы которые находятся по общим заголовком из:
>
> An origin server that does differentiate resources based on the host
> requested (sometimes referred to as virtual hosts or vanity host
> names) MUST use the following rules for determining the requested
> resource on an HTTP/1.1 request:
>
> именно таким правилом и пользуется nginx _при выборе_ виртуального хоста.
Если nginx _выбрал_ виртуальный хост good-site.com
зачем после выбора он резко меняет свое мнение и начинает
делать запросы к backend`у на виртуальный хост bad-site.com ?
Каким образом это согласуется со здравым смыслом?
Если пользователь nginx написал в конфиге nginx:
server {
server_name good-site.com;
// ...
}
И nginx выбрал этот name-based виртуальный хост, с какой радости
тогда на backend уходит запрос для виртуального хоста bad-site.com ?
В конфиге рядом есть отдельный
server {
server_name bad-site.com;
// ...
}
И пользователь nginx хочет, чтобы все запросы к bad-site.com
nginx обрабатывал именно в этом блоке server.
Сейчас же - вполне возможна такая ситуация, что запрос клиента
nginx со своей стороны будет обрабатывать как виртуальный хост
good-site.com, но на backend он отправит запрос к bad-site.com
и наоборот. Это - BUG. Workaround: proxy_set_header Host $host;
Действительно, в RFC 2616 про это ничего не написано,
но разве здравый смысл не подсказывает, что все запросы
к name-based виртуальному хосту good-site.com backend`а
должны происходить исключительно из блока server
который соответствует server_name good-site.com ?
И если nginx ведет себя не так,
то разве не очевидно, что это BUG ?
Например, запрос клиента попал в блок
server {
server_name good-site.com;
// ...
}
при этом в переменную $host записано значение good-site.com.
Если вдруг внутри этого server`а nginx начнет отдавать
статику от совсем другого виртуального хоста, например,
от bad-site.com - это ведь будет BUG, верно?
Аналогично это BUG и в случае с динамикой.
>> хотя согласно требований RFC - обе эти формы записи:
>>
>> GET http://good-site.com/pub/WWW/TheProject.html HTTP/1.1
>>
>> и
>>
>> GET /pub/WWW/TheProject.html HTTP/1.1
>> Host: good-site.com
>>
>> полностью эквивалентны между собой. и nginx имеет
>> полное право и даже обязанность трансформировать
>>
>> запрос с absoluteURI в запрос с relativeURI
>> и network location of the URI (authority)
>> MUST be transmitted in a Host header field.
> Как сервер он такой обязанности не имеет, а в данном конкретном соединении,
> как я уже пояснил вначале, он выступает в роли сервера и никакой другой.
nginx выступает и в роли сервера и в роли клиента,
в зависимости от particular connection. Такую обязанность
он имеет как http клиент при совершении запросов к upstream.
> Сервер вообще не обязан делать запросы и что-то куда-то transmitted.
http клиент обязан выполнять требования RFC 2616 -
http://tools.ietf.org/search/rfc2616#section-5.1.2
>> Та часть nginx, которая работает в режиме сервера определяет Host
>> правильно. И правильно сохранет его в свою переменную $host.
>>
>> Но дальше в нарушение RFC вместо $host зачем-то используется $http_host
>> не смотря на прямой запрет: Any Host header field value in the request
>> MUST be ignored.
>>
>> А несоответствие требованиям RFC 2616 - это ведь BUG, верно?
>
> Поскольку мысль повторена по меньшей мере 3 раза, то и в третий раз,
> на всякий случай: на ту часть nginx, которая работает в режиме сервера,
> не распространяется клиентских требований RFC 2616, что в самом же RFC 2616
> прописано, а именно роли клиента и сервера взаимоисключающие, и закреплены
> для каждого индивидуального соединения.
Роли клиента и сервера взаимоисключающие
только for a particular connection.
При запросе клиент->nginx - он сервер,
а при запросе nginx->upsteam - он клиент.
Если nginx знает что это name-based virtual host с именем good-site.com
он MUST NOT обманывать upstream server и говорить, что это bad-site.com
--
Best regards,
Gena
Подробная информация о списке рассылки nginx-ru