Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-tornado6 for openSUSE:Factory 
checked in at 2024-08-02 17:26:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-tornado6 (Old)
 and      /work/SRC/openSUSE:Factory/.python-tornado6.new.7232 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-tornado6"

Fri Aug  2 17:26:09 2024 rev:18 rq:1190823 version:6.4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-tornado6/python-tornado6.changes  
2024-05-20 18:09:31.917764967 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-tornado6.new.7232/python-tornado6.changes    
    2024-08-02 17:26:25.355041172 +0200
@@ -1,0 +2,34 @@
+Wed Jul 31 09:32:23 UTC 2024 - Dominique Leuenberger <dims...@opensuse.org>
+
+- Update to version 6.4.1:
+  + Security Improvements:
+    - Parsing of the ``Transfer-Encoding`` header is now stricter.
+      Unexpected transfer-encoding values were previously ignored
+      and treated as the HTTP/1.0 default of read-until-close. This
+      can lead to framing issues with certain proxies. We now treat
+      any unexpected value as an error.
+    - Handling of whitespace in headers now matches the RFC more
+      closely. Only space and tab characters are treated as
+      whitespace and stripped from the beginning and end of header
+      values. Other unicode whitespace characters are now left
+      alone. This could also lead to framing issues with certain
+      proxies.
+    - `tornado.curl_httpclient` now prohibits carriage return and
+      linefeed headers in HTTP headers (matching the behavior of
+      `simple_httpclient`). These characters could be used for
+      header injection or request smuggling if untrusted data were
+      used in headers.
+  + General Changes:
+    - `tornado.iostream`: `SLIOStream` now understands changes to
+      error codes from OpenSSL 3.2. The main result of this change
+      is to reduce the noise in the logs for certain errors.
+    - `tornado.simple_httpclient`: `simple_httpclient` now
+      prohibits carriage return characters in HTTP headers. It had
+      previously prohibited only linefeed characters.
+    - `tornado.testing`: `.AsyncTestCase` subclasses can now be
+      instantiated without being associated with a test method.
+      Improves compatibility with test discovery in Pytest 8.2.
+- Drop  support-pytest-8.2.patch: fixed upstream.
+- Drop openssl-3.2.patch: fixed upstream.
+
+-------------------------------------------------------------------

Old:
----
  openssl-3.2.patch
  support-pytest-8.2.patch
  tornado-6.4.tar.gz

New:
----
  tornado-6.4.1.tar.gz

BETA DEBUG BEGIN:
  Old:- Drop  support-pytest-8.2.patch: fixed upstream.
- Drop openssl-3.2.patch: fixed upstream.
  Old:      Improves compatibility with test discovery in Pytest 8.2.
- Drop  support-pytest-8.2.patch: fixed upstream.
- Drop openssl-3.2.patch: fixed upstream.
BETA DEBUG END:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-tornado6.spec ++++++
--- /var/tmp/diff_new_pack.0016rw/_old  2024-08-02 17:26:26.919105667 +0200
+++ /var/tmp/diff_new_pack.0016rw/_new  2024-08-02 17:26:26.919105667 +0200
@@ -19,7 +19,7 @@
 %{?sle15_python_module_pythons}
 %define         skip_python2 1
 Name:           python-tornado6
-Version:        6.4
+Version:        6.4.1
 Release:        0
 Summary:        Open source version of scalable, non-blocking web server that 
power FriendFeed
 License:        Apache-2.0
@@ -28,10 +28,6 @@
 Source99:       python-tornado6-rpmlintrc
 # PATCH-FIX-OPENSUSE ignore-resourcewarning-doctests.patch -- ignore resource 
warnings on OBS
 Patch0:         ignore-resourcewarning-doctests.patch
-# PATCH-FIX-OPENSUSE openssl-3.2.patch gh#tornadoweb/tornado#3355
-Patch1:         openssl-3.2.patch
-# PATCH-FIX-UPSTREAM gh#tornadoweb/tornado#3374
-Patch2:         support-pytest-8.2.patch
 BuildRequires:  %{python_module base >= 3.8}
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module pip}

++++++ tornado-6.4.tar.gz -> tornado-6.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/PKG-INFO new/tornado-6.4.1/PKG-INFO
--- old/tornado-6.4/PKG-INFO    2023-11-29 04:20:19.857731000 +0100
+++ new/tornado-6.4.1/PKG-INFO  2024-06-06 20:12:53.604281000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tornado
-Version: 6.4
+Version: 6.4.1
 Summary: Tornado is a Python web framework and asynchronous networking 
