Протокол HTTP

Протокол HTTP - основной протокол, по которому работает Интернет. Аббревиатура HTTP расшифровывается как "HyperText Transfer Protocol", или, по-нашему, "Протокол передачи гипертекста". HTTP является общепринятым стандартом, описанным в RFC 2616.

По протоколу HTTP общаются между собой браузер пользователя и веб-сервер. Общение происходит через TCP-соединение в форме "запрос-ответ": браузер отсылает серверу запрос с указанием адреса страницы и всевозможных параметров, на что сервер отвечает, отправляя числовой код ответа, некоторый набор вспомогательных данных и, наконец, запрошенные клиентом данные.

И запрос клиента, и ответ сервера передаются в виде открытого текста. Давайте рассмотрим пример такого запроса, открыв инструмент для мониторинга HTTP и зайдя на любимый сайт.

Зайдя на веб страницу, в мониторе сетевой активности увидим запрос браузера:

GET /book/programming HTTP/1.1
User-Agent: Opera/9.80 (Windows NT 6.1; U; en) Presto/2.10.229 Version/11.60
Host: labaka.ru
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate
Referer: http://labaka.ru/user/80
If-Modified-Since: Mon, 12 Dec 2011 21:34:05 +0000
If-None-Match: "1323725645"
Cookie: SESS5e085eb26f46746f97527610fcb532c7=FUG984ylskjghow87AqoX5U; has_js=1
Connection: Keep-Alive

И соответствующий ему ответ сервера:

HTTP/1.1 200 OK
Server: nginx
Date: Mon, 12 Dec 2011 21:43:20 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Last-Modified: Mon, 12 Dec 2011 21:43:20 +0000
Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0
ETag: "1323726200"
Content-Language: ru
Set-Cookie: SESS5e085eb26f46746f97527610fcb532c7=FUG984ylskjghow87AqoX5U; expires=Thu, 05 Jan 2012 02:10:28 GMT; path=/; domain=.labaka.ru; HttpOnly
Link: </book/programming>; rel="canonical",</node/18>; rel="shortlink"
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 4753

(здесь следует 4753 байт данных, это HTML запрошенной веб-страницы, сжатый алгоритмом gzip)

И запрос, и ответ, построены по одному принципу: первой строкой запроса является команда серверу и путь, с указанием версии протокола HTTP, по которой идёт общение. В первой строке ответа сервер передаёт версию протокола HTTP ответа, а также код успеха или ошибки выполнения запроса. За первой строкой запроса или ответа следует несколько параметров, называемых "заголовком HTTP". В заголовке запроса клиент передаёт серверу информацию о браузере, который совершает запрос, сообщает о способности обработать сжатый ответ, о возможности кеширования и поддержке различных типов данных. В заголовке ответа сервер сообщает о состоянии запрошенного ресурса, о том, какое сжатие используется для передачи ответа, о размере ответа, дате последнего изменения запрошенного ресурса, необходимости кеширования и так далее. Полный список поддерживаемых полей заголовка HTTP весьма обширен: подробнее о них можно узнать в спецификации протокола HTTP.

Методы запросов HTTP

Методом в HTTP называется действие (команда), которое браузер запрашивает у сервера. Это самое первое слово в запросе клиента. В примере выше - это GET. Есть пара наиболее часто используемых методов HTTP, постоянно используемых вами, даже когда вы об этом не догадываетесь:

  • GET - получить содержимое ресурса. Запрос GET делается браузером при кликах по обычным ссылкам на сайте. Клиент не отправляет серверу никаких данных, кроме адреса запрашиваемого ресурса и заголовков. Сервер отвечает, отсылая содержимое ресурса вместе со служебными заголовками. Возможны ситуации, когда сервер не передаёт содержимое ресурса, например если ресурс не найден, недоступен, или не изменился с последнего запроса клиента.
  • POST - отправить данные на сервер. POST обычно делается браузером при нажатии кнопки Отправить/ОК/ит.д. на всевозможных формах на веб-страницах. Клиент передаёт серверу кроме заголовков ещё и тело запроса, в котором некоторым образом закодированы передаваемые данные. Передаваемые данные могут быть произвольными, всё зависит от программного обеспечения на сервере, обрабатывающего запрос: это может быть текст комментария, который пользователь оставляет на сайте, содержимое файла, отправляемого пользователем, и так далее. В PHP параметры, переданные в теле запроса POST, доступны через массив $_POST. На запрос POST сервер отвечает так же, как и на запрос GET: может вернуть ресурс или веб страницу, а может и ничего не вернуть.

Адреса ресурсов HTTP

Каждый адрес ресурса в WWW, или как ещё называют адреса - URL (Uniform Resource Locator), состоит из нескольких компонентов:

