AJP 协议是面向包的协议,采用二进制形式代替文本形式,以提高性能。 Web Server 一般维持和 Web Container 的多个 TCP Connecions ,即 TCP 连接池,多个 request/respons 循环重用同一个 Connection 。但是当 Connection 被分配( Assigned )到某个请求时,该请求完成之前,其他请求不得使用该连接。
Tcp Connection 具有两种状态:
(1). Idle
没有请求正使用该连接。
(2). Assigned
当前连接正在处理某个请求 .
数据类型 :
AJP 协议中包括四种数据类型: Byte, Boolean , Integer and String .
Byte: 一个字节
Boolean : 一个字节, 1 = true, 0 = false 。
Integer :两个字节,无符号整数,高位字节在前。
String: 可变字符串,最大长度为 2^16. 字符串的前而会有二个字节( Integer 型)表示字符串的长度, -1 表示 null 。字符串后面会跟上 终结符 ”\0” ,而且字符串长度不包括这个终结符。
AJP 的包结构 , 图表 1 :
包方向 |
0 |
1 |
2 |
3 |
4…(n+3) |
Server->Container |
0x12 |
0x34 |
数据长度 (n) |
数据 (payload) |
|
Container->Server |
A |
B |
数据长度 (n) |
数据 (payload) |
图表 1
可以看出,从 apache 发向 tomcat 包都带有 0x1234 头,而从 tomcat 发向 apache 的包都带有 AB(ascii 码 ) 头,随后二字节代表数据的长度(不包括前四个字节) . 所认 AJP 包的最大长度可以接近 2^16 ,但目前的版本支持的最大包长度为 2^13 ,即 8K 。
再看数据部分 (payload), 除 Server->Container 的请求体包外,其他包的数据部分的首字节为其消息类型 (code) ,如下表(描述部分是原文,译成中文本人认为更难理解,英文表义比中文是好一些) :
方向 |
code |
包类型 |
描述 |
Server->Container |
2 |
Forward Request |
Begin the request-processing cycle with the following data 。 |
7 |
Shutdown |
The web server asks the container to shut itself down |
|
8 |
Ping |
The web server asks the container to take control (secure login phase). |
|
10 |
Cping |
The web server asks the container to respond quickly with a CPong |
|
none |
Data |
Size (2 bytes) and corresponding body data. |
|
Container->Server |
3 |
Send Body Chunk |
Send a chunk of the body from the servlet container to the web server |
4 |
Send Headers |
Send the response headers from the servlet container to the web server |
|
5 |
End Response |
Marks the end of the response |
|
6 |
Get Body Chunk |
Get further data from the request if it hasn't all been transferred yet |
|
9 |
CPong Reply |
The reply to a CPing request |
Forward Request 包数据部分( payload )结构:
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 |
---------------------------------------------------------------------------------------------------------------------------------
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) |
---------------------------------------------------------------------------------------------------------------------------------
attribute_name := sc_a_name | (sc_a_req_attribute string) attribute_value := (string) |
(1) prefix_code 所有的 Forward Request 包都是 0x02.
(2) Method: 一个字节,对方法的编码,其对应如下(只列了部分):
Command Name |
code |
POST |
4 |
OPTIONS |
1 |
PUT |
5 |
GET |
2 |
DELETE |
6 |
HEAD |
3 |
TRACE |
7 |
( 参考: http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html)
(3) protocol, req_uri, remote_addr, remote_host, server_name, server_port, is_ssl : 每个请求包都有这几个字段,格式都是 长度+字符串值 +\0 结束符。
(4) num_headers: 请求头的个数,两个字节。
(5) request_headers:
请求头名称分化为两类, 一类请求头被转换为 0xA0xx 格式(如下表所示),其他请求头仍然用原字符串编码。
请求头 |
Code 值 |
Code 名称 |
accept |
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 0xA00C |
pragma |
0xA00C |
SC_REQ_PRAGMA |
referer |
0xA00D |
SC_REQ_REFERER |
user-agent |
0xA00E |
SC_REQ_USER_AGENT |
(6) Java 代码读取头两个字节的整数型,如果高位字节为 ”0xA0” ,则第二字节为上在列表的索引。如果高位字节不是 ”0xA0” ,则这两个字节为随后请求头名称的长度。
(7) Attributes :很少用,直接看 tomcat 文档吧。
(8) request_terminator: 一个字节 0xFF, 请求结束符。
响应包数据部分 (payload) 结构 :
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_SEND_BODY_CHUNK := prefix_code 3 chunk_length (integer) chunk *(byte)
AJP13_END_RESPONSE := prefix_code 5 reuse (boolean)
AJP13_GET_BODY_CHUNK := prefix_code 6 requested_length (integer) |
1. response_headers: 和请求头一样,一类响应头被转换为 0xA0xx 格式(如下表所示),其他响应头名称采用原字符串编码。
请求头 |
Code 值 |
Code 名称 |
Content-Type |
0xA001 |
SC_RESP_CONTENT_TYPE |
Content-Language |
0xA002 |
SC_RESP_CONTENT_LANGUAGE |
Content-Length |
0xA003 |
SC_RESP_CONTENT_LENGTH |
Date |
0xA004 |
SC_RESP_DATE |
Last-Modified |
0xA005 |
SC_RESP_LAST_MODIFIED |
Location |
0xA006 |
SC_RESP_LOCATION |
Set-Cookie |
0xA007 |
SC_RESP_SET_COOKIE |
Set-Cookie2 |
0xA008 |
SC_RESP_SET_COOKIE2 |
Servlet-Engine |
0xA009 |
SC_RESP_SERVLET_ENGINE |
Status |
0xA00A |
SC_RESP_STATUS |
WWW-Authenticate |
0xA00B |
SC_RESP_WWW_AUTHENTICATE |
2. reuse: 如果为 1, 表示该连接可以被子重用,否则这个连接应该关闭。
下面我们来看一个简单 AJP 请求过程中抓到的请求包:
http://localhost/test/testCluster.jsp 请求:
1 , 2 字节是上文所说的 Server->Container 包头, 3 , 4 字节表示包长度 (0x01b0=432), 即从第 5 个字节到最后的一个字节( ff )的长度。第 5 个字节( 02 )代表是 ”Forward Request” 包。
第 6 个字节 (02) 代表是 Get 请求。第 7 , 8 字节 (0x0008) 代表 protocol 字符串的长度,后 8 个字节为 protocol 字符串 (HTTP/1.1), 第 9 个字节为 protocol 字符串的终结符“ \0 ”。
18-41 字节 代表 req_uri 字符串 (0x15+2+1=24 个 ) 。
42-53 字节代表 remote_addr 字符串 (0x09+2+1=12 个 )
54-55 两字节 (0xffff=-1) 代表 null 字符串 , 即 remote_host 不存在。
56-67 两字节 代表 server_name 字符串 (0x09+2+1=12 个 )
68-69 两字节 (0x0050=80) 代表 server_port
70 字节 (0x00) 代表 is_ssl 为 false
71-72 两字节 (0x0009) 代表该请求有 9 个请求头 , 如图中的前 9 个红色 椭圆 .
第 10 个红色椭圆 (0x06) 代表 Attributes 的 route.
第 11 个红椭圆之后,代表 AJP_REMOTE_PORT 与 JK_LB_ACTIVATION 两个请求属性 .
最后一个字节 0xFF 表示请求结束。
响应头数据包:
和请示头比较类似,不再细描述。其中第 5 个字节 0x04 代表 ”Send Headers” 响应。
并且没有终结符字节 0xFF.
响应正文数据包 :
响应结束 End Response:
其中最后一个字节 (01) ,代表当前连接仍然可用。
(完)