library, originally developed at FriendFeed.
 Home-page: http://www.tornadoweb.org/
 Author: Facebook
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/docs/releases/v6.4.1.rst 
new/tornado-6.4.1/docs/releases/v6.4.1.rst
--- old/tornado-6.4/docs/releases/v6.4.1.rst    1970-01-01 01:00:00.000000000 
+0100
+++ new/tornado-6.4.1/docs/releases/v6.4.1.rst  2024-06-06 20:12:50.000000000 
+0200
@@ -0,0 +1,41 @@
+What's new in Tornado 6.4.1
+===========================
+
+Jun 6, 2024
+-----------
+
+Security Improvements
+~~~~~~~~~~~~~~~~~~~~~
+
+- Parsing of the ``Transfer-Encoding`` header is now stricter. Unexpected 
transfer-encoding values
+  were previously ignored and treated as the HTTP/1.0 default of 
read-until-close. This can lead to
+  framing issues with certain proxies. We now treat any unexpected value as an 
error.
+- Handling of whitespace in headers now matches the RFC more closely. Only 
space and tab characters
+  are treated as whitespace and stripped from the beginning and end of header 
values. Other unicode
+  whitespace characters are now left alone. This could also lead to framing 
issues with certain
+  proxies. 
+- ``tornado.curl_httpclient`` now prohibits carriage return and linefeed 
headers in HTTP headers
+  (matching the behavior of ``simple_httpclient``). These characters could be 
used for header
+  injection or request smuggling if untrusted data were used in headers.
+
+General Changes
+~~~~~~~~~~~~~~~
+
+`tornado.iostream`
+~~~~~~~~~~~~~~~~~~
+
+- `.SSLIOStream` now understands changes to error codes from OpenSSL 3.2. The 
main result of this
+  change is to reduce the noise in the logs for certain errors.
+
+``tornado.simple_httpclient``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``simple_httpclient`` now prohibits carriage return characters in HTTP 
headers. It had previously
+  prohibited only linefeed characters.
+
+`tornado.testing`
+~~~~~~~~~~~~~~~~~
+
+- `.AsyncTestCase` subclasses can now be instantiated without being associated 
with a test
+  method. This improves compatibility with test discovery in Pytest 8.2.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/docs/releases.rst 
new/tornado-6.4.1/docs/releases.rst
--- old/tornado-6.4/docs/releases.rst   2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/docs/releases.rst 2024-06-06 20:12:50.000000000 +0200
@@ -4,6 +4,7 @@
 .. toctree::
    :maxdepth: 2
 
+   releases/v6.4.1
    releases/v6.4.0
    releases/v6.3.3
    releases/v6.3.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/requirements.txt 
new/tornado-6.4.1/requirements.txt
--- old/tornado-6.4/requirements.txt    2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/requirements.txt  2024-06-06 20:12:50.000000000 +0200
@@ -8,7 +8,7 @@
     # via sphinx
 babel==2.11.0
     # via sphinx
-black==22.12.0
+black==24.4.2
     # via -r requirements.in
 build==0.10.0
     # via pip-tools
@@ -38,11 +38,11 @@
     #   virtualenv
 flake8==6.0.0
     # via -r requirements.in
-idna==3.4
+idna==3.7
     # via requests
 imagesize==1.4.1
     # via sphinx
-jinja2==3.1.2
+jinja2==3.1.4
     # via sphinx
 markupsafe==2.1.2
     # via jinja2
@@ -56,6 +56,7 @@
     #   mypy
 packaging==23.1
     # via
+    #   black
     #   build
     #   pyproject-api
     #   sphinx
@@ -83,7 +84,7 @@
     # via build
 pytz==2022.7.1
     # via babel
-requests==2.31.0
+requests==2.32.2
     # via sphinx
 snowballstemmer==2.2.0
     # via sphinx
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/__init__.py 
new/tornado-6.4.1/tornado/__init__.py
--- old/tornado-6.4/tornado/__init__.py 2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/__init__.py       2024-06-06 20:12:50.000000000 
+0200
@@ -22,8 +22,8 @@
 # is zero for an official release, positive for a development branch,
 # or negative for a release candidate or beta (after the base version
 # number has been incremented)
-version = "6.4"
-version_info = (6, 4, 0, 0)
+version = "6.4.1"
+version_info = (6, 4, 0, 1)
 
 import importlib
 import typing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/concurrent.py 
new/tornado-6.4.1/tornado/concurrent.py
--- old/tornado-6.4/tornado/concurrent.py       2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/concurrent.py     2024-06-06 20:12:50.000000000 
+0200
@@ -118,6 +118,7 @@
 
        The ``callback`` argument was removed.
     """
+
     # Fully type-checking decorators is tricky, and this one is
     # discouraged anyway so it doesn't have all the generic magic.
     def run_on_executor_decorator(fn: Callable) -> Callable[..., Future]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/curl_httpclient.py 
new/tornado-6.4.1/tornado/curl_httpclient.py
--- old/tornado-6.4/tornado/curl_httpclient.py  2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/curl_httpclient.py        2024-06-06 
20:12:50.000000000 +0200
@@ -19,6 +19,7 @@
 import functools
 import logging
 import pycurl
+import re
 import threading
 import time
 from io import BytesIO
@@ -44,6 +45,8 @@
 
 curl_log = logging.getLogger("tornado.curl_httpclient")
 
+CR_OR_LF_RE = re.compile(b"\r|\n")
+
 
 class CurlAsyncHTTPClient(AsyncHTTPClient):
     def initialize(  # type: ignore
@@ -347,14 +350,15 @@
         if "Pragma" not in request.headers:
             request.headers["Pragma"] = ""
 
-        curl.setopt(
-            pycurl.HTTPHEADER,
-            [
-                b"%s: %s"
-                % (native_str(k).encode("ASCII"), 
native_str(v).encode("ISO8859-1"))
-                for k, v in request.headers.get_all()
-            ],
-        )
+        encoded_headers = [
+            b"%s: %s"
+            % (native_str(k).encode("ASCII"), 
native_str(v).encode("ISO8859-1"))
+            for k, v in request.headers.get_all()
+        ]
+        for line in encoded_headers:
+            if CR_OR_LF_RE.search(line):
+                raise ValueError("Illegal characters in header (CR or LF): %r" 
% line)
+        curl.setopt(pycurl.HTTPHEADER, encoded_headers)
 
         curl.setopt(
             pycurl.HEADERFUNCTION,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/gen.py 
new/tornado-6.4.1/tornado/gen.py
--- old/tornado-6.4/tornado/gen.py      2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/gen.py    2024-06-06 20:12:50.000000000 +0200
@@ -66,6 +66,7 @@
    via ``singledispatch``.
 
 """
+
 import asyncio
 import builtins
 import collections
@@ -165,13 +166,11 @@
 @overload
 def coroutine(
     func: Callable[..., "Generator[Any, Any, _T]"]
-) -> Callable[..., "Future[_T]"]:
-    ...
+) -> Callable[..., "Future[_T]"]: ...
 
 
 @overload
-def coroutine(func: Callable[..., _T]) -> Callable[..., "Future[_T]"]:
-    ...
+def coroutine(func: Callable[..., _T]) -> Callable[..., "Future[_T]"]: ...
 
 
 def coroutine(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/http1connection.py 
new/tornado-6.4.1/tornado/http1connection.py
--- old/tornado-6.4/tornado/http1connection.py  2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/http1connection.py        2024-06-06 
20:12:50.000000000 +0200
@@ -38,6 +38,8 @@
 
 from typing import cast, Optional, Type, Awaitable, Callable, Union, Tuple
 
+CR_OR_LF_RE = re.compile(b"\r|\n")
+
 
 class _QuietException(Exception):
     def __init__(self) -> None:
@@ -389,14 +391,11 @@
             self._request_start_line = start_line
             lines.append(utf8("%s %s HTTP/1.1" % (start_line[0], 
start_line[1])))
             # Client requests with a non-empty body must have either a
-            # Content-Length or a Transfer-Encoding.
+            # Content-Length or a Transfer-Encoding. If Content-Length is not
+            # present we'll add our Transfer-Encoding below.
             self._chunking_output = (
                 start_line.method in ("POST", "PUT", "PATCH")
                 and "Content-Length" not in headers
-                and (
-                    "Transfer-Encoding" not in headers
-                    or headers["Transfer-Encoding"] == "chunked"
-                )
             )
         else:
             assert isinstance(start_line, httputil.ResponseStartLine)
@@ -418,9 +417,6 @@
                 and (start_line.code < 100 or start_line.code >= 200)
                 # No need to chunk the output if a Content-Length is specified.
                 and "Content-Length" not in headers
-                # Applications are discouraged from touching Transfer-Encoding,
-                # but if they do, leave it alone.
-                and "Transfer-Encoding" not in headers
             )
             # If connection to a 1.1 client will be closed, inform client
             if (
@@ -453,8 +449,8 @@
         )
         lines.extend(line.encode("latin1") for line in header_lines)
         for line in lines:
