svn commit: r1934964 - httpd/httpd/trunk/test/modules/proxy
[email protected] Thu, 04 Jun 2026 07:32:47 -0000
Newsgroups
gmane.comp.apache.cvs
Message-ID
<178055836774.2665975.13216027920031521980@svn03-he-fi>
Author: ichristod
Date: Thu Jun 4 07:32:47 2026
New Revision: 1934964
Log:
mod_proxy_ajp: Add protocol robustness tests
Add coverage for AJP message size limits, header length handling,
and chunk message handling.
Added:
httpd/httpd/trunk/test/modules/proxy/test_04_ajp.py (contents, props changed)
Added: httpd/httpd/trunk/test/modules/proxy/test_04_ajp.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ httpd/httpd/trunk/test/modules/proxy/test_04_ajp.py Thu Jun 4 07:32:47 2026 (r1934964)
@@ -0,0 +1,126 @@
+import pytest
+import re
+
+from pyhttpd.conf import HttpdConf
+from .env import TCPFaker
+
+magic_numbers = b"\x41\x42"
+send_headers_type = b"\x04"
+send_chunk_type = b"\x03"
+status_code_200 = b"\x00\xc8"
+status_msg_ok = b"OK"
+ajp_max_size = 8192
+
+
+def ajp_packet_from_tomcat(body):
+ return magic_numbers + len(body).to_bytes(2, "big") + body
+
+
+class _AJPFaker(TCPFaker):
+
+ @staticmethod
+ def max_size_message(data):
+ body = send_headers_type + b"\x00" * (ajp_max_size - 1)
+ return ajp_packet_from_tomcat(body)
+
+ @staticmethod
+ def num_headers_len(data):
+ # message_type(send_headers) + status_code_200 + len(inner_body)
+ # + "OK"
+ body = (send_headers_type
+ + status_code_200
+ + len(status_msg_ok).to_bytes(2, "big")
+ + status_msg_ok
+ + b"\x00")
+ return ajp_packet_from_tomcat(body)
+
+ @staticmethod
+ def missing_null_terminator(data):
+ # message_type(send_headers) + status_code_200 + len(inner_body)
+ # + "OK" without null terminator
+ body = (send_headers_type
+ + status_code_200
+ + len(status_msg_ok).to_bytes(2, "big")
+ + status_msg_ok)
+ return ajp_packet_from_tomcat(body)
+
+ @staticmethod
+ def tiny_body_chunk(data):
+ # message_type(send_body_chunk) + chunk_size(0xffff),3 bytes total
+ chunk_body = (send_chunk_type + b"\xff\xff")
+ return ajp_packet_from_tomcat(chunk_body)
+
+
+class TestProxyAjp:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ if not env.has_shared_module("proxy_ajp"):
+ pytest.skip("mod_proxy_ajp not available")
+ faker = _AJPFaker("127.0.0.1", env.http_port2)
+ faker.start()
+ conf = HttpdConf(env)
+ conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port)
+ conf.add([
+ f"ProxyPass / ajp://127.0.0.1:{env.http_port2}/",
+ ])
+ conf.end_vhost()
+ conf.install()
+ assert env.apache_restart() == 0
+ yield faker
+ faker.stop()
+
+ def test_proxy_004_01(self, env, _class_scope):
+ _class_scope._make_response = _AJPFaker.max_size_message
+ r = env.curl_get(env.mkurl("http", "test1", "/"))
+ assert r.response["status"] == 503
+ try:
+ found = env.httpd_error_log.scan_recent(
+ re.compile(r'.*AH01081:.*'), timeout=5)
+ except TimeoutError:
+ found = False
+
+ assert found
+ env.httpd_error_log.ignore_recent(
+ lognos=["AH01081", "AH01080", "AH01031", "AH00878", "AH00992"])
+
+ def test_proxy_004_02(self, env, _class_scope):
+ _class_scope._make_response = _AJPFaker.tiny_body_chunk
+ env.curl_get(env.mkurl("http", "test1", "/"))
+ try:
+ found = env.httpd_error_log.scan_recent(
+ re.compile(r'.*ajp_parse_data: Message too small.*'),
+ timeout=5)
+ except TimeoutError:
+ found = False
+
+ assert found
+ env.httpd_error_log.ignore_recent(
+ lognos=["AH00893"],
+ matches=[r'.*ajp_parse_data: Message too small.*'])
+
+ def test_proxy_004_03(self, env, _class_scope):
+ _class_scope._make_response = _AJPFaker.num_headers_len
+ r = env.curl_get(env.mkurl("http", "test1", "/"))
+ assert r.exit_code == 0
+
+ _class_scope._make_response = _AJPFaker.missing_null_terminator
+ r = env.curl_get(env.mkurl("http", "test1", "/"))
+ assert r.exit_code == 0
+
+ try:
+ found = env.httpd_error_log.scan_recent(
+ re.compile(r'.*AH03229: ajp_msg_get_uint16.*'), timeout=5)
+ except TimeoutError:
+ found = False
+ assert found
+
+ try:
+ found = env.httpd_error_log.scan_recent(
+ re.compile(r'.*AH03229: ajp_msg_get_string.*'), timeout=5)
+ except TimeoutError:
+ found = False
+ assert found
+
+ env.httpd_error_log.ignore_recent(
+ lognos=["AH03229", "AH10405", "AH00985", "AH00893"])