You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/08/20 16:07:45 UTC
svn commit: r1892476 [11/12] - in /httpd/httpd/trunk: ./ test/ test/modules/
test/modules/http2/ test/modules/http2/conf/ test/modules/http2/data/
test/modules/http2/htdocs/ test/modules/http2/htdocs/cgi/
test/modules/http2/htdocs/cgi/files/ test/modul...
Added: httpd/httpd/trunk/test/modules/http2/test_004_post.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_004_post.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_004_post.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_004_post.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,217 @@
+import email.parser
+import json
+import os
+import re
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ env.setup_data_1k_1m()
+ HttpdConf(env).add_vhost_cgi().install()
+ assert env.apache_restart() == 0
+
+ # upload and GET again using curl, compare to original content
+ def curl_upload_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/upload.py")
+ fpath = os.path.join(env.gen_dir, fname)
+ r = env.curl_upload(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert r.response["status"] >= 200 and r.response["status"] < 300
+
+ r2 = env.curl_get(r.response["header"]["location"])
+ assert r2.exit_code == 0
+ assert r2.response["status"] == 200
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r2.response["body"]
+
+ def test_004_01(self, env):
+ self.curl_upload_and_verify(env, "data-1k", ["--http1.1"])
+ self.curl_upload_and_verify(env, "data-1k", ["--http2"])
+
+ def test_004_02(self, env):
+ self.curl_upload_and_verify(env, "data-10k", ["--http1.1"])
+ self.curl_upload_and_verify(env, "data-10k", ["--http2"])
+
+ def test_004_03(self, env):
+ self.curl_upload_and_verify(env, "data-100k", ["--http1.1"])
+ self.curl_upload_and_verify(env, "data-100k", ["--http2"])
+
+ def test_004_04(self, env):
+ self.curl_upload_and_verify(env, "data-1m", ["--http1.1"])
+ self.curl_upload_and_verify(env, "data-1m", ["--http2"])
+
+ def test_004_05(self, env):
+ self.curl_upload_and_verify(env, "data-1k", ["-v", "--http1.1", "-H", "Expect: 100-continue"])
+ self.curl_upload_and_verify(env, "data-1k", ["-v", "--http2", "-H", "Expect: 100-continue"])
+
+ @pytest.mark.skipif(True, reason="python3 regresses in chunked inputs to cgi")
+ def test_004_06(self, env):
+ self.curl_upload_and_verify(env, "data-1k", ["--http1.1", "-H", "Content-Length: "])
+ self.curl_upload_and_verify(env, "data-1k", ["--http2", "-H", "Content-Length: "])
+
+ @pytest.mark.parametrize("name, value", [
+ ("HTTP2", "on"),
+ ("H2PUSH", "off"),
+ ("H2_PUSHED", ""),
+ ("H2_PUSHED_ON", ""),
+ ("H2_STREAM_ID", "1"),
+ ("H2_STREAM_TAG", r'\d+-1'),
+ ])
+ def test_004_07(self, env, name, value):
+ url = env.mkurl("https", "cgi", "/env.py")
+ r = env.curl_post_value(url, "name", name)
+ assert r.exit_code == 0
+ assert r.response["status"] == 200
+ m = re.match("{0}=(.*)".format(name), r.response["body"].decode('utf-8'))
+ assert m
+ assert re.match(value, m.group(1))
+
+ # verify that we parse nghttp output correctly
+ def check_nghttp_body(self, env, ref_input, nghttp_output):
+ with open(env.test_src(os.path.join(env.gen_dir, ref_input)), mode='rb') as f:
+ refbody = f.read()
+ with open(env.test_src(nghttp_output), mode='rb') as f:
+ text = f.read()
+ o = env.nghttp().parse_output(text)
+ assert "response" in o
+ assert "body" in o["response"]
+ if refbody != o["response"]["body"]:
+ with open(env.test_src(os.path.join(env.gen_dir, '%s.parsed' % ref_input)), mode='bw') as f:
+ f.write(o["response"]["body"])
+ assert len(refbody) == len(o["response"]["body"])
+ assert refbody == o["response"]["body"]
+
+ def test_004_20(self, env):
+ self.check_nghttp_body(env, 'data-1k', 'data/nghttp-output-1k-1.txt')
+ self.check_nghttp_body(env, 'data-10k', 'data/nghttp-output-10k-1.txt')
+ self.check_nghttp_body(env, 'data-100k', 'data/nghttp-output-100k-1.txt')
+
+ # POST some data using nghttp and see it echo'ed properly back
+ def nghttp_post_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ fpath = os.path.join(env.gen_dir, fname)
+
+ r = env.nghttp().upload(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert r.response["status"] >= 200 and r.response["status"] < 300
+
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r.response["body"]
+
+ def test_004_21(self, env):
+ self.nghttp_post_and_verify(env, "data-1k", [])
+ self.nghttp_post_and_verify(env, "data-10k", [])
+ self.nghttp_post_and_verify(env, "data-100k", [])
+ self.nghttp_post_and_verify(env, "data-1m", [])
+
+ def test_004_22(self, env):
+ self.nghttp_post_and_verify(env, "data-1k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-10k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-100k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-1m", ["--no-content-length"])
+
+ # upload and GET again using nghttp, compare to original content
+ def nghttp_upload_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/upload.py")
+ fpath = os.path.join(env.gen_dir, fname)
+
+ r = env.nghttp().upload_file(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert r.response["status"] >= 200 and r.response["status"] < 300
+ assert r.response["header"]["location"]
+
+ r2 = env.nghttp().get(r.response["header"]["location"])
+ assert r2.exit_code == 0
+ assert r2.response["status"] == 200
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r2.response["body"]
+
+ def test_004_23(self, env):
+ self.nghttp_upload_and_verify(env, "data-1k", [])
+ self.nghttp_upload_and_verify(env, "data-10k", [])
+ self.nghttp_upload_and_verify(env, "data-100k", [])
+ self.nghttp_upload_and_verify(env, "data-1m", [])
+
+ def test_004_24(self, env):
+ self.nghttp_upload_and_verify(env, "data-1k", ["--expect-continue"])
+ self.nghttp_upload_and_verify(env, "data-100k", ["--expect-continue"])
+
+ @pytest.mark.skipif(True, reason="python3 regresses in chunked inputs to cgi")
+ def test_004_25(self, env):
+ self.nghttp_upload_and_verify(env, "data-1k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-10k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-100k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-1m", ["--no-content-length"])
+
+ def test_004_30(self, env):
+ # issue: #203
+ resource = "data-1k"
+ full_length = 1000
+ chunk = 200
+ self.curl_upload_and_verify(env, resource, ["-v", "--http2"])
+ logfile = os.path.join(env.server_logs_dir, "test_004_30")
+ if os.path.isfile(logfile):
+ os.remove(logfile)
+ HttpdConf(env).add("""
+LogFormat "{ \\"request\\": \\"%r\\", \\"status\\": %>s, \\"bytes_resp_B\\": %B, \\"bytes_tx_O\\": %O, \\"bytes_rx_I\\": %I, \\"bytes_rx_tx_S\\": %S }" issue_203
+CustomLog logs/test_004_30 issue_203
+ """).add_vhost_cgi().install()
+ assert env.apache_restart() == 0
+ url = env.mkurl("https", "cgi", "/files/{0}".format(resource))
+ r = env.curl_get(url, 5, ["--http2"])
+ assert 200 == r.response["status"]
+ r = env.curl_get(url, 5, ["--http1.1", "-H", "Range: bytes=0-{0}".format(chunk-1)])
+ assert 206 == r.response["status"]
+ assert chunk == len(r.response["body"].decode('utf-8'))
+ r = env.curl_get(url, 5, ["--http2", "-H", "Range: bytes=0-{0}".format(chunk-1)])
+ assert 206 == r.response["status"]
+ assert chunk == len(r.response["body"].decode('utf-8'))
+ # now check what response lengths have actually been reported
+ lines = open(logfile).readlines()
+ log_h2_full = json.loads(lines[-3])
+ log_h1 = json.loads(lines[-2])
+ log_h2 = json.loads(lines[-1])
+ assert log_h2_full['bytes_rx_I'] > 0
+ assert log_h2_full['bytes_resp_B'] == full_length
+ assert log_h2_full['bytes_tx_O'] > full_length
+ assert log_h1['bytes_rx_I'] > 0 # input bytes recieved
+ assert log_h1['bytes_resp_B'] == chunk # response bytes sent (payload)
+ assert log_h1['bytes_tx_O'] > chunk # output bytes sent
+ assert log_h2['bytes_rx_I'] > 0
+ assert log_h2['bytes_resp_B'] == chunk
+ assert log_h2['bytes_tx_O'] > chunk
+
+ def test_004_40(self, env):
+ # echo content using h2test_module "echo" handler
+ def post_and_verify(fname, options=None):
+ url = env.mkurl("https", "cgi", "/h2test/echo")
+ fpath = os.path.join(env.gen_dir, fname)
+ r = env.curl_upload(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert r.response["status"] >= 200 and r.response["status"] < 300
+
+ ct = r.response["header"]["content-type"]
+ mail_hd = "Content-Type: " + ct + "\r\nMIME-Version: 1.0\r\n\r\n"
+ mime_msg = mail_hd.encode() + r.response["body"]
+ # this MIME API is from hell
+ body = email.parser.BytesParser().parsebytes(mime_msg)
+ assert body
+ assert body.is_multipart()
+ filepart = None
+ for part in body.walk():
+ if fname == part.get_filename():
+ filepart = part
+ assert filepart
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == filepart.get_payload(decode=True)
+
+ post_and_verify("data-1k", [])
Added: httpd/httpd/trunk/test/modules/http2/test_005_status.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_005_status.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_005_status.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_005_status.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,70 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_cgi().install()
+ assert env.apache_restart() == 0
+
+ def test_005_01(self, env):
+ url = env.mkurl("https", "cgi", "/.well-known/h2/state")
+ r = env.curl_get(url, 5)
+ assert 200 == r.response["status"]
+ st = r.response["json"]
+
+ # remove some parts that are very dependant on client/lib versions
+ # or connection time etc.
+ del st["settings"]["SETTINGS_INITIAL_WINDOW_SIZE"]
+ del st["peerSettings"]["SETTINGS_INITIAL_WINDOW_SIZE"]
+ del st["streams"]["1"]["created"]
+ del st["streams"]["1"]["flowOut"]
+ del st["stats"]["in"]["frames"]
+ del st["stats"]["in"]["octets"]
+ del st["stats"]["out"]["frames"]
+ del st["stats"]["out"]["octets"]
+ del st["connFlowOut"]
+
+ assert st == {
+ "version": "draft-01",
+ "settings": {
+ "SETTINGS_MAX_CONCURRENT_STREAMS": 100,
+ "SETTINGS_MAX_FRAME_SIZE": 16384,
+ "SETTINGS_ENABLE_PUSH": 0
+ },
+ "peerSettings": {
+ "SETTINGS_MAX_CONCURRENT_STREAMS": 100,
+ "SETTINGS_MAX_FRAME_SIZE": 16384,
+ "SETTINGS_ENABLE_PUSH": 0,
+ "SETTINGS_HEADER_TABLE_SIZE": 4096,
+ "SETTINGS_MAX_HEADER_LIST_SIZE": -1
+ },
+ "connFlowIn": 2147483647,
+ "sentGoAway": 0,
+ "streams": {
+ "1": {
+ "state": "HALF_CLOSED_REMOTE",
+ "flowIn": 65535,
+ "dataIn": 0,
+ "dataOut": 0
+ }
+ },
+ "stats": {
+ "in": {
+ "requests": 1,
+ "resets": 0,
+ },
+ "out": {
+ "responses": 0,
+ },
+ "push": {
+ "cacheDigest": "AQg",
+ "promises": 0,
+ "submits": 0,
+ "resets": 0
+ }
+ }
+ }
Added: httpd/httpd/trunk/test/modules/http2/test_006_assets.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_006_assets.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_006_assets.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_006_assets.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,74 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_test1().install()
+ assert env.apache_restart() == 0
+
+ # single page without any assets
+ def test_006_01(self, env):
+ url = env.mkurl("https", "test1", "/001.html")
+ r = env.nghttp().assets(url, options=["-Haccept-encoding: none"])
+ assert 0 == r.exit_code
+ assert 1 == len(r.assets)
+ assert r.assets == [
+ {"status": 200, "size": "251", "path": "/001.html"}
+ ]
+
+ # single image without any assets
+ def test_006_02(self, env):
+ url = env.mkurl("https", "test1", "/002.jpg")
+ r = env.nghttp().assets(url, options=["-Haccept-encoding: none"])
+ assert 0 == r.exit_code
+ assert 1 == len(r.assets)
+ assert r.assets == [
+ {"status": 200, "size": "88K", "path": "/002.jpg"}
+ ]
+
+ # gophertiles, yea!
+ def test_006_03(self, env):
+ # create the tiles files we originally had checked in
+ exp_assets = [
+ {"status": 200, "size": "10K", "path": "/004.html"},
+ {"status": 200, "size": "742", "path": "/004/gophertiles.jpg"},
+ ]
+ for i in range(2, 181):
+ with open(f"{env.server_docs_dir}/test1/004/gophertiles_{i:03d}.jpg", "w") as fd:
+ fd.write("0123456789\n")
+ exp_assets.append(
+ {"status": 200, "size": "11", "path": f"/004/gophertiles_{i:03d}.jpg"},
+ )
+
+ url = env.mkurl("https", "test1", "/004.html")
+ r = env.nghttp().assets(url, options=["-Haccept-encoding: none"])
+ assert 0 == r.exit_code
+ assert 181 == len(r.assets)
+ assert r.assets == exp_assets
+
+ # page with js and css
+ def test_006_04(self, env):
+ url = env.mkurl("https", "test1", "/006.html")
+ r = env.nghttp().assets(url, options=["-Haccept-encoding: none"])
+ assert 0 == r.exit_code
+ assert 3 == len(r.assets)
+ assert r.assets == [
+ {"status": 200, "size": "543", "path": "/006.html"},
+ {"status": 200, "size": "216", "path": "/006/006.css"},
+ {"status": 200, "size": "839", "path": "/006/006.js"}
+ ]
+
+ # page with image, try different window size
+ def test_006_05(self, env):
+ url = env.mkurl("https", "test1", "/003.html")
+ r = env.nghttp().assets(url, options=["--window-bits=24", "-Haccept-encoding: none"])
+ assert 0 == r.exit_code
+ assert 2 == len(r.assets)
+ assert r.assets == [
+ {"status": 200, "size": "316", "path": "/003.html"},
+ {"status": 200, "size": "88K", "path": "/003/003_img.jpg"}
+ ]
Added: httpd/httpd/trunk/test/modules/http2/test_100_conn_reuse.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_100_conn_reuse.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_100_conn_reuse.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_100_conn_reuse.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,56 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_noh2().add_vhost_test1().add_vhost_cgi().install()
+ assert env.apache_restart() == 0
+
+ # make sure the protocol selection on the different hosts work as expected
+ def test_100_01(self, env):
+ # this host defaults to h2, but we can request h1
+ url = env.mkurl("https", "cgi", "/hello.py")
+ assert "2" == env.curl_protocol_version( url )
+ assert "1.1" == env.curl_protocol_version( url, options=[ "--http1.1" ] )
+
+ # this host does not enable h2, it always falls back to h1
+ url = env.mkurl("https", "noh2", "/hello.py")
+ assert "1.1" == env.curl_protocol_version( url )
+ assert "1.1" == env.curl_protocol_version( url, options=[ "--http2" ] )
+
+ # access a ServerAlias, after using ServerName in SNI
+ def test_100_02(self, env):
+ url = env.mkurl("https", "cgi", "/hello.py")
+ hostname = ("cgi-alias.%s" % env.http_tld)
+ r = env.curl_get(url, 5, [ "-H", "Host:%s" % hostname ])
+ assert 200 == r.response["status"]
+ assert "HTTP/2" == r.response["protocol"]
+ assert hostname == r.response["json"]["host"]
+
+ # access another vhost, after using ServerName in SNI, that uses same SSL setup
+ def test_100_03(self, env):
+ url = env.mkurl("https", "cgi", "/")
+ hostname = ("test1.%s" % env.http_tld)
+ r = env.curl_get(url, 5, [ "-H", "Host:%s" % hostname ])
+ assert 200 == r.response["status"]
+ assert "HTTP/2" == r.response["protocol"]
+ assert "text/html" == r.response["header"]["content-type"]
+
+ # access another vhost, after using ServerName in SNI,
+ # that has different SSL certificate. This triggers a 421 (misdirected request) response.
+ def test_100_04(self, env):
+ url = env.mkurl("https", "cgi", "/hello.py")
+ hostname = ("noh2.%s" % env.http_tld)
+ r = env.curl_get(url, 5, [ "-H", "Host:%s" % hostname ])
+ assert 421 == r.response["status"]
+
+ # access an unknown vhost, after using ServerName in SNI
+ def test_100_05(self, env):
+ url = env.mkurl("https", "cgi", "/hello.py")
+ hostname = ("unknown.%s" % env.http_tld)
+ r = env.curl_get(url, 5, [ "-H", "Host:%s" % hostname ])
+ assert 421 == r.response["status"]
Added: httpd/httpd/trunk/test/modules/http2/test_101_ssl_reneg.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_101_ssl_reneg.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_101_ssl_reneg.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_101_ssl_reneg.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,126 @@
+import re
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add(
+ f"""
+ SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384
+ <Directory \"{env.server_dir}/htdocs/ssl-client-verify\">
+ Require all granted
+ SSLVerifyClient require
+ SSLVerifyDepth 0
+ </Directory>"""
+ ).start_vhost(
+ env.https_port, "ssl", with_ssl=True
+ ).add(
+ f"""
+ Protocols h2 http/1.1"
+ <Location /renegotiate/cipher>
+ SSLCipherSuite ECDHE-RSA-CHACHA20-POLY1305
+ </Location>
+ <Location /renegotiate/err-doc-cipher>
+ SSLCipherSuite ECDHE-RSA-CHACHA20-POLY1305
+ ErrorDocument 403 /forbidden.html
+ </Location>
+ <Location /renegotiate/verify>
+ SSLVerifyClient require
+ </Location>
+ <Directory \"{env.server_dir}/htdocs/sslrequire\">
+ SSLRequireSSL
+ </Directory>
+ <Directory \"{env.server_dir}/htdocs/requiressl\">
+ Require ssl
+ </Directory>"""
+ ).end_vhost().install()
+ # the dir needs to exists for the configuration to have effect
+ env.mkpath("%s/htdocs/ssl-client-verify" % env.server_dir)
+ env.mkpath("%s/htdocs/renegotiate/cipher" % env.server_dir)
+ env.mkpath("%s/htdocs/sslrequire" % env.server_dir)
+ env.mkpath("%s/htdocs/requiressl" % env.server_dir)
+ assert env.apache_restart() == 0
+
+ # access a resource with SSL renegotiation, using HTTP/1.1
+ def test_101_01(self, env):
+ url = env.mkurl("https", "ssl", "/renegotiate/cipher/")
+ r = env.curl_get(url, options=["-v", "--http1.1", "--tlsv1.2", "--tls-max", "1.2"])
+ assert 0 == r.exit_code
+ assert r.response
+ assert 403 == r.response["status"]
+
+ # try to renegotiate the cipher, should fail with correct code
+ def test_101_02(self, env):
+ url = env.mkurl("https", "ssl", "/renegotiate/cipher/")
+ r = env.curl_get(url, options=[
+ "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384"
+ ])
+ assert 0 != r.exit_code
+ assert not r.response
+ assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
+
+ # try to renegotiate a client certificate from Location
+ # needs to fail with correct code
+ def test_101_03(self, env):
+ url = env.mkurl("https", "ssl", "/renegotiate/verify/")
+ r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"])
+ assert 0 != r.exit_code
+ assert not r.response
+ assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
+
+ # try to renegotiate a client certificate from Directory
+ # needs to fail with correct code
+ def test_101_04(self, env):
+ url = env.mkurl("https", "ssl", "/ssl-client-verify/index.html")
+ r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"])
+ assert 0 != r.exit_code
+ assert not r.response
+ assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
+
+ # make 10 requests on the same connection, none should produce a status code
+ # reported by erki@example.ee
+ def test_101_05(self, env):
+ r = env.run([env.h2load, "-n", "10", "-c", "1", "-m", "1", "-vvvv",
+ f"{env.https_base_url}/ssl-client-verify/index.html"])
+ assert 0 == r.exit_code
+ r = env.h2load_status(r)
+ assert 10 == r.results["h2load"]["requests"]["total"]
+ assert 10 == r.results["h2load"]["requests"]["started"]
+ assert 10 == r.results["h2load"]["requests"]["done"]
+ assert 0 == r.results["h2load"]["requests"]["succeeded"]
+ assert 0 == r.results["h2load"]["status"]["2xx"]
+ assert 0 == r.results["h2load"]["status"]["3xx"]
+ assert 0 == r.results["h2load"]["status"]["4xx"]
+ assert 0 == r.results["h2load"]["status"]["5xx"]
+
+ # Check that "SSLRequireSSL" works on h2 connections
+ # See <https://bz.apache.org/bugzilla/show_bug.cgi?id=62654>
+ def test_101_10a(self, env):
+ url = env.mkurl("https", "ssl", "/sslrequire/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert 404 == r.response["status"]
+
+ # Check that "require ssl" works on h2 connections
+ # See <https://bz.apache.org/bugzilla/show_bug.cgi?id=62654>
+ def test_101_10b(self, env):
+ url = env.mkurl("https", "ssl", "/requiressl/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert 404 == r.response["status"]
+
+ # Check that status works with ErrorDoc, see pull #174, fixes #172
+ def test_101_11(self, env):
+ url = env.mkurl("https", "ssl", "/renegotiate/err-doc-cipher")
+ r = env.curl_get(url, options=[
+ "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384"
+ ])
+ assert 0 != r.exit_code
+ assert not r.response
+ assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
Added: httpd/httpd/trunk/test/modules/http2/test_102_require.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_102_require.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_102_require.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_102_require.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,38 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ conf = HttpdConf(env).start_vhost(env.https_port, "ssl", with_ssl=True)
+ conf.add("""
+ Protocols h2 http/1.1
+ SSLOptions +StdEnvVars
+ <Location /h2only.html>
+ Require expr \"%{HTTP2} == 'on'\"
+ </Location>
+ <Location /noh2.html>
+ Require expr \"%{HTTP2} == 'off'\"
+ </Location>""")
+ conf.end_vhost()
+ conf.install()
+ # the dir needs to exists for the configuration to have effect
+ env.mkpath("%s/htdocs/ssl-client-verify" % env.server_dir)
+ assert env.apache_restart() == 0
+
+ def test_102_01(self, env):
+ url = env.mkurl("https", "ssl", "/h2only.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert 404 == r.response["status"]
+
+ def test_102_02(self, env):
+ url = env.mkurl("https", "ssl", "/noh2.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert 403 == r.response["status"]
Added: httpd/httpd/trunk/test/modules/http2/test_103_upgrade.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_103_upgrade.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_103_upgrade.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_103_upgrade.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,119 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_test1().add_vhost_test2().add_vhost_noh2(
+ ).start_vhost(
+ env.https_port, "test3", doc_root="htdocs/test1", with_ssl=True
+ ).add(
+ """
+ Protocols h2 http/1.1
+ Header unset Upgrade"""
+ ).end_vhost(
+ ).start_vhost(
+ env.http_port, "test1b", doc_root="htdocs/test1", with_ssl=False
+ ).add(
+ """
+ Protocols h2c http/1.1
+ H2Upgrade off
+ <Location /006.html>
+ H2Upgrade on
+ </Location>"""
+ ).end_vhost(
+ ).install()
+ assert env.apache_restart() == 0
+
+ # accessing http://test1, will not try h2 and advertise h2 in the response
+ def test_103_01(self, env):
+ url = env.mkurl("http", "test1", "/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" in r.response["header"]
+ assert "h2c" == r.response["header"]["upgrade"]
+
+ # accessing http://noh2, will not advertise, because noh2 host does not have it enabled
+ def test_103_02(self, env):
+ url = env.mkurl("http", "noh2", "/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" not in r.response["header"]
+
+ # accessing http://test2, will not advertise, because h2 has less preference than http/1.1
+ def test_103_03(self, env):
+ url = env.mkurl("http", "test2", "/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" not in r.response["header"]
+
+ # accessing https://noh2, will not advertise, because noh2 host does not have it enabled
+ def test_103_04(self, env):
+ url = env.mkurl("https", "noh2", "/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" not in r.response["header"]
+
+ # accessing https://test2, will not advertise, because h2 has less preference than http/1.1
+ def test_103_05(self, env):
+ url = env.mkurl("https", "test2", "/index.html")
+ r = env.curl_get(url)
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" not in r.response["header"]
+
+ # accessing https://test1, will advertise h2 in the response
+ def test_103_06(self, env):
+ url = env.mkurl("https", "test1", "/index.html")
+ r = env.curl_get(url, options=["--http1.1"])
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" in r.response["header"]
+ assert "h2" == r.response["header"]["upgrade"]
+
+ # accessing https://test3, will not send Upgrade since it is suppressed
+ def test_103_07(self, env):
+ url = env.mkurl("https", "test3", "/index.html")
+ r = env.curl_get(url, options=["--http1.1"])
+ assert 0 == r.exit_code
+ assert r.response
+ assert "upgrade" not in r.response["header"]
+
+ # upgrade to h2c for a request, where h2c is preferred
+ def test_103_20(self, env):
+ url = env.mkurl("http", "test1", "/index.html")
+ r = env.nghttp().get(url, options=["-u"])
+ assert 200 == r.response["status"]
+
+ # upgrade to h2c for a request where http/1.1 is preferred, but the clients upgrade
+ # wish is honored nevertheless
+ def test_103_21(self, env):
+ url = env.mkurl("http", "test2", "/index.html")
+ r = env.nghttp().get(url, options=["-u"])
+ assert 404 == r.response["status"]
+
+ # ugrade to h2c on a host where h2c is not enabled will fail
+ def test_103_22(self, env):
+ url = env.mkurl("http", "noh2", "/index.html")
+ r = env.nghttp().get(url, options=["-u"])
+ assert not r.response
+
+ # ugrade to h2c on a host where h2c is preferred, but Upgrade is disabled
+ def test_103_23(self, env):
+ url = env.mkurl("http", "test1b", "/index.html")
+ r = env.nghttp().get(url, options=["-u"])
+ assert not r.response
+
+ # ugrade to h2c on a host where h2c is preferred, but Upgrade is disabled on the server,
+ # but allowed for a specific location
+ def test_103_24(self, env):
+ url = env.mkurl("http", "test1b", "/006.html")
+ r = env.nghttp().get(url, options=["-u"])
+ assert 200 == r.response["status"]
Added: httpd/httpd/trunk/test/modules/http2/test_104_padding.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_104_padding.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_104_padding.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_104_padding.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,100 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+def frame_padding(payload, padbits):
+ mask = (1 << padbits) - 1
+ return ((payload + 9 + mask) & ~mask) - (payload + 9)
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ conf = HttpdConf(env)
+ conf.add_vhost_cgi()
+ conf.start_vhost(env.https_port, "pad0", doc_root="htdocs/cgi", with_ssl=True)
+ conf.add("Protocols h2 http/1.1")
+ conf.add("H2Padding 0")
+ conf.add("AddHandler cgi-script .py")
+ conf.end_vhost()
+ conf.start_vhost(env.https_port, "pad1", doc_root="htdocs/cgi", with_ssl=True)
+ conf.add("Protocols h2 http/1.1")
+ conf.add("H2Padding 1")
+ conf.add("AddHandler cgi-script .py")
+ conf.end_vhost()
+ conf.start_vhost(env.https_port, "pad2", doc_root="htdocs/cgi", with_ssl=True)
+ conf.add("Protocols h2 http/1.1")
+ conf.add("H2Padding 2")
+ conf.add("AddHandler cgi-script .py")
+ conf.end_vhost()
+ conf.start_vhost(env.https_port, "pad3", doc_root="htdocs/cgi", with_ssl=True)
+ conf.add("Protocols h2 http/1.1")
+ conf.add("H2Padding 3")
+ conf.add("AddHandler cgi-script .py")
+ conf.end_vhost()
+ conf.start_vhost(env.https_port, "pad8", doc_root="htdocs/cgi", with_ssl=True)
+ conf.add("Protocols h2 http/1.1")
+ conf.add("H2Padding 8")
+ conf.add("AddHandler cgi-script .py")
+ conf.end_vhost()
+ conf.install()
+ assert env.apache_restart() == 0
+
+ # default paddings settings: 0 bits
+ def test_104_01(self, env):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ # we get 2 frames back: one with data and an empty one with EOF
+ # check the number of padding bytes is as expected
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ assert r.results["paddings"] == [
+ frame_padding(len(data)+1, 0),
+ frame_padding(0, 0)
+ ]
+
+ # 0 bits of padding
+ def test_104_02(self, env):
+ url = env.mkurl("https", "pad0", "/echo.py")
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ assert r.results["paddings"] == [0, 0]
+
+ # 1 bit of padding
+ def test_104_03(self, env):
+ url = env.mkurl("https", "pad1", "/echo.py")
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ for i in r.results["paddings"]:
+ assert i in range(0, 2)
+
+ # 2 bits of padding
+ def test_104_04(self, env):
+ url = env.mkurl("https", "pad2", "/echo.py")
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ for i in r.results["paddings"]:
+ assert i in range(0, 4)
+
+ # 3 bits of padding
+ def test_104_05(self, env):
+ url = env.mkurl("https", "pad3", "/echo.py")
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ for i in r.results["paddings"]:
+ assert i in range(0, 8)
+
+ # 8 bits of padding
+ def test_104_06(self, env):
+ url = env.mkurl("https", "pad8", "/echo.py")
+ for data in ["x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx", "xxxxxxx", "xxxxxxxx"]:
+ r = env.nghttp().post_data(url, data, 5)
+ assert 200 == r.response["status"]
+ for i in r.results["paddings"]:
+ assert i in range(0, 256)
Added: httpd/httpd/trunk/test/modules/http2/test_105_timeout.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_105_timeout.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_105_timeout.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_105_timeout.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,96 @@
+import socket
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ # Check that base servers 'Timeout' setting is observed on SSL handshake
+ def test_105_01(self, env):
+ conf = HttpdConf(env)
+ conf.add("""
+ AcceptFilter http none
+ Timeout 1.5
+ """)
+ conf.add_vhost_cgi()
+ conf.install()
+ assert env.apache_restart() == 0
+ host = 'localhost'
+ # read with a longer timeout than the server
+ sock = socket.create_connection((host, int(env.https_port)))
+ try:
+ # on some OS, the server does not see our connection until there is
+ # something incoming
+ sock.send(b'0')
+ sock.settimeout(4)
+ buff = sock.recv(1024)
+ assert buff == b''
+ except Exception as ex:
+ print(f"server did not close in time: {ex}")
+ assert False
+ sock.close()
+ # read with a shorter timeout than the server
+ sock = socket.create_connection((host, int(env.https_port)))
+ try:
+ sock.settimeout(0.5)
+ sock.recv(1024)
+ assert False
+ except Exception as ex:
+ print(f"as expected: {ex}")
+ sock.close()
+
+ # Check that mod_reqtimeout handshake setting takes effect
+ def test_105_02(self, env):
+ conf = HttpdConf(env)
+ conf.add("""
+ AcceptFilter http none
+ Timeout 10
+ RequestReadTimeout handshake=1 header=5 body=10
+ """)
+ conf.add_vhost_cgi()
+ conf.install()
+ assert env.apache_restart() == 0
+ host = 'localhost'
+ # read with a longer timeout than the server
+ sock = socket.create_connection((host, int(env.https_port)))
+ try:
+ # on some OS, the server does not see our connection until there is
+ # something incoming
+ sock.send(b'0')
+ sock.settimeout(4)
+ buff = sock.recv(1024)
+ assert buff == b''
+ except Exception as ex:
+ print(f"server did not close in time: {ex}")
+ assert False
+ sock.close()
+ # read with a shorter timeout than the server
+ sock = socket.create_connection((host, int(env.https_port)))
+ try:
+ sock.settimeout(0.5)
+ sock.recv(1024)
+ assert False
+ except Exception as ex:
+ print(f"as expected: {ex}")
+ sock.close()
+
+ # Check that mod_reqtimeout handshake setting do no longer apply to handshaked
+ # connections. See <https://github.com/icing/mod_h2/issues/196>.
+ def test_105_03(self, env):
+ conf = HttpdConf(env)
+ conf.add("""
+ Timeout 10
+ RequestReadTimeout handshake=1 header=5 body=10
+ """)
+ conf.add_vhost_cgi()
+ conf.install()
+ assert env.apache_restart() == 0
+ url = env.mkurl("https", "cgi", "/necho.py")
+ r = env.curl_get(url, 5, [
+ "-vvv",
+ "-F", ("count=%d" % 100),
+ "-F", ("text=%s" % "abcdefghijklmnopqrstuvwxyz"),
+ "-F", ("wait1=%f" % 1.5),
+ ])
+ assert 200 == r.response["status"]
Added: httpd/httpd/trunk/test/modules/http2/test_106_shutdown.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_106_shutdown.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_106_shutdown.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_106_shutdown.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,45 @@
+#
+# mod-h2 test suite
+# check HTTP/2 timeout behaviour
+#
+import time
+from threading import Thread
+
+import pytest
+
+from h2_conf import HttpdConf
+from h2_result import ExecResult
+
+
+class TestShutdown:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ conf = HttpdConf(env)
+ conf.add_vhost_cgi()
+ conf.install()
+ assert env.apache_restart() == 0
+
+ def test_106_01(self, env):
+ url = env.mkurl("https", "cgi", "/necho.py")
+ lines = 100000
+ text = "123456789"
+ wait2 = 1.0
+ self.r = None
+ def long_request():
+ args = ["-vvv",
+ "-F", f"count={lines}",
+ "-F", f"text={text}",
+ "-F", f"wait2={wait2}",
+ ]
+ self.r = env.curl_get(url, 5, args)
+
+ t = Thread(target=long_request)
+ t.start()
+ time.sleep(0.5)
+ assert env.apache_restart() == 0
+ t.join()
+ # noinspection PyTypeChecker
+ r: ExecResult = self.r
+ assert r.response["status"] == 200
+ assert len(r.response["body"]) == (lines * (len(text)+1))
Added: httpd/httpd/trunk/test/modules/http2/test_200_header_invalid.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_200_header_invalid.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_200_header_invalid.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_200_header_invalid.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,172 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(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_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_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_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_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 200 == r.response["status"]
+ r = env.curl_get(url, options=["-H", "x: %sx" % (val[:8188])])
+ assert 431 == r.response["status"]
+
+ # test header field lengths check, LimitRequestFieldSize (default 8190)
+ def test_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 200 == r.response["status"]
+ 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_200_12(self, env):
+ url = env.mkurl("https", "cgi", "/")
+ opt = []
+ for i in range(98): # curl sends 2 headers itself (user-agent and accept)
+ opt += ["-H", "x: 1"]
+ r = env.curl_get(url, options=opt)
+ assert 200 == r.response["status"]
+ r = env.curl_get(url, options=(opt + ["-H", "y: 2"]))
+ assert 200 == r.response["status"]
+
+ # test header field count, LimitRequestFields (default 100)
+ # different header names count each
+ def test_200_13(self, env):
+ url = env.mkurl("https", "cgi", "/")
+ opt = []
+ for i in range(98): # curl sends 2 headers itself (user-agent and accept)
+ opt += ["-H", "x{0}: 1".format(i)]
+ r = env.curl_get(url, options=opt)
+ assert 200 == r.response["status"]
+ r = env.curl_get(url, options=(opt + ["-H", "y: 2"]))
+ assert 431 == r.response["status"]
+
+ # test "LimitRequestFields 0" setting, see #200
+ def test_200_14(self, env):
+ conf = HttpdConf(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 = HttpdConf(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 200 == r.response["status"]
+
+ # the uri limits
+ def test_200_15(self, env):
+ conf = HttpdConf(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 200 == r.response["status"]
+ 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_200_16(self, env):
+ conf = HttpdConf(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
+ assert r.response
+ 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
+ assert r.response
+ assert r.response["status"] == 400
Added: httpd/httpd/trunk/test/modules/http2/test_201_header_conditional.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_201_header_conditional.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_201_header_conditional.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_201_header_conditional.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,69 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add(
+ """
+ KeepAlive on
+ MaxKeepAliveRequests 30
+ KeepAliveTimeout 30"""
+ ).add_vhost_test1().install()
+ assert env.apache_restart() == 0
+
+ # check handling of 'if-modified-since' header
+ def test_201_01(self, env):
+ url = env.mkurl("https", "test1", "/006/006.css")
+ r = env.curl_get(url)
+ assert 200 == r.response["status"]
+ lm = r.response["header"]["last-modified"]
+ assert lm
+ r = env.curl_get(url, options=["-H", "if-modified-since: %s" % lm])
+ assert 304 == r.response["status"]
+ r = env.curl_get(url, options=["-H", "if-modified-since: Tue, 04 Sep 2010 11:51:59 GMT"])
+ assert 200 == r.response["status"]
+
+ # check handling of 'if-none-match' header
+ def test_201_02(self, env):
+ url = env.mkurl("https", "test1", "/006/006.css")
+ r = env.curl_get(url)
+ assert 200 == r.response["status"]
+ etag = r.response["header"]["etag"]
+ assert etag
+ r = env.curl_get(url, options=["-H", "if-none-match: %s" % etag])
+ assert 304 == r.response["status"]
+ r = env.curl_get(url, options=["-H", "if-none-match: dummy"])
+ assert 200 == r.response["status"]
+
+ @pytest.mark.skipif(True, reason="304 misses the Vary header in trunk and 2.4.x")
+ def test_201_03(self, env):
+ url = env.mkurl("https", "test1", "/006.html")
+ r = env.curl_get(url, options=["-H", "Accept-Encoding: gzip"])
+ assert 200 == r.response["status"]
+ for h in r.response["header"]:
+ print("%s: %s" % (h, r.response["header"][h]))
+ lm = r.response["header"]["last-modified"]
+ assert lm
+ assert "gzip" == r.response["header"]["content-encoding"]
+ assert "Accept-Encoding" in r.response["header"]["vary"]
+
+ r = env.curl_get(url, options=["-H", "if-modified-since: %s" % lm,
+ "-H", "Accept-Encoding: gzip"])
+ assert 304 == r.response["status"]
+ for h in r.response["header"]:
+ print("%s: %s" % (h, r.response["header"][h]))
+ assert "vary" in r.response["header"]
+
+ # Check if "Keep-Alive" response header is removed in HTTP/2.
+ def test_201_04(self, env):
+ url = env.mkurl("https", "test1", "/006.html")
+ r = env.curl_get(url, options=["--http1.1", "-H", "Connection: keep-alive"])
+ assert 200 == r.response["status"]
+ assert "timeout=30, max=30" == r.response["header"]["keep-alive"]
+ r = env.curl_get(url, options=["-H", "Connection: keep-alive"])
+ assert 200 == r.response["status"]
+ assert "keep-alive" not in r.response["header"]
Added: httpd/httpd/trunk/test/modules/http2/test_202_trailer.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_202_trailer.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_202_trailer.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_202_trailer.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,81 @@
+import os
+import pytest
+
+from h2_conf import HttpdConf
+
+
+def setup_data(env):
+ s100 = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678\n"
+ with open(os.path.join(env.gen_dir, "data-1k"), 'w') as f:
+ for i in range(10):
+ f.write(s100)
+
+
+# The trailer tests depend on "nghttp" as no other client seems to be able to send those
+# rare things.
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ setup_data(env)
+ HttpdConf(env).add_vhost_cgi(h2proxy_self=True).install()
+ assert env.apache_restart() == 0
+
+ # check if the server survives a trailer or two
+ def test_202_01(self, env):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ fpath = os.path.join(env.gen_dir, "data-1k")
+ r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 1"])
+ assert 300 > r.response["status"]
+ assert 1000 == len(r.response["body"])
+
+ r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 1b", "--trailer", "XXX: test"])
+ assert 300 > r.response["status"]
+ assert 1000 == len(r.response["body"])
+
+ # check if the server survives a trailer without content-length
+ def test_202_02(self, env):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ fpath = os.path.join(env.gen_dir, "data-1k")
+ r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 2", "--no-content-length"])
+ assert 300 > r.response["status"]
+ assert 1000 == len(r.response["body"])
+
+ # check if echoing request headers in response from GET works
+ def test_202_03(self, env):
+ url = env.mkurl("https", "cgi", "/echohd.py?name=X")
+ r = env.nghttp().get(url, options=["--header", "X: 3"])
+ assert 300 > r.response["status"]
+ assert b"X: 3\n" == r.response["body"]
+
+ # check if echoing request headers in response from POST works
+ def test_202_03b(self, env):
+ url = env.mkurl("https", "cgi", "/echohd.py?name=X")
+ r = env.nghttp().post_name(url, "Y", options=["--header", "X: 3b"])
+ assert 300 > r.response["status"]
+ assert b"X: 3b\n" == r.response["body"]
+
+ # check if echoing request headers in response from POST works, but trailers are not seen
+ # This is the way CGI invocation works.
+ def test_202_04(self, env):
+ url = env.mkurl("https", "cgi", "/echohd.py?name=X")
+ r = env.nghttp().post_name(url, "Y", options=["--header", "X: 4a", "--trailer", "X: 4b"])
+ assert 300 > r.response["status"]
+ assert b"X: 4a\n" == r.response["body"]
+
+ # The h2 status handler echoes a trailer if it sees a trailer
+ def test_202_05(self, env):
+ url = env.mkurl("https", "cgi", "/.well-known/h2/state")
+ fpath = os.path.join(env.gen_dir, "data-1k")
+ r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 2"])
+ assert 200 == r.response["status"]
+ assert "1" == r.response["trailer"]["h2-trailers-in"]
+
+ # Check that we can send and receive trailers throuh mod_proxy_http2
+ def test_202_06(self, env):
+ url = env.mkurl("https", "cgi", "/h2proxy/.well-known/h2/state")
+ fpath = os.path.join(env.gen_dir, "data-1k")
+ r = env.nghttp().upload(url, fpath, options=["--trailer", "test: 2"])
+ assert 200 == r.response["status"]
+ assert 'trailer' in r.response
+ assert "1" == r.response['trailer']["h2-trailers-in"]
Added: httpd/httpd/trunk/test/modules/http2/test_300_interim.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_300_interim.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_300_interim.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_300_interim.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,39 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_test1().add_vhost_cgi().install()
+ assert env.apache_restart() == 0
+
+ def setup_method(self, method):
+ print("setup_method: %s" % method.__name__)
+
+ def teardown_method(self, method):
+ print("teardown_method: %s" % method.__name__)
+
+ # check that we normally do not see an interim response
+ def test_300_01(self, env):
+ url = env.mkurl("https", "test1", "/index.html")
+ r = env.curl_post_data(url, 'XYZ')
+ assert 200 == r.response["status"]
+ assert "previous" not in r.response
+
+ # check that we see an interim response when we ask for it
+ def test_300_02(self, env):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ r = env.curl_post_data(url, 'XYZ', options=["-H", "expect: 100-continue"])
+ assert 200 == r.response["status"]
+ assert "previous" in r.response
+ assert 100 == r.response["previous"]["status"]
+
+ # check proper answer on unexpected
+ def test_300_03(self, env):
+ url = env.mkurl("https", "cgi", "/echo.py")
+ r = env.curl_post_data(url, 'XYZ', options=["-H", "expect: the-unexpected"])
+ assert 417 == r.response["status"]
+ assert "previous" not in r.response
Added: httpd/httpd/trunk/test/modules/http2/test_400_push.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_400_push.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_400_push.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_400_push.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,199 @@
+import os
+import pytest
+
+from h2_conf import HttpdConf
+
+
+# The push tests depend on "nghttp"
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).start_vhost(
+ env.https_port, "push", doc_root="htdocs/test1", with_ssl=True
+ ).add(r""" Protocols h2 http/1.1"
+ RewriteEngine on
+ RewriteRule ^/006-push(.*)?\.html$ /006.html
+ <Location /006-push.html>
+ Header add Link "</006/006.css>;rel=preload"
+ Header add Link "</006/006.js>;rel=preloadX"
+ </Location>
+ <Location /006-push2.html>
+ Header add Link "</006/006.css>;rel=preloadX, </006/006.js>; rel=preload"
+ </Location>
+ <Location /006-push3.html>
+ Header add Link "</006/006.css>;rel=preloa,</006/006.js>;rel=preload"
+ </Location>
+ <Location /006-push4.html>
+ Header add Link "</006/006.css;rel=preload, </006/006.js>; preload"
+ </Location>
+ <Location /006-push5.html>
+ Header add Link '</006/006.css>;rel="preload push"'
+ </Location>
+ <Location /006-push6.html>
+ Header add Link '</006/006.css>;rel="push preload"'
+ </Location>
+ <Location /006-push7.html>
+ Header add Link '</006/006.css>;rel="abc preload push"'
+ </Location>
+ <Location /006-push8.html>
+ Header add Link '</006/006.css>;rel="preload"; nopush'
+ </Location>
+ <Location /006-push20.html>
+ H2PushResource "/006/006.css" critical
+ H2PushResource "/006/006.js"
+ </Location>
+ <Location /006-push30.html>
+ H2Push off
+ Header add Link '</006/006.css>;rel="preload"'
+ </Location>
+ <Location /006-push31.html>
+ H2PushResource "/006/006.css" critical
+ </Location>
+ <Location /006-push32.html>
+ Header add Link "</006/006.css>;rel=preload"
+ </Location>
+ """).end_vhost(
+ ).install()
+ assert env.apache_restart() == 0
+
+ ############################
+ # Link: header handling, various combinations
+
+ # plain resource without configured pushes
+ def test_400_00(self, env):
+ url = env.mkurl("https", "push", "/006.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # 2 link headers configured, only 1 triggers push
+ def test_400_01(self, env):
+ url = env.mkurl("https", "push", "/006-push.html")
+ r = env.nghttp().get(url, options=["-Haccept-encoding: none"])
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.css' == promises[0]["request"]["header"][":path"]
+ assert 216 == len(promises[0]["response"]["body"])
+
+ # Same as 400_01, but with single header line configured
+ def test_400_02(self, env):
+ url = env.mkurl("https", "push", "/006-push2.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.js' == promises[0]["request"]["header"][":path"]
+
+ # 2 Links, only one with correct rel attribue
+ def test_400_03(self, env):
+ url = env.mkurl("https", "push", "/006-push3.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.js' == promises[0]["request"]["header"][":path"]
+
+ # Missing > in Link header, PUSH not triggered
+ def test_400_04(self, env):
+ url = env.mkurl("https", "push", "/006-push4.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # More than one value in "rel" parameter
+ def test_400_05(self, env):
+ url = env.mkurl("https", "push", "/006-push5.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.css' == promises[0]["request"]["header"][":path"]
+
+ # Another "rel" parameter variation
+ def test_400_06(self, env):
+ url = env.mkurl("https", "push", "/006-push6.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.css' == promises[0]["request"]["header"][":path"]
+
+ # Another "rel" parameter variation
+ def test_400_07(self, env):
+ url = env.mkurl("https", "push", "/006-push7.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.css' == promises[0]["request"]["header"][":path"]
+
+ # Pushable link header with "nopush" attribute
+ def test_400_08(self, env):
+ url = env.mkurl("https", "push", "/006-push8.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # 2 H2PushResource config trigger on GET, but not on POST
+ def test_400_20(self, env):
+ url = env.mkurl("https", "push", "/006-push20.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 2 == len(promises)
+
+ fpath = os.path.join(env.gen_dir, "data-400-20")
+ with open(fpath, 'w') as f:
+ f.write("test upload data")
+ r = env.nghttp().upload(url, fpath)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # H2Push configured Off in location
+ def test_400_30(self, env):
+ url = env.mkurl("https", "push", "/006-push30.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # - suppress PUSH
+ def test_400_50(self, env):
+ url = env.mkurl("https", "push", "/006-push.html")
+ r = env.nghttp().get(url, options=['-H', 'accept-push-policy: none'])
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 0 == len(promises)
+
+ # - default pushes desired
+ def test_400_51(self, env):
+ url = env.mkurl("https", "push", "/006-push.html")
+ r = env.nghttp().get(url, options=['-H', 'accept-push-policy: default'])
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+
+ # - HEAD pushes desired
+ def test_400_52(self, env):
+ url = env.mkurl("https", "push", "/006-push.html")
+ r = env.nghttp().get(url, options=['-H', 'accept-push-policy: head'])
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert '/006/006.css' == promises[0]["request"]["header"][":path"]
+ assert b"" == promises[0]["response"]["body"]
+ assert 0 == len(promises[0]["response"]["body"])
+
+ # - fast-load pushes desired
+ def test_400_53(self, env):
+ url = env.mkurl("https", "push", "/006-push.html")
+ r = env.nghttp().get(url, options=['-H', 'accept-push-policy: fast-load'])
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
Added: httpd/httpd/trunk/test/modules/http2/test_401_early_hints.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_401_early_hints.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_401_early_hints.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_401_early_hints.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,46 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+# The push tests depend on "nghttp"
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).start_vhost(
+ env.https_port, "hints", doc_root="htdocs/test1", with_ssl=True
+ ).add(""" Protocols h2 http/1.1"
+ H2EarlyHints on
+ RewriteEngine on
+ RewriteRule ^/006-(.*)?\\.html$ /006.html
+ <Location /006-hints.html>
+ H2PushResource "/006/006.css" critical
+ </Location>
+ <Location /006-nohints.html>
+ Header add Link "</006/006.css>;rel=preload"
+ </Location>
+ """).end_vhost(
+ ).install()
+ assert env.apache_restart() == 0
+
+ # H2EarlyHints enabled in general, check that it works for H2PushResource
+ def test_401_31(self, env):
+ url = env.mkurl("https", "hints", "/006-hints.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ early = r.response["previous"]
+ assert early
+ assert 103 == int(early["header"][":status"])
+ assert early["header"]["link"]
+
+ # H2EarlyHints enabled in general, but does not trigger on added response headers
+ def test_401_32(self, env):
+ url = env.mkurl("https", "hints", "/006-nohints.html")
+ r = env.nghttp().get(url)
+ assert 200 == r.response["status"]
+ promises = r.results["streams"][r.response["id"]]["promises"]
+ assert 1 == len(promises)
+ assert "previous" not in r.response
Added: httpd/httpd/trunk/test/modules/http2/test_500_proxy.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_500_proxy.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_500_proxy.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_500_proxy.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,119 @@
+import os
+import re
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ env.setup_data_1k_1m()
+ HttpdConf(env).add_vhost_cgi(proxy_self=True).install()
+ assert env.apache_restart() == 0
+
+ def setup_method(self, method):
+ print("setup_method: %s" % method.__name__)
+
+ def teardown_method(self, method):
+ print("teardown_method: %s" % method.__name__)
+
+ def test_500_01(self, env):
+ url = env.mkurl("https", "cgi", "/proxy/hello.py")
+ r = env.curl_get(url, 5)
+ assert 200 == r.response["status"]
+ assert "HTTP/1.1" == r.response["json"]["protocol"]
+ assert "" == r.response["json"]["https"]
+ assert "" == r.response["json"]["ssl_protocol"]
+ assert "" == r.response["json"]["h2"]
+ assert "" == r.response["json"]["h2push"]
+
+ # upload and GET again using curl, compare to original content
+ def curl_upload_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/proxy/upload.py")
+ fpath = os.path.join(env.gen_dir, fname)
+ r = env.curl_upload(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert 200 <= r.response["status"] < 300
+
+ # why is the scheme wrong?
+ r2 = env.curl_get(re.sub(r'http:', 'https:', r.response["header"]["location"]))
+ assert r2.exit_code == 0
+ assert r2.response["status"] == 200
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r2.response["body"]
+
+ def test_500_10(self, env):
+ self.curl_upload_and_verify(env, "data-1k", ["--http2"])
+ self.curl_upload_and_verify(env, "data-10k", ["--http2"])
+ self.curl_upload_and_verify(env, "data-100k", ["--http2"])
+ self.curl_upload_and_verify(env, "data-1m", ["--http2"])
+
+ # POST some data using nghttp and see it echo'ed properly back
+ def nghttp_post_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/proxy/echo.py")
+ fpath = os.path.join(env.gen_dir, fname)
+ r = env.nghttp().upload(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert 200 <= r.response["status"] < 300
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r.response["body"]
+
+ def test_500_20(self, env):
+ self.nghttp_post_and_verify(env, "data-1k", [])
+ self.nghttp_post_and_verify(env, "data-10k", [])
+ self.nghttp_post_and_verify(env, "data-100k", [])
+ self.nghttp_post_and_verify(env, "data-1m", [])
+
+ def test_500_21(self, env):
+ self.nghttp_post_and_verify(env, "data-1k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-10k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-100k", ["--no-content-length"])
+ self.nghttp_post_and_verify(env, "data-1m", ["--no-content-length"])
+
+ # upload and GET again using nghttp, compare to original content
+ def nghttp_upload_and_verify(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/proxy/upload.py")
+ fpath = os.path.join(env.gen_dir, fname)
+
+ r = env.nghttp().upload_file(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert 200 <= r.response["status"] < 300
+ assert r.response["header"]["location"]
+
+ # why is the scheme wrong?
+ r2 = env.nghttp().get(re.sub(r'http:', 'https:', r.response["header"]["location"]))
+ assert r2.exit_code == 0
+ assert r2.response["status"] == 200
+ with open(env.test_src(fpath), mode='rb') as file:
+ src = file.read()
+ assert src == r2.response["body"]
+
+ def test_500_22(self, env):
+ self.nghttp_upload_and_verify(env, "data-1k", [])
+ self.nghttp_upload_and_verify(env, "data-10k", [])
+ self.nghttp_upload_and_verify(env, "data-100k", [])
+ self.nghttp_upload_and_verify(env, "data-1m", [])
+
+ def test_500_23(self, env):
+ self.nghttp_upload_and_verify(env, "data-1k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-10k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-100k", ["--no-content-length"])
+ self.nghttp_upload_and_verify(env, "data-1m", ["--no-content-length"])
+
+ # upload using nghttp and check returned status
+ def nghttp_upload_stat(self, env, fname, options=None):
+ url = env.mkurl("https", "cgi", "/proxy/upload.py")
+ fpath = os.path.join(env.gen_dir, fname)
+
+ r = env.nghttp().upload_file(url, fpath, options=options)
+ assert r.exit_code == 0
+ assert 200 <= r.response["status"] < 300
+ assert r.response["header"]["location"]
+
+ def test_500_24(self, env):
+ for i in range(100):
+ self.nghttp_upload_stat(env, "data-1k", ["--no-content-length"])
Added: httpd/httpd/trunk/test/modules/http2/test_600_h2proxy.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_600_h2proxy.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_600_h2proxy.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_600_h2proxy.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,27 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ env.setup_data_1k_1m()
+ conf = HttpdConf(env)
+ conf.add_vhost_cgi(h2proxy_self=True)
+ conf.add("LogLevel proxy_http2:trace2")
+ conf.add("LogLevel proxy:trace2")
+ conf.install()
+ assert env.apache_restart() == 0
+
+ def test_600_01(self, env):
+ url = env.mkurl("https", "cgi", "/h2proxy/hello.py")
+ r = env.curl_get(url, 5)
+ assert r.response["status"] == 200
+ assert r.response["json"]["protocol"] == "HTTP/2.0"
+ assert r.response["json"]["https"] == "on"
+ assert r.response["json"]["ssl_protocol"] != ""
+ assert r.response["json"]["h2"] == "on"
+ assert r.response["json"]["h2push"] == "off"
+ assert r.response["json"]["host"] == f"cgi.{env.http_tld}"
Added: httpd/httpd/trunk/test/modules/http2/test_700_load_get.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_700_load_get.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_700_load_get.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_700_load_get.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,54 @@
+import pytest
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ HttpdConf(env).add_vhost_cgi().add_vhost_test1().install()
+ assert env.apache_restart() == 0
+
+ def check_h2load_ok(self, env, r, n):
+ assert 0 == r.exit_code
+ r = env.h2load_status(r)
+ assert n == r.results["h2load"]["requests"]["total"]
+ assert n == r.results["h2load"]["requests"]["started"]
+ assert n == r.results["h2load"]["requests"]["done"]
+ assert n == r.results["h2load"]["requests"]["succeeded"]
+ assert n == r.results["h2load"]["status"]["2xx"]
+ assert 0 == r.results["h2load"]["status"]["3xx"]
+ assert 0 == r.results["h2load"]["status"]["4xx"]
+ assert 0 == r.results["h2load"]["status"]["5xx"]
+
+ # test load on cgi script, single connection, different sizes
+ @pytest.mark.parametrize("start", [
+ 1000, 80000
+ ])
+ def test_700_10(self, env, start):
+ text = "X"
+ chunk = 32
+ for n in range(0, 5):
+ args = [env.h2load, "-n", "%d" % chunk, "-c", "1", "-m", "10",
+ f"--base-uri={env.https_base_url}"]
+ for i in range(0, chunk):
+ args.append(env.mkurl("https", "cgi", ("/mnot164.py?count=%d&text=%s" % (start+(n*chunk)+i, text))))
+ r = env.run(args)
+ self.check_h2load_ok(env, r, chunk)
+
+ # test load on cgi script, single connection
+ @pytest.mark.parametrize("conns", [
+ 1, 2, 16, 32
+ ])
+ def test_700_11(self, env, conns):
+ text = "X"
+ start = 1200
+ chunk = 64
+ for n in range(0, 5):
+ args = [env.h2load, "-n", "%d" % chunk, "-c", "%d" % conns, "-m", "10",
+ f"--base-uri={env.https_base_url}"]
+ for i in range(0, chunk):
+ args.append(env.mkurl("https", "cgi", ("/mnot164.py?count=%d&text=%s" % (start+(n*chunk)+i, text))))
+ r = env.run(args)
+ self.check_h2load_ok(env, r, chunk)
Added: httpd/httpd/trunk/test/modules/http2/test_710_load_post_static.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_710_load_post_static.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_710_load_post_static.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_710_load_post_static.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,62 @@
+import pytest
+import os
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ env.setup_data_1k_1m()
+ HttpdConf(env).add_vhost_test1().install()
+ assert env.apache_restart() == 0
+
+ def check_h2load_ok(self, env, r, n):
+ assert 0 == r.exit_code
+ r = env.h2load_status(r)
+ assert n == r.results["h2load"]["requests"]["total"]
+ assert n == r.results["h2load"]["requests"]["started"]
+ assert n == r.results["h2load"]["requests"]["done"]
+ assert n == r.results["h2load"]["requests"]["succeeded"]
+ assert n == r.results["h2load"]["status"]["2xx"]
+ assert 0 == r.results["h2load"]["status"]["3xx"]
+ assert 0 == r.results["h2load"]["status"]["4xx"]
+ assert 0 == r.results["h2load"]["status"]["5xx"]
+
+ # test POST on static file, slurped in by server
+ def test_710_00(self, env):
+ url = env.mkurl("https", "test1", "/index.html")
+ n = 10
+ m = 1
+ conn = 1
+ fname = "data-10k"
+ args = [env.h2load, "-n", "%d" % n, "-c", "%d" % conn, "-m", "%d" % m,
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)
+
+ def test_710_01(self, env):
+ url = env.mkurl("https", "test1", "/index.html")
+ n = 1000
+ m = 100
+ conn = 1
+ fname = "data-1k"
+ args = [env.h2load, "-n", "%d" % n, "-c", "%d" % conn, "-m", "%d" % m,
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)
+
+ def test_710_02(self, env):
+ url = env.mkurl("https", "test1", "/index.html")
+ n = 100
+ m = 50
+ conn = 1
+ fname = "data-100k"
+ args = [env.h2load, "-n", "%d" % n, "-c", "%d" % conn, "-m", "%d" % m,
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)
Added: httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py?rev=1892476&view=auto
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py (added)
+++ httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py Fri Aug 20 16:07:44 2021
@@ -0,0 +1,70 @@
+import pytest
+import os
+
+from h2_conf import HttpdConf
+
+
+class TestStore:
+
+ @pytest.fixture(autouse=True, scope='class')
+ def _class_scope(self, env):
+ env.setup_data_1k_1m()
+ HttpdConf(env).add_vhost_cgi(proxy_self=True, h2proxy_self=True).install()
+ assert env.apache_restart() == 0
+
+ def check_h2load_ok(self, env, r, n):
+ assert 0 == r.exit_code
+ r = env.h2load_status(r)
+ assert n == r.results["h2load"]["requests"]["total"]
+ assert n == r.results["h2load"]["requests"]["started"]
+ assert n == r.results["h2load"]["requests"]["done"]
+ assert n == r.results["h2load"]["requests"]["succeeded"]
+ assert n == r.results["h2load"]["status"]["2xx"]
+ assert 0 == r.results["h2load"]["status"]["3xx"]
+ assert 0 == r.results["h2load"]["status"]["4xx"]
+ assert 0 == r.results["h2load"]["status"]["5xx"]
+
+ # test POST on cgi, where input is read
+ def test_711_10(self, env):
+ url = env.mkurl("https", "test1", "/echo.py")
+ n = 100
+ m = 5
+ conn = 1
+ fname = "data-100k"
+ args = [
+ env.h2load, "-n", str(n), "-c", str(conn), "-m", str(m),
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url
+ ]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)
+
+ # test POST on cgi via http/1.1 proxy, where input is read
+ def test_711_11(self, env):
+ url = env.mkurl("https", "test1", "/proxy/echo.py")
+ n = 100
+ m = 5
+ conn = 1
+ fname = "data-100k"
+ args = [
+ env.h2load, "-n", str(n), "-c", str(conn), "-m", str(m),
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url
+ ]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)
+
+ # test POST on cgi via h2proxy, where input is read
+ def test_711_12(self, env):
+ url = env.mkurl("https", "test1", "/h2proxy/echo.py")
+ n = 100
+ m = 5
+ conn = 1
+ fname = "data-100k"
+ args = [
+ env.h2load, "-n", str(n), "-c", str(conn), "-m", str(m),
+ f"--base-uri={env.https_base_url}",
+ "-d", os.path.join(env.gen_dir, fname), url
+ ]
+ r = env.run(args)
+ self.check_h2load_ok(env, r, n)