AJPv13 延伸建議

簡介

這份文件是 Apache JServ 協定版本 1.3(也稱為 ajp13)演進的建議。我不會在這裡涵蓋完整的協定,只會涵蓋 ajp13 的附加元件。第 n 次傳遞包含來自 tomcat-dev 清單的意見,以及在開發過程中發現的遺漏事項。

AJP13 中遺漏的功能

ajp13 是將 servlet 引擎(例如 tomcat)連結到網路伺服器(例如 Apache)的良好協定

  • 使用持久連線,避免每次請求重新連線的時間
  • 編碼許多 http 指令以減少串流大小
  • 將許多網路伺服器資訊(例如 SSL 憑證)傳送給 servlet 引擎

但 ajp13 缺乏對下列項目的支援

  • 網路伺服器和 servlet 引擎之間的安全性。任何人都可以連線到 ajp13 連接埠(不使用登入機制)。例如,您可以透過 telnet 連線,並透過不傳送任何資料(連線中沒有逾時)來保持遠端執行緒執行。
  • 從 servlet 引擎傳遞到網路伺服器的內容資訊。JK(網路伺服器連接器)設定的一部分是指示網路伺服器要處理哪個 URI。mod_jk JkMount 指令告訴網路伺服器必須將哪個 URI 轉發到 servlet 引擎。servlet 引擎已經知道它處理哪個 URI,而 TC 3.3 已經能夠從可用內容清單產生 JK 的設定檔。
  • 伺服器引擎至網路伺服器之內容狀態更新。大型網站具有 Tomcat 農場,例如 ISP 和虛擬主機,可能需要停止內容以進行管理目的。在這種情況下,前端網路伺服器必須知道內容目前已關閉,以最終將要求轉送至另一部 Tomcat
  • 在傳送要求前驗證連線狀態。實際上,JK 會將要求傳送至伺服器引擎,然後等待回應。但是,Socket API 的其中一項優點是,您可以在不產生任何錯誤報告的情況下對已關閉的連線進行 write(),但對已關閉的連線進行 read() 會傳回錯誤代碼。

建議新增至 AJP13 的附加元件

讓我們在此說明可新增至 AJP13 的功能和附加元件。由於本文件為提案,因此一開始必須預期會有一段時間的混亂。請務必在 Tomcat 清單上進行討論,以協助釐清重點、新增功能,但目前的清單看來像是「最低限度」

  • 連線時間的高階登入功能
  • 基本授權系統,其中網路伺服器和伺服器引擎中存在共用密鑰。
  • 基本協定協商,僅用於確保未來在 AJP13 中新增功能時,目前的實作仍可運作。
  • 清除處理「未知封包」
  • 從網路伺服器傳遞至伺服器引擎的延伸環境變數。
  • 新增 Servlet 2.3 API 所需的額外 SSL 資訊(例如 SSL_KEY_SIZE)

高階登入

  1. 網路伺服器傳送 LOGIN INIT CMD + NEGOCIATION DATA + WEB SERVER INFO
  2. TOMCAT 回應 LOGIN SEED CMD + RANDOM DATA
  3. 網路伺服器計算 RANDOM DATA+SECRET DATA 的 MD5
  4. 網路伺服器傳送 LOGIN COMP CMD + MD5 (SECRET DATA + RANDOM DATA)
  5. TOMCAT 回應 LOGIN STATUS CMD + NEGOCIED DATA + SERVLET ENGINE INFO
為了防止 DOS 攻擊,servlet 引擎將僅等待 15/30 秒的 LOGIN CMD,並報告逾時例外狀況以供管理員調查。登入指令將包含基本協定協商資訊,例如壓縮能力、加密、內容資訊(在啟動時)、執行時間的內容更新(上/下)、SSL 環境變數層級、支援的 AJP 協定層級(層級 1/層級 2/層級 3...)。Web 伺服器資訊將包含 Web 伺服器資訊和連接器名稱(例如 Apache 1.3.26 + mod_ssl 2.8.8 + mod_jk 1.2.41 + mod_perl 1.25)。servlet 引擎將使用自己的遮罩(其可執行的動作)遮罩協商遮罩,並在接受登入時回傳。這有助於在 Web 伺服器上執行基本 AJP13 實作(層級 1),並與 servlet 引擎側的更進階協定處理常式搭配使用,反之亦然。AJP13 被設計為精簡且快速,因此 Web 伺服器中存在的許多 SSL 資訊並未轉發至 servlet 引擎。我們在此新增四個協商旗標,以提供更多關於用戶端 SSL 資料(憑證)、伺服器 SSL 資料、使用的加密和雜項資料(逾時...)的資訊。

訊息串流

+----------------+------------------+-----------------+
| LOGIN INIT CMD | NEGOCIATION DATA | WEB SERVER INFO |
+----------------+------------------+-----------------+

