Введение

Частый вопрос на собеседованиях у бэкенд-разработчиков (и, действительно, это важно знать): что такое HTTP и как он работает?

Вот об этом мы сегодня и поговорим. Постараемся нырнуть достаточно глубоко, заодно задеть HTTPS и в целом сконцентрировать в этой статье все знания об HTTP, которые нужны любому бэкенд-разработчику.

Что такое HTTP?

HTTP (HyperText Transfer Protocol, протокол передачи гипертекста) — это, как понятно из названия, протокол, то есть некая договоренность о том, как сервер и клиент взаимодействуют в контексте передачи гипертекста (это странное слово значит всего лишь текст с гиперссылками). Сейчас, впрочем, по HTTP передают далеко не только текст со ссылками (но и его тоже).

Работает HTTP на прикладном уровне, и работает он поверх TCP (который, в свою очередь, работает на транспортном уровне. Когда-нибудь я напишу статью об уровнях модели OSI, но пока она даже не в бэклоге). По умолчанию HTTP использует порт 80/tcp, но может работать и на любом другом порту — все зависит от настроек сервера.

Используем мы его очень часто, так как это основной протокол, по которому работает WWW. Когда ваш браузер открывает сайт, он делает это по HTTP (точнее, сначала по DNS, потом по HTTP, а потом (крайне вероятно) по HTTPS (который самостоятельным протоколом не является, это расширение протокола HTTP, и мы его в этой статье тоже затронем)).

HTTP работает в рамках клиент-серверной архитектуры, то есть выглядит это примерно так:

  • клиент (часто браузер) запрашивает у сервера некий ресурс
  • сервер обрабатывает запрос и возвращает клиенту что-то (и это не всегда то, что просил клиент): возможно, это тот самый ресурс, может быть, что это указание, где этот ресурс на самом деле лежит, а может быть, и ошибка.

Давайте чуть подробнее разберем обмен сообщениями между клиентом и сервером.

HTTP-запросы и HTTP-ответы

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

Как запросы, так и ответы выглядят примерно так:

  1. Стартовая строка — в ней содержится версия HTTP, которую мы используем, и другая информация — типа запрашиваемого ресурса или кода ответа.
  2. Заголовки HTTP (далее мы поговорим о них подробнее): несколько строчек текста в заранее оговоренном формате, которые как-то уточняют запрос или содержимое ответа.
  3. Пустая строка, которая означает, что все метаданные (версия протокола, заголовки — в общем, все, что было в первых двух пунктах) отправлены.
  4. Тело сообщения (которое существует не всегда) — к примеру, данные, связанные с запросом, или HTML-страница, переданная в ответе.

Давайте смотреть на все пункты подробнее.

HTTP-запрос

Стартовая строка HTTP-запроса

Стартовая строка HTTP-запроса выглядит как-то так:

GET /index.html HTTP/1.1

Первая часть (GET) — это метод запроса. Он говорит серверу, что мы вообще от него хотим. Вот список самых популярных HTTP-методов:

  • GET — запрашивает какой-то ресурс у сервера (в примере /index.html)
  • HEAD — это как GET, только сервер вернет исключительно заголовки, не сам ресурс. Используется, к примеру, для того, чтобы узнать размер ресурса перед его загрузкой
  • POST — позволяет отправить данные на сервер. Используется для создания чего-то, часто - для авторизации / аутентификации, загрузки данных на сервер (типа изображений или документов)
  • PUT / PATCH — позволяет изменить что-то, уже существующее на сервере
  • DELETE — что-то удаляет на сервере
  • OPTIONS — спрашивает информацию о сервере, в том числе список поддерживаемых сервером методов

Вторая часть /index.html — это URI (Uniform Resource Identifier, унифицированный идентификатор ресурса) — это путь к ресурсу, который мы хотим загрузить / поменять / создать (в зависимости от метода и конфигурации сервера).

Третья часть — версия HTTP, в соответствии с которой составлен запрос. В примере — 1.1. Вообще, сейчас гораздо более популярна версия HTTP/2, но сайты с HTTP/1.1 вполне себе встречаются.

Заголовки HTTP-запроса

