Repository: twisted/twisted

Issue:
Replace cgi.escape with html.escape
the idea is to take the non controversial changes from https://github.com/twisted/twisted/issues/11919 and merge them in a separate PR

we can then also merge the controlversial changes... if we need to revert the merge for the controversial change, we can do that


Discussion:

Resolving patch:
diff --git a/docs/core/howto/tutorial/listings/finger/finger22.py b/docs/core/howto/tutorial/listings/finger/finger22.py
--- a/docs/core/howto/tutorial/listings/finger/finger22.py
+++ b/docs/core/howto/tutorial/listings/finger/finger22.py
@@ -1,6 +1,4 @@
 # Do everything properly, and componentize
-import cgi
-
 from zope.interface import Interface, implementer
 
 from OpenSSL import SSL
diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py
--- a/src/twisted/web/http.py
+++ b/src/twisted/web/http.py
@@ -31,6 +31,7 @@
     it, as in the HTTP 1.1 chunked I{Transfer-Encoding} (RFC 7230 section 4.1).
     This limits how much data may be buffered when decoding the line.
 """
+from __future__ import annotations
 
 __all__ = [
     "SWITCHING",
@@ -107,6 +108,7 @@
 import tempfile
 import time
 import warnings
+from email.message import EmailMessage
 from io import BytesIO
 from typing import AnyStr, Callable, List, Optional, Tuple
 from urllib.parse import (
@@ -224,15 +226,18 @@
 monthname_lower = [name and name.lower() for name in monthname]
 
 
-def _parseHeader(line):
-    # cgi.parse_header requires a str
-    key, pdict = cgi.parse_header(line.decode("charmap"))
+def _parseContentType(line: bytes) -> tuple[bytes, dict[str, bytes]]:
+    msg = EmailMessage()
+    msg["content-type"] = line.decode("charmap")
+
+    key = msg.get_content_type()
+    pdict = msg["content-type"].params
 
     # We want the key as bytes, and cgi.parse_multipart (which consumes
     # pdict) expects a dict of str keys but bytes values
-    key = key.encode("charmap")
+    encodedKey = key.encode("charmap")
     pdict = {x: y.encode("charmap") for x, y in pdict.items()}
-    return (key, pdict)
+    return (encodedKey, pdict)
 
 
 def urlparse(url):
@@ -973,7 +978,7 @@ def requestReceived(self, command, path, version):
 
         if self.method == b"POST" and ctype and clength:
             mfd = b"multipart/form-data"
-            key, pdict = _parseHeader(ctype)
+            key, pdict = _parseContentType(ctype)
             # This weird CONTENT-LENGTH param is required by
             # cgi.parse_multipart() in some versions of Python 3.7+, see
             # bpo-29979. It looks like this will be relaxed and backported, see


Test patch:
diff --git a/src/twisted/web/test/test_cgi.py b/src/twisted/web/test/test_cgi.py
--- a/src/twisted/web/test/test_cgi.py
+++ b/src/twisted/web/test/test_cgi.py
@@ -87,9 +87,8 @@
 """
 
 URL_PARAMETER_CGI = """\
-import cgi
-fs = cgi.FieldStorage()
-param = fs.getvalue("param")
+import os
+param = str(os.environ['QUERY_STRING'])
 print("Header: OK")
 print("")
 print(param)
@@ -384,7 +383,7 @@ def test_urlParameters(self):
         return d
 
     def _test_urlParameters_1(self, res):
-        expected = f"1234{os.linesep}"
+        expected = f"param=1234{os.linesep}"
         expected = expected.encode("ascii")
         self.assertEqual(res, expected)