Время от времени приходится рассказывать про HTTP. Как программистам, так и тестировщикам. Это краткий конспект подобных рассказов со упоминанием некоторых дополнительных тем, которые тоже неплохо бы изучить. Основная цель – дать максимум практических знаний, которых хватит для 99% практических ситуаций.

HTTP работает с “ресурсами”. Для простоты можно думать, что это файлы. Например, GET /books/123/authors.json?limit=2&page=2 можно понять как получить авторов книги ID 123 в формате JSON со второй страницы, если размер страницы равен 2 авторам. Это Restful-стиль, он считается “правильным”, но не всегда подходит. Чаще всего применяется для доступа к данным (CRUD сценарии). Когда же приложение в основном работает с командами, то стиль несколько другой (для того же примера): GET /api/authorsList?book=123&limit=2&page=2. На практике таких API гораздо больше, т.к. и CRUD-сценариев не так уж много, и сам подход ближе к серверному коду. Хорошо бы когда-нибудь прочитать про Restful APIs и их использовать по делу.

HTTP-запрос – это текст (разве что иногда кроме тела запроса) и состоит из (в порядке появления в запросе):

  1. Глагола (GET, POST, …)
  2. URI (/my/lala?a=b&page=2&page=3) – обычно хост есть в заголовках, а порт мы заранее знаем, т.к. на нем запустились. Имеет ограничение по длине, но непонятно какое (зависит от браузера, сервера и промежуточных прокси). Более-менее безопасно 16к. Совсем безопасно 1к.
  3. Заголовков – не обязательно наличие
  4. Тела запроса – не обязательно наличие, а для некоторых глаголов и не допускается

HTTP-ответ очень похож на запрос (код ответа, заголовки и тело), на данном уровне отдельно разбирать будем только коды ответа:

  1. 200 – все хорошо
  2. 201 – новый ресурс создан (для restful-стиля)
  3. 301 – ресурс переехал на новый адрес навсегда (адрес будет в заголовке Location)
  4. 302 – ресурс переехал на новый адрес временно
  5. 404 – ресурс не найден (иногда для скрытности означает и 401/403)
  6. 401 – нужно авторизоваться
  7. 403 – авторизовались, но прав недостаточно (или не авторизовались)
  8. 422 – серверу не нравятся параметры, он не может ответить (ошибка запроса)
  9. 500 – серверу плохо (ошибка сервера)
  10. 502, 503, 504 – что-то не так с инфраструктурой (ошибка сервера, но не самого приложения)

При каких кодах имеет смысл повторять запрос? 5хх, при остальных вряд ли ответ поменяется. Повторять лучше с задержками. Или не повторять и отдать на откуп пользователю (он сам решит когда повторять и стоит ли – может с поддержкой свяжется или подождет часок).

Есть 2 стиля работы с ошибками (по сути аналогично restful и процедурный):

  1. мы отдаем как можно больше разных кодов ответа, чтобы уже на этом уровне было понятно, что происходит
  2. если технически мы получили запрос и отработали, то всегда возвращаем 200. А вот уже ошибка это или нет – это внутри JSON-ответа. Т.е. мы относимся к HTTP как к некоторому транспортному протоколу для удаленного вызова методов и особо внутрь него не лезем.

Глаголы:

  1. GET – получение, один из основных, тела запроса нет, не должен менять данные на сервере
  2. POST – отправить сущность в определенный ресурс (в restful на подпути появится новый ресурс, например)
  3. PUT – заменить новой сущностью указанный ресурс
  4. PATCH – частичное изменение указанного ресурса
  5. HEAD – это GET без тела ответа (если нужны только заголовки, например, размер или когда последний раз ресурс был модифицирован)
  6. DELETE – удаляет ресурс
  7. OPTIONS – узнать какие глаголы поддерживаются по указанному пути. На практике обычно используется из-за CORS (ограничение доступа с одной веб-страницы на другую из-за безопасности).
  8. CONNECT – для установки двухстороннего соединения с сервером, например, HTTPS (открывает шифрованный канал)
  9. TRACE – не видел, чтобы кто-то это использовал

URI:

  1. path (/mylala)
  2. query params (a=[b], page=[2,3]) – массивы значений в теории могут передаваться как повторы параметров, но как это интерпретировать остается на усмотрение сервера

Заголовки:

  1. Могут быть любыми, рассмотрим самые важные
  2. Host – указывает DNS-имя, т.к. соединение производится по IP и по другому сервер, который обслуживает несколько сайтов не поймет какой отдавать
  3. Content-Type – тип данных ресурса
  4. Content-Length – размер ресурса
  5. Authorization – передается как мы авторизовались на сервере (обычно такое отправляет приложение, веб-страница авторизуется через cookies или URI)
  6. Cookie – знаменитые кукиес (параметры, которые браузер у себя сохраняет между запросами – обычно для авторизации или слежки)
  7. Set-Cookie – заголовок ответа: говорит браузеру запомнить новый параметр кукиес
  8. Accept – какой тип ресурса хотим получить (например, иногда можно получить одни и те же данные в XML, JSON, CVS, XLSX форматах)
  9. Location – обычно в ответах говорит, где ресурс (коды 3xx и 201)
  10. Content-Range – получить кусочек ресурса
  11. Referer – браузеры говорят с какой страницы делают запрос или с какой перешли на эту
  12. Upgrade – предлагает переход на другие протоколы, например, WebSocket или HTTP/2.0
  13. Cache-Control, Age, Date, Digest, ETag, Expires, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Last-Modified – управляют кешированием (если интересно, то легко можно найти соответствующую статью в интернете про них – нужно выбирать по свой случай правильный вариант)

Тело запроса:

  1. Отсутствует у глаголов GET, HEAD, CONNECT, OPTIONS, TRACE
  2. Тип содержимого определяется заголовком Content-Type
  3. Популярные типы: application/json, multipart/form-data (это стандартные формы из HTML), но может быть любым

Описать HTTP API можно с помощью OpenSpec (он же Swagger). Хорошо, если сервер автоматически корректно генерирует такое описание – лучшая защита от устарения. Из него же можно генерировать HTTP-клиенты. Стоит отметить, что тип тела ответа в OpenSpec зависит от http code (200, 404, …): по описанию для одного кода не может быть 2 разные схемы. Подробнее про OpenSpec в других местах когда-нибудь стоит прочитать.

Посмотреть запросы можно в браузере в плагине для разработчиков. Для более сложных случаев (сервер отправляет запросы или мобильное приложение) можно воспользоваться Charles (или аналогами).

Если запускать запросы, то многим нравится Postman (в частности, возможностью передать другому человеку коллекцию запросов). Лично мне больше нравится httpie. Postman больше подходит для тестировщиков (запросов много, много ими пользуются и хранят), httpie для программистов (сделал и быстро забыл).

Кроме запрос-ответ поверх HTTP можно организовать двухстороннее общение сервера и клиента. Это большая магия, основная технология – WebSocket, но обычно это все сильно обернуто и не требует особого понимания от новичков.

Хотя это и довольно сильное упрощение, получилось достаточно много.