+----------------+----------------+
| LOGIN SEED CMD | MD5 of entropy |
+----------------+----------------+

+----------------+----------------------------+
| LOGIN COMP CMD | MD5 of RANDOM + SECRET KEY |
+----------------+----------------------------+

+-----------+---------------+---------------------+
| LOGOK CMD | NEGOCIED DATA | SERVLET ENGINE INFO |
+-----------+---------------+---------------------+

+------------+--------------+
| LOGNOK CMD | FAILURE CODE |
+------------+--------------+
  • LOGIN INIT CMD、LOGIN SEED CMD、LOGIN COMP CMD、LOGOK CMD、LOGNOK CMD 長度為 1 位元組。
  • MD5、RANDOM 的 MD5 + SECRET KEY 長度為 32 個字元。
  • 協商資料、協商資料、失敗代碼長度為 32 位元。
  • WEB 伺服器資訊、SERVLET 引擎資訊為 CString。
將透過 workers.properties 中的新屬性 secretkey 設定金鑰
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.secretkey=myverysecretkey

關閉功能

AJP13 遺漏了 AJP12 的一項功能,也就是關閉指令。登出將告知 servlet 引擎自行關閉。

+--------------+----------------------------+
| SHUTDOWN CMD | MD5 of RANDOM + SECRET KEY |
+--------------+----------------------------+

+------------+
| SHUTOK CMD |
+------------+

+-------------+--------------+
| SHUTNOK CMD | FAILURE CODE |
+-------------+--------------+
  • SHUTDOWN CMD、SHUTOK CMD、SHUTNOK CMD 長度為 1 位元組。
  • RANDOM 的 MD5 + SECRET KEY 長度為 32 個字元。
  • 失敗代碼長度為 32 位元。

延伸環境變數功能

備註:在 JK 中處理 AJP13 時,我真正發現了「JkEnvVar」。由於已在原始實作中提供,以下「延伸環境變數功能」說明可能未在延伸 AJP13 中實作。說明:許多使用者會希望將部分 Web 伺服器環境變數傳遞至其 servlet 引擎。為了減少網路流量,Web 伺服器將傳送一個表格,以較簡短的方式說明外部變數。我們將在此使用 AJP13 中已有的功能,也就是屬性清單:在 AJP13 中,我們有

AJP13_FORWARD_REQUEST :=
    prefix_code      2
    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)

    ?context       (byte string)
    ?servlet_path  (byte string)
    ?remote_user   (byte string)
    ?auth_type     (byte string)
    ?query_string  (byte string)
    ?route         (byte string)
    ?ssl_cert      (byte string)
    ?ssl_cipher    (byte string)
    ?ssl_session   (byte string)

    ?attributes   *(attribute_name attribute_value)
    request_terminator (byte)
使用簡短的「Web 伺服器屬性名稱」將減少網路流量。
+-------------------+---------------------------+-------------------------------+----+
| EXTENDED VARS CMD | WEB SERVER ATTRIBUTE NAME | SERVLET ENGINE ATTRIBUTE NAME | ES |
+-------------------+---------------------------+-------------------------------+----+
例如
JkExtVars S1 SSL_CLIENT_V_START javax.servlet.request.ssl_start_cert_date
JkExtVars S2 SSL_CLIENT_V_END   javax.servlet.request.ssl_end_cert_date
JkExtVars S3 SSL_SESSION_ID     javax.servlet.request.ssl_session_id


+-------------------+----+-------------------------------------------+
| EXTENDED VARS CMD | S1 | javax.servlet.request.ssl_start_cert_date |
+-------------------+----+-------------------------------------------+
+----+-----------------------------------------+
| S2 | javax.servlet.request.ssl_end_cert_date |
+----+-----------------------------------------+
+----+-----------------------------------------+
| S3 | javax.servlet.request.ssl_end_cert_date |
+----+-----------------------------------------+
在延伸 AJP13 中傳輸期間,我們將看到包含 S1、S2、S3 的屬性名稱,以及 2001/01/03、2002/01/03、0123AFE56 的屬性值。此範例顯示延伸 SSL 變數的使用方式,但任何「個人」Web 伺服器變數(例如自訂驗證變數)都可以在 servlet 引擎中重複使用。成本僅為 AJP 流量中增加一些位元組。
  • EXTENDED VARS CMD 長度為 1 個位元組。
  • WEB 伺服器屬性名稱、SERVLET 引擎屬性名稱為 CString。
  • ES 為空的 CString。

將內容資訊轉送自 Servlet 引擎至 Web 伺服器

在登入階段之後,Web 伺服器會要求 Servlet 引擎處理的內容和 URL/URI 清單。這將簡化許多網站的安裝、減少 tomcat-user 清單上的組態問題,並為 servlet API 2.3 做好準備。此模式將由新的指令 JkAutoMount 啟用,例如:JkAutoMount examples myworker1 /examples/ 如果我們要取得 Servlet 引擎處理的所有內容,可以使用通配符,例如:JkAutoMount * myworker1 * Servlet 引擎可能有多個內容,例如 /examples、/admin、/test。我們可能只想對特定工作者使用部分內容。這在過去的 Apache HTTP Server 中已完成,例如透過在 Apache 的每個 [虛擬] 區域中手動設定 JkMount。如果您的 Web 伺服器支援虛擬主機,我們也會將該資訊轉送至 Servlet 引擎,而 Servlet 引擎只會傳回該虛擬主機的內容。在這種情況下,Servlet 引擎只會傳回與特定虛擬伺服器 (在 server.xml 中定義) 相符的 URL/URI。這項功能將有助於 ISP 和大型網站,這些網站會在負載平衡組態中相互使用大量的 Tomcat 農場。

+-----------------+-------------------+----------+----------+----+
| CONTEXT QRY CMD | VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
+-----------------+-------------------+----------+----------+----+

+------------------+-------------------+----------+-------------------+----------+---------------+----+
| CONTEXT INFO CMD | VIRTUAL HOST NAME | CONTEXTA | URL1 URL2 URL3 ES | CONTEXTB | URL1 URL2 ... | ES |
+------------------+-------------------+----------+-------------------+----------+---------------+----+
我們將透過內容查詢找出 Servlet 引擎為內容清單處理的 URL/MIMES 清單。在通配符模式中,CONTEXTA 將只包含 '*'
  • 內容查詢 CMD 和內容資訊 CMD 長度為 1 個位元組。
  • 虛擬主機名稱為 CString,即以空位元組 (/0) 結尾的字元陣列。
  • 空字串只是一個空位元組 (/0)。
  • ES 為空的 CString。表示 URI/URL 結束或內容結束。
注意
當不使用 VirtualMode 時,虛擬主機名稱為 '*”。在這種情況下,Servlet 引擎將傳送所有處理的內容。

自 Servlet 引擎至 Web 伺服器的內容資訊更新

內容更新是每次內容停用/重新啟用時從 Servlet 引擎傳來的訊息。當指令 JkUpdateMount 使用時,將使用更新。此指令將設定 AJP13_CONTEXT_UPDATE_NEG 旗標。例如:JkUpdateMount myworker1

+--------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT UPDATE CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+--------------------+-------------------+----------+--------+----------+--------+----+
  • 內容更新 CMD、狀態長度為 1 個位元組。
  • 虛擬主機名稱、內容為 CString。
  • ES 為空的 CString。表示內容結束。
注意
當 VirtualMode 未使用時,虛擬主機名稱為 '*'. STATUS 為一個位元組,表示內容為 UP/DOWN/INVALID

內容狀態查詢至 Servlet 引擎

此查詢將由網路伺服器使用,以確定特定內容為 UP、DOWN 或 INVALID(且應移除)。

+-------------------+--------------------+----------+----------+----+
| CONTEXT STATE CMD |  VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
+-------------------+--------------------+----------+----------+----+

+-------------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT STATE REPLY CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+-------------------------+-------------------+----------+-------------------+--------+----+
  • 內容狀態指令、內容狀態回覆指令、STATUS 長度為 1 個位元組。
  • 虛擬主機名稱、內容為 CString
  • ES 為一個空的 CString
注意
當 VirtualMode 未使用時,虛擬主機名稱為一個空的字串。

處理未知封包

有時,即使使用協商良好的通訊協定,我們也可能遇到一端(網路伺服器或 servlet 引擎)收到無法理解的訊息的情況。在這種情況下,接收器將傳送一個「未知封包指令」,並附加未處理的訊息。

+--------------------+------------------------+-------------------+
| UNKNOWN PACKET CMD | UNHANDLED MESSAGE SIZE | UNHANDLED MESSAGE |
+--------------------+------------------------+-------------------+
根據訊息,傳送者將報告錯誤,並在可能的情況下嘗試將訊息轉送至另一個端點。
  • 未知封包指令長度為 1 個位元組。
  • 未處理訊息大小長度為 16 位元。
  • 未處理訊息為一個位元組陣列(長度包含在未處理訊息大小中)
注意
新增未處理訊息大小(開發中)

傳送請求前驗證連線

備註:此功能可能永遠不會使用,因為它可能會減慢正常處理速度,因為在轉送請求之前需要在網路伺服器端進行額外的 IO(讀取)......Socket API 的優點之一是您可以在半關閉的 socket 上寫入。當 servlet 引擎關閉 socket 時,網路伺服器只會在下次對 socket 進行 read() 時發現它。基本上,在 AJP13 通訊協定中,網路伺服器將 HTTP 標頭和 HTTP 主體(以 8K 塊進行 POST)傳送至 servlet 引擎,然後嘗試接收回覆。如果連線中斷,網路伺服器只會在接收時發現它。我們可以使用緩衝機制,但當您使用 servlet 引擎進行超過 8ko 資料的上傳作業時會發生什麼情況?AJP13 通訊協定中的技巧是在服務結束後新增一些位元組以進行讀取

EXAMPLE OF DISCUSSION BETWEEN WEB SERVER AND SERVLET ENGINE

AJP HTTP-HEADER (+ HTTP-POST)   (WEB->SERVLET)

AJP HTTP-REPLY					(SERVLET->WEB)

AJP END OF DISCUSSION			(SERVLET->WEB)
						
---> AJP STATUS 				(SERVLET->WEB AJP13)
AJP STATUS 不會在請求/回應 #N 結束時由 servlet 引擎讀取,而是在下一個階段的開始時讀取。更多時候,網路伺服器也可以使用依賴作業系統的功能(或更好的 APR 功能)來確定是否還有更多資料需要讀取。這些資料可能是內容更新。這將避免網路伺服器向停用的內容傳送請求。在這種情況下,如果使用負載平衡,它將尋找另一個 servlet 引擎來處理請求。此功能將有助於 ISP 和擁有大量 tomcat 的大型網站,在不中斷服務的情況下更新其 servlet 引擎。
+------------+-------------+
| STATUS CMD | STATUS DATA |
+------------+-------------+
  • 狀態指令和狀態資料長度為一個位元組。

結論

擴充 AJP13 協定的目標是克服原始 AJP13 的部分限制。更簡單的設定、更佳支援大型網站和 Tomcat 農場、簡單的驗證系統和協定更新的準備。使用 JK(原生)和 Servlet 引擎(Java)中穩定的 ajp13 實作,這是眾所周知的 ajp13 合理演進。

擴充 AJP13 索引中的指令和 ID

AJP13 協定中要新增的指令和 ID 索引

指令 ID

指令名稱指令編號
AJP13_LOGINIT_CMD0x10
AJP13_LOGSEED_CMD0x11
AJP13_LOGCOMP_CMD0x12
AJP13_LOGOK_CMD0x13
AJP13_LOGNOK_CMD0x14
AJP13_CONTEXT_QRY_CMD0x15
AJP13_CONTEXT_INFO_CMD0x16
AJP13_CONTEXT_UPDATE_CMD0x17
AJP13_STATUS_CMD0x18
AJP13_SHUTDOWN_CMD0x19
AJP13_SHUTOK_CMD0x1A
AJP13_SHUTNOK_CMD0x1B
AJP13_CONTEXT_STATE_CMD0x1C
AJP13_CONTEXT_STATE_REP_CMD0x1D
AJP13_UNKNOW_PACKET_CMD0x1E

協商旗標

指令名稱編號說明
AJP13_CONTEXT_INFO_NEG0x80000000登入後,網路伺服器需要內容資訊
AJP13_CONTEXT_UPDATE_NEG0x40000000網路伺服器需要內容更新
AJP13_GZIP_STREAM_NEG0x20000000網路伺服器需要壓縮串流
AJP13_DES56_STREAM_NEG0x10000000網路伺服器需要使用密鑰加密 DES56 串流
AJP13_SSL_VSERVER_NEG0x08000000伺服器 SSL 變數的延伸資訊
AJP13_SSL_VCLIENT_NEG0x04000000用戶端 SSL 變數的延伸資訊
AJP13_SSL_VCRYPTO_NEG0x02000000加密 SSL 變數的延伸資訊
AJP13_SSL_VMISC_NEG0x01000000雜項 SSL 變數的延伸資訊

協商 ID編號說明
AJP13_PROTO_SUPPORT_AJPXX_NEG0x00FF0000支援協定的遮罩
AJP13_PROTO_SUPPORT_AJP13L1_NEG0x00010000通訊可以使用 AJP13 等級 1
AJP13_PROTO_SUPPORT_AJP13L2_NEG0x00020000通訊可以使用 AJP13 等級 2
AJP13_PROTO_SUPPORT_AJP13L3_NEG0x00040000通訊可以使用 AJP13 等級 3

所有其他旗標都必須設定為 0,因為它們保留供未來使用。

失敗 ID

失敗 ID編號
AJP13_BAD_KEY_ERR0xFFFFFFFF
AJP13_ENGINE_DOWN_ERR0xFFFFFFFE
AJP13_RETRY_LATER_ERR0xFFFFFFFD
AJP13_SHUT_AUTHOR_FAILED_ERR0xFFFFFFFC

狀態

失敗 ID編號
AJP13_CONTEXT_DOWN0x01
AJP13_CONTEXT_UP0x02
AJP13_CONTEXT_OK0x03