Apache. Документация на русском


Разделы:   1    2    3    4    5    6    7    8    9      10      11    12    13    14    15    16  

Раздел 10. Модули Апача

Пункты:   85    86    88    89    90    91    92    93    94    95    96    97    98    99    100    101    102    103    104    105    106    107    108    109    110    111    112    113    114    115    116    117    118    119    120    121    122    123    124    125    126    127    128    129    130    131    132    133    134    135    136    137    138    139    140    141    142    143    144    145    146    147    148    149    150    151    152    153    154    155    156    157    158    159    160    161    163    164    165    166    167    168      170      171    172    173    174    175    176    177    178    179    180    181    182    183    184    185    186    187    188    189    190    191    192    193    194    195    196    197    198    199    200    201    203    204    205    206    207    208    209    210    211    212    213  

 <         > 
  RU            EN  

Пункт 170. Модуль Apache mod_proxy_ajp

Этот модуль требует обслуживания mod_proxy . Он обеспечивает поддержку Apache JServ Protocol version 1.3 (далее AJP13 ).

Таким образом, для того, чтобы получить возможность обработки AJP13 протокола, mod_proxy он mod_proxy_ajp должен присутствовать и на сервере.

Предупреждение

Не включайте прокси, пока не защитите свой сервер. Открытые прокси-серверы опасны как для вашей сети, так и для Интернета в целом.

Применение

Этот модуль используется для реверсирования прокси на внутренний сервер приложений (например, Apache Tomcat) с использованием протокола AJP13. Использование похоже на обратный прокси-сервер HTTP, но использует ajp:// префикс:

Простой обратный прокси

 ProxyPass "/app" "ajp://backend.example.com:8009/app" 

Также могут использоваться балансиры:

Обратный прокси балансировщика

 <Прокси "балансировщик://кластер">
 BalancerMember "ajp://app1.example.com:8009" loadfactor=1
 BalancerMember "ajp://app2.example.com:8009" loadfactor=2
 ProxySet lbmethod=bytraffic
</прокси>
ProxyPass "/app" "balancer://cluster/app" 

Обратите внимание, что обычно никакая ProxyPassReverse директива не требуется. Запрос AJP включает в себя исходный заголовок хоста, переданный прокси-серверу, и можно ожидать, что сервер приложений будет генерировать заголовки, ссылающиеся на себя, относительно этого хоста, поэтому нет необходимости в перезаписи.

Основное исключение — это когда путь URL-адреса на прокси-сервере отличается от пути на серверной части. В этом случае заголовок перенаправления может быть переписан относительно исходного URL-адреса хоста (а не ajp:// URL-адреса серверной части), например:

Перезапись прокси-пути

 ProxyPass "/apps/foo" "ajp://backend.example.com:8009/foo"
ProxyPassReverse "/apps/foo" "http://www.example.com/foo" 

Однако обычно лучше развернуть приложение на внутреннем сервере по тому же пути, что и прокси, чем использовать этот подход.

Переменные среды

Переменные среды, имена которых имеют префикс, AJP_ пересылаются на исходный сервер как атрибуты запроса AJP (префикс AJP_ удаляется из имени ключа).

Обзор протокола

Протокол AJP13 ориентирован на пакеты. Двоичный формат, по-видимому, был выбран вместо более читаемого обычного текста из соображений производительности. Веб-сервер взаимодействует с контейнером сервлетов через соединения TCP. Чтобы сократить дорогостоящий процесс создания сокета, веб-сервер будет пытаться поддерживать постоянные TCP-соединения с контейнером сервлетов и повторно использовать соединение для нескольких циклов запроса/ответа.

Как только соединение назначается конкретному запросу, оно не будет использоваться ни для каких других до тех пор, пока не завершится цикл обработки запроса. Другими словами, запросы не мультиплексируются по соединениям. Это значительно упрощает код на обоих концах соединения, хотя и приводит к одновременному открытию большего количества соединений.

После того как веб-сервер открыл соединение с контейнером сервлетов, соединение может находиться в одном из следующих состояний:

  • Idle
    По этому соединению не обрабатывается ни один запрос.
  • Назначено
    Соединение обрабатывает определенный запрос.

Как только соединение назначается для обработки определенного запроса, основная информация о запросе (например, заголовки HTTP и т. д.) отправляется по соединению в очень сжатой форме (например, общие строки кодируются как целые числа). Подробная информация об этом формате приведена ниже в разделе «Структура пакета запроса». Если в запросе есть тело (content-length > 0) , оно отправляется отдельным пакетом сразу после него.

В этот момент контейнер сервлетов предположительно готов начать обработку запроса. При этом он может отправлять следующие сообщения обратно на веб-сервер:

  • SEND_HEADERS
    Отправить набор заголовков обратно в браузер.
  • SEND_BODY_CHUNK
    Отправить часть данных тела обратно в браузер.
  • GET_BODY_CHUNK
    Получить дополнительные данные из запроса, если они еще не все переданы. Это необходимо, потому что пакеты имеют фиксированный максимальный размер и в тело запроса могут быть включены произвольные объемы данных (например, для загруженных файлов). (Примечание: это не связано с передачей по частям HTTP).
  • END_RESPONSE
    Завершить цикл обработки запроса.

Каждое сообщение сопровождается пакетом данных различного формата. Подробности см. в разделе Структуры ответных пакетов ниже.

Базовая структура пакета

Этот протокол немного унаследован от XDR, но он во многом отличается (например, нет выравнивания по 4 байтам).

AJP13 использует сетевой порядок байтов для всех типов данных.

В протоколе есть четыре типа данных: байты, логические значения, целые числа и строки.

Байт
Один байт.
логический
Один байт, 1 = true , 0 = false . Использование других ненулевых значений как истинных (т. е. в стиле C) может работать в одних местах, но не в других.
Целое число
Число в диапазоне 0 to 2^16 (32768) . Хранится в 2 байтах со старшим байтом первым.
Нить
Строка переменного размера (длина ограничена 2^16). Закодировано с длиной, упакованной сначала в два байта, за которыми следует строка (включая завершающий '\0'). Обратите внимание, что закодированная длина не включает завершающий «\0» — это похоже на strlen . Это немного сбивает с толку на стороне Java, которая изобилует странными операторами автоинкремента, чтобы пропустить эти терминаторы. Я считаю, что причина, по которой это было сделано, заключалась в том, чтобы позволить коду C быть более эффективным при чтении строк, которые контейнер сервлета отправляет обратно - с завершающим символом \0 код C может передавать ссылки в один буфер без копирования . если бы \0 отсутствовал, код C должен был бы скопировать что-то, чтобы получить представление о строке.

Размер пакета

Согласно большей части кода, максимальный размер пакета составляет 8 * 1024 bytes (8K) . Фактическая длина пакета закодирована в заголовке.

Заголовки пакетов

Пакеты, отправляемые с сервера в контейнер, начинаются с 0x1234 . Пакеты, отправляемые из контейнера на сервер, начинаются с AB (это код ASCII для A, за которым следует код ASCII для B). После этих первых двух байтов следует целое число (закодированное, как указано выше) с длиной полезной нагрузки. Хотя это может означать, что максимальная полезная нагрузка может достигать 2 ^ 16, на самом деле код устанавливает максимальное значение 8 КБ.

Формат пакета (Сервер->Контейнер)
Байт 0 1 2 3 4...(n+3)
Содержание 0x12 0x34 Длина данных (n) Данные
Формат пакета (Контейнер->Сервер)
Байт 0 1 2 3 4...(n+3)
Содержание А Б Длина данных (n) Данные

Для большинства пакетов первый байт полезной нагрузки кодирует тип сообщения. Исключением являются пакеты тела запроса, отправляемые с сервера в контейнер — они отправляются со стандартным заголовком пакета ( 0x1234 и затем длиной пакета), но без какого-либо кода префикса после этого.

Веб-сервер может отправлять контейнеру сервлетов следующие сообщения:

Код Тип пакета Значение
2 Переслать запрос Начните цикл обработки запросов со следующими данными
7 Неисправность Веб-сервер просит контейнер закрыть себя.
8 пинг Веб-сервер просит контейнер взять на себя управление (фаза безопасного входа).
10 CPing Веб-сервер просит контейнер быстро ответить с помощью CPong.
никто Данные Размер (2 байта) и соответствующие данные тела.

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

Первый Data пакет отправляется сразу после Forward Request веб-сервером.

Контейнер сервлета может отправлять на веб-сервер следующие типы сообщений:

Код Тип пакета Значение
3 Отправить фрагмент тела Отправьте часть тела из контейнера сервлета на веб-сервер (и, предположительно, в браузер).
4 Отправить заголовки Отправьте заголовки ответа из контейнера сервлета на веб-сервер (и, предположительно, в браузер).
5 Завершить ответ Отмечает конец ответа (и, следовательно, цикла обработки запроса).
6 Получить кусок тела Получить дополнительные данные из запроса, если они еще не все переданы.
9 Ответить Ответ на запрос CPing

Каждое из приведенных выше сообщений имеет различную внутреннюю структуру, подробно описанную ниже.

Структура пакета запроса

Для сообщений с сервера в контейнер типа Forward Request :

 AJP13_FORWARD_REQUEST :=
 префикс_код (байт) 0x02 = JK_AJP13_FORWARD_REQUEST
 метод (байт)
 протокол (строка)
 req_uri (строка)
 remote_addr (строка)
 удаленный_хост (строка)
 имя_сервера (строка)
 порт_сервера (целое число)
 is_ssl (логическое значение)
 num_headers (целое число)
 request_headers *(req_header_name req_header_value)
 атрибуты *(имя_атрибута значение_атрибута)
 request_terminator (байт) OxFF 

Имеют request_headers следующую структуру:

 req_header_name :=
 sc_req_header_name | (строка) [см. ниже, как это анализируется]
sc_req_header_name := 0xA0xx (целое число)
req_header_value := (строка) 

Они attributes являются необязательными и имеют следующую структуру:

 attribute_name := sc_a_name | (строка sc_a_req_attribute)
attribute_value := (строка) 

Не то чтобы это был самый важный заголовок content-length , потому что он определяет, будет ли контейнер немедленно искать другой пакет.

Подробное описание элементов Forward Request

Префикс запроса

Для всех запросов это будет 2. Подробнее о других кодах префиксов см. выше.

Метод

Метод HTTP, закодированный как один байт:

Имя командыКод
ПАРАМЕТРЫ1
ПОЛУЧАТЬ2
ГОЛОВА3
ПОЧТА4
ПОМЕЩАТЬ5
УДАЛИТЬ6
СЛЕД7
PROPFIND8
PROPPATCH9
MKCOL10
КОПИРОВАТЬ11
ДВИГАТЬСЯ12
ЗАМОК13
РАЗБЛОКИРОВАТЬ14
ACL-список15
ОТЧЕТ16
КОНТРОЛЬ ВЕРСИЙ17
РЕГИСТРИРОВАТЬСЯ18
ПРОВЕРИТЬ19
ОТМЕНИТЬ ПРОВЕРКУ20
ПОИСК21
MKWORKSPACE22
ОБНОВЛЯТЬ23
ЭТИКЕТКА24
ОБЪЕДИНИТЬ25
БАЗОВЫЙ_КОНТРОЛЬ26
МАКАКТИВНОСТЬ27

Более поздняя версия ajp13 будет передавать дополнительные методы, даже если их нет в этом списке.

протокол, req_uri, удаленный_адрес, удаленный_хост, имя_сервера, порт_сервера, is_ssl

Все это довольно очевидно. Каждый из них является обязательным и будет отправлен для каждого запроса.

Заголовки

Структура следующая: сначала кодируется request_headers количество заголовков . num_headers Затем следует ряд пар имя req_header_name /значение заголовка req_header_value . Общие имена заголовков кодируются целыми числами для экономии места. Если имя заголовка отсутствует в списке основных заголовков, оно кодируется обычным образом (в виде строки с префиксом длины). Список общих заголовков sc_req_header_name и их кодов выглядит следующим образом (все регистрозависимы):

ИмяКодовое значениеКодовое имя
принимать0xA001SC_REQ_ACCEPT
принять кодировку0xA002SC_REQ_ACCEPT_CHARSET
принять-кодирование0xA003SC_REQ_ACCEPT_ENCODING
принять-язык0xA004SC_REQ_ACCEPT_LANGUAGE
авторизация0xA005SC_REQ_AUTHORIZATION
связь0xA006SC_REQ_CONNECTION
Тип содержимого0xA007SC_REQ_CONTENT_TYPE
длина содержимого0xA008SC_REQ_CONTENT_LENGTH
печенье0xA009SC_REQ_COOKIE
cookie20xA00ASC_REQ_COOKIE2
хозяин0xA00BSC_REQ_HOST
прагма0xA00CSC_REQ_PRAGMA
реферер0xA00DSC_REQ_REFERER
пользовательский агент0xA00ESC_REQ_USER_AGENT

Код Java, который читает это, захватывает первое двухбайтовое целое число, и если он видит '0xA0' в самом значимом байте, он использует целое число во втором байте в качестве индекса в массиве имен заголовков. Если первый байт не равен 0xA0 , предполагается, что двухбайтовое целое число представляет собой длину строки, которая затем считывается.

Это работает в предположении, что никакие имена заголовков не будут иметь длину больше, чем 0x9FFF (==0xA000 - 1) , что вполне разумно, хотя и несколько произвольно.

Примечание:

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

Атрибуты

Атрибуты с префиксом ? (например ?context ) являются необязательными. Для каждого имеется однобайтовый код, указывающий тип атрибута, а затем его значение (строка или целое число). Их можно отправлять в любом порядке (хотя код C всегда отправляет их в порядке, указанном ниже). Специальный код завершения посылается, чтобы сигнализировать об окончании списка необязательных атрибутов. Список байтовых кодов:

ИнформацияКод ЗначениеТип значенияПримечание
?контекст0x01-В настоящее время не реализовано
?servlet_path0x02-В настоящее время не реализовано
?remote_user0x03Нить
?auth_type0x04Нить
?Строка запроса0x05Нить
?jvm_route0x06Нить
?ssl_cert0x07Нить
?ssl_cipher0x08Нить
?ssl_session0x09Нить
?req_атрибут0x0AНитьИмя (следует название атрибута)
?ssl_key_size0x0BЦелое число
сделано0xFF-request_terminator

И в настоящее время context не servlet_path устанавливаются кодом C, и большая часть кода Java полностью игнорирует все, что отправляется для этих полей (и некоторые из них фактически сломаются, если строка будет отправлена после одного из этих кодов). Я не знаю, является ли это ошибкой, нереализованной функцией или просто рудиментарным кодом, но он отсутствует с обеих сторон соединения.

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

, , и относятся к соответствующим query_string частям HTTP и HTTPS. ssl_cert ssl_cipher ssl_session

, используется jvm_route для поддержки фиксированных сеансов — связывания сеанса пользователя с конкретным экземпляром Tomcat при наличии нескольких серверов с балансировкой нагрузки.

Помимо этого списка основных атрибутов, любое количество других атрибутов может быть отправлено через req_attribute код 0x0A . Пара строк, представляющих имя и значение атрибута, отправляется сразу после каждого экземпляра этого кода. Значения среды передаются через этот метод.

Наконец, после того, как все атрибуты отправлены, 0xFF отправляется терминатор атрибута . Это сигнализирует как об окончании списка атрибутов, так и об окончании пакета запроса.

Структура пакета ответа

для сообщений, которые контейнер может отправить обратно на сервер.

 AJP13_SEND_BODY_CHUNK :=
 префикс_код 3
 chunk_length (целое число)
 чанк *(байт)
 chunk_terminator (байт) Ox00
AJP13_SEND_HEADERS :=
 префикс_код 4
 http_status_code (целое число)
 http_status_msg (строка)
 num_headers (целое число)
 response_headers * (res_header_name header_value)
res_header_name :=
 sc_res_header_name | (строка) [см. ниже, как это анализируется]
sc_res_header_name := 0xA0 (байт)
header_value := (строка)
AJP13_END_RESPONSE :=
 префикс_код 5
 повторное использование (логическое значение)
AJP13_GET_BODY_CHUNK :=
 префикс_код 6
 запрашиваемая_длина (целое число) 

Подробности:

Отправить фрагмент тела

Фрагмент в основном представляет собой двоичные данные и отправляется непосредственно обратно в браузер.

Отправить заголовки

Код состояния и сообщение являются обычными вещами HTTP (например, 200 и OK ). Имена заголовков ответа кодируются так же, как имена заголовков запроса. См. header_encoding выше для получения подробной информации о том, как коды отличаются от строк.
Коды для общих заголовков:

ИмяКодовое значение
Тип содержимого0xA001
Язык содержания0xA002
Длина содержимого0xA003
Дата0xA004
Последнее изменение0xA005
Расположение0xA006
Set-Cookie0xA007
Set-Cookie20xA008
Сервлет-движок0xA009
Положение дел0xA00A
WWW-аутентификация0xA00B

После кода или имени заголовка строки сразу кодируется значение заголовка.

Завершить ответ

Сигнализирует об окончании этого цикла обработки запросов. Если reuse флаг равен true (anything other than 0 in the actual C code) , это TCP-соединение теперь можно использовать для обработки новых входящих запросов. Если reuse ложно (==0), соединение должно быть закрыто.

Получить кусок тела

Контейнер запрашивает дополнительные данные из запроса (если тело было слишком большим, чтобы поместиться в первый отправленный пакет или когда запрос разбит на части). Сервер отправит назад пакет тела с объемом данных, который является минимальным из request_length , максимальным размером тела отправки (8186 (8 Kbytes - 6)) и количеством байтов, фактически оставшихся для отправки из тела запроса.
Если в теле больше нет данных (т. е. контейнер сервлета пытается прочитать за конец тела), сервер отправит обратно пустой пакет, который является телом пакета с полезной нагрузкой, равной 0. (0x12,0x34,0x00,0x00)



 <         > 

Пункты:   85    86    88    89    90    91    92    93    94    95    96    97    98    99    100    101    102    103    104    105    106    107    108    109    110    111    112    113    114    115    116    117    118    119    120    121    122    123    124    125    126    127    128    129    130    131    132    133    134    135    136    137    138    139    140    141    142    143    144    145    146    147    148    149    150    151    152    153    154    155    156    157    158    159    160    161    163    164    165    166    167    168      170      171    172    173    174    175    176    177    178    179    180    181    182    183    184    185    186    187    188    189    190    191    192    193    194    195    196    197    198    199    200    201    203    204    205    206    207    208    209    210    211    212    213  

Рейтинг@Mail.ru