Skip to content

Commit c1b41a0

Browse files
modify http headers (#414)
* reworked `vhost` tests with new framework. * parameterized vhost tests for h2 connection. * renamed directory and files - old name does not reflect essence of tests. Now all tests are inherited from base class - AddHeaderBase. These directives and other config tests are separated. * added tests for base logic. * added tests: - set large header; - multiple appending to same header; - multiple replacing; - add header from static table; - add header from dynamic table; * updated tests: - test_overwrite_non_singular_header - singular header must not change. - other - added more checks. * reworked tests - old version is too hard. Added tests: - adding non-exist header for `..._hdr_add`; - removing one and many headers; - removing special headers; - removing non-exist header; - h2 request tests with dynamic table; Removed `test_multiple_replacing_same_header`, `test_overwrite_non_singular_header`, `test_overwrite_singular_header`, `test_overwrite_raw_header` tests because now it is low priority logic. * added tests for two special headers. * added checks for all headers. * moved common class and removed utils.py * test_logic.py - typo fixed. `test_add_some_hdrs` is duplicate.Removed. * added test with many header modifications. * added tests with long header name and header from static table with cache enabled. * disabled tests and decreased size of headers because response exceeds SETTINGS_MAX_HEADER_LIST_SIZE.
1 parent 8fa599d commit c1b41a0

File tree

10 files changed

+910
-231
lines changed

10 files changed

+910
-231
lines changed

http2_general/test_h2_headers.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,32 @@ def test_transfer_encoding_header_in_request(self):
129129
"400",
130130
)
131131

132+
def test_long_header_name_in_request(self):
133+
"""Max length for header name - 1024. See fw/http_parser.c HTTP_MAX_HDR_NAME_LEN"""
134+
for length, status_code in ((1023, "200"), (1024, "200"), (1025, "400")):
135+
with self.subTest(length=length, status_code=status_code):
136+
self.start_all_services()
137+
138+
client = self.get_client("deproxy")
139+
client.send_request(self.post_request + [("a" * length, "text")], status_code)
140+
141+
def test_long_header_name_in_response(self):
142+
"""Max length for header name - 1024. See fw/http_parser.c HTTP_MAX_HDR_NAME_LEN"""
143+
for length, status_code in ((1023, "200"), (1024, "200"), (1025, "502")):
144+
with self.subTest(length=length, status_code=status_code):
145+
self.start_all_services()
146+
147+
client = self.get_client("deproxy")
148+
server = self.get_server("deproxy")
149+
server.set_response(
150+
"HTTP/1.1 200 OK\r\n"
151+
+ "Date: test\r\n"
152+
+ "Server: debian\r\n"
153+
+ f"{'a' * length}: text\r\n"
154+
+ "Content-Length: 0\r\n\r\n"
155+
)
156+
client.send_request(self.post_request, status_code)
157+
132158

133159
class DuplicateSingularHeader(H2Base):
134160
def test_two_header_as_bytes_from_dynamic_table(self):

http_general/test_headers.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from framework import tester
66

77
__author__ = "Tempesta Technologies, Inc."
8-
__copyright__ = "Copyright (C) 2022 Tempesta Technologies, Inc."
8+
__copyright__ = "Copyright (C) 2023 Tempesta Technologies, Inc."
99
__license__ = "GPL2"
1010

1111

@@ -291,3 +291,68 @@ def test_forwarded_and_empty_host_header(self):
291291
),
292292
expected_status_code="400",
293293
)
294+
295+
296+
class TestHeadersParsing(tester.TempestaTest):
297+
backends = [
298+
{
299+
"id": "deproxy",
300+
"type": "deproxy",
301+
"port": "8000",
302+
"response": "static",
303+
"response_content": (
304+
"HTTP/1.1 200 OK\r\n"
305+
+ "Date: test\r\n"
306+
+ "Server: debian\r\n"
307+
+ "Content-Length: 0\r\n\r\n"
308+
),
309+
}
310+
]
311+
312+
tempesta = {
313+
"config": """
314+
listen 80;
315+
server ${server_ip}:8000;
316+
317+
block_action attack reply;
318+
block_action error reply;
319+
"""
320+
}
321+
322+
clients = [
323+
{
324+
"id": "deproxy",
325+
"type": "deproxy",
326+
"addr": "${tempesta_ip}",
327+
"port": "80",
328+
},
329+
]
330+
331+
def test_long_header_name_in_request(self):
332+
"""Max length for header name - 1024. See fw/http_parser.c HTTP_MAX_HDR_NAME_LEN"""
333+
for length, status_code in ((1023, "200"), (1024, "200"), (1025, "400")):
334+
with self.subTest(length=length, status_code=status_code):
335+
self.start_all_services()
336+
337+
client = self.get_client("deproxy")
338+
client.send_request(
339+
f"GET / HTTP/1.1\r\nHost: localhost\r\n{'a' * length}: text\r\n\r\n",
340+
status_code,
341+
)
342+
343+
def test_long_header_name_in_response(self):
344+
"""Max length for header name - 1024. See fw/http_parser.c HTTP_MAX_HDR_NAME_LEN"""
345+
for length, status_code in ((1023, "200"), (1024, "200"), (1025, "502")):
346+
with self.subTest(length=length, status_code=status_code):
347+
self.start_all_services()
348+
349+
client = self.get_client("deproxy")
350+
server = self.get_server("deproxy")
351+
server.set_response(
352+
"HTTP/1.1 200 OK\r\n"
353+
+ "Date: test\r\n"
354+
+ "Server: debian\r\n"
355+
+ f"{'a' * length}: text\r\n"
356+
+ "Content-Length: 0\r\n\r\n"
357+
)
358+
client.send_request(f"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n", status_code)

