четверг, 11 августа 2011 г.

HTTP/1.0 Internals

Это просто пост-памятка, чтоб навести порядок в знаниях. Итак, что интересного есть внутри протокола HTTP/1.0 для разработчика веб-приложений? Как всё работает по стандарту и на практике?

Методы

HTTP/1.0 содержит следующие методы: GET (получение данных), POST (создание), PUT (создание или изменение), DELETE (удаление), HEAD, LINK и UNLINK.

LINK и UNLINK — задуманы для установления связи между ресурсами, не используются на практике и интереса не представляют.

HEAD отличается от GET только тем, что не содержит тело ответа и не поддерживает условный запрос (If-Modified-Since). На практике используется только чтобы удостовериться, что ресурс существует. GET и HEAD кэшируются.

Что интересно — ни от одного из методов не требуется идемпотентности, однако есть интересная оговорка, что только POST может создать ресурс (сервер в этом случае должен вернуть код состояния 201, а в тексте сообщения описать, что он создал). В описании метода также PUT впрочем, также сказано, что он тоже может создать ресурс, если данных достаточно.

PUT отличается от POST только смыслом, вкладываемым в имя ресурса: для POST это адрес какого-то обработчика данных, а для PUT – это именно идентификатор ресурса. POST не кэшируется. Про кеширование PUT и DELETE ничего не сказано, но логично предположить, что оно также невозможно.

Нужно отметить, что на практике далеко не всегда можно послать запросы PUT и DELETE. Поэтому их часто заменяют на POST, а дополнительную информацию об операцию производить передают косвенно. Например, для эмуляции DELETE можно просто оставить пустым тело запроса. А Google в своих протоколах использует поле X-HTTP-Method-Override для переопределения POST на PUT или DELETE.

Коды состояния

