本文件是对当前 Apache JServ 协议版本 1.3(也称为 ajp13)演进的提案。我不会在此处介绍完整协议,只会介绍 ajp13 中的附加内容。此第 n 次通过包含了来自 tomcat-dev 列表的评论和在开发过程中发现的错误。
AJP13 中缺少的功能
ajp13 是将 servlet 引擎(如 tomcat)与 Web 服务器(如 Apache)连接起来的一个好协议
- 使用持久连接以避免每次请求的重新连接时间
- 对许多 http 命令进行编码以减小流大小
- 将 Web 服务器的许多信息(如 SSL 证书)发送到 servlet 引擎
但 ajp13 缺少对以下内容的支持
- Web 服务器和 servlet 引擎之间的安全性。任何人都可以连接到 ajp13 端口(不使用登录机制)。例如,你可以使用 telnet 连接并通过不发送任何数据(连接中没有超时)来保持远程线程处于活动状态
- 从 servlet 引擎传递到 Web 服务器的上下文信息。JK(Web 服务器连接器)配置的一部分是向 Web 服务器指示要处理哪个 URI。mod_jk JkMount 指令告诉 Web 服务器必须将哪个 URI 转发到 servlet 引擎。servlet 引擎已经知道它处理哪个 URI,TC 3.3 已经能够根据可用上下文的列表为 JK 生成一个配置文件。
- 从 servlet 引擎到 Web 服务器的上下文状态更新。拥有 Tomcat 集群的大型网站(如 ISP 和虚拟主机)可能需要出于管理目的而停止某个上下文。在这种情况下,前端 Web 服务器必须知道上下文当前已关闭,以便最终将请求中继到另一个 Tomcat
- 在发送请求之前验证连接状态。实际上,JK 将请求发送到 servlet 引擎,然后等待答案。但套接字 API 的一个好处是,你可以向已关闭的连接写(),而不会出现任何错误报告,但向已关闭的连接读() 会返回错误代码。
AJP13 的建议附加内容
我们在此描述可添加到 AJP13 的功能和附加组件。由于此文档是一份提案,因此一开始可能会出现相当程度的混乱。请务必在 tomcat 列表中讨论以帮助澄清要点、添加功能,但当前列表似乎是“最低限度”
- 连接时的高级登录功能
- 基本授权系统,其中 Web 服务器和 Servlet 引擎中存在共享密钥
- 基本协议协商,只是为了确保将来如果向 AJP13 添加功能,当前实现仍将有效
- 干净地处理“未知数据包”
- 从 Web 服务器传递到 Servlet 引擎的扩展环境变量
- 添加 Servlet 2.3 API 所需的额外 SSL 信息(如 SSL_KEY_SIZE)
高级登录
- WEB 服务器发送 LOGIN INIT CMD + NEGOCIATION DATA + WEB SERVER INFO
- TOMCAT 响应 LOGIN SEED CMD + RANDOM DATA
- WEB 服务器计算 RANDOM DATA+SECRET DATA 的 MD5
- WEB 服务器发送 LOGIN COMP CMD + MD5 (SECRET DATA + RANDOM DATA)
- TOMCAT 响应 LOGIN STATUS CMD + NEGOCIED DATA + SERVLET ENGINE INFO
消息流
+----------------+------------------+-----------------+
| 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 + SECRET KEY 的 MD5 长度为 32 个字符。
- 协商数据、协商数据、故障代码长度为 32 位。
- Web 服务器信息、Servlet 引擎信息为 CString。
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 |
+-------------+--------------+
- 关闭命令、关闭确认命令、关闭拒绝命令长度为 1 字节。
- 随机数 + 密钥的 MD5 长度为 32 个字符。
- 故障代码长度为 32 位。
扩展的环境变量功能
备注:在 JK 中处理 AJP13 时,我确实发现了“JkEnvVar”。以下“扩展的环境变量功能”说明可能不会在扩展的 AJP13 中实现,因为原始实现中已提供。说明:许多用户希望将一些 Web 服务器环境变量传递给他们的 servlet 引擎。为了减少网络流量,Web Servlet 将发送一个表,以较短的方式描述外部变量。我们将在其中使用 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)
+-------------------+---------------------------+-------------------------------+----+
| 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 |
+----+-----------------------------------------+
- 扩展变量命令长度为 1 字节。
- Web 服务器属性名称、Servlet 引擎属性名称为 CString。
- ES 是一个空的 CString。
将上下文信息转发给 Web 服务器的 Servlet 引擎
在登录阶段之后,Web 服务器将请求 servlet 引擎处理的上下文和 URL/URI 列表。这将简化许多网站的安装,减少对 tomcat 用户列表中配置的疑问,并为 servlet API 2.3 做好准备。此模式将通过一个新的指令 JkAutoMount 激活,即:JkAutoMount examples myworker1 /examples/ 如果我们想要获取 servlet 引擎处理的所有上下文,可以使用通配符:即:JkAutoMount * myworker1 * 一个 servlet 引擎可以有许多上下文,如 /examples、/admin、/test。我们可能只想对给定的工作程序使用一些上下文。以前在 Apache HTTP Server 中通过在 Apache 的每个[虚拟]区域中手动设置 JkMount 来完成此操作。如果您的 Web 服务器支持虚拟主机,我们还将把该信息转发给 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 |
+------------------+-------------------+----------+-------------------+----------+---------------+----+
- 上下文查询命令和上下文信息命令长度为 1 字节。
- 虚拟主机名称为 CString,即以空字节(/0)结尾的字符数组。
- 空字符串只是一个空字节 (/0)。
- ES 是一个空 CString。表示 URI/URL 的结尾或 CONTEXT 的结尾。
当不使用 VirtualMode 时,虚拟主机名是“*”。在这种情况下,servlet 引擎将发送处理的所有上下文。
从 Servlet 引擎到 Web 服务器的上下文信息更新
上下文更新是每次上下文停用/重新激活时从 servlet 引擎发出的消息。当指令 JkUpdateMount 使用时,将使用更新。此指令将设置 AJP13_CONTEXT_UPDATE_NEG 标志。例如:JkUpdateMount myworker1
+--------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT UPDATE CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+--------------------+-------------------+----------+--------+----------+--------+----+
- CONTEXT UPDATE CMD、STATUS 的长度为 1 个字节。
- 虚拟主机名、CONTEXT 是 CString。
- ES 是一个空 CString。表示 CONTEXT 的结尾。
当不使用 VirtualMode 时,虚拟主机名是“*”。STATUS 是一个字节,表示上下文是 UP/DOWN/INVALID
对 Servlet 引擎的上下文状态查询
Web 服务器将使用此查询来确定给定上下文是 UP、DOWN 还是 INVALID(并且应删除)。
+-------------------+--------------------+----------+----------+----+
| CONTEXT STATE CMD | VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
+-------------------+--------------------+----------+----------+----+
+-------------------------+-------------------+----------+--------+----------+--------+----+
| CONTEXT STATE REPLY CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
+-------------------------+-------------------+----------+-------------------+--------+----+
- CONTEXT STATE CMD、CONTEXT STATE REPLY CMD、STATUS 的长度为 1 个字节。
- 虚拟主机名、CONTEXT 是 CString
- ES 是一个空 CString
当不使用 VirtualMode 时,虚拟主机名是一个空字符串。
处理未知数据包
有时,即使使用协商良好的协议,我们也可能遇到一端(Web 服务器或 servlet 引擎)收到无法理解的消息的情况。在这种情况下,接收方将发送“UNKNOW PACKET CMD”,并附加未处理的消息。
+--------------------+------------------------+-------------------+
| UNKNOWN PACKET CMD | UNHANDLED MESSAGE SIZE | UNHANDLED MESSAGE |
+--------------------+------------------------+-------------------+
- UNKNOWN PACKET CMD 的长度为 1 个字节。
- UNHANDLED MESSAGE SIZE 的长度为 16 位。
- UNHANDLED MESSAGE 是一个字节数组(长度包含在 UNHANDLED MESSAGE SIZE 中)
添加了 UNHANDLED MESSAGE SIZE(开发)
在发送请求之前验证连接
注意:此功能可能永远不会使用,因为它可能会减慢正常进程,因为在转发请求之前需要在 Web 服务器端进行额外的 IO(读取)......套接字 API 的一个优点是,您可以在半关闭的套接字上写入。当 servlet 引擎关闭套接字时,Web 服务器只会在下一次对套接字进行 read() 时发现它。基本上,在 AJP13 协议中,Web 服务器将 HTTP 标头和 HTTP 正文(按 8K 块 POST)发送到 servlet 引擎,然后尝试接收答复。如果连接断开,Web 服务器只会收到时间才了解到。我们可以使用缓冲方案,但是当您使用 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)
+------------+-------------+
| STATUS CMD | STATUS DATA |
+------------+-------------+
- STATUS CMD 和 STATUS DATA 的长度为一个字节。