summaryrefslogtreecommitdiffstats
path: root/test/modules/http2/test_200_header_invalid.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/modules/http2/test_200_header_invalid.py')
-rw-r--r--test/modules/http2/test_200_header_invalid.py182
1 files changed, 182 insertions, 0 deletions
diff --git a/test/modules/http2/test_200_header_invalid.py b/test/modules/http2/test_200_header_invalid.py
new file mode 100644
index 0000000..fdbfbe4
--- /dev/null
+++ b/test/modules/http2/test_200_header_invalid.py
@@ -0,0 +1,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