-            if b"\n" in line:
-                raise ValueError("Newline in header: " + repr(line))
+            if CR_OR_LF_RE.search(line):
+                raise ValueError("Illegal characters (CR or LF) in header: %r" 
% line)
         future = None
         if self.stream.closed():
             future = self._write_future = Future()
@@ -560,7 +556,7 @@
             return connection_header != "close"
         elif (
             "Content-Length" in headers
-            or headers.get("Transfer-Encoding", "").lower() == "chunked"
+            or is_transfer_encoding_chunked(headers)
             or getattr(start_line, "method", None) in ("HEAD", "GET")
         ):
             # start_line may be a request or response start line; only
@@ -598,13 +594,6 @@
         delegate: httputil.HTTPMessageDelegate,
     ) -> Optional[Awaitable[None]]:
         if "Content-Length" in headers:
-            if "Transfer-Encoding" in headers:
-                # Response cannot contain both Content-Length and
-                # Transfer-Encoding headers.
-                # http://tools.ietf.org/html/rfc7230#section-3.3.3
-                raise httputil.HTTPInputError(
-                    "Response with both Transfer-Encoding and Content-Length"
-                )
             if "," in headers["Content-Length"]:
                 # Proxies sometimes cause Content-Length headers to get
                 # duplicated.  If all the values are identical then we can
@@ -631,20 +620,22 @@
         else:
             content_length = None
 
+        is_chunked = is_transfer_encoding_chunked(headers)
+
         if code == 204:
             # This response code is not allowed to have a non-empty body,
             # and has an implicit length of zero instead of read-until-close.
             # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
-            if "Transfer-Encoding" in headers or content_length not in (None, 
0):
+            if is_chunked or content_length not in (None, 0):
                 raise httputil.HTTPInputError(
                     "Response with code %d should not have body" % code
                 )
             content_length = 0
 
+        if is_chunked:
+            return self._read_chunked_body(delegate)
         if content_length is not None:
             return self._read_fixed_body(content_length, delegate)
-        if headers.get("Transfer-Encoding", "").lower() == "chunked":
-            return self._read_chunked_body(delegate)
         if self.is_client:
             return self._read_body_until_close(delegate)
         return None
@@ -863,3 +854,33 @@
     if HEXDIGITS.fullmatch(s) is None:
         raise ValueError("not a hexadecimal integer: %r" % s)
     return int(s, 16)
+
+
+def is_transfer_encoding_chunked(headers: httputil.HTTPHeaders) -> bool:
+    """Returns true if the headers specify Transfer-Encoding: chunked.
+
+    Raise httputil.HTTPInputError if any other transfer encoding is used.
+    """
+    # Note that transfer-encoding is an area in which postel's law can lead
+    # us astray. If a proxy and a backend server are liberal in what they 
accept,
+    # but accept slightly different things, this can lead to mismatched framing
+    # and request smuggling issues. Therefore we are as strict as possible here
+    # (even technically going beyond the requirements of the RFCs: a value of
+    # ",chunked" is legal but doesn't appear in practice for legitimate 
traffic)
+    if "Transfer-Encoding" not in headers:
+        return False
+    if "Content-Length" in headers:
+        # Message cannot contain both Content-Length and
+        # Transfer-Encoding headers.
+        # http://tools.ietf.org/html/rfc7230#section-3.3.3
+        raise httputil.HTTPInputError(
+            "Message with both Transfer-Encoding and Content-Length"
+        )
+    if headers["Transfer-Encoding"].lower() == "chunked":
+        return True
+    # We do not support any transfer-encodings other than chunked, and we do 
not
+    # expect to add any support because the concept of transfer-encoding has
+    # been removed in HTTP/2.
+    raise httputil.HTTPInputError(
+        "Unsupported Transfer-Encoding %s" % headers["Transfer-Encoding"]
+    )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/httputil.py 
new/tornado-6.4.1/tornado/httputil.py
--- old/tornado-6.4/tornado/httputil.py 2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/httputil.py       2024-06-06 20:12:50.000000000 
+0200
@@ -62,6 +62,9 @@
     from asyncio import Future  # noqa: F401
     import unittest  # noqa: F401
 
