1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
import pytest
from .env import H2Conf, H2TestEnv
@pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here")
class TestInvalidHeaders:
@pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env):
H2Conf(env).add_vhost_cgi().install()
assert env.apache_restart() == 0
# let the hecho.py CGI echo chars < 0x20 in field name
# for almost all such characters, the stream gets aborted with a h2 error and
# there will be no http status, cr and lf are handled special
def test_h2_200_01(self, env):
url = env.mkurl("https", "cgi", "/hecho.py")
for x in range(1, 32):
r = env.curl_post_data(url, "name=x%%%02xx&value=yz" % x)
if x in [10]:
assert 0 == r.exit_code, "unexpected exit code for char 0x%02x" % x
assert 500 == r.response["status"], "unexpected status for char 0x%02x" % x
elif x in [13]:
assert 0 == r.exit_code, "unexpected exit code for char 0x%02x" % x
assert 200 == r.response["status"], "unexpected status for char 0x%02x" % x
else:
assert 0 != r.exit_code, "unexpected exit code for char 0x%02x" % x
# let the hecho.py CGI echo chars < 0x20 in field value
# for almost all such characters, the stream gets aborted with a h2 error and
# there will be no http status, cr and lf are handled special
def test_h2_200_02(self, env):
url = env.mkurl("https", "cgi", "/hecho.py")
for x in range(1, 32):
if 9 != x:
r = env.curl_post_data(url, "name=x&value=y%%%02x" % x)
if x in [10, 13]:
assert 0 == r.exit_code, "unexpected exit code for char 0x%02x" % x
assert 200 == r.response["status"], "unexpected status for char 0x%02x" % x
else:
assert 0 != r.exit_code, "unexpected exit code for char 0x%02x" % x
# let the hecho.py CGI echo 0x10 and 0x7f in field name and value
def test_h2_200_03(self, env):
url = env.mkurl("https", "cgi", "/hecho.py")
for h in ["10", "7f"]:
r = env.curl_post_data(url, "name=x%%%s&value=yz" % h)
assert 0 != r.exit_code
r = env.curl_post_data(url, "name=x&value=y%%%sz" % h)
assert 0 != r.exit_code
# test header field lengths check, LimitRequestLine (default 8190)
def test_h2_200_10(self, env):
url = env.mkurl("https", "cgi", "/")
val = "1234567890" # 10 chars
for i in range(3): # make a 10000 char string
val = "%s%s%s%s%s%s%s%s%s%s" % (val, val, val, val, val, val, val, val, val, val)
# LimitRequestLine 8190 ok, one more char -> 431
r = env.curl_get(url, options=["-H", "x: %s" % (val[:8187])])
assert r.response["status"] == 200
r = env.curl_get(url, options=["-H", "x: %sx" % (val[:8188])])
assert 431 == r.response["status"]
# same with field name
r = env.curl_get(url, options=["-H", "y%s: 1" % (val[:8186])])
assert r.response["status"] == 200
r = env.curl_get(url, options=["-H", "y%s: 1" % (val[:8188])])
assert 431 == r.response["status"]
# test header field lengths check, LimitRequestFieldSize (default 8190)
def test_h2_200_11(self, env):
url = env.mkurl("https", "cgi", "/")
val = "1234567890" # 10 chars
for i in range(3): # make a 10000 char string
val = "%s%s%s%s%s%s%s%s%s%s" % (val, val, val, val, val, val, val, val, val, val)
# LimitRequestFieldSize 8190 ok, one more char -> 400 in HTTP/1.1
# (we send 4000+4185 since they are concatenated by ", " and start with "x: "
r = env.curl_get(url, options=["-H", "x: %s" % (val[:4000]), "-H", "x: %s" % (val[:4185])])
assert r.response["status"] == 200
r = env.curl_get(url, options=["--http1.1", "-H", "x: %s" % (val[:4000]), "-H", "x: %s" % (val[:4189])])
assert 400 == r.response["status"]
r = env.curl_get(url, options=["-H", "x: %s" % (val[:4000]), "-H", "x: %s" % (val[:4191])])
assert 431 == r.response["status"]
# test header field count, LimitRequestFields (default 100)
# see #201: several headers with same name are mered and count only once
def test_h2_200_12(self, env):
url = env.mkurl("https", "cgi", "/")
opt = []
# curl sends 3 headers itself (user-agent, accept, and our AP-Test-Name)
for i in range(97):
opt += ["-H", "x: 1"]
r = env.curl_get(url, options=opt)
assert r.response["status"] == 200
r = env.curl_get(url, options=(opt + ["-H", "y: 2"]))
assert r.response["status"] == 200
# test header field count, LimitRequestFields (default 100)
# different header names count each
def test_h2_200_13(self, env):
url = env.mkurl("https", "cgi", "/")
opt = []
# curl sends 3 headers itself (user-agent, accept, and our AP-Test-Name)
for i in range(97):
opt += ["-H", f"x{i}: 1"]
r = env.curl_get(url, options=opt)
assert r.response["status"] == 200
r = env.curl_get(url, options=(opt + ["-H", "y: 2"]))
assert 431 == r.response["status"]
# test "LimitRequestFields 0" setting, see #200
def test_h2_200_14(self, env):
conf = H2Conf(env)
conf.add("""
LimitRequestFields 20
""")
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/")
opt = []
for i in range(21):
opt += ["-H", "x{0}: 1".format(i)]
r = env.curl_get(url, options=opt)
assert 431 == r.response["status"]
conf = H2Conf(env)
conf.add("""
LimitRequestFields 0
""")
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/")
opt = []
for i in range(100):
opt += ["-H", "x{0}: 1".format(i)]
r = env.curl_get(url, options=opt)
assert r.response["status"] == 200
# the uri limits
def test_h2_200_15(self, env):
conf = H2Conf(env)
conf.add("""
LimitRequestLine 48
""")
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/")
r = env.curl_get(url)
assert r.response["status"] == 200
url = env.mkurl("https", "cgi", "/" + (48*"x"))
r = env.curl_get(url)
assert 414 == r.response["status"]
# nghttp sends the :method: header first (so far)
# trigger a too long request line on it
# the stream will RST and we get no response
url = env.mkurl("https", "cgi", "/")
opt = ["-H:method: {0}".format(100*"x")]
r = env.nghttp().get(url, options=opt)
assert r.exit_code == 0, r
assert not r.response
# invalid chars in method
def test_h2_200_16(self, env):
conf = H2Conf(env)
conf.add_vhost_cgi()
conf.install()
assert env.apache_restart() == 0
url = env.mkurl("https", "cgi", "/hello.py")
opt = ["-H:method: GET /hello.py"]
r = env.nghttp().get(url, options=opt)
assert r.exit_code == 0, r
# nghttp version >= 1.45.0 check pseudo headers and RST streams,
# which means we see no response.
if r.response is not None:
assert r.response["status"] == 400
url = env.mkurl("https", "cgi", "/proxy/hello.py")
r = env.nghttp().get(url, options=opt)
assert r.exit_code == 0, r
if r.response is not None:
assert r.response["status"] == 400
|