ПРОЕКТЫ 


  АРХИВ 


Apache-Talk @lexa.ru 

Inet-Admins @info.east.ru 

Filmscanners @halftone.co.uk 

Security-alerts @yandex-team.ru 

nginx-ru @sysoev.ru 


  СТАТЬИ 


  ПЕРСОНАЛЬНОЕ 


  ПРОГРАММЫ 



ПИШИТЕ
ПИСЬМА












     АРХИВ :: nginx-ru
Nginx-ru mailing list archive (nginx-ru@sysoev.ru)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: proxy cache key и fastcgi cache key



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 mailing list
nginx-ru@xxxxxxxxx
http://mailman.nginx.org/mailman/listinfo/nginx-ru


 




Copyright © Lexa Software, 1996-2009.