+# To be used with str.strip() and related methods.
+HTTP_WHITESPACE = " \t"
+
 
 @lru_cache(1000)
 def _normalize_header(name: str) -> str:
@@ -171,7 +174,7 @@
             # continuation of a multi-line header
             if self._last_key is None:
                 raise HTTPInputError("first header line cannot start with 
whitespace")
-            new_part = " " + line.lstrip()
+            new_part = " " + line.lstrip(HTTP_WHITESPACE)
             self._as_list[self._last_key][-1] += new_part
             self._dict[self._last_key] += new_part
         else:
@@ -179,7 +182,7 @@
                 name, value = line.split(":", 1)
             except ValueError:
                 raise HTTPInputError("no colon in header line")
-            self.add(name, value.strip())
+            self.add(name, value.strip(HTTP_WHITESPACE))
 
     @classmethod
     def parse(cls, headers: str) -> "HTTPHeaders":
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/iostream.py 
new/tornado-6.4.1/tornado/iostream.py
--- old/tornado-6.4/tornado/iostream.py 2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/iostream.py       2024-06-06 20:12:50.000000000 
+0200
@@ -1374,7 +1374,7 @@
                 return
             elif err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
                 return self.close(exc_info=err)
-            elif err.args[0] == ssl.SSL_ERROR_SSL:
+            elif err.args[0] in (ssl.SSL_ERROR_SSL, ssl.SSL_ERROR_SYSCALL):
                 try:
                     peer = self.socket.getpeername()
                 except Exception:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/simple_httpclient.py 
new/tornado-6.4.1/tornado/simple_httpclient.py
--- old/tornado-6.4/tornado/simple_httpclient.py        2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/simple_httpclient.py      2024-06-06 
20:12:50.000000000 +0200
@@ -429,9 +429,9 @@
                 self.request.method == "POST"
                 and "Content-Type" not in self.request.headers
             ):
-                self.request.headers[
-                    "Content-Type"
-                ] = "application/x-www-form-urlencoded"
+                self.request.headers["Content-Type"] = (
+                    "application/x-www-form-urlencoded"
+                )
             if self.request.decompress_response:
                 self.request.headers["Accept-Encoding"] = "gzip"
             req_path = (self.parsed.path or "/") + (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/__main__.py 
new/tornado-6.4.1/tornado/test/__main__.py
--- old/tornado-6.4/tornado/test/__main__.py    2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/test/__main__.py  2024-06-06 20:12:50.000000000 
+0200
@@ -2,6 +2,7 @@
 
 This only works in python 2.7+.
 """
+
 from tornado.test.runtests import all, main
 
 # tornado.testing.main autodiscovery relies on 'all' being present in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/escape_test.py 
new/tornado-6.4.1/tornado/test/escape_test.py
--- old/tornado-6.4/tornado/test/escape_test.py 2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/test/escape_test.py       2024-06-06 
20:12:50.000000000 +0200
@@ -194,9 +194,11 @@
     (
         "www.external-link.com and www.internal-link.com/blogs extra",
         {
-            "extra_params": lambda href: 'class="internal"'
-            if href.startswith("http://www.internal-link.com";)
-            else 'rel="nofollow" class="external"'
+            "extra_params": lambda href: (
+                'class="internal"'
+                if href.startswith("http://www.internal-link.com";)
+                else 'rel="nofollow" class="external"'
+            )
         },
         '<a href="http://www.external-link.com"; rel="nofollow" 
class="external">www.external-link.com</a>'  # noqa: E501
         ' and <a href="http://www.internal-link.com/blogs"; 
class="internal">www.internal-link.com/blogs</a> extra',  # noqa: E501
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/httpclient_test.py 
new/tornado-6.4.1/tornado/test/httpclient_test.py
--- old/tornado-6.4/tornado/test/httpclient_test.py     2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/test/httpclient_test.py   2024-06-06 
20:12:50.000000000 +0200
@@ -725,6 +725,22 @@
                 if el.logged_stack:
                     break
 
+    def test_header_crlf(self):
+        # Ensure that the client doesn't allow CRLF injection in headers. RFC 
9112 section 2.2
+        # prohibits a bare CR specifically and "a recipient MAY recognize a 
single LF as a line
+        # terminator" so we check each character separately as well as the 
(redundant) CRLF pair.
+        for header, name in [
+            ("foo\rbar:", "cr"),
+            ("foo\nbar:", "lf"),
+            ("foo\r\nbar:", "crlf"),
+        ]:
+            with self.subTest(name=name, position="value"):
+                with self.assertRaises(ValueError):
+                    self.fetch("/hello", headers={"foo": header})
+            with self.subTest(name=name, position="key"):
+                with self.assertRaises(ValueError):
+                    self.fetch("/hello", headers={header: "foo"})
+
 
 class RequestProxyTest(unittest.TestCase):
     def test_request_set(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/httpserver_test.py 
new/tornado-6.4.1/tornado/test/httpserver_test.py
--- old/tornado-6.4/tornado/test/httpserver_test.py     2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/test/httpserver_test.py   2024-06-06 
20:12:50.000000000 +0200
@@ -581,6 +581,76 @@
             )
         self.assertEqual(400, start_line.code)
 
+    def test_chunked_request_body_duplicate_header(self):
+        # Repeated Transfer-Encoding headers should be an error (and not 
confuse
+        # the chunked-encoding detection to mess up framing).
+        self.stream.write(
+            b"""\
+POST /echo HTTP/1.1
+Transfer-Encoding: chunked
+Transfer-encoding: chunked
+
+2
+ok
+0
+
+"""
+        )
+        with ExpectLog(
+            gen_log,
+            ".*Unsupported Transfer-Encoding chunked,chunked",
+            level=logging.INFO,
+        ):
+            start_line, headers, response = self.io_loop.run_sync(
+                lambda: read_stream_body(self.stream)
+            )
+        self.assertEqual(400, start_line.code)
+
+    def test_chunked_request_body_unsupported_transfer_encoding(self):
+        # We don't support transfer-encodings other than chunked.
+        self.stream.write(
+            b"""\
+POST /echo HTTP/1.1
+Transfer-Encoding: gzip, chunked
+
+2
+ok
+0
+
+"""
+        )
+        with ExpectLog(
+            gen_log, ".*Unsupported Transfer-Encoding gzip, chunked", 
level=logging.INFO
+        ):
+            start_line, headers, response = self.io_loop.run_sync(
+                lambda: read_stream_body(self.stream)
+            )
+        self.assertEqual(400, start_line.code)
+
+    def test_chunked_request_body_transfer_encoding_and_content_length(self):
+        # Transfer-encoding and content-length are mutually exclusive
+        self.stream.write(
+            b"""\
+POST /echo HTTP/1.1
+Transfer-Encoding: chunked
+Content-Length: 2
+
+2
+ok
+0
+
+"""
+        )
+        with ExpectLog(
+            gen_log,
+            ".*Message with both Transfer-Encoding and Content-Length",
+            level=logging.INFO,
+        ):
+            start_line, headers, response = self.io_loop.run_sync(
+                lambda: read_stream_body(self.stream)
+            )
+        self.assertEqual(400, start_line.code)
+
     @gen_test
     def test_invalid_content_length(self):
         # HTTP only allows decimal digits in content-length. Make sure we don't
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/httputil_test.py 
new/tornado-6.4.1/tornado/test/httputil_test.py
--- old/tornado-6.4/tornado/test/httputil_test.py       2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/test/httputil_test.py     2024-06-06 
20:12:50.000000000 +0200
@@ -334,6 +334,25 @@
                     gen_log.warning("failed while trying %r in %s", newline, 
encoding)
                     raise
 
+    def test_unicode_whitespace(self):
+        # Only tabs and spaces are to be stripped according to the HTTP 
standard.
+        # Other unicode whitespace is to be left as-is. In the context of 
headers,
+        # this specifically means the whitespace characters falling within the
+        # latin1 charset.
+        whitespace = [
+            (" ", True),  # SPACE
+            ("\t", True),  # TAB
+            ("\u00a0", False),  # NON-BREAKING SPACE
+            ("\u0085", False),  # NEXT LINE
+        ]
+        for c, stripped in whitespace:
+            headers = HTTPHeaders.parse("Transfer-Encoding: %schunked" % c)
+            if stripped:
+                expected = [("Transfer-Encoding", "chunked")]
+            else:
+                expected = [("Transfer-Encoding", "%schunked" % c)]
+            self.assertEqual(expected, list(headers.get_all()))
+
     def test_optional_cr(self):
         # Both CRLF and LF should be accepted as separators. CR should not be
         # part of the data when followed by LF, but it is a normal char
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/ioloop_test.py 
new/tornado-6.4.1/tornado/test/ioloop_test.py
--- old/tornado-6.4/tornado/test/ioloop_test.py 2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/test/ioloop_test.py       2024-06-06 
20:12:50.000000000 +0200
@@ -261,6 +261,7 @@
         the object should be closed (by IOLoop.close(all_fds=True),
         not just the fd.
         """
+
         # Use a socket since they are supported by IOLoop on all platforms.
         # Unfortunately, sockets don't support the .closed attribute for
         # inspecting their close status, so we must use a wrapper.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/simple_httpclient_test.py 
new/tornado-6.4.1/tornado/test/simple_httpclient_test.py
--- old/tornado-6.4/tornado/test/simple_httpclient_test.py      2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/test/simple_httpclient_test.py    2024-06-06 
20:12:50.000000000 +0200
@@ -828,7 +828,7 @@
         with ExpectLog(
             gen_log,
             (
-                "Malformed HTTP message from None: Response "
+                "Malformed HTTP message from None: Message "
                 "with both Transfer-Encoding and Content-Length"
             ),
             level=logging.INFO,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/test/testing_test.py 
new/tornado-6.4.1/tornado/test/testing_test.py
--- old/tornado-6.4/tornado/test/testing_test.py        2023-11-29 
04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/test/testing_test.py      2024-06-06 
20:12:50.000000000 +0200
@@ -5,7 +5,6 @@
 from tornado.web import Application
 import asyncio
 import contextlib
-import inspect
 import gc
 import os
 import platform
@@ -118,7 +117,11 @@
         super().tearDown()
 
 
-class AsyncTestCaseWrapperTest(unittest.TestCase):
+class AsyncTestCaseReturnAssertionsTest(unittest.TestCase):
+    # These tests verify that tests that return non-None values (without being 
decorated with
+    # @gen_test) raise errors instead of incorrectly succeeding. These tests 
should be removed or
+    # updated when the _callTestMethod method is removed from AsyncTestCase 
(the same checks will
+    # still happen, but they'll be performed in the stdlib as 
DeprecationWarnings)
     def test_undecorated_generator(self):
         class Test(AsyncTestCase):
             def test_gen(self):
@@ -135,7 +138,10 @@
         "pypy destructor warnings cannot be silenced",
     )
     @unittest.skipIf(
-        sys.version_info >= (3, 12), "py312 has its own check for test case 
returns"
+        # This check actually exists in 3.11 but it changed in 3.12 in a way 
that breaks
+        # this test.
+        sys.version_info >= (3, 12),
+        "py312 has its own check for test case returns",
     )
     def test_undecorated_coroutine(self):
         class Test(AsyncTestCase):
@@ -176,17 +182,6 @@
         self.assertEqual(len(result.errors), 1)
         self.assertIn("Return value from test method ignored", 
result.errors[0][1])
 
-    def test_unwrap(self):
-        class Test(AsyncTestCase):
-            def test_foo(self):
-                pass
-
-        test = Test("test_foo")
-        self.assertIs(
-            inspect.unwrap(test.test_foo),
-            test.test_foo.orig_method,  # type: ignore[attr-defined]
-        )
-
 
 class SetUpTearDownTest(unittest.TestCase):
     def test_set_up_tear_down(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/testing.py 
new/tornado-6.4.1/tornado/testing.py
--- old/tornado-6.4/tornado/testing.py  2023-11-29 04:20:18.000000000 +0100
+++ new/tornado-6.4.1/tornado/testing.py        2024-06-06 20:12:50.000000000 
+0200
@@ -84,39 +84,6 @@
     return 5
 
 
-class _TestMethodWrapper(object):
-    """Wraps a test method to raise an error if it returns a value.
-
-    This is mainly used to detect undecorated generators (if a test
-    method yields it must use a decorator to consume the generator),
-    but will also detect other kinds of return values (these are not
-    necessarily errors, but we alert anyway since there is no good
-    reason to return a value from a test).
-    """
-
-    def __init__(self, orig_method: Callable) -> None:
-        self.orig_method = orig_method
-        self.__wrapped__ = orig_method
-
-    def __call__(self, *args: Any, **kwargs: Any) -> None:
-        result = self.orig_method(*args, **kwargs)
-        if isinstance(result, Generator) or inspect.iscoroutine(result):
-            raise TypeError(
-                "Generator and coroutine test methods should be"
-                " decorated with tornado.testing.gen_test"
-            )
-        elif result is not None:
-            raise ValueError("Return value from test method ignored: %r" % 
result)
-
-    def __getattr__(self, name: str) -> Any:
-        """Proxy all unknown attributes to the original method.
-
-        This is important for some of the decorators in the `unittest`
-        module, such as `unittest.skipIf`.
-        """
-        return getattr(self.orig_method, name)
-
-
 class AsyncTestCase(unittest.TestCase):
     """`~unittest.TestCase` subclass for testing `.IOLoop`-based
     asynchronous code.
@@ -173,12 +140,6 @@
         self.__stop_args = None  # type: Any
         self.__timeout = None  # type: Optional[object]
 
-        # It's easy to forget the @gen_test decorator, but if you do
-        # the test will silently be ignored because nothing will consume
-        # the generator.  Replace the test method with a wrapper that will
-        # make sure it's not an undecorated generator.
-        setattr(self, methodName, _TestMethodWrapper(getattr(self, 
methodName)))
-
         # Not used in this class itself, but used by @gen_test
         self._test_generator = None  # type: Optional[Union[Generator, 
Coroutine]]
 
@@ -289,6 +250,30 @@
         self.__rethrow()
         return ret
 
+    def _callTestMethod(self, method: Callable) -> None:
+        """Run the given test method, raising an error if it returns non-None.
+
+        Failure to decorate asynchronous test methods with ``@gen_test`` can 
lead to tests
+        incorrectly passing.
+
+        Remove this override when Python 3.10 support is dropped. This check 
(in the form of a
+        DeprecationWarning) became a part of the standard library in 3.11.
+
+        Note that ``_callTestMethod`` is not documented as a public interface. 
However, it is
+        present in all supported versions of Python (3.8+), and if it goes 
away in the future that's
+        OK because we can just remove this override as noted above.
+        """
+        # Calling super()._callTestMethod would hide the return value, even in 
python 3.8-3.10
+        # where the check isn't being done for us.
+        result = method()
+        if isinstance(result, Generator) or inspect.iscoroutine(result):
+            raise TypeError(
+                "Generator and coroutine test methods should be"
+                " decorated with tornado.testing.gen_test"
+            )
+        elif result is not None:
+            raise ValueError("Return value from test method ignored: %r" % 
result)
+
     def stop(self, _arg: Any = None, **kwargs: Any) -> None:
         """Stops the `.IOLoop`, causing one pending (or future) call to 
`wait()`
         to return.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado/websocket.py 
new/tornado-6.4.1/tornado/websocket.py
--- old/tornado-6.4/tornado/websocket.py        2023-11-29 04:20:18.000000000 
+0100
+++ new/tornado-6.4.1/tornado/websocket.py      2024-06-06 20:12:50.000000000 
+0200
@@ -1392,9 +1392,9 @@
             # from the server).
             # TODO: set server parameters for deflate extension
             # if requested in self.compression_options.
-            request.headers[
-                "Sec-WebSocket-Extensions"
-            ] = "permessage-deflate; client_max_window_bits"
+            request.headers["Sec-WebSocket-Extensions"] = (
+                "permessage-deflate; client_max_window_bits"
+            )
 
         # Websocket connection is currently unable to follow redirects
         request.follow_redirects = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado.egg-info/PKG-INFO 
new/tornado-6.4.1/tornado.egg-info/PKG-INFO
--- old/tornado-6.4/tornado.egg-info/PKG-INFO   2023-11-29 04:20:19.000000000 
+0100
+++ new/tornado-6.4.1/tornado.egg-info/PKG-INFO 2024-06-06 20:12:53.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tornado
-Version: 6.4
+Version: 6.4.1
 Summary: Tornado is a Python web framework and asynchronous networking 
library, originally developed at FriendFeed.
 Home-page: http://www.tornadoweb.org/
 Author: Facebook
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/tornado-6.4/tornado.egg-info/SOURCES.txt 
new/tornado-6.4.1/tornado.egg-info/SOURCES.txt
--- old/tornado-6.4/tornado.egg-info/SOURCES.txt        2023-11-29 
04:20:19.000000000 +0100
+++ new/tornado-6.4.1/tornado.egg-info/SOURCES.txt      2024-06-06 
20:12:53.000000000 +0200
@@ -114,6 +114,7 @@
 docs/releases/v6.3.2.rst
 docs/releases/v6.3.3.rst
 docs/releases/v6.4.0.rst
+docs/releases/v6.4.1.rst
 tornado/__init__.py
 tornado/_locale_data.py
 tornado/auth.py

Reply via email to