]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
make the http2 test suite working again
authorStefan Eissing <icing@apache.org>
Thu, 27 Apr 2023 11:35:51 +0000 (11:35 +0000)
committerStefan Eissing <icing@apache.org>
Thu, 27 Apr 2023 11:35:51 +0000 (11:35 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1909452 13f79535-47bb-0310-9956-ffa450edef68

13 files changed:
test/modules/http1/htdocs/cgi/upload.py
test/modules/http2/htdocs/cgi/echohd.py
test/modules/http2/htdocs/cgi/env.py
test/modules/http2/htdocs/cgi/hecho.py
test/modules/http2/htdocs/cgi/mnot164.py
test/modules/http2/htdocs/cgi/necho.py
test/modules/http2/htdocs/cgi/requestparser.py [new file with mode: 0644]
test/modules/http2/htdocs/cgi/upload.py
test/modules/http2/test_003_get.py
test/modules/http2/test_004_post.py
test/modules/http2/test_200_header_invalid.py
test/pyhttpd/nghttp.py
test/pyhttpd/result.py

index 0bdcbb8a6aa179fe365e82f4138210115ee0119b..8cdefacb5b0ef8ca5487306c465a73539616cc0c 100755 (executable)
@@ -29,9 +29,9 @@ def get_request_params():
                 oforms[name] = values[0]
         elif ctype.startswith("multipart/"):
             def on_field(field):
-                oforms[field.field_name] = field.value
+                oforms[field.field_name.decode()] = field.value.decode()
             def on_file(file):
-                ofiles[field.field_name] = field.value
+                ofiles[file.field_name.decode()] = file.value
             multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
     return oforms, ofiles
 
index 591d30795a09d33016cb7aab8d27d88718c4aa59..a85a4e3d7ac471ca91f72e4e9665dd40e962daa1 100644 (file)
@@ -1,29 +1,6 @@
 #!/usr/bin/env python3
 import os, sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
index f07079d746fb753ab7832f2e04efa0a6bc115baf..455c623a2fe1819e9761f1429701c6de5b561394 100644 (file)
@@ -1,29 +1,6 @@
 #!/usr/bin/env python3
 import os, sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
index 1c11e87bda7518fcded7d3d9dd6bacec636bbf0e..abffd33be60cbf7714f6627d5c59ea22b5a89c99 100644 (file)
@@ -1,29 +1,6 @@
 #!/usr/bin/env python3
 import os, sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
index b365f1f5c9ef179274c7816080d28158873822ec..43a86ea1094d5ad06caf73d8d321ed6acb104bb3 100644 (file)
@@ -1,29 +1,6 @@
 #!/usr/bin/env python3
 import os, sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
index c32106a952ef62d5abd014258dea9b45a0843764..715904b4a33f81ff7b9934a19aae7d9138543281 100644 (file)
@@ -1,30 +1,7 @@
 #!/usr/bin/env python3
 import time
 import os, sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
@@ -63,11 +40,12 @@ Content-Type: text/html\n
     <p>No count was specified: %s</p>
     </body></html>""" % (count))
 
-except KeyError:
+except KeyError as ex:
     print("Status: 200 Ok")
-    print("""\
+    print(f"""\
 Content-Type: text/html\n
-    <html><body>
+    <html><body>uri: uri={os.environ['REQUEST_URI']} ct={os.environ['CONTENT_TYPE']} ex={ex}
+    forms={forms}
     Echo <form method="POST" enctype="application/x-www-form-urlencoded">
     <input type="text" name="count">
     <input type="text" name="text">
diff --git a/test/modules/http2/htdocs/cgi/requestparser.py b/test/modules/http2/htdocs/cgi/requestparser.py
new file mode 100644 (file)
index 0000000..c7e0648
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+import os
+import sys
+from urllib import parse
+import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
+import shutil
+
+
+try:  # Windows needs stdio set for binary mode.
+    import msvcrt
+
+    msvcrt.setmode(0, os.O_BINARY)  # stdin  = 0
+    msvcrt.setmode(1, os.O_BINARY)  # stdout = 1
+except ImportError:
+    pass
+
+
+class FileItem:
+
+    def __init__(self, mparse_item):
+        self.item = mparse_item
+
+    @property
+    def file_name(self):
+        return os.path.basename(self.item.file_name.decode())
+
+    def save_to(self, destpath: str):
+        fsrc = self.item.file_object
+        fsrc.seek(0)
+        with open(destpath, 'wb') as fd:
+            shutil.copyfileobj(fsrc, fd)
+
+
+def get_request_params():
+    oforms = {}
+    ofiles = {}
+    if "REQUEST_URI" in os.environ:
+        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
+        for name, values in qforms.items():
+            oforms[name] = values[0]
+    if "CONTENT_TYPE" in os.environ:
+        ctype = os.environ["CONTENT_TYPE"]
+        if ctype == "application/x-www-form-urlencoded":
+            s = sys.stdin.read()
+            qforms = parse.parse_qs(s)
+            for name, values in qforms.items():
+                oforms[name] = values[0]
+        elif ctype.startswith("multipart/"):
+            def on_field(field):
+                oforms[field.field_name.decode()] = field.value.decode()
+            def on_file(file):
+                ofiles[file.field_name.decode()] = FileItem(file)
+            multipart.parse_form(headers={"Content-Type": ctype},
+                                 input_stream=sys.stdin.buffer,
+                                 on_field=on_field, on_file=on_file)
+    return oforms, ofiles
+
index e7b8f8b1e86a761b6b597d7ce501fd1ad0cf8488..fa1e5d64665a67a911d811d4671176992ee0d3e2 100644 (file)
@@ -1,38 +1,7 @@
 #!/usr/bin/env python3
 import os
 import sys
-from urllib import parse
-import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`)
-
-
-try:  # Windows needs stdio set for binary mode.
-    import msvcrt
-
-    msvcrt.setmode(0, os.O_BINARY)  # stdin  = 0
-    msvcrt.setmode(1, os.O_BINARY)  # stdout = 1
-except ImportError:
-    pass
-
-def get_request_params():
-    oforms = {}
-    ofiles = {}
-    if "REQUEST_URI" in os.environ:
-        qforms = parse.parse_qs(parse.urlsplit(os.environ["REQUEST_URI"]).query)
-        for name, values in qforms.items():
-            oforms[name] = values[0]
-    if "HTTP_CONTENT_TYPE" in os.environ:
-        ctype = os.environ["HTTP_CONTENT_TYPE"]
-        if ctype == "application/x-www-form-urlencoded":
-            qforms = parse.parse_qs(parse.urlsplit(sys.stdin.read()).query)
-            for name, values in qforms.items():
-                oforms[name] = values[0]
-        elif ctype.startswith("multipart/"):
-            def on_field(field):
-                oforms[field.field_name] = field.value
-            def on_file(file):
-                ofiles[field.field_name] = field.value
-            multipart.parse_form(headers={"Content-Type": ctype}, input_stream=sys.stdin.buffer, on_field=on_field, on_file=on_file)
-    return oforms, ofiles
+from requestparser import get_request_params
 
 
 forms, files = get_request_params()
@@ -43,9 +12,9 @@ status = '200 Ok'
 if 'file' in files:
     fitem = files['file']
     # strip leading path from file name to avoid directory traversal attacks
-    fname = fitem.filename
+    fname = os.path.basename(fitem.file_name)
     fpath = f'{os.environ["DOCUMENT_ROOT"]}/files/{fname}'
-    fitem.save_as(fpath)
+    fitem.save_to(fpath)
     message = "The file %s was uploaded successfully" % (fname)
     print("Status: 201 Created")
     print("Content-Type: text/html")
index 5928448d2a8b5398a23767cc53dd4546f62e82e4..cff915a836d81308bd26cc43ae4bcc6e450a4417 100644 (file)
@@ -194,10 +194,14 @@ content-type: text/html
     @pytest.mark.parametrize("path", [
         "/004.html", "/proxy/004.html", "/h2proxy/004.html"
     ])
-    def test_h2_003_50(self, env, path):
+    def test_h2_003_50(self, env, path, repeat):
         # check that the resource supports ranges and we see its raw content-length
         url = env.mkurl("https", "test1", path)
-        r = env.curl_get(url, 5)
+        # TODO: sometimes we see a 503 here from h2proxy
+        for i in range(10):
+            r = env.curl_get(url, 5)
+            if r.response["status"] != 503:
+                break
         assert r.response["status"] == 200
         assert "HTTP/2" == r.response["protocol"]
         h = r.response["header"]
index 16e9a0c9c93aebe90a6d2ab2be1158993c4b8165..9a5560c83de14dfed96cca81e8958917243943c5 100644 (file)
@@ -124,6 +124,7 @@ class TestPost:
         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 'location' in r.response["header"], f'{r}'
         assert r.response["header"]["location"]
 
         r2 = env.nghttp().get(r.response["header"]["location"])
index 44ad69bc6be2fc7ca6a8c698272706b741b754f8..6adfd45fc51de2f17bbd2c06744464ab3af2f4e2 100644 (file)
@@ -17,13 +17,14 @@ class TestInvalidHeaders:
     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)
+            data = f'name=x%{x:02x}x&value=yz'
+            r = env.curl_post_data(url, data)
             if 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
+                assert 0 == r.exit_code, f'unexpected exit code for char 0x{x:02}'
+                assert 200 == r.response["status"], f'unexpected status for char 0x{x:02}'
             else:
-                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
+                assert 0 == r.exit_code, f'"unexpected exit code for char 0x{x:02}'
+                assert 500 == r.response["status"], f'posting "{data}" unexpected status, {r}'
 
     # let the hecho.py CGI echo chars < 0x20 in field value
     # for almost all such characters, the stream returns a 500
index bfcbf29a2617517e975cdaf8b859a61681d0fe13..43721f599abf74dc21c14621ce8a8a2d8c51d478 100644 (file)
@@ -247,11 +247,11 @@ class Nghttp:
     def post_name(self, url, name, timeout=5, options=None):
         reqbody = ("%s/nghttp.req.body" % self.TMP_DIR)
         with open(reqbody, 'w') as f:
-            f.write("--DSAJKcd9876\n")
-            f.write("Content-Disposition: form-data; name=\"value\"; filename=\"xxxxx\"\n")
-            f.write("Content-Type: text/plain\n")
-            f.write("\n%s\n" % name)
-            f.write("--DSAJKcd9876\n")
+            f.write("--DSAJKcd9876\r\n")
+            f.write("Content-Disposition: form-data; name=\"value\"; filename=\"xxxxx\"\r\n")
+            f.write("Content-Type: text/plain\r\n")
+            f.write(f"\r\n{name}")
+            f.write("\r\n--DSAJKcd9876\r\n")
         if not options:
             options = []
         options.extend([ 
@@ -270,20 +270,23 @@ class Nghttp:
         reqbody = ("%s/nghttp.req.body" % self.TMP_DIR)
         with open(fpath, 'rb') as fin:
             with open(reqbody, 'wb') as f:
-                f.write(("""--DSAJKcd9876
-Content-Disposition: form-data; name="xxx"; filename="xxxxx"
-Content-Type: text/plain
-
-testing mod_h2
---DSAJKcd9876
-Content-Disposition: form-data; name="file"; filename="%s"
-Content-Type: application/octet-stream
-Content-Transfer-Encoding: binary
-
-""" % fname).encode('utf-8'))
+                preamble = [
+                    '--DSAJKcd9876',
+                    'Content-Disposition: form-data; name="xxx"; filename="xxxxx"',
+                    'Content-Type: text/plain',
+                    '',
+                    'testing mod_h2',
+                    '\r\n--DSAJKcd9876',
+                    f'Content-Disposition: form-data; name="file"; filename="{fname}"',
+                    'Content-Type: application/octet-stream',
+                    'Content-Transfer-Encoding: binary',
+                    '', ''
+                ]
+                f.write('\r\n'.join(preamble).encode('utf-8'))
                 f.write(fin.read())
-                f.write("""
---DSAJKcd9876""".encode('utf-8'))
+                f.write('\r\n'.join([
+                    '\r\n--DSAJKcd9876', ''
+                ]).encode('utf-8'))
         if not options:
             options = []
         options.extend([ 
index 3789461be41214c160f86535388b95c20662c616..4bf9ff200d9805d385977b7ce94e71140e0ac736 100644 (file)
@@ -28,7 +28,14 @@ class ExecResult:
             self._json_out = None
 
     def __repr__(self):
-        return f"ExecResult[code={self.exit_code}, args={self._args}, stdout={self._stdout}, stderr={self._stderr}]"
+        out = [
+            f"ExecResult[code={self.exit_code}, args={self._args}\n",
+            "----stdout---------------------------------------\n",
+            self._stdout.decode(),
+            "----stderr---------------------------------------\n",
+            self._stderr.decode()
+        ]
+        return ''.join(out)
 
     @property
     def exit_code(self) -> int: