Гипертекстный протокол HTTP
Базовая схема идентификации (Authentication)
Базовая схема авторизации основана на модели, в которой агент пользователя должен идентифицировать себя с помощью имени пользователя и пароля, необходимого для каждой области ( realm ). Значение атрибута realm должно рассматриваться в виде неотображаемой строки символов, которая может сравниваться с другим значение realm на этом сервере.
Сервер обслужит запрос только в случае, если убедится в корректности имени и пароля пользователя для данной зоны защиты Request-URI. Не существует каких-либо опционных параметров авторизации.
При получении неавторизованного запроса для URI в пределах зоны защиты (protection space) сервер может реагировать посылкой требования в виде:
WWW-Authenticate: Basic realm="WallyWorld"
где WallyWorld — строка, присвоенная сервером для идентификации зоны защиты Request-URI.
Чтобы получить авторизацию, клиент посылает свое имя и пароль, разделенные символом двоеточие (:) и закодированные согласно base64.
basiccredentials = "Basic" SP basiccookie basiccookie = userpass = userid : password userid = * password = *TEXT
Идентификационная информация может быть чувствительной к применению строчных или прописных букв.
Если агент пользователя хочет послать имя пользователя "Aladdin" и пароль "open sesame", он использует следующее поле заголовка:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Краткое изложение схемы авторизации
Краткое изложение идей авторизации для HTTP представлено в документе RFC-2069 [7.32].
Согласование содержимого
Большинство HTTP откликов включают в себя объекты, которые содержат информацию для интерпретации человекомпользователем. Естественно, желательно обеспечить пользователя наилучшим объектом, соответствующим запросу. К сожалению, для серверов и кэшей не все пользователи имеют одни и те же предпочтения и не все агенты пользователя в равной степени способны обрабатывать все типы объектов. По этой причине HTTP имеет несколько механизмов обеспечения согласования содержимого — процесс выбора наилучшего представления для данного отклика, когда доступны несколько возможностей.
Замечание. Это не называется согласованием формата, потому что альтернативные представления могут принадлежать к одному и тому же типу среды, но использовать различные возможности этого типа, например, различные иностранные языки.
Любой отклик, содержащий тело объекта, может быть предметом согласования, включая отклики об ошибках. Существует два вида согласования содержимого, которые возможны в HTTP: под управлением сервера и под управлением агента пользователя. Эти два сорта согласования могут использоваться отдельно или в сочетании. Один из методов, называемый прозрачным согласованием, реализуется, когда кэш использует информацию, полученную от исходного сервера в результате согласования под управлением агента пользователя. Эта информация применяется для последующих запросов, где осуществляется согласование под управлением сервера.
О втором методе мы поговорим более подробно.
Согласование, управляемое сервером
Если выбор наилучшего представления для отклика выполнен с использованием некоторого алгоритма на сервере, такая процедура называется согласованием под управлением сервера. Выбор базируется на доступных представлениях отклика (размеры, в пределах которых он может варьироваться, язык, кодировка и т.д.) и на содержимом конкретных полей заголовка в сообщениизапросе или другой информации, имеющей отношение к запросу (например, сетевой адрес клиента).
Согласование, управляемое сервером, предпочтительнее, когда алгоритм выбора из числа доступных представлений трудно описать агенту пользователя. Согласование под управлением сервера привлекательно тогда, когда сервер хочет послать свои наилучшие предложения клиенту вместе с первым откликом (надеясь избежать задержек RTT последующих запросов — вдруг именно это предложение удовлетворит пользователя). Чтобы улучшить предложения сервера, агент пользователя может включить в запрос поля заголовка ( Accept, AcceptLanguage, AcceptEncoding и т.д.), которые описывают его предпочтения для данного запроса. Согласование под управлением сервера имеет следующие недостатки.
- Серверу трудно определить, что является лучшим для любого заданного пользователя, так как это потребовало бы полного знания как возможностей агента пользователя, так конкретного назначения отклика (например, хочет ли пользователь видеть отклик на экране или отпечатать на принтере).
- Заставлять агента пользователя описывать свои возможности при каждом запросе крайне неэффективно (ведь лишь небольшой процент запросов имеют несколько вариантов представления) и потенциально нарушает конфиденциальность пользователя.
- Такое согласование усложняет реализацию исходного сервера и алгоритм генерации откликов на запросы.
- Оно может ограничить возможность общедоступного кэша использовать один и тот же отклик для запросов многих пользователей.
HTTP/1.1 включает в себя следующие поля заголовка запроса для активации согласования, управляемого сервером, через описание возможностей агента пользователя и предпочтений самого пользователя: Accept, AcceptCharset, AcceptEncoding, AcceptLanguage и User-Agent. Однако исходный сервер не ограничен этими рамками и может варьировать отклик, основываясь на свойствах запроса, включая информацию помимо полей заголовка запроса или используя расширения полей заголовка нерассмотренные в данной спецификации.
Протокол HTTP/1.1 предусматривает следующие поля заголовка запроса, активизирующие согласование, управляемое сервером, и описывающие возможности агента пользоватеря и предпочтения клиента: Accept, AcceptCharset, AcceptEncoding, AcceptLanguage и User-Agent. Поле Vary может использоваться для описания пределов, в которых может варьироваться отклик (то есть, пределы, в которых исходный сервер выбирает свои наилучшие предложения отклика из многообразия представлений).
Согласование, управляемое агентом (Agentdriven Negotiation)
При согласовании, управляемом агентом, выбор наилучшего представления для отклика выполняется агентом пользователя после получения стартового отклика от исходного сервера. Выбор базируется на списке имеющихся представлений отклика, включенном в поля заголовка (эта спецификация резервирует имя поля Alternates ) или в тело объекта исходного отклика, при этом каждое представление идентифицируется своим собственным URI. Выбор из числа представлений может быть выполнен автоматически (если агент пользователя способен на это) или вручную пользователем, который будет выбирать вариант из гипертекстного меню.
Согласование, управляемое агентом, имеет преимущество тогда, когда отклик варьируется в определенных пределах (таких как тип, язык или кодирование), когда исходный сервер не способен определить возможности агента пользователя, проанализировав запрос, и вообще когда общедоступные кэши используются для распределения нагрузки серверов и для снижения сетевого трафика.
Согласование под управлением агента имеет и недостаток: требуется второй запрос для получения наилучшего альтернативного представления. Этот второй запрос эффективен только тогда, когда используется кэширование. Кроме того, эта спецификация не определяет какого-либо механизма поддержки автоматического выбора, хотя она и не препятствует применению любого такого механизма в рамках расширения HTTP/1.1.
HTTP/1.1 определяет статусные коды 300 (Multiple Choices — множественный выбор) и 406 (Not Acceptable — Не приемлем) для активации согласования под управлением агента, когда сервер не хочет или не может обеспечить свой механизм согласования.
Прозрачное согласование (Transparent Negotiation)
Прозрачное согласование представляет собой комбинацию управления сервера и агента. Когда кэш получает форму списка возможных представлений откликов и кэшу полностью поняты пределы вариации, тогда кэш становится способным выполнить согласование под управлением сервера для исходного сервера при последующих запросах этого ресурса.
Прозрачное согласование имеет преимущество распределения работы согласования, которое в противном случае было бы выполнено исходным сервером. При этом отсутствует также задержка, сопряженная со вторым запросом при схеме согласования под управлением агента, когда кэш способен правильно прогнозировать отклик.
Эта спецификация не определяет какого-либо механизма для прозрачного согласования, хотя она и не препятствует использованию таких механизмов в будущих версиях HTTP/1.1. Выполнение прозрачного согласования кэшем HTTP/1.1 должно включать в отклик поле заголовка Vary (определяя пределы его вариаций), обеспечивая корректную работу со всеми клиентами HTTP/1.1.
Кэширование в HTTP
HTTP обычно используется для распределенных информационных систем, где эксплуатационные характеристики могут быть улучшены за счет применения кэширования откликов. Протокол HTTP/1.1 включает в себя много элементов, предназначенных для оптимизации такого кэширования. Поскольку эти элементы завязаны с другими аспектами протокола и друг с другом, полезно описать базовую схему кэширования в HTTP.
Кэширование представляется бесполезным, если оно значительно не улучшит работу. Целью кэширования в HTTP/1.1 является исключение во многих случаях необходимости посылать запросы, а в некоторых других случаях — полные отклики. При этом уменьшается число необходимых RTT для многих операций. Для этих целей используется механизм истечения срока (expiration). Одновременно снижаются требования к полосе пропускания сети, для чего применяется механизм проверки пригодности.
Требования к рабочим характеристикам, доступности и работе без соединения заставляют нас несколько снизить семантическую прозрачность. Протокол HTTP/1.1 позволяет исходным серверам, кэшам и клиентам снизить прозрачность, когда это необходимо. Так как непрозрачность операций может поставить в тупик недостаточно опытных пользователей, а также привести к определенной несовместимости для некоторых серверных приложений (таких, как торговля по заказу), протокол рекомендует не убирать прозрачность полностью, а лишь несколько ослабить. Следовательно, протокол HTTP/1.1 обеспечивает следующие важные моменты:
- протокольные особенности, которые гарантируют полную семантическую прозрачность, когда этого требуют все участники процесса;
- протокольные особенности, которые позволяют исходному серверу или агенту пользователя запросить и управлять непрозрачными операциями;
- протокольные особенности, позволяющие кэшу присоединить предупреждения к откликам, которые не сохраняют запрошенный уровень семантической прозрачности.
Базовым принципом является возможность для клиентов детектировать любое ослабление семантической прозрачности.
Разработчики серверов, кэшей или клиентов могут столкнуться с решениями, которые не обсуждались в данной спецификации. Если решение может повлиять на семантическую прозрачность, разработчик может ошибаться относительно прозрачной работы, не проведя детального анализа и не убедившись, что нарушение такой прозрачности дает существенные преимущества.
Корректность кэша
Корректный кэш должен реагировать на запрос откликом новейшей версии, которой он владеет. Разумеется, отклик должен соответствовать запросу и отвечать одному из следующих условий:
- он был проверен на эквивалентность с тем, который был прислан исходным сервером при соответствующем запросе;
- он должен быть сравнительно новым. В варианте по умолчанию это означает, что он отвечает минимальным требованиям клиента, сервера и кэша по новизне. Если исходный сервер задает такие требования, то это только его требования на новизну;
- он включает в себя предупреждение о нарушении требований новизны клиента или исходного сервера;
- это сообщениеотклик 304 (Not Modified), 305 (Proxy Redirect) или ошибка (4xx или 5xx).
Когда кэш не может осуществлять обмен с исходным сервером, он должен реагировать в соответствии с вышеприведенными правилами, если отклик может быть корректно обслужен; в противном случае он должен отослать сигнал ошибки или предупреждение, указывающее, что произошел отказ в системе коммуникаций.
Если кэш получает отклик (полный отклик или код 304 (Not Modified)), который уже не является свежим, кэш должен переадресовать его запросившему клиенту без добавления нового предупреждения и, не удаляя существующего заголовка Warning. Кэшу не следует пытаться перепроверить отклик, так как это может привести к бесконечному зацикливанию. Агент пользователя, который получает устаревший отклик без Warning, может отобразить предупреждение для пользователя.
Предупреждения
Когда кэш присылает опосредованный отклик, который "недостаточно свеж", к нему должно быть присоединено предупреждение об этом с использованием заголовка Warning. Это предупреждение позволяет клиентам предпринять соответствующие действия.
Предупреждения могут использоваться для других целей как кэшами, так и прочими системами. Использование предупреждения, а не статусного кода ошибки, отличает эти отклики от действительных отказов в системе.
Предупреждения всегда допускают кэширование, так как они никогда не ослабляют прозрачности отклика. То есть, предупреждения могут передаваться HTTP/1.0 кэшам без опасения, что такие кэши просто передадут их как заголовки объектов отклика.
Предупреждениям приписаны номера в интервале от 0 до 99. Данная спецификация определяет номера кодов и их значения для каждого из предупреждений, позволяя клиенту или кэшу обеспечить во многих случаях (но не во всех) автоматическую обработку ситуаций.
Предупреждения несут в себе, помимо кода, и текст. Текст может быть написан на любом из естественных языков (предположительно базирующемся на заголовках Accept клиента) и включать в себя опционное указание того, какой набор символов используется.
К отклику может быть присоединено несколько предупреждений (исходным сервером или кэшем), включая несколько предупреждений с идентичным кодом. Например, сервер может выдать одно и то же предупреждение на английском и мордовском.
Когда к отклику присоединено несколько предупреждений, не будет практичным и разумным отображать их все на экране для пользователя. Эта версия HTTP не специфицирует строгих правил приоритета для принятия решения, какие предупреждения отображать и в каком порядке, но предлагает некоторые эвристические соображения.
Механизмы управления кэшем
Базовым механизмом кэша в HTTP/1.1 являются неявные директивы кэша. В некоторых случаях серверу или клиенту может потребоваться выдать прямую директиву кэшу. Для этих целей используется заголовок Cache-Control.
Заголовок Cache-Control позволяет клиенту или серверу передать большое число директив через запросы или отклики. Эти директивы переписывают указания, которые действуют по умолчанию при реализации алгоритма работы кэша. Если возникает явный конфликт между значениями заголовков, то используется наиболее регламентирующее требование (то есть, то, которое наиболее вероятно сохраняет прозрачность семантики).
Однако в некоторых случаях директивы Cache-Control сформулированы так, что явно ослабляют семантическую прозрачность (например, maxstale или public ).
Прямые предупреждения агента пользователя
Многие агенты пользователя позволяют переписывать базовый механизм кэширования. Например, агент пользователя может специфицировать, какие кэшированные объекты (даже явно старые) не проверять на новизну. Или агент пользователя может всегда добавлять "Cache-Control: maxstale=3600" к каждому запросу.
Если пользователь переписал базовый механизм кэширования, агент пользователя должен явно указывать всякий раз, когда это важно, что отображаемая информация не отвечает требованиям прозрачности (в частности, если отображаемый объект известен как устаревший). Так как протокол обычно разрешает агенту пользователя определять, является ли отклик устаревшим, эта индикация нужна только тогда, когда такое действительно случится. Для индикации не нужно диалоговое окно; это может быть иконка (например, изображение гнилой рыбы) или какойто иной визуальный индикатор.
Если пользователь переписал механизм кэширования таким образом, что эффективность кэша была непомерно понижена, агент пользователя должен непрерывно отображать индикацию (например, изображение горящих денег), чтобы пользователь, беспечно расходующий ресурсы, страдал от заметной задержки откликов на его действия.
Исключения для правил и предупреждений
В некоторых случаях оператор кэша может выбрать такую конфигурацию, которая возвращает устаревшие отклики, даже если клиенты этого не запрашивали. Это решение не должно приниматься легко, но может быть необходимо по причинам доступности или эффективности, особенно когда кэш имеет плохую связь с исходным сервером. Всякий раз, когда кэш возвращает устаревший отклик, он должен пометить его соответствующим образом (используя заголовок Warning ). Это позволяет клиентскому программному обеспечению предупредить пользователя о возможных проблемах.
Такой подход позволяет также агенту пользователя предпринять шаги для получения свежего отклика или информации из первых рук. Поэтому кэшу не следует присылать устаревшие отклики, когда клиент запрашивает информацию из первых рук, если только невозможно это сделать по техническим или политическим причинам.
Работа под управлением клиента
Когда основным источником устаревшей информации является исходный сервер (и в меньшей мере промежуточные кэши), клиенту может быть нужно контролировать решение кэша о том, следует ли присылать кэшированный отклик без его проверки. Клиенты выполняют это, используя несколько директив заголовка Cache-Control.
Запрос клиента может специфицировать максимальный возраст, который он считает приемлемым для неверифицированного отклика. Клиент может также специфицировать минимальное время, в течение которого отклик еще считается пригодным для использования. Обе эти опции увеличивают ограничения, накладываемые на работу кэша.
Клиент может также специфицировать, восприятие устаревшие отклики с возрастом не более заданного. Это ослабляет ограничения, налагаемые на работу кэшей, и, таким образом, может привести к нарушению семантической прозрачности, заданной исходным сервером, хотя это может быть необходимо для поддержания автономной работы кэша в условиях плохой коннективности.
Модель истечения срока годности. Определение срока годности под управлением сервера
Кэширование в HTTP работает наилучшим образом, когда кэши могут полностью исключить запросы к исходному серверу. Другими словами, кэш должен возвращать свежий отклик без обращения к серверу.
Предполагается, что серверы припишут в явном виде значения времени пригодности (expiration time) откликам в предположении, что объекты вряд ли изменятся семантически значимым образом до истечения этого времени. Это сохраняет семантическую прозрачность при условии, что время жизни выбрано корректно.
Механизм времени пригодности (expiration) применим только к откликам, взятым из кэша, а не к откликам, полученным из первых рук и переадресованных запрашивающему клиенту.
Если исходный сервер хочет усилить семантическую прозрачность кэша, тогда он может установить время истечения действия в прошлое, чтобы проверялся каждый запрос. Это означает, что всякий запрос изначально будет считаться устаревшим и кэш будет вынужден проверить его, прежде чем использовать для последующих запросов.
Если исходный сервер хочет заставить любой HTTP/1.1 кэш, вне зависимости от его конфигурации, проверять каждый запрос, он может применить директиву Cache-Control mustrevalidate.
Серверы определяют реальные времена сроков пригодности с помощью заголовка Expires или директивы максимального возраста заголовка Cache-Control.
Время пригодности (expiration time) не может использоваться для того, чтобы заставить агента пользователя обновить картинку на дисплее или перезагрузить ресурс; его семантика применима только для механизма кэширования, а такой механизм нуждается только в контроле истечения времени жизни ресурса, когда инициируется новый запрос доступа к этому ресурсу.
Эвристический контроль пригодности
Так как исходные серверы не всегда предоставляют значение времени пригодности в явном виде, HTTP кэши присваивают им значения эвристически, используя алгоритмы, которые привлекают для оценки вероятного значения времени пригодности другие заголовки (такие как Last-Modified time). Спецификация HTTP/1.1 не предлагает каких-либо специальных алгоритмов, но налагает предельные ограничения на полученный результат. Так как эвристические значения времени жизни могут подвергнуть риску семантическую прозрачность, они должны применяться с осторожностью. Предпочтительнее, чтобы исходные серверы задавали время пригодности явно.
Вычисление возраста
Чтобы определить, является ли запись в кэш свежей, нужно знать, превышает ли ее возраст предельное время жизни.
В этом обсуждении используется термин "сейчас" для обозначения текущего показания часов на ЭВМ, выполняющей вычисление. ЭВМ, которая применяет HTTP, и в особенности ЭВМ, работающие в качестве исходных серверов и кэшей, должны задействовать NTP [7.28] или некоторый схожий протокол для синхронизации их часов с использованием общего точного временного стандарта.
Следует обратить внимание на то, что HTTP/1.1 требует от исходного сервера посылки в каждом отклике заголовка Date, сообщая время, когда был сгенерирован отклик. Мы пользуемся термином date_value для обозначения значения заголовка Date, в форме, приемлемой для арифметических операций.
HTTP/1.1 использует заголовок отклика Age для того, чтобы облегчить передачу информации о возрасте объектов между кэшами. Значение заголовка Age равно оценке отправителем времени, прошедшего с момента генерации отклика исходным сервером. В случае кэшированного отклика, который был перепроверен исходным сервером, значение Age базируется на времени перепроверки, а не на времени жизни оригинального отклика.
По существу значение Age равно сумме времени, в течение которого отклик был резидентен в каждом из кэшей вдоль пути от исходного сервера, плюс время распространения данных по сети.
Мы пользуемся термином age_value для значения поля заголовка Age, в удобном для выполнения арифметических операций формате. Возраст отклика может быть вычислен двумя совершенно независимыми способами.
- Текущее время минус date_value, если местные часы синхронизованы с часами исходного сервера. Если результат отрицателен, он заменяется на нуль.
- age_value, если все кэши вдоль пути отклика поддерживают HTTP/1.1.
Таким образом, мы имеем два независимых способа вычисления возраста отклика при его получении; допускается их комбинирование, например:
corrected_received_age = max(now date_value, age_value)
и, поскольку мы имеем синхронизованные часы или все узлы вдоль пути поддерживают HTTP/1.1, получается достаточно надежный результат.
Заметьте, что поправка применяется в каждом кэше HTTP/1.1 вдоль пути так, что если встретится на пути кэш HTTP/1.0, правильное значение возраста будет получено, потому что его часы почти синхронизованы. Нам не нужна сквозная синхронизация (хотя ее неплохо бы иметь).
Изза задержек, вносимых сетью, значительное время может пройти с момента генерации отклика сервером и получением его следующим кэшем или клиентом. Если не внести поправки на эту задержку, можно получить неправдоподобные значения возраста отклика.
Так как запрос, который определяет возвращаемое значение Age, должен быть инициирован до генерации этого значения Age, мы можем сделать поправку на задержки, вносимые сетью, путем записи времени, когда послан запрос. Затем, когда получено значение Age, оно должно интерпретироваться относительно времени посылки запроса, а не времени, когда был получен отклик. Этот алгоритм выдает результат, который не зависит от величины сетевой задержки. Таким образом, вычисляется:
corrected_initial_age = corrected_received_age + (now request_time)
где request_time равно времени (согласно местным часам), когда был послан запрос, вызвавший данный отклик.
Резюме алгоритма вычисления возраста при получении отклика кэшем:
/* * age_value * равно значению Age: заголовок, полученный кэшем с этим откликом. • date_value * равно значению Date исходного сервера: заголовок * request_time * равно местному времени, когда кэш сделал запрос, который явился причиной этого кэшированного отклика * response_time * равно местному времени, когда кэш получил отклик * now * равно текущему (местному) времени */ apparent_age = max(0, response_time date_value); corrected_received_age = max(apparent_age, age_value); response_delay = response_time request_time; corrected_initial_age = corrected_received_age + response_delay; resident_time = now response_time; current_age = corrected_initial_age + resident_time;
Когда кэш посылает отклик, он должен добавить к corrected_initial_age время, в течение которого отклик оставался резидентно локальным. Он должен ретранслировать этот полный возраст, используя заголовок Age, следующему кэшуполучателю.
Клиент не может надежно сказать, что отклик получен из первых рук, но присутствие заголовка Age определенно указывает на то, что это не так. Кроме того, если значение в отклике соответствует более раннему времени, чем местное время запроса клиента, отклик, вероятно, получен не из первых рук (в отсутствие серьезного сбоя часов).