Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

executable file 365 lines (237 sloc) 65 KB

Протокол HTTP (черновик)

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

Как передаются данные в сети

Прежде чем изучать протокол HTTP, нам необходимо понять вкратце, как вообще передаются данные в компьютерных сетях, в том числе в Интернете. Если упростить ситуацию, сеть соединяет между собой узлы (компьютеры или другие устройства). У каждого узла сети есть уникальный IP-адрес (IPv4 адреса выглядят как 4 числа, разделенных точками, вроде 192.168.15.21). Узлы могут отправлять друг другу IP-пакеты данных, каждый пакет содержит адрес отправителя, адрес получателя и полезную нагрузку (payload) - небольшое количество байт (обычно не более 1500 байт в одном пакете, и не более ~65500 байт, если передавать данные фрагментами в нескольких пакетах, на которые автоматически разбиваются большие пакеты). Подробно формат пакетов описан в протоколе IP.

Полезные данные кодируются в виде набора байт. Один байт - это группа из 8 бит, каждый из которых может принимать значение 0 или 1 (байты еще называют октетами, от слова «восемь»). Эти 8 бит могут образовать 28 = 256 разных комбинаций, которые обычно обозначают числами от 0 до 255 (или от 00 до FF в шестнадцатеричной системе счисления). Таким образом, содержимое пакета можно представить в виде набора чисел от 0 до 255. Программы, которые пересылают друг другу пакеты, сами решают, что именно обозначают те или иные значения и как кодируется информация в пакете. То есть любые данные, которые передаются по сети - текст, изображения, голос, видео, данные о положении и действиях игровых персонажей - все они кодируются с помощью набора байтов (а описание, что именно обозначают эти числа, как раз и называется протоколом).

Использовать для пересылки данных IP-пакеты напрямую не очень удобно, по таким причинам:

  • в пакете указан IP-адрес узла-получателя, но не указано, какой из программ, запущенных на этом узле, предназначен пакет
  • хотя сеть старается доставить пакет до получателя, пакет может потеряться в пути. Каждая программа сама должна как-то обнаруживать такие ситуации и отправлять пакет повторно
  • размер пакета ограничен, и если программе надо передать большой объем данных, она должна сама разбить их на пакеты, пронумеровать их, переслать, а при получении - все тщательно проверить и собрать обратно

По этим причинам программы обычно используют дополнительные протоколы (договоренности, правила и стандарты) передачи данных, которые работают поверх IP-протокола. Есть два таких распространенных протокола:

  • протокол UDP решает проблему указания программы-получателя, добавляя в IP-пакет номер порта - число от 1 до 65535, которое указывает программу на компьютере-получателе, которой предназначен пакет. Он не решает проблему потерь пакетов и проблему ограничения размера пакета и потому используется там, где нужна минимальная задержка, но допустимы редкие потери данных. Например, в сетевых играх, при передаче голоса и звука в программах-мессенджерах.
  • протокол TCP тоже добавляет в пакеты номер порта для указания программы-получателя. Еще он умеет передавать сколь угодно большие объемы данных, разбивая их на маленькие IP-пакеты при передаче и собирая обратно при получении, а также отслеживает потерявшиеся пакеты и пересылает их повторно. Протокол TCP позволяет двум программам установить друг с другом TCP-соединение, по которому они могут пересылать в обе стороны поток байт любой длины. Протокол TCP дает больше возможностей, чем UDP, но он требует немного больше времени на установление соединения, а при потере данных возникает небольшая задержка из-за необходимости повторной передачи. Он используются там, где надежность передачи данных важнее скорости их доставки.

Программы обычно используют один из этих двух протоколов, и сами решают, как они кодируют полезную информацию в виде байт. Способ кодирования информации называют прикладным (придуманным для конкретной задачи) протоколом (application protocol).

Порт - это число от 1 до 65535, которое указывает на программу, которой предназначен пакет. Когда программа хочет получать пакеты из сети, она "открывает" (резервирует, "слушает") порт с определенными номером. При поступлении пакета из сети операционная система смотрит на указанный в нем номер порта и передает его программе, "слушающей" этот порт, а если такой программы нет, то отбрасывает пакет или возвращает отправителю сообщение об ошибке. Одновременно один порт может "слушать" только одна программа.

