Протокол — это набор синтаксических и сематических правил, использующихся при обмене данных между двумя компьютерами, который определяет команды, их синтаксис и порядок отправки, а также очередность отправки команд. Хотя протоколов взаимодействия очень много, веб-разработчики в основном имеют дело с прикладными протоколами и в первую очередь с HTTP-протоколом, который служит для обмена информацией между веб-сервером и браузером (клиентом).
Примечание. Все стандарты в сети Иртернет оформляются в виде RFC-документов; так протокол HTTP описывается главным образом в RFC2616. Ссылки на него можно получить при помощи любой любой поисковой системы.
Работа протокола HTTP не сводится только к передаче сервером HTML-документа по запросу клиента. Помимо HTML-документа, файла или изображения браузер и сервер обмениваются HTTP-заголовками, через которые клиент может сообщить о своих предпочтениях, типе и версии браузера и операционной системы. Сервер, в свою очередь, может сообщить клиенту об ошибочном запросе, "попросить" его установить cookie и т.п. Язык разметки HTML и серверный язык РНР разрабатывались таким образом, чтобы разработчик мог работать без знания HTTP — это позволит отделить уровни протокола и веб-приложения друг от друга. Тем не менее, совершенно абстрагироваться от протокола HTTP не удается, так как его использование зачастую позволяет более гибко управлять работой веб-приложения. В связи с этим в РНР введено несколько инструментов управления протоколом HTTP, которые будут обсуждаться далее в этом разделе:
Помимо этого, будет рассмотрен смежный вопрос преобразования IP-адресов и доменных имен.
В ответ на запрос серверу, клиент получает HTTP-документ,который состоит из HTTP-заголовков и тела документа, содержащего HTML-страницу или изображение. HTTP-заголовки, как правило, формируются сервером автоматически и в большинстве случаев веб-разработчику нет надобности отправлятьих в ручную. Впрочем, последнее справедливо только для РНР, тогда как, например, при создании GGI-программы припомощи Perl или С разработчик вынужден самостоятельно реализовывать эту часть HTTP-протокола.
Примечание. HTTP-документ может не содержать тела документа, но всегда содержит HTTP-заголовки.
Браузер, получив HTTP-заголовки, автоматически выполняет предписание, даже не показывая посетителю их содержимое. Таким образом, HTTP-заголовки служат своеобразной метаинформацией, которой сервер и клиент обмениваются скрытно. Впрочем, иногда необходимо вмешиваться в эту скрытую часть работы клиента и сервера, поскольку она управляет многими важными процессами, такими как переадресация, кэширование, аутентификация и т.д. Кроме того скрипты сами могут выступать в роли клиентов, обращаясь к страницам других сайтов: в этом случае они вынуждены брать на себя всю работу по реализации и обработке HTTP-протокола.
Для управления HTTP-заголовками в РНР предназначены функции, представленные в таблице.
Функции для управления HTTP-заголовками
Функция | Описание |
header() | Отправляет HTTP-заголовок |
headers_list() | Возвращает список отправленных или готовых к отправке HTTP-заголовков |
headers_sent() | Проверяет отправлены ли HTTP-заголовки |
Функция header(), позволяющая отправить клиенту произвольный HTTP-заголовок, имеет следующий синтаксис:
header ($header [, $replace [, $http_response_code]])
Данная функция отправляет HTTP-заголовок $header. Второй параметр $replace определяет поведение интерпретатора РНР, если тот встречает два одинаковых заголовка: если параметр принимает значение true, отправляется последний заголовок, в противном случае — отправляется первый заголовок. Третий параметр $http_response_code позволяет задать код возврата HTTP.
Простейшей процедурой, которую можно осуществить при помощи функции header(), является переадресация, осуществляемая при помощи HTTP-заголовка Location.
Примечание. Вместо полного сетевого адреса (начинающегося с префикса http://) можно использовать относительные адреса: в этом случае браузер сам подставит адрес сайта.
Переадресация при помощи HTTP-заголовка Location
<?php
header("Location: http://sevidi.narod.ru");
?>
В окне веб-браузера это будет выглядеть ТАК.
Механизм HTTP-заголовков дублирован в языке разметки HTTP и добиться схожего поведения можно при помощи передачи HTTP-заголовка через META-тег.
Переадресация средствами HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Переадресация средствами HTML</title>
<META http-equiv="refresh" content="0; URL=http://sevidi.narod.ru">
</head>
<body>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
Если в качестве второго параметра передать функции header() значение true, то все предыдущие HTTP-заголовки с таким же именем будут заменяться последующими.
Переадресация на сайт http://sevidi.narod.ru
<?php
header("Location: http://www.softtime.ru", true);
header("Location: http://sevidi.narod.ru", true);
?>
В окне веб-браузера это будет выглядеть ТАК.
На каждый запрос клиента сервер может возвращать HTTP-код состояния, отражающий вид переадресации при окончательном или временном перемещении документа. Если документ найден и успешно отправлен клиенту, в HTTP-заголовки помещается код состояния 200; если документ не найден — 404; в случае переадресации, клиенту отправляется код состояния 302 — "ресурс временно перемещен". Иногда бывает полезно изменить код состояния на 301 — "ресурс перемещен постоянно".
Изменение состояния кода
<?php
header("Location: http://sevidi.narod.ru", true);
header ("HTTP/1.1 301 Moved Remanently");
?>
В окне веб-браузера это будет выглядеть ТАК.
Коды состояния разделяются на классы, каждый из которых начинается с новой сотни:
Не все коды состояния имеют смысл; часть из них зарезервирована для дальнейших расширений. В таблицах ниже, описываются коды состояния для протокола HTTP 1.1, используемого в настоящее время для распространения HTTP-страниц в сети Интернет.
Информационные HTTP-коды состояния
HTTP-код | Описание |
100 Continue |
Сервер готов получить оставшуюся часть запроса |
101 Switching Protocols |
Сервер готов переключить протокол приложения на протокол, указанный в заголовке запроса Upgrade, предоставленного клиентом. Переключение должно выполняться, только если указанный протокол имеет преимущество над старым, например, клиент может отправить запрос, чтобы сервер использовал вместо текущего более новый протокол HTTP |
HTTP-коды успешного выполнения запроса
HTTP-код | Описание |
200 OK |
Сервер успешно обработал запрос, и клиент может получить запрашиваемый документ в теле ответа |
201 Created Locatuion |
Сервер успешно создал новый URL, заданный в HTTP-заголовке Locatuion |
202 Accepted |
Запрос принят сервером для обработки, но она еще не завершена |
203 Non-Authoritative Information |
Метаинформация в заголовке запроса не связана с данным сервером и скопирована с другого сервера |
204 No Content |
Выполнение запроса завершено, но никакой информации отправлять обратно не требуется. Клиент может продолжать просматривать текущий документ |
205 Reset Content |
Клиент должен сбросить текущий документ. Этот HTTP-заголовок можно использовать для сброса и удаления всех значений полей ввода в HTML-форме |
206 Prtial Content |
Сервер выполнил неполный запрос ресурса методом GET. Этот HTTP-код используется для ответа на запросы, содержащие HTTP-заголовок Range. Сервер отправляет HTTP-заголовок Content-Range, чтобы указать, какой сегмент данных приложен |
300 Multiple Choices |
Запрашиваемый ресурс соответствует набору документов. Сервер может отправить информацию о каждом документе с его собственным местоположением и информацией по согласованию содержимого, оставляя выбор на усмотрение клиента |
301 Moved Permanently |
Запрашиваемый ресурс на сервере отсутствует. Для переадресации клиента на новый URL отправляется HTTP-заголовок Locatuion. Все последующие запросы должны отправляться на новый URL |
302 Moved Temporarily |
Запрашиваемый ресурс временно перемещен. Для переадресации клиента на новый URL отправляется HTTP-заголовок Locatuion. В последующих запросах клиент может продолжать использовать старый URL |
303 See Other |
Запрашиваемый ресурс найден в другом месте, его местоположение уточняется сервером при помощи HTTP-заголовка Locatuion |
304 Not Modified |
Сервер использует этот HTTP-код в соответствии с HTTP-заголовком if-Modified-Since. Это означает, что запрашиваеьый документ не модефицировался с даты, определенной в HTTP-заголовке if-Modified-Since |
305 Use Proxy |
Для получения запрашиваемого ресурса клиент должен использовать прокси-сервер, адрес которого определяется сервером в HTTP-заголовке Locatuion |
307 Temporary Redirect |
Запрашиваемый ресурс временно перемещен в другое место. Для переадресации клиента на новый URL отправляется HTTP-заголовке Locatuion |
HTTP-коды ошибочного запроса
HTTP-код | Описание |
400 Bad Request |
В запросе клиента обнаружена синтаксическая ошибка |
401 Unauthorized |
Запрос требует аутентификации клиента. Для уточнения типа аутентификации и области запрашиваемого ресурса сервер отправляет HTTP-заголовок WWW-Authentificate |
402 Payment Required |
Данный HTTP-код зарезервирован для будущего использования в электронной коммерции |
403 Forbidden |
Доступ к запрашиваемому ресурсу запрещен. Клиент не должен повторять запрос. |
404 Not Found |
Запрашиваемый документ отсутствует на сервере |
405 Method Not Allowed |
Метод запроса, используемый клиентом, неприемлем. Сервер отправляет HTTP-заголовок Allow, в котором уточняются допустимые методы для получения доступа к запрашиваемому ресурсу |
406 Not Acceptable |
Запрашиваемый ресурс недоступен в том формате, который может принимать клиент (клиент обычно уточняет эти форматы в специальном HTTP-заголовке Accept). Если запрос не является запросом HEAD на получение лишь HTTP-заголовков с сервера без тела документа, то сервер может отправить заголовки Content-Language, Content-Encoding, Content-Type, чтобы определить, какие форматы являются доступными |
407 Proxy Authentification Required |
Несанкционированный запрос доступа к прокси-серверу: клиент должен аутентифицировать себя на прокси-сервере. Сервер отправляет HTTP-заголовок Proxy Authentificatе со схемой аутентификации и областью запрашиваемого ресурса |
408 Request Time-Out |
Клиент не завершил свой запрос за время ожидания запроса, заданное серверу,однако можетповторить запрос |
409 Conflict |
Возник конфликт запроса клиента с другим запросом. Вместе скодом состояния сервер может переслать информацию о типе конфликта |
410 Gone |
Запрашиваемый ресурс удаленен с сервера |
411 Length Required |
Клиент должен прислать в запросе HTTP-заголовок Content-Length |
412 Precondition Filed |
Если запрос клиента содежит один или более HTTP-заголовков if ..., сервер использует этот HTTP-код для оповещения, что одно или более условий, заданных в этих заголовках, не выполняются |
413 Request Entity Too Large |
Сервер отказывается выполнять запрос: слишком длинное тело сообщений |
414 Request-URI Too Long |
Сервер отказывается выполнять запрос: слишком длинный URI(URL) |
415 Unsupported Media Type |
Сервер отказывается выполнять запрос: отсутствует поддержка формата тела сообщения |
417 Expectation Failed |
Сервер не смог выполнить требования HTTP-заголовка Expect Request |
HTTP-коды ошибочного запроса
HTTP-код | Описание |
500 Internal Server Error |
Ошибка конфигурации сервера или внешней программы |
501 Not Implemented |
Сервер не поддерживает функции, требуемые для выполнения запроса |
502 Bad Gateway |
Неверный ответ вышестоящего сервера или прокси сервера |
503 Servise Unavailable |
Служба временно недоступна. С целью оповещения о времени открытия доступа к службе сервер может отправить заголовок Retry-After |
504 Gateway Time-Out |
Шлюз или прокси-сервер временно заблокирован |
505 HTTP Version Not Supported |
Не поддерживается используемая клиентом версия протокола HTTP |
При работе HTTP-заголовками следует помнить, что они всегда предваряют содержимое страницы. Вывод любой информации (при помощи конструкции echo, функции print() или непосредственно вне тегов <?php и ?>) в окно браузера приводит к тому, что начинается отправка HTTP-документа. Согласно протоколу, HTTP-заголовки отправляются перед телом документа, и последующие попытки отправить дополнительные заголовки заканчиваются неудачей.
Перевод строки перед функцией header() блокирует ее работу
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<?php
header("Location: http://sevidi.narod.ru");
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Перевод строки перед функцией header() блокирует ее работу</title>
</head>
<body>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
В результате переадресация не осуществляется, а функция header() выводит предупреждение Warning: Cannot modify header information - headers already sent by (output started at D:\sevidi\373.php:2) in D:\sevidi\373.php on line 3, сообщающее, что отправка HTTP-заголовка невозможна, поскольку уже начата передача тела HTTP-документа. Иногда пробелы и переводы строк могут быть расположены внутри файлов, включаемых при помощи конструкций include() и require().
В ряде случаев нельзя быть заранее уверенным, отправлены HTTP-заголовки или все еще имеется возможность использовать функцию header(). Чтобы выяснить это, можно воспользоваться функцией headers_sent(), которая имеет следующий синтаксис:
dool headers_sent(&$file [, &$line])
Функция возвращает false, если HTTP-заголовки не были отправлены клиенту, и true — в противном случае. После отправки HTTP-заголовков функция при помощи необязательных аргументов $file и $line может сообщить, в каком файле и строке была начата передача тела HTTP-документа.
Использование функции headers_sent()
<?php
echo "Hello<br>";
if(!headers_sent($filename, $linenum))
{
header("Location: http://sevidi.narod.ru");
exit;
}
else
{
echo "Передача HTTP-документа начата в файле $filename в строке $linenum. Поэтому перенаправление на другой ресурс невозможно.";
exit;
}
?>
В окне веб-браузера это будет выглядеть ТАК.
Иногда бывает полезно проконтролировать, какие HTTP-заголовки были отправлены клиенту. Для этого удобно воспользоваться функцией headers_list(), которая имеет следующий синтаксис:
array headers_list()
Функция возвращает массив, содержащий отправленные клиенту HTTP-заголовки. Рассмотрим пример работы функции headers_list(); получаемый в результате ее работы массив для удобства восприятия выводится при помощи функции print_r() в тегах <pre> и </pre>, сохраняющих отступы и переводы строк.
Примечание. Клиенту отправляется произвольный HTTP-заголовок X-my-header со значение "Hello world". Нестандартные HTTP-заголовки, как правило, всегда предваряются префиксом Х-.
Использование функции headers_list()
<?php
header("X-my-header: Hello world!");
$arr=headers_list();
echo "<pre>";
print_r($arr);
echo "</pre>";
?>
В окне веб-браузера это будет выглядеть ТАК.
Скрипт выведет следующий дамп массива HTTP-заголовков:
Array ( [0] => X-Powered-By: PHP/5.2.8 [1] => X-my-header: Hello world! )
Это не полный список HTTP-заголовков, которые получает клиент: это лишь те заголовки, которые отправляет РНР-скрипт. Помимо них клиент получает HTTP-заголовки, отправленные веб-сервером. Получить полный список HTTP-заголовков к странице можно при помощи сокетов.