scheme://username:password@domain:port/path?query_string#fragment_id

Расшифруем их назначение и роль в обмене между сервером и клиентом:

  • scheme - протокол. Интерпретируется браузером. http -  означает обычный HTTP, https - HTTP через зашифрованное соединение. По умолчанию браузер использует http, поэтому в адресной строке браузера можно опустить http://.
  • username:password - имя пользователя и пароль, интерпретируются браузером. На основании этих значений браузер добавляет в заголовки запроса логин и пароль. Это не те логин и пароль, которые принимаются сервером через формы входа на большинство сайтов. Здесь речь идёт о так называемой Basic-аутентификации, встроенной в сам протокол HTTP.
  • domain - доменное имя сайта, к которому идёт обращение. Браузер находит IP-адрес, соответствующий данному доменному имени, обращаясь к DNS-серверу. С найденным IP-адресом устанавливается TCP-соединение, по которому и делается HTTP-запрос, причём доменное имя передаётся в этом запросе в заголовке Host. Вместо доменного имени можно указать просто IP-адрес веб-сервера.
  • port - номер порта сервера, с которым будет установлено TCP-соединение, по умолчанию 80. Браузер отсылает серверу номер порта, если он отличается от стандартного, в поле заголовка Host вместе с доменным именем сайта, например: Host: labaka.ru:88.
  • path - путь к ресурсу на сервере. Как правило, этот путь определяет, какой скрипт будет вызван сервером по данному запросу, или какой файл должен быть выдан сервером клиенту. Это значение, включая начальный слеш, передаётся серверу в первой строке запроса, между именем метода и версией протокола HTTP:
    "GET /book/programming HTTP/1.1"
  • query_string - параметры запроса. В языках для веб-программирования эти параметры легко доступны из программы (в PHP - через массив $_GET). Параметры передаются серверу вместе с путём к ресурсу, например:
    "GET /book/programming?parameter=value HTTP/1.1"
  • fragment_id - идентификатор фрагмента, не передаётся на сервер вовсе. Эта часть адреса интерпретируется браузером: при наличии в веб-странице, полученной с сервера, якоря с данным именем (конструкция HTML вида <a name="fragment_id"></a>), браузер промотает страницу до этого места. Идентификатор фрагмента также может быть интерпретирован JavaScript, выполняющимся на этой странице.

Виртуальные серверы HTTP

Благодаря тому, что клиент с каждым запросом HTTP передаёт в заголовке Host доменное имя сайта, к которому обращается, возможен следующий "фокус".

  • Администратор создаёт несколько доменных имён, например site1.com, site2.com, site3.com.
  • Для всех этих доменов администратор прописывает один и тот же IP-адрес веб-сервера.
  • Веб-сервер настраивается так, что при запросах с заголовком Host: site1.com выдаются страницы сайта site1.com, и аналогично для остальных сайтов: веб-сервер использует значение заголовка Host, чтобы решить, страницу какого сайта вернуть пользователю.
  • Пользователи заходят на сайты site1.com, site2.com, site3.com, указывая в строке браузера доменное имя сайта. При этом запросы отсылаются к одному и тому же веб-серверу, поскольку всем трём доменам назначен один и тот же IP-адрес, НО в заголовке Host находится именно то доменное имя, которое набрал пользователь в строке браузера.
  • В результате, на одном веб-сервере работают три никак не связанных между собой сайта, а пользователь даже не подозревает, что физически они находятся на одном сервере!

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

А что будет, если обратиться к такому сереру, указав в адресной строке браузера не доменное имя сайта, а IP адрес? В этом случае всё определяется конфигурацией веб-сервера, но, скорее всего, клиент уж точно не попадёт ни на один из виртуальных серверов, ведь в поле заголовка Host не указано доменное имя.

Куки и сессии HTTP

Куки (cookie) - механизм, работающий через HTTP, позволяющий серверу оставлять на компьютере клиента некоторые данные - "куки". Работает это так:

  • Клиент отправляет серверу некоторый запрос.
  • Сервер отвечает клиенту, причём в специальном поле заголовка Set-Cookie указывает данные, которые тем самым "просит" браузер клиента запомнить. (см. пример ответа сервера выше).
    Данные указываются сервером в виде "имя=значение", причем сервер может попросить клиента сохранить сразу несколько значений под разными именами. Кроме этого, сервер указывает желаемое время жизни куки, в течение которого браузеру следует сохранять куки, а также домен и путь к ресурсу, для которого предназначен куки.
  • Браузер, получив от сервера Set-Cookie, решает, так уж ли ему нужно хранить эти данные (браузер сверяется с настройками, сделанными пользователем и с политикой безопасности). Если браузер решил данные сохранить, то он запоминает их в особой базе данных для куки. У каждого браузера своя такая база. Каждый куки имеет время жизни, после которого удаляется из базы браузера. Кроме этого, для каждого куки браузер запоминает, с какого домена этот куки был получен: например если вы зашли на labaka.ru и этот сервер выслал вам куки, то именно домен labaka.ru и будет зафиксирован браузером, как источник данного куки.
  • При последующих запросах, отправляемых к данному сайту, браузер будет отправлять все сохранённые куки для этого домена в поле заголовка Cookie. (см. пример запроса).