HTTP/1.0 описывает следующие коды состояния:

  • 200 (OK) — стандартный ответ, означает что операция успешно завершена, тело ответа содержит подробности;
  • 201 (Создан) — возвращается в ответ на POST и PUT, означает, что ресурс создан, а тело ответа содержит подробности, например, адрес созданного ресурса;
  • 202 (Принято) — означает, что запрос принят к рассмотрению, но еще не выполнен. Логично предположить, что возвращается в ответ на POST, PUT, DELETE. Тело ответа содержит подробности, например, где смотреть прогресс исполнения;
  • 204 (Нет содержимого) — тоже самое, что и 200, но для случая когда тела ответа нет. Агент пользователя, получив такой ответ не меняет текущий вид документа;
  • 300 (Множественный выбор) — не используется;
  • 301 (Постоянно перенесен), 302 (Временно перемещен) — запрос будет повторен с новым, присланным адресом. Новый адрес ресурса передается как поле Location в заголовке ответа. Коды можно возвращать в ответ на GET и не следует возвращать в ответ на POST, PUT, DELETE, т.к. в этом случае агент пользователя должен запросить подтверждения пользователя на повтор операции. Тело ответа должно содержать пояснения;
  • 304 (Не модифицирован) — можно вернуть как ответ на условный GET. Означает, что сообщение не изменилось и можно использовать ранее кэшированное. А вот поля ответа расцениваются как новая информация. Тело ответа должно отсутствовать;
  • 400 (Испорченный запрос), 502 (Ошибка шлюза) — низкоуровневые ошибки http-сервера;
  • 401 (Не санкционировано) — означает, что выполнение запроса требует аутентификации. Причем не любой, а именно стандартной, описанной в стандарте протокола. Поскольку такие случаи редки, это делает данный код неупотребимым;
  • 403 (Запрещено) — означает, что доступ к ресурсу или метод запрещены. Также может использоваться вместо 404 если нельзя сообщать пользователю информацию об отсутствии ресурса;
  • 404 (Не найден) — означает, что ресурс с таким адресом не найден;
  • 500 (Внутренняя ошибка сервера) — именно это и означает;
  • 501 (Не реализовано) — именно это и означает;
  • 503 (Сервис недоступен) — применяется при плановом отключении функционала сервера. Следует возвращать поле Retry-After для информирования агента, когда ему повторить запрос.

    поля запросов

    Pragma — используется только со значением no-cache. В этом случае использование кэшированных данных запрещается, соответственно прокси должен передать запрос далее, и клиент сможет получить гарантированно свежий ответ. Теоретически сервер тоже может посылать такое поле, но смысл такого действия в протоколе не определен и перекладывается на получателя.

    Authorization — применяется для при аутентификации пользователя, ничем не интересно.

    From — предназначен для отсылки почтового адреса пользователя, но только если пользователь разрешит. OMG. Сложно сказать, что  творилось в голове разработчиков стандарта, когда они это сочиняли.

    If-Modified-Since — очень интересное поле. Применяется только с GET и его наличие превращает запрос в “условный”. Алгоритм работы: если дата в поле валидна, ресурс с запрошенного времени не изменялся и код результата определенно будет 200, то вместо 200 следует вернуть 304. Таким образом экономим трафик или обновляем кэш.

    Referer — в этом поле агент может указать адрес, где агент узнал адрес запрашиваемого ресурса. Пересылается далеко не всегда, и часто режется прокси.

    User-Agent — в этом поле клиент указывает информацию о себе. Может и не соответствовать действительности.

    Accept — в этом поле агент указывает ответ какого MIME-типа он желал бы получить. Обработка этого поля — это забота веб-сервера, что конечному разработчику обычно не интересно. Достаточно знать, что сервер может отдавать ответ блоками, т.н. chunks.

    Accept-Charset — в этом поле агент указывает желаемую кодировку ответа. Поскольку практика давно показала, что кроме UTF-8 ничего применять нельзя, то особого интереса поле не представляет.

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

    Accept-Language — желаемый язык контента в ответе.

    поля ответов

    Date — дата запроса/ответа. Теоретически клиент тоже может послать такое поле, но смысл? А вот серверу всегда следует посылать такое поле, т.к. эта дата активно используется при кешировании и более того, если этого не делать, то клиент назначит эту дату самостоятельно.

    Location — указывается для ответов 300/301/302 и означает новый адрес запрашиваемого ресурса.

    Server — поле в котором сервер может указать информацию о себе. Интересно, но абсолютно бесполезно.

    WWW-Authenticate — применяется для при аутентификации пользователя, ничем не интересно.

    Allow — в этом заголовке сервер может перечислить методы, которые он поддерживает для указанного адреса. Почему-то нельзя возвращать это поле при POST-запросах. WTF. OMG. На практике, впрочем, всё равно не применяется.

    Content-Encoding — если ответ сжат, то в этом поле указан метод сжатия;

    Content-Length — это поле обязательно заполняется сервером если в ответе присутствует тело ответа. Если равно нулю, то считается, что тело ответа есть, но пустое.

    Content-Type — содержит MIME-тип ответа и кодировку. Если кодировка не указана, то считается что она ISO-8859-1.

    Expires — содержит дату, после которой ответ сервера считается протухшим. Точнее, если дата поля Expires меньше или равна дате поля Date, то клиент должен прекратить использование кэшированных данных. Ноль и невалидная дата также должны рассматриваться агентом как указание на незамедлительное протухание кэша. Действие поля не распространяется на использование кнопки “Назад” в браузере, так что обычно в этом случае возьмутся данные из кэша.

    Last-Modified — дата последнего изменения передаваемого документа. Не может быть больше значения поля Date. В стандарте сказано, что если у получателя есть копия сообщения более старого, чем указывает заголовок Last-Modified, то можно считать эту копию протухшей. Поскольку новое сообщение и так затрет старое в кэше, то очевидно, это имеет смысл только для ответов без тела сообщения: HEAD, 204, 304. Что в свою очередь, если подумать, практического смысла не несет. Такое вот загадочное поле.

    Content-Language — язык ответа.

    Retry-After — поле только для кода 503. Информирует агента об интервале повторения запроса. На практике игнорируется мозиллой.

    Title — заголовок/название ответа. Теоретически может пригодиться при передаче простых текстовых файлов. На практике похоже что браузеры этот заголовок вообще не понимают для любых типов возвращаемого контента.

    URI — может содержать несколько адресов, по которым может быть идентифицирован и может быть доступен запрошенный ресурс.

    ОБЩИЕ поля

    Link — для методов LINK и UNLINK содержит информацию о связываемом ресурсе.

    MIME-Version — в стандарте сказано, что его следует игнорировать. О как.

    Выводы

    Легко заметить, что стандарт местами весьма противоречив и исключительно бестолково написан. В следующей версии, HTTP/1.1, порядка немного добавили. С практической точки зрения, для разработчика веб-сервисов представляют интерес только методы GET, POST, PUT, DELETE и поля Pragma, Date, Expires, If-Modified-Since, Referer, User-Agent, Location, Content-Type, Retry-After. Не так уж и много. С некоторым удивлением осознал бесполезность поля Last-Modified.

  •