t_modify_http_headers/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__all__ = ["test_common_config", "test_common_config_h2", "test_logic"]
2+
3+
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
"""Functional tests for adding user difined headers."""
2+
3+
__author__ = "Tempesta Technologies, Inc."
4+
__copyright__ = "Copyright (C) 2017-2023 Tempesta Technologies, Inc."
5+
__license__ = "GPL2"
6+
7+
from framework import tester
8+
9+
10+
class AddHeaderBase(tester.TempestaTest):
11+
tempesta = {
12+
"config": """
13+
listen 80;
14+
listen 443 proto=h2;
15+
16+
server ${server_ip}:8000;
17+
18+
tls_certificate ${tempesta_workdir}/tempesta.crt;
19+
tls_certificate_key ${tempesta_workdir}/tempesta.key;
20+
tls_match_any_server_name;
21+
""",
22+
}
23+
24+
clients = [
25+
{
26+
"id": "deproxy-1",
27+
"type": "deproxy",
28+
"addr": "${tempesta_ip}",
29+
"port": "80",
30+
},
31+
]
32+
33+
backends = [
34+
{
35+
"id": "deproxy",
36+
"type": "deproxy",
37+
"port": "8000",
38+
"response": "static",
39+
"response_content": (
40+
"HTTP/1.1 200 OK\r\n"
41+
+ "Date: test\r\n"
42+
+ "Server: debian\r\n"
43+
+ "Content-Length: 0\r\n\r\n"
44+
),
45+
}
46+
]
47+
48+
request = (
49+
f"GET / HTTP/1.1\r\n"
50+
+ "Host: localhost\r\n"
51+
+ "Connection: keep-alive\r\n"
52+
+ "Accept: */*\r\n"
53+
+ "\r\n"
54+
)
55+
cache: bool
56+
directive: str
57+
58+
def update_tempesta_config(self, config: str):
59+
tempesta = self.get_tempesta()
60+
cache = "cache 2;\ncache_fulfill * *;\n" if self.cache else "cache 0;"
61+
tempesta.config.defconfig += config + "\n" + cache
62+
63+
def base_scenario(self, config: str, expected_headers: list):
64+
client = self.get_client("deproxy-1")
65+
server = self.get_server("deproxy")
66+
67+
self.update_tempesta_config(config=config)
68+
69+
self.start_all_services()
70+
71+
for _ in range(2 if self.cache else 1):
72+
client.send_request(self.request, "200")
73+
74+
for header in expected_headers:
75+
if self.directive in ["req_hdr_set", "req_hdr_add"]:
76+
self.assertIn(header[1], list(server.last_request.headers.find_all(header[0])))
77+
self.assertNotIn(
78+
header[1], list(client.last_response.headers.find_all(header[0]))
79+
)
80+
else:
81+
self.assertIn(header[1], list(client.last_response.headers.find_all(header[0])))
82+
self.assertNotIn(
83+
header[1], list(server.last_request.headers.find_all(header[0]))
84+
)
85+
86+
return client, server
87+
88+
89+
class TestReqAddHeader(AddHeaderBase):
90+
cache = False
91+
directive = "req_hdr_add"
92+
request = (
93+
f"GET / HTTP/1.1\r\n"
94+
+ "Host: localhost\r\n"
95+
+ "Connection: keep-alive\r\n"
96+
+ "Accept: */*\r\n"
97+
+ "\r\n"
98+
)
99+
100+
def test_add_some_hdrs(self):
101+
client, server = self.base_scenario(
102+
config=(
103+
f'{self.directive} x-my-hdr "some text";\n'
104+
f'{self.directive} x-my-hdr-2 "some other text";\n'
105+
),
106+
expected_headers=[("x-my-hdr", "some text"), ("x-my-hdr-2", "some other text")],
107+
)
108+
return client, server
109+
110+
def test_add_some_hdrs_custom_location(self):
111+
client, server = self.base_scenario(
112+
config=(
113+
'location prefix "/" {\n'
114+
f'{self.directive} x-my-hdr "some text";\n'
115+
f'{self.directive} x-my-hdr-2 "some other text";\n'
116+
"}\n"
117+
),
118+
expected_headers=[("x-my-hdr", "some text"), ("x-my-hdr-2", "some other text")],
119+
)
120+
return client, server
121+
122+
def test_add_hdrs_derive_config(self):
123+
client, server = self.base_scenario(
124+
config=(f'{self.directive} x-my-hdr "some text";\n' 'location prefix "/" {}\n'),
125+
expected_headers=[("x-my-hdr", "some text")],
126+
)
127+
return client, server
128+
129+
def test_add_hdrs_override_config(self):
130+
client, server = self.base_scenario(
131+
config=(
132+
f'{self.directive} x-my-hdr "some text";\n'
133+
'location prefix "/" {\n'
134+
f'{self.directive} x-my-hdr-2 "some other text";\n'
135+
"}\n"
136+
),
137+
expected_headers=[("x-my-hdr-2", "some other text")],
138+
)
139+
140+
if self.directive == "req_hdr_add":
141+
self.assertNotIn(("x-my-hdr", "some text"), server.last_request.headers.items())
142+
else:
143+
self.assertNotIn(("x-my-hdr", "some text"), client.last_response.headers.items())
144+
return client, server
145+
146+
147+
class TestRespAddHeader(TestReqAddHeader):
148+
cache = False
149+
directive = "resp_hdr_add"
150+
151+
152+
class TestCachedRespAddHeader(TestReqAddHeader):
153+
cache = True
154+
directive = "resp_hdr_add"
155+
156+
157+
class TestReqSetHeader(TestReqAddHeader):
158+
directive = "req_hdr_set"
159+
cache = False
160+
request = (
161+
f"GET / HTTP/1.1\r\n"
162+
+ "Host: localhost\r\n"
163+
+ "Connection: keep-alive\r\n"
164+
+ "Accept: */*\r\n"
165+
+ "x-my-hdr: original text\r\n"
166+
+ "x-my-hdr-2: other original text\r\n"
167+
+ "\r\n"
168+
)
169+
backends = [
170+
{
171+
"id": "deproxy",
172+
"type": "deproxy",
173+
"port": "8000",
174+
"response": "static",
175+
"response_content": (
176+
"HTTP/1.1 200 OK\r\n"
177+
+ "Date: test\r\n"
178+
+ "Server: debian\r\n"
179+
+ "x-my-hdr: original text\r\n"
180+
+ "x-my-hdr-2: other original text\r\n"
181+
+ "Content-Length: 0\r\n\r\n"
182+
),
183+
}
184+
]
185+
186+
def header_not_in(self, header):
187+
server = self.get_server("deproxy")
188+
self.assertNotIn(header, server.last_request.headers.items())
189+
190+
def header_in(self, header):
191+
server = self.get_server("deproxy")
192+
self.assertIn(header, server.last_request.headers.items())
193+
194+
def test_add_some_hdrs(self):
195+
super(TestReqSetHeader, self).test_add_some_hdrs()
196+
self.header_not_in(("x-my-hdr", "original text"))
197+
self.header_not_in(("x-my-hdr-2", "other original text"))
198+
199+
def test_add_some_hdrs_custom_location(self):
200+
super(TestReqSetHeader, self).test_add_some_hdrs_custom_location()
201+
self.header_not_in(("x-my-hdr", "original text"))
202+
self.header_not_in(("x-my-hdr-2", "other original text"))
203+
204+
def test_add_hdrs_derive_config(self):
205+
super(TestReqSetHeader, self).test_add_hdrs_derive_config()
206+
self.header_not_in(("x-my-hdr", "original text"))
207+
self.header_in(("x-my-hdr-2", "other original text"))
208+
209+
def test_add_hdrs_override_config(self):
210+
super(TestReqSetHeader, self).test_add_hdrs_override_config()
211+
self.header_in(("x-my-hdr", "original text"))
212+
self.header_not_in(("x-my-hdr-2", "other original text"))
213+
214+
215+
class TestRespSetHeader(TestReqSetHeader):
216+
directive = "resp_hdr_set"
217+
cache = False
218+
219+
def header_in(self, header):
220+
client = self.get_client("deproxy-1")
221+
self.assertIn(header, client.last_response.headers.items())
222+
223+
def header_not_in(self, header):
224+
client = self.get_client("deproxy-1")
225+
self.assertNotIn(header, client.last_response.headers.items())
226+
227+
228+
class TestCachedRespSetHeader(TestRespSetHeader):
229+
cache = True
230+
directive = "resp_hdr_set"

0 commit comments

Comments
 (0)