Заголовки запроса этот самый запрос уточняют. Давайте перечислим самые популярные из них:

  • Host — единственный обязательный заголовок запроса. Содержит доменное имя или IP-адрес, к которому мы обращаемся. Для чего это нужно? В том числе для того, чтобы сервер понимал, к какому именно доменному имени / IP-адресу мы обращаемся — ведь на сервере может быть размещено несколько сайтов на разных IP-адресах или доменных именах (это называется “IP-Based Virtual Host” / “Name-Based Virtual Host”).
  • User-Agent — описывает клиент, который обращается к серверу.
  • Refer — содержит информацию о том, откуда поступил текущий запрос (к примеру, вы переходите с сайта http://site1.com по ссылке на сайт http://site2.com — тогда в заголовке Refer, вероятно, будет содержаться http://site1.com).
  • Accept — указывает, какие типы медиафайлов умеет принимать клиент.
  • Cookie — может содержать куки, предварительно отправленные на сервер или отправленные сервером (с помощью заголовка ответа Set-Cookie, который мы разберем позже).
  • Authorization — обычно работает так: после какого-то рода аутентификации клиента на сервере сервер возвращает некий токен, который клиент после отправляет в каждом запросе. Вот как раз в заголовке Authorization этот токен и отправляется.

А вот здесь можно почитать о других HTTP-заголовках.

Тело сообщения HTTP-запроса

HTTP-запрос завершается телом. Но далеко не у каждого запроса предполагается наличие тела: тело есть у запросов, что-то меняющих на сервере — то есть у запросов с методами GET, HEAD, OPTIONS наличие тела не предполагается (хотя никто не запрещает его передавать), а у запросов с методами POST, PUT, PATCH наличие тела предполагается (хотя его может не быть). Запросы DELETE обычно не имеют тела, потому что, хоть и что-то меняют на сервере, но не добавляют никаких новых данных. Но, опять же, и у таких запросов может быть тело.

HTTP-ответ

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

Стартовая строка (строка статуса) HTTP-ответа

Стартовая строка (которую еще называют строкой статуса) HTTP-ответа может выглядеть, к примеру, так:

HTTP 1/1 200 OK

Здесь первая часть (HTTP 1/1) — это знакомая нам по стартовой строке HTTP-запроса версия HTTP-протокола. Вторая часть (200 в примере) — это код состояния, а третья (OK) — его текстовое описание. Коды состояния мы разберем подробнее. Итак, коды состояния бывают такие:

  • 1xx — коды, начинающиеся с единицы, никак не влияют на обработку запроса и просто передают информацию. Например, код 101 Switching Protocols сообщает клиенту о смене протокола. Вот такой вот ответ говорит клиенту, что сервер переходит с HTTP на WebSockets:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
  • 2xx — коды, начинающиеся с двойки, возвращаются тогда, когда клиентский запрос был успешно обработан. Например, код 200 OK вернется вместе с запрошенным ресурсом, а 201 Created вернется, когда что-то успешно создано на сервере.

  • 3xx — коды, начинающиеся с тройки, возвращаются, когда серверу нужно куда-то перенаправить клиента. Например, код 301 Moved Permanently означает, что запрошенный ресурс теперь доступен по новому URI (который передается в заголовке Location).

  • 4xx — коды, которые начинаются с четверки, означают, что клиент отправил некорректный запрос. Например, код 403 Forbidden означает, что у клиента нет доступа к запрошенному ресурсу, а всем известный 404 Not Found означает, что запрошенный ресурс не найден.

  • 5xx — коды, начинающиеся с пятерки, означают ошибку на сервере. К примеру, код 504 Gateway Timeout говорит клиенту о том, что сервер, выполняя роль прокси, не дождался ответа от апстрима вовремя.

О всех возможных кодах состояния можно почитать здесь.

Заголовки HTTP-ответа

Заголовки ответа используются для того, чтобы этот самый ответ уточнить, и не влияют на тело ответа. Заголовки HTTP-ответа выглядят абсолютно так же, как заголовки запроса.

Вот примеры часто встречающихся заголовков ответа:

  • Server — содержит информацию о сервере. Пример: Server: nginx.
  • Set-Cookie — мы уже упоминали этот заголовок ранее. Он устанавливает куки, которые клиент затем сохраняет у себя. Пример: Set-Cookie:PHPSSID=bf42938f.
  • WWW-Authenticate — говорит клиенту, какой тип аутентификации используется для доступа к запрашиваемому ресурсу. Например: WWW-Authenticate: BASIC realm=»localhost».

Тело ответа

Обычно тело ответа присутствует, но иногда вся необходимая информация содержится в заголовке (типа 201 Created или 204 No Content), и тела у ответа тогда нет.

HTTP/2

Для того, чтобы ответить на вопрос, зачем создавался HTTP/2 и в чем его основное преимущество, мы начнем с протокола SPDY, разработанного в Google как замена некоторых частей HTTP — в частности, по задумке разработчиков, вместо отдельного соединения для каждого ресурса используется мультиплексирование — таким образом, для передачи нескольких файлов теперь требовалось только одно соединение, что значительно ускоряло работу сайта. В 2011-2012 годов этот протокол поддерживался большинством браузеров и активно использовался в вебе.

В 2015 году Рабочая группа HTTP IETF (Инженерного Совета Интернета) пересмотрела протокол HTTP и разработала версию протокола HTTP/2, которая в том числе включала мультиплексирование. В мае того же года протокол HTTP/2 был официально стандартизирован.

Особенности HTTP/2

  • мультиплексированные потоки

В отличие от модели “запрос-ответ”, используемой в HTTP/1.1, в HTTP/2 используются мультиплексированные потоки — данные в обе стороны передаются в рамках одного TCP-соединения (статья о TCP тут:) в бинарном формате. Такой подход дает кучу преимуществ:

    - параллельные мультиплексированные запросы и ответы не блокируют друг друга
    - несмотря на передачу кучи данных, для наибольшей эффективности работы сети используется одно TCP-соединение
    - сервер может отправлять данные по своей инициативе (Server Push)
    - клиент может приоритезировать потоки, отдавая предпочтение конкретным потокам данных
    - заголовки сжимаются с помощью алгоритма HPACK

Таким образом, HTTP/2 ощутимо быстрее HTTP/1.1. И да, на текущий момент гораздо популярнее. Кстати, переходя к следующей главе: HTTP/2 требует использования HTTPS.

HTTPS

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

Поэтому еще в 1994 году было разработано расширение протокола HTTP — HTTP Secure, или HTTPS. HTTPS, в отличие от HTTP, использует порт 443/tcp по умолчанию. До 2015 года он использовал протокол шифрования SSL, теперь же он использует TLS, но суть не поменялась.

HTTPS работает благодаря TLS/SSL-сертификату. Этот сертификат можно назвать некой цифровой подписью сайта, с его помощью подтверждается подлинность сайта. Перед тем, как установить защищенное соединение, клиент запрашивает этот сертификат и обращается к центру сертификации, который этот сертификат выпустил, чтобы подтвердить легальность сертификата. Если сертификат действителен, клиент считает сайт безопасным и начинает обмен данными.

Кроме подтверждения подлинности сайта, сертификат шифрует данные. После определения подлинности сайта начинается обмен шифрами. Шифрование HTTPS происходит с помощью асимметричного и симметричного ключа. Давайте рассмотрим их подробнее.

Асимметричный ключ

Каждая сторона имеет два ключа: публичный и приватный. Публичный ключ доступен любому, приватный — только владельцу. Когда клиент хочет отправить сообщение, он находит публичный ключ сервера, шифрует им сообщение, создавая секретный ключ, и отправляет серверу. Сервер расшифровывает полученное сообщение своим приватным ключом и делает то же самое: достает публичный ключ клиента, шифрует им сообщение, отправляет клиенту, клиент расшифровывает его своим приватным ключом. Теперь у каждой из сторон есть одинаковый секретный ключ.

Симметричный ключ

Когда клиент и сервер установили первичное соединение и создали секретный ключ, они используют его для шифрования всех данных, которые передают друг другу. Так как ключ не надо создавать заново каждый раз, симметричный ключ быстрее.

В завершение

Вот здесь есть вся документация по HTTP. По сравнению с ней, эта статья — просто ознакомительная :)

Завершить хотелось бы тем, что HTTP и сам используется как транспорт — для таких протоколов, как SOAP, XML-RPC, WebDAV. REST, опять же, использует HTTP как транспорт. Мы можем считать HTTP как минимум одним из главных протоколов интернета.