В браузере можно посмотреть, какие куки оставил нам сервер. Например, в Firefox достаточно зайти на "правый клик - View Page Info - Security - View Cookies":

Благодаря механизму cookie появляется возможность установить "сессию", или "сеанс" между клиентом и сервером. Сессия - это связная "беседа" клиента и сервера между собой. Рассмотрим на примере:

  • Вы входите на сайт. Сервер не знает вас, поэтому пишет "Привет, гость". Вы заходите на страницу логина, где вводите свой логин и пароль.
  • Сервер, получив от вас правильный логин и пароль, теперь знает, что вас зовут Username. Теперь серверу надо как-то запомнить, что на том конце провода находится Username и все последующие запросы от этого клиента - от Username. Сервер, между тем, знает о механизме куков.
  • Сервер формирует набор данных о клиенте, вошедшем на сайт. Это может быть, например, имя клиента и его номер в базе данных сервера. Этому набору данных сервер придумывает некий уникальный идентификатор, который крайне сложно угадать, например "FUG984ylskjghow87AqoX5U". Набор данных под этим идентификатором сервер запоминает у себя в файле, в памяти или в базе данных: там, откуда эти данные можно быстро извлечь, зная идентификатор. Назовём этот набор данных "данными сессии", а идентификатор - "номером сессии".
  • Номер сессии сервер отправляет клиенту в виде куки, вместе с веб страницей, на которой отображается сообщение об успешном входе на сайт. Какое будет имя куки, решает сервер: например PHPSESSID. Назовём это именем сессии. Это имя, как правило, будет одинаковым для всех входящих на сайт клиентов. Номера сессий, разумеется, у разных клиентов будут постоянно разные, без повторений.
  • Браузер запоминает куки с номером сессии и отправляет его обратно серверу с каждым последующим запросом.
  • Сервер, получая от клиента номер сессии с каждым запросом, находит у себя данные сессии по этому номеру, благодаря чему знает, что это именно Username ходит по сайту, а не кто-то другой. Соответственно, сервер отображает данные, которые следует видеть этому пользователю.
  • Когда пользователь кликает по кнопке "выйти с сайта", сервер удаляет из памяти данные сессии, соответствующей этому пользователю, и выдаёт браузеру заголовок Set-Cookie, указывая вместо номера сессии пустое значение. Это - сигнал браузеру удалить куки из базы. После этого сессия считается разорванной и пользователь снова видит странички "для анонимусов".

Важные моменты о сессиях, которые следует понимать:

  • В самом протоколе HTTP нет ни единого упоминания о сессиях: пары "запрос-ответ" HTTP никак не связаны друг с другом. Понятие сессии появляется на более высоком уровне: сессиями управляет веб-приложение, работающее на сервере. HTTP является лишь транспортом для передачи заголовков Set-Cookie/Cookie, а интерпретацией заголовков занимается веб приложение и браузер клиента.
  • Данные сессии хранятся на сервере, этих данных может быть так много, как требуется веб-приложению. У клиента хранится только номер сессии. Данные сессии на сервере - это "короткоживущие" данные, актуальные только на время сеанса пользователя. Данные клиента, которые надо запоминать надолго, сервер хранит в базе данных или в файлах.
  • Номер сессии является чрезвычайно ценной информацией, ведь зная этот номер, можно "представиться" серверу некоторым пользователем, даже не являясь им. Для этого достаточно каким-либо способом украсть номер сессии (перехватить HTTP-трафик пользователя-жертвы, или, если он не понимает что делает, попросить его сообщить вам свой номер сессии, скопировав его из браузера). Для предотвращения перехвата используется протокол HTTPS, при котором весь трафик между клиентом и сервером надёжно шифруется.
  • Номер сессии может передаваться не только через Cookie, но и, в некоторых случаях, через параметры URL вида "http://какой-то-то-сайт.ком/?PHPSESSID=FUG984ylsk". Адрес такого вида должен являться для пользователя сверхсекретным, ибо содержит информацию, которая идентифицирует данного пользователя на сервере.
  • Детальная информация по механизму сессий HTTP содержится в RFC 6265.

Раздел:

Темы: