Spec-Zone.ru › Apache HTTP Server

Модуль Apache mod_proxy_ajp

Описание: Модуль поддержки AJP для mod_proxy
Статус: Расширение
Идентификатор модуля: proxy_ajp_module
Файл исходного кода: mod_proxy_ajp.c
Совместимость: Доступен в версии 2.1 и более поздних

Резюме

Этот модуль требует сервиса 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"

Опции, такие как опция secret Tomcat (требуется по умолчанию с Tomcat 8.5.51 и 9.0.31), могут быть добавлены как отдельный параметр в конце ProxyPass или BalancerMember. Этот параметр доступен в Apache HTTP Server 2.4.42 и более поздних версиях:

Простая обратная прокси с опцией secret

ProxyPass "/app" "ajp://backend.example.com:8009/app" secret=YOUR_AJP_SECRET

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

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

<Proxy "balancer://cluster">
    BalancerMember "ajp://app1.example.com:8009" loadfactor=1
    BalancerMember "ajp://app2.example.com:8009" loadfactor=2
    ProxySet lbmethod=bytraffic
</Proxy>
ProxyPass "/app" "balancer://cluster/app"

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

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

Переписывание проксированного пути

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Байт
Один байт.
Булево значение
Один байт, 1 = true, 0 = false. Использование других ненулевых значений в качестве true (т.е. в стиле 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)
Содержание A B Длина данных (n) Данные

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

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

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

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

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

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

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

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

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

Для сообщений с сервера в контейнер типа Переадресация запроса:

AJP13_FORWARD_REQUEST :=
    prefix_code      (byte) 0x02 = JK_AJP13_FORWARD_REQUEST
    method           (byte)
    protocol         (string)
    req_uri          (string)
    remote_addr      (string)
    remote_host      (string)
    server_name      (string)
    server_port      (integer)
    is_ssl           (boolean)
    num_headers      (integer)
    request_headers *(req_header_name req_header_value)
    attributes      *(attribut_name attribute_value)
    request_terminator (byte) OxFF

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

req_header_name :=
    sc_req_header_name | (string)  [see below for how this is parsed]

sc_req_header_name := 0xA0xx (integer)

req_header_value := (string)

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

attribute_name := sc_a_name | (sc_a_req_attribute string)

attribute_value := (string)

Важно, что заголовок content-length, поскольку он определяет, ищет ли контейнер другой пакет сразу.

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

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

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

Метод

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

Имя команды Код
OPTIONS 1
GET 2
HEAD 3
POST 4
PUT 5
DELETE 6
TRACE 7
PROPFIND 8
PROPPATCH 9
MKCOL 10
COPY 11
MOVE 12
LOCK 13
UNLOCK 14
ACL 15
REPORT 16
VERSION-CONTROL 17
CHECKIN 18
CHECKOUT 19
UNCHECKOUT 20
SEARCH 21
MKWORKSPACE 22
UPDATE 23
LABEL 24
MERGE 25
BASELINE_CONTROL 26
MKACTIVITY 27

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

protocol, req_uri, remote_addr, remote_host, server_name, server_port, is_ssl

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

Заголовки

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

Имя Значение кода Имя кода
accept 0xA001 SC_REQ_ACCEPT
accept-charset 0xA002 SC_REQ_ACCEPT_CHARSET
accept-encoding 0xA003 SC_REQ_ACCEPT_ENCODING
accept-language 0xA004 SC_REQ_ACCEPT_LANGUAGE
authorization 0xA005 SC_REQ_AUTHORIZATION
connection 0xA006 SC_REQ_CONNECTION
content-type 0xA007 SC_REQ_CONTENT_TYPE
content-length 0xA008 SC_REQ_CONTENT_LENGTH
cookie 0xA009 SC_REQ_COOKIE
cookie2 0xA00A SC_REQ_COOKIE2
host 0xA00B SC_REQ_HOST
pragma 0xA00C SC_REQ_PRAGMA
referer 0xA00D SC_REQ_REFERER
user-agent 0xA00E SC_REQ_USER_AGENT

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

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

Примечание:

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

Атрибуты

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

Информация Значение кода Тип значения Примечание
?context 0x01 - В настоящее время не реализовано
?servlet_path 0x02 - В настоящее время не реализовано
?remote_user 0x03 Строка
?auth_type 0x04 Строка
?query_string 0x05 Строка
?jvm_route 0x06 Строка
?ssl_cert 0x07 Строка
?ssl_cipher 0x08 Строка
?ssl_session 0x09 Строка
?req_attribute 0x0A Строка Имя (следующим идёт имя атрибута)
?ssl_key_size 0x0B Целое число
?secret 0x0C Строка Поддерживается начиная с 2.4.42
are_done 0xFF - request_terminator

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

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

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

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

secret отправляется, когда параметр secret=secret_keyword используется в директивах ProxyPass или BalancerMember. Бэкенд должен поддерживать секрет, и значения должны совпадать. request.secret или requiredSecret документированы в конфигурации Apache Tomcat.

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

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

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

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

AJP13_SEND_BODY_CHUNK :=
  prefix_code   3
  chunk_length  (integer)
  chunk        *(byte)
  chunk_terminator (byte) Ox00


AJP13_SEND_HEADERS :=
  prefix_code       4
  http_status_code  (integer)
  http_status_msg   (string)
  num_headers       (integer)
  response_headers *(res_header_name header_value)

res_header_name :=
    sc_res_header_name | (string)   [see below for how this is parsed]

sc_res_header_name := 0xA0 (byte)

header_value := (string)

AJP13_END_RESPONSE :=
  prefix_code       5
  reuse             (boolean)


AJP13_GET_BODY_CHUNK :=
  prefix_code       6
  requested_length  (integer)

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

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

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

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

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

Имя Значение кода
Content-Type 0xA001
Content-Language 0xA002
Content-Length 0xA003
Date 0xA004
Last-Modified 0xA005
Location 0xA006
Set-Cookie 0xA007
Set-Cookie2 0xA008
Servlet-Engine 0xA009
Status 0xA00A
WWW-Authenticate 0xA00B

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

Конец ответа

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

Получение фрагмента тела

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

© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/mod/mod_proxy_ajp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API