Прикладные протоколы обычно делятся на два вида: текстовые и бинарные (двоичные). В текстовом протоколе информация сначала кодируется в виде текста, а затем этот текст кодируется в виде байт с помощью определенной кодировки (то есть каждому символу соответствует один или несколько байт). В бинарном протоколе данные кодируются напрямую в числа-байты. Бинарный протокол компактнее, требует меньше времени на кодирование/декодирование, он более эффективен, но текстовый протокол проще для понимания человеком, и с ним удобнее работать.

Приведу пример. Допустим, мы разрабатываем электронный термометр, который должен каждые N секунд пересылать по сети измеренную температуру на компьютер (на котором запущена программа, принимающая и сохраняющая эти данные). Допустим, что IP-адрес компьютера и номер порта программы, на который надо отправлять данные, заложены в термометр. Какой протокол мы можем придумать для решения этой задачи?

Из 2 транспортных протоколов - UDP и TCP - я выберу UDP, так как он проще устроен, и позволяет отправлять данные пакетами, не тратя время на установку и завершение соединения. Пакеты UDP могут иногда теряться, но это нам не критично, так как наш прибор шлет данные постоянно. Итак, каждые N секунд наш термометр будет отправлять один UDP-пакет, содержащий текущую температуру. Как мы ее закодируем в виде байт?

  • если мы хотим использовать бинарный протокол, то мы можем сделать крошечный UDP-пакет, содержащий в качестве полезной нагрузки единственный байт, обозначающий температуру. Температура на улице может меняться (на планете Земля) примерно от -90 до +60 градусов, а в байте могут быть значения от 0 до 255, потому мы прибавим к температуре 90 и получим число от 0 до 150. Получатель, разумеется, должен не забыть после получения вычесть 90 из полученного значения. Также, это позволяет передавать только целые значения температуры, чтобы передавать дробные значения, придется использовать больше одного байта.
  • если мы хотим использовать текстовый протокол, то мы просто представляем температуру в виде текста (например, 32, -20 или -4.7) и передаем UDP-пакет, где содержится этот текст в виде набора байт. Если использовать кодировку ASCII или utf-8, то цифры и знак минус там кодируются одним байтом и наша полезная нагрузка будет занимать 2-3 байта. Это больше, чем при использовании бинарного протокола, но зато эти данные проще прочесть человеку, если он захочет посмотреть на них.

Возвращаяясь к протоколу HTTP

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

Есть две разновидности протокола HTTP - "просто" HTTP и HTTPS. HTTPS (S значит Secure) - это вариант HTTP, который отличается только тем, что данные в нем передаются не в открытом виде, а по зашифрованному с помощью протокола SSL/TLS каналу, так, что сторонний наблюдатель не может подсмотреть или подменить их.

Протокол HTTP версии 1.1 является текстовым и использует транспортный протокол TCP (с номером порта 80 для HTTP и 443 для HTTPS). Клиент (например, браузер) устанавливает TCP-соединение с сервером, отправляет по нему один запрос, сервер его обрабатывает и отправляет ответ, после чего соединение закрывается. Чтобы отправить еще один запрос, нужно установить новое соединение. Так сделано ради упрощения протокола. Недавно (в 2015 году) появился протокол HTTP версии 2, который работает по тем же принципам, но является бинарным и имеет некоторые отличия, о них будет написано ниже.

Как было написано выше, клиент посылает запрос, а сервер отвечает на него. Есть всего несколько видов запросов, виды запроса называются методами HTTP. Также, в запросе всегда указывается URL - «адрес» страницы, которую хочет получить клиент, или на которую он хочет отправить данные, или сделать с ней что-то еще. То есть метод обозначает, какую операцию клиент хочет сделать, а URL - с какой страницей. Подробнее про URL и их структуру.

Вот основные методы:

  • самый распространенный метод - GET - позволяет клиенту запросить с сервера страницу или файл с определенным URL (URL - это «адрес» страницы вроде http://example.com/page.html). Сервер в ответ на запрос либо отправляет содержимое страницы или файла, либо сообщает об ошибке (например, если страницы с таким адресом на сервере нету)
  • метод POST позволяет клиенту отправить какие-то данные на сервер вместе с запросом. Обычно он используется при заполнении какой-то формы на сайте, например, формы регистрации, формы отправки комментария, формы загрузки файла на сервер
  • остальные методы не используются браузером при просмотре сайтов, но могут использоваться другими программами. Среди них: метод PUT позволяет добавить на сервер новый контент, метод PATCH - изменить существующий файл или контент, DELETE - удалить контент с сервера, HEAD - получить информацию о файле или странице
  • подробнее про методы можно почитать тут: https://ru.wikipedia.org/wiki/HTTP#.D0.9C.D0.B5.D1.82.D0.BE.D0.B4.D1.8B
  • еще подробнее методы описаны на англ. в RFC7231 - в спорных случаях можно смотреть туда.

Формат HTTP запросов

Вот пример HTTP-запроса, который браузер может отправить на сервер для получения страницы http://example.com/some/page.html. Так как HTTP - это текстовый протокол, то этот запрос легко понять человеку (если он знаком с протоколом):

GET /some/page.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) Chrome/0.2.149.27 Safari/525.13
Accept-Language: ru-RU,ru;q=0.8,en;q=0.4
Accept-Encoding: gzip, deflate, sdch

Запрос в HTTP состоит из трех частей: стартовой строки, заголовков и необязательного тела запроса. Стартовая строка - это первая строка запроса, GET /some/page.html HTTP/1.1 и она содержит ровно 3 кусочка, разделенные пробелами:

  • метод (GET), обозначающий, что хочет сделать клиент (в данном случае: получить страницу)
  • URL, на который отправлен запрос (без указания имени сервера)
  • используемая версия протокола, в данном случае HTTP/1.1

За стартовой строкой идут заголовки, каждый заголовок пишется на новой строке и имеет вид Название: значение. Названия заголовков не чувствительны к регистру (а метод, URL или значение заголовка - чувствительны). Заголовки передают дополнительную информацию о запросе и о клиенте. Обязательным является только заголовок Host, где указано имя сервера (оно берется из URL), на который отправлен запрос, остальные заголовки не обязательны, но могут помочь серверу дать более подходящий ответ. Вот, что обозначают использованные в примере заголовки:

  • User-Agent по задумке должен содержать название и версию браузера. Но в силу исторических причин сегодня он содержит много лишней и неточной информации. В примере запроса выше браузер называется Chrome, но он указывает в заголовке слова Mozilla и Safari, чтобы плохо написанные старые серверы, которые до сих пор думают, что в мире только 2 браузера - Mozilla и Internet Explorer - не приняли его за Internet Explorer и не давали страницу, заточенную под этот браузер. Тут можно прочитать подробнее об истории User-Agent: http://front-end.su/user-agent-history. В настоящее время определять на сервере браузер по этому заголовку и отдавать разный контент в зависимости от него считается плохой практикой.
  • Accept-Language указывает, на каких языках предпочел бы получить содержимое клиент. Они полезны, если сервер может выводить информацию на разных языках.
  • Accept-Encoding указывает, какие форматы сжатия данных поддерживает браузер. Сервер может передавать данные в сжатом виде для экономии трафика и ускорения передачи.
  • есть еще много других заголовков запроса, о которых можно прочесть тут: https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%B7%D0%B0%D0%B3%D0%BE%D0%BB%D0%BE%D0%B2%D0%BA%D0%BE%D0%B2_HTTP

Заголовки обязательно заканчиваются одной пустой строкой. Если используется метод запроса вроде POST или PUT, в котором есть тело с отправляемыми на сервер данными, то они идут после этой пустой строки. В примере использован метод GET, в таком запросе тела нету.

Для полноты приведу пример запроса POST с данными. Такой запрос обычно отправляется браузером при заполнении и отправки формы, например, формы добавления комментария. В теле передаются данные, которые пользователь ввел в форму. Есть 2 основных способа кодирования данных - multipart/form-data и application/x-www-form-urlencoded (способ выбирается атрибутом enctype в HTML-теге <form>). Вот пример запроса:

POST /send-comment HTTP/1.1
Host: example.com
Content-Length: 44
Content-Type: application/x-www-form-urlencoded

text=Hello+world%21&captcha=175203&name=Ivan

Видно, что здесь есть 2 дополнительных заголовка: Content-Type указывает тип и способ кодирования данных в теле запроса, а Content-Length - длину тела запроса в байтах (она помогает серверу понять, когда передача запроса заканчивается и можно больше не ждать данных от клиента). Вообще, если в запросе есть тело, то должен присутствовать заголовок Content-Type, обозначающий тип данных в нем. В самом теле запроса с помощью процентного кодирования закодированы такие данные, введенные пользователем в форму:

  • name=Ivan
  • text=Hello world!
  • captcha=175203

В ответ на POST запрос сервер обычно не отдает какую-то страницу, а перенаправляет клиента на другую страницу, в данном примере сервер может перенаправить пользователя на страницу, где отображается добавленный комментарий. Однако, если форма была заполнена неверно (например, неправильно введен код капчи), сервер может вернуть пользователю в ответе страницу с сообщением об ошибке.

Формат HTTP-ответа

Получив запрос, сервер обрабатывает его и выдает ответ. Ответ может быть успешным (передаю запрошенную страницу), сообщать о какой-то ошибке (страницы с таким URL нету) или перенаправлять клиента на другой адрес (страница была, но поменяла URL).

Вот пример успешного ответа (я получил этот ответ, запросив URL http://example.com/ методом GET):

HTTP/1.1 200 Ok
Content-Type: text/html; charset=UTF-8
Content-Length: 1270
Date: Sun, 03 Sep 2017 22:43:05 GMT
Server: nginx/1.0
Cache-Control: max-age=604800

<!doctype html>
<html>
<head>
<title>Example Domain</title>
(далее идет HTML-код страницы, который здесь не показан)

Ответ, как и запрос, состоит из трех похожих частей: стартовой строки, заголовков и тела. Стартовая строка содержит:

  • версию протокола, которую использовал сервер (HTTP/1.1). Очевидно, что она не должна быть выше версии, которую указал клиент в запросе, иначе он не сможет разобрать ответ.
  • код ответа, который сообщает о результате. В данном случае код 200 обозначает "запрос успешно обработан, в теле ответа содержится содержимое запрошенной страницы или файла"
  • текстовая расшифровка кода ответа, предназначенная для людей (Ok)

Далее идут заголовки, каждый на новой строке. Они передают дополнительную информацию о сервере и теле ответа. Все заголовки являются необязательными. Вот, что обозначают указанные в примере заголовки:

  • Content-Type обозначает тип содержимого в теле ответа. В данном случае тут указано text/html - ответ содержит HTML-файл (страницы сайтов верстаются в формате HTML) с текстом в кодировке utf-8. Могут быть и другие значения, например, если сервер хочет передать изображение, то он укажет его формат, вроде image/png или image/jpeg. Эти обозначения называются MIME-типы, вот их список: https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_MIME-%D1%82%D0%B8%D0%BF%D0%BE%D0%B2
  • Content-Length содержит длину тела ответа в байтах. Она помогает клиенту (браузеру) проверить, что содержимое получено целиком, и соединение не разорвалось раньше времени.
  • Date указывает дату и время на сервере, когда был сформирован ответ
  • Server содержит название и версию программы-сервера
  • Cache-Control используется для кеширования. Он говорит клиенту, в течение какого времени (в секундах, 604800 секунд - это 7 дней) полученный файл останется актуальным и не изменится. Если пользователь захочет открыть эту страницу повторно, и она есть в кеше браузера, и она не устарела, браузер может отобразить сохраненную в кеше копию, не делая запрос на сервер. Обычно кеширование используется для таких файлов (статических файлов), которые очень редко меняются. Ну например, картинка-логотип сайта есть на каждой странице, и вряд ли она часто меняется, потому, задав для нее кеширование, можно сэкономить трафик и ускорить загрузку сайта при повторном заходе. Для страниц на часто обновляющихся сайтах кеширование обычно не используется, или ставится маленькое значение, иначе пользователи будут видеть устаревшие данные.

Список заголовков завершается пустой строкой, после которой идет тело ответа. В данном случае в теле ответа содержится HTML-код запрошенной страницы сайта. Браузер отобразит этот код на экране. Если запрошен файл другого типа - например картинка, которая кодируется не в текстовом, а в бинарном формате, то тело будет выглядеть как набор бессмысленных символов, вот пример ответа, в котором сервер отдает картинку:

HTTP/1.1 200 OK
Date: Sun, 03 Sep 2017 23:11:34 GMT
Content-Type: image/png
Content-Length: 21357
Cache-Control: max-age=31536000

‰PNG
�

IHDR‡›��Hу€к�tEXtSoftwareAdobe ImageReadyqЙe<S�IDATxЪн}�`UU¶цє7Ѕ“@�zG@z��)"Rм€Ќ�A±ЌmЮst,Ју,3c�ќ��:6м:цЋ��©""Ez
ќй=№н_ЯЪkЯ{rIh  ВМџѓЗ$·њІч·ЧъV=®@ @
[ГVУжn�‚†­��
[�8�¶�p4l
аhШ�АС°5ЂЈakGГЦЋ†­��
[�8�¶†­��
[�8�¶�p4lGq‹¬л�vмШuдИt»Ёёё„��PBb"y<�ю5@H"€‰ЋЎхлЧчМИHяҐYіж”µeKух�7м+--хDDD�№j?®ЧгҐ$>^ы6m(22’


hЧо]Ф¶m;jХЄ�щэ~BЄ�ЮЫ»o�нЭ»—<U�№�ј–“›CqqсФЁQ#Є¬Ё �џ«  €*«*)55Ќј^�щј^щ;&&ЋЏ�апWQEe9ЕЕЖ‘Чз“sшшgee%5oЮ‚RR�Сц�[S‹
‹»ДДDЏ‰ЋЋй”””ь;>xn а'·�cQLnѕ·ииhю.®С\'6ьДсЪ·kЗЧ•BU|ѕѓm'
иwlБQЯ›‹�ЙнvчПЙЙy’�цдттЉ/КЛЛ6”””\_VV¶ #=}4ѓЈ��WУf�УE‰       Ћї�ќН� ј<q�sЮ
(остальные данные не показаны)

Эти бинарные данные непонятны человеку, но зато позволяют закодировать картинку, используя минимальное число байт.

Статус (код) ответа

Ответ на запрос не всегда бывают успешным. Ну например, при запросе несуществующей страницы можно получить ответ с кодом 404 - запрошенная страница не найдена. Результат запроса представлен кодом от 100 до 599 (в примере выше был 200 - все успешно), и первая цифра кода обозначает общий смысл:

  • 1xx - информационный код - этот код используется редко, более того, он отправляется зачастую до получения запроса целиком и отправки ответа, и обозначает не ответ, а информационное сообщение, которое сервер хочет передать клиенту. Ну например, когда клиент начинает отправлять запрос POST, сервер может (еще не получив сам запрос целиком и не обработав его, а только получив заголовки) отправить строку HTTP/1.1 100 Continue, которая не является ответом на запрос, а информирует клиента о том, что сервер согласен принять данные в теле запроса и клиент может продолжать их передавать. Мы не будем рассматривать тут эти коды.
  • 2xx - успех - информирует о том, что запрос обработан успешно. Примеры успешных кодов: 200 Ok - запрошенный файл существует и приложен в теле ответа, 204 No content (обычно в ответ на запрос вроде POST) - запрос успешно обработан, но в ответ на него сервер решил ничего не отправлять и потому тела в ответе нету
  • 3xx - перенаправление - говорит о том, что клиент должен обратиться по другому адресу, который указан в заголовке Location (например: Location: http://example.com/other-page). Получив такой ответ, клиент устанавливает новое соединение и отправляет новый запрос на указанный адрес. Примеры кодов: 301 Moved Permanently - страница перехала на новый URL, 303 See Other - обычно используется в ответ на отправку POST-запроса с данными формы, говорит о том, что сервер обработал запрос и браузер должен получить с помощью GET и отобразить указанную в Location страницу.
  • 4xx - этим кодом сервер сообщает клиенту, что тот отправил неправильный запрос. Примеры кодов: 403 Forbidden - доступ к запрашиваемой странице запрещен, 404 Not Found - запрошенная страница или файл не найден, 405 Method Not Allowed - сервер не поддерживает использованный клиентом метод, 451 Unavailable For Legal Reasons - доступ к содержимому ограничен по юридическим причинам (например, по требованию правообладателей или государственных органов).
  • 5xx - коды 500-599 говорят об ошибке, произошедшей на сервере, из-за которой он не может отдать ответ. Клиент может попробовать повторить запрос позже. Примеры кодов: 503 Service Unavailable - сервер временно не обрабатывает запросы (например, из-за слишком большой нагрузки), 500 Internal Server Error - при обработке запроса на сервере произошла какая-то ошибка.

Числовые коды предназначены в первую очередь для программ, пользователь их не видит. Обычно в случае ошибки сервер также в теле ответа передает HTML-страницу, предназначенную для показа пользователю, которая объясняет причины ошибки. Однако сервер может и не передавать ничего - в этом случае браузер покажет встроенную в него страницу ошибки. Пример страницы, которую возвращает сервер yandex.ru при ошибке 404: http://yandex.ru/404-example

Процесс загрузки страницы

Разобравшись с основами протокола HTTP, рассмотрим, что происходит при загрузке страницы браузером.

  • допустим, пользователь вводит URL страницы http://example.com/some/page и жмет Enter
  • браузер разбирает URL (урок, описывающий формат URL) и извлекает из него такую информацию: используемый протокол - http, имя сервера - example.com, адрес страницы на сервере - /some/page
  • HTTP-клиент в браузере устанавливает TCP-соединение с сервером example.com на порт 80 (так как это порт по умолчанию для HTTP, и другого в URL не указано), и отправляет серверу HTTP-запрос, начинающийся со строки GET /some/page HTTP/1.1
  • сервер обрабатывает запрос, если такая страница или файл есть, он отдает ответ с кодом 200, если нет - ответ с кодом ошибки
  • допустим, код ответа 200. Браузер смотрит на заголовок Content-Type, который указывает тип содержимого в теле ответа и пытается отобразить это содержимое (если конечно он поддерживает такой тип, если нет - предложит пользователю сохранить файл на диск и открыть его самостоятельно). Например, если тип ответа - image/png, то браузер будет интерпретировать тело ответа как PNG-файл, если тип ответа text/plain - то отобразит содержимое как текст, если text/html - то как HTML страницу (текст с оформлением и форматированием)
  • если в ответ получена HTML-страница, то в ней могут содержаться ссылки на дополнительное содержимое, например, картинки, айдиофайлы или видеоролики. Для каждого такого элемента браузер возьмет его URL и сделает отдельный HTTP-запрос на сервер. В случае успешного получения контента браузер отобразит его на странице.

Использование HTTP в API и WebDAV

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

Протокол WebDAV на основе HTTP может использоваться для работы с файлами на удаленном сервере - просмотр списка файлов, загрузка, сохранение. Некоторые ОС (Windows например) умеют монтировать удаленную WebDAV-папку и работать с ней как с обычным диском.

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

Например, коммерческий сервис «Яндекс-диск» позволяет с помощью API на основе HTTP программно работать с файлами в аккаунте пользователя. Вот описание этого API: https://tech.yandex.ru/disk/

А коммерческий сервис «Яндекс.Карты» использует HTTP API для предоставления возможности геокодирования (преобразования адресов в координаты): https://tech.yandex.ru/maps/mapsapi/

Именно в API обычно используются редкие методы вроде PUT, DELETE, PATCH и другие.

Дополнительные возможности HTTP

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

Куки

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

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

Сервер может передать куки только как дополнение к ответу на HTTP-запрос. Куки хранятся индивидуально для каждого домена, и сайт a.com никак не может получить куки другого домена b.com. Общий размер кук на один домен ограничен величиной около 4000 байт, и они отправляются на сервер в каждом запросе, потому стоит минимизировать их размер. Пользователь обычно может просмотреть полученные куки в настройках браузера, запретить их прием, а также удалить их.

Механизм кук работает так:

  • когда сервер хочет сохранить или изменить куку в клиенте, он передает вместе с HTTP-ответом один или несколько специальных заголовков Set-Cookie, в которых описывает параметры куки. Например: Set-Cookie: lang=en
  • клиент (браузер) проверяет параметры, и если они корректны, сохраняет куку в хранилище на указанный срок
  • когда клиент будет делать следующий запрос, он найдет в хранилище все куки, относящиеся к домену, и отправит их вместе с запросом в заголовке Cookie, например, Cookie: language=english; city=moscow
  • программа на сервере может как-то учесть присланные значения кук при формировании страницы.

При установке куки в заголовке Set-Cookie указывается название и значение куки, после которого могут идти дополнительные необязательные параметры, например:

Set-Cookie: lang=en; Path=/; Domain=.example.com; Secure; HttpOnly; Expires=Wed, 21 Oct 2018 12:00:00 GMT

Вот, какие параметры куки можно задать:

  • срок хранения, после которого куку надо удалить, например Expires=Wed, 21 Oct 2018 07:28:00 GMT. Если его не указать, кука хранится до закрытия браузера.
  • домен, на который должна передаваться кука (Domain=.example.com). Разумеется, сайт a.example.com не может задать куку для чужого домена b.com, но он может указать значение .example.com, которое говорит, что кука должна быть доступна на любых поддоменах example.com вроде x.example.com, а не только на самом a.example.com.
  • путь, с которого должен начинаться URL страницы, например кука с параметром Path=/some будет передаваться на страницу /somepage, но не будет передаваться на страницу /otherpage.
  • параметр Secure, говорящий, что кука должна передаваться только при запросе по защищенному протоколу HTTPS
  • параметр HttpOnly, говорящий, что JS-скрипты на странице не могут прочесть значение куки (для защиты от воровства этой куки при XSS-уязвимости в некоторых случаях)

Если сервер хочет задать несколько кук, он отправляет несколько заголовков Set-Cookie. Чтобы удалить куку, отправляется заголовок с установкой пустого значения куки и датой в прошлом, например Set-Cookie: country=; Expires=Wed, 1 Jan 2000 12:00:00 GMT. При этом в нем должны быть указаны те же значения Domain и Path, которые были указаны при установке этой куки, иначе она не удалится.

Подробнее про куки:

Кеширование

Современные страницы сайтов часто состоят из большого количества файлов - HTML-страница, CSS-стили (задающие оформление страницы), JS-скрипты (программы, делающие страницу интерактивной), картинки. По умолчанию браузеру приходится загружать все эти файлы, чтобы отобразить страницу. Затем пользователь переходит на следующую страницу, и снова необходимо загрузить все эти файлы. Однако часть файлов редко меняется (или никогда не меняется) и есть смысл сохранять их в памяти или на диске, чтобы не запрашивать повторно (например, картинка с логотипом сайта вряд ли часто меняется). Такое сохранение данных называется кеширование. Оно позволяет экономить трафик и ускорить загрузку сайта при переходе на вторую и следующие страницы. Также, кеширование облегчает работу поисковых роботов. Однако, неправильная настройка кеширования может привести к тому, что пользователь будет видеть устаревшую информацию.

В настройках браузера обычно можно задать, какой объем на диске выделяется для кеширования файлов.

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

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

Безусловное кеширование включается заголовком Expires. Он указывает, до какого срока файл точно не изменится, например:

Expires: Sun, 02 Sep 2018 08:28:31 GMT

(для любопытных: дата указана в формате Internet Message Format, который описан в документе RFC2731 (англ.)). Клиент может смело сохранять ответ и до указанной даты брать его из кеша без запроса на сервер.

Условное кеширование работает чуть сложнее. Для его поддержки сервер при отдаче контента добавляет либо заголовок Last-Modified, указывающий дату последнего изменения файла, либо ETag, указывающий идентификатор (любой набор символов), который обязательно изменится при обновлении файла (например, можно использовать хеш файла или номер ревизии из системы управления версиями). Вот пример этих заголовков:

Last-Modified: Mon, 14 Mar 2016 18:08:11 GMT
ETag: "536d-52e0629e7c568"

Браузер сохраняет ответ в кеш. Когда ему снова понадобится этот файл, он отправляет запрос на сервер для проверки, актуальна ли закешированная копия или устарела. Браузер делает обычный GET-запрос, но чтобы показать, что у него есть кешированная копия, он добавляет заголовок If-Modified-Since (указав в нем дату из ранее полученного заголовка Last-Modified) или If-None-Match (указав в нем ETag). Вот пример такого запроса:

GET /some/file HTTP/1.1
Host: example.com
If-Modified-Since: Mon, 14 Mar 2016 18:08:11 GMT

Сервер в ответ на это может поступить так:

  • если он не поддерживает такое кеширование, то он проигнорирует заголовок If-Modified-Since и снова пришлет запрошенный файл
  • если файл на сервере был обновлен после указанной даты, сервер вышлет новую копию в ответе с кодом 200
  • если файл не обновился, то сервер отправит ответ без тела с кодом 304 Not Modified, обозначающим, что клиент может использовать копию из кеша

Кроме этих заголовков, дополнительные параметры кеширования могут задаваться в заголовке Cache-Control. При нажатии пользователем кнопки обновлении страницы некоторые браузеры могут игнорировать кеш и загружать все файлы заново.

Подробнее про кеширование:

Сжатие

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

  • если клиент поддерживает прием сжатых данных, он указывает знакомые ему форматы сжатия в заголовке Accept-Encoding, например: Accept-Encoding: compress, gzip
  • если сервер поддерживает один из указанных алгоритмов, то он может отправить в ответе содержимое файла в сжатом виде. При этом он добавляет заголовок Content-Encoding, указывающий, что применено сжатие и какой алгоритм был использован, например: Content-Encoding: gzip
  • клиент при получении ответа проверяет этот заголовок и, если сжатие применялось, распаковывает содержимое тела ответа
  • сжимается только тело ответа, не стартовая строка или заголовки

Сжатие хорошо работает на текстовых форматах данных: HTML, CSS, JS-файлах, позволяя экономить трафик и ускорить загрузку за счет небольшого увеличения нагрузки на процессор сервера. Однако для файлов, которые изначально сжаты (картинки, аудио, видеофайлы) сжатие не только не даст выгоды, но может даже увеличить размер файла.

Для примера, на момент написания статьи HTML-код главной страницы Википедии весил 125 Кб в несжатом виде. При использовании gzip размер данных уменьшился до 25Кб (в пять раз).

Частичная загрузка

Если при скачивании больших файлов по HTTP происходит разрыв соединения, то клиенту ничего не остается, как начать скачивать файл заново. Однако, если сервер и клиент поддерживают частичную передачу данных, то они могут продолжить скачивание с того места, где оно прервалось. Вот, как это работает:

  • если сервер поддерживает частиное скачивание, он добавляет в ответ заголовок вроде Accept-Ranges: bytes. Клиент может это запомнить, если хочет.
  • допустим, клиент скачал первые 100 000 байт файла, после чего соединение разорвалось
  • чтобы сообщить серверу, что у него есть начало файла и он хотел бы продолжить скачивание не с начала, он добавляет в GET-запрос заголовок Range: bytes=100001-, показывая, что хотел бы получить файл, начиная с 100 001 байта
  • если сервер не поддерживает частичное скачивание, он игнорирует заголовок и отдает ответ с кодом 200 и полным содержимым файла
  • если сервер поддерживает такую возможность, то он отдает ответ с кодом 206 Partial Content, добавляет заголовок Content-Range: bytes 100001-999999/999999 (указывая, что общая длина файла 999 999 байт) и отдает в теле только часть файла, начиная с 100 001 байта

Также, частичное скачивание можно использовать, чтобы запросить только начало файла, указав в запросе заголовок вроде Range: bytes=1-1000.

Подробнее: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests

Метод HEAD

Метод HEAD аналогичен методу GET, однако в ответ на него сервер должен отправить только стартовую строку и заголовки, но не передавать тело ответа. Его можно использовать, например, для проверки, есть ли такая страница на сервере, не скачивая ее. Или для получения какой-то информации о странице или сервере через заголовки ответа. Браузеры не используют этот метод, но им могут, например, пользоваться роботы поисковых систем для проверки - не обновилась ли ранее проиндексированная страница, без её скачивания.

Отправка форм методом GET

Обычно при заполнении пользователем формы ее данные отправляются методом POST. Однако, для некоторых форм, которые не меняют состояние сервера, не добавляют или изменяют информацию на нем, удобнее передавать запрос методом GET в URL. Обычно это используется в формах поиска, в постраничной навигации, в фильтрах для выбора отображаемых данных.

При таком подходе данные формы добавляются в URL после знака вопроса и кодируются с помощью URL-кодирования (процентного кодирования). После чего браузер запрашивает страницу методом GET с получившимся URL. Вот пример URL, который может получиться при отправке формы поиска:

http://example.com/search?text=hello%20world&searchIn=articles

Здесь указаны 2 параметра: text=Hello world (пробел был закодирован как %20) и searchIn=articles.

Также, это можно использовать на страницах со списками и фильтрами, вот как может выглядеть URL для поиска товаров в интернет-магазине:

http://shop.example.com/products?category=tv&brand=samsung&page=2

Преимущество этого способа в том, что параметры из формы указаны в URL и можно переслать такой URL кому-то или сохранить в закладки, его могут проиндексировать поисковые системы. Недостатки - длина URL обычно ограничена 1000-2000 байтами.

Версии HTTP, особенности HTTP/2.0

Полезные функции в PHP/JS

Полезные команды

Задачи

HTTP и PHP

$_GET, $_POST, $_COOKIE, $_SERVER, setcookie(), header() curl, библиотеки поверх

  • HTTP клиент
  • отправка запроса к API

Стандарты

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

  • RFC7230: Протокол HTTP: синтаксис и маршрутизация сообщений
  • RFC7231: Семантика и содержание
  • RFC7232: Запросы с указанием условия
  • RFC7233: Запросы с указанием диапазона
  • RFC7234: Кеширование
  • RFC7235: Аутентификация
You can’t perform that action at this time.