Compare commits
No commits in common. "c10s" and "c9-beta" have entirely different histories.
@ -1 +0,0 @@
|
||||
1
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
/tornado-*.tar.gz
|
||||
SOURCES/tornado-6.1.0.tar.gz
|
||||
|
1
.python-tornado.metadata
Normal file
1
.python-tornado.metadata
Normal file
@ -0,0 +1 @@
|
||||
c23c617c7a0205e465bebad5b8cdf289ae8402a2 SOURCES/tornado-6.1.0.tar.gz
|
60
SOURCES/0001-Add-test-for-open-redirect-issue.patch
Normal file
60
SOURCES/0001-Add-test-for-open-redirect-issue.patch
Normal file
@ -0,0 +1,60 @@
|
||||
Subject: [PATCH 1/2] Add test for open redirect issue
|
||||
|
||||
Backported from upstream:
|
||||
- https://github.com/tornadoweb/tornado/commit/b56245730e
|
||||
---
|
||||
tornado/test/web_test.py | 31 ++++++++++++++++++++++++++++++-
|
||||
1 file changed, 30 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
|
||||
index 5490ba2..c641ca1 100644
|
||||
--- a/tornado/test/web_test.py
|
||||
+++ b/tornado/test/web_test.py
|
||||
@@ -1426,6 +1426,35 @@ class StaticDefaultFilenameTest(WebTestCase):
|
||||
self.assertTrue(response.headers["Location"].endswith("/static/dir/"))
|
||||
|
||||
|
||||
+class StaticDefaultFilenameRootTest(WebTestCase):
|
||||
+ def get_app_kwargs(self):
|
||||
+ return dict(
|
||||
+ static_path=os.path.abspath(relpath("static")),
|
||||
+ static_handler_args=dict(default_filename="index.html"),
|
||||
+ static_url_prefix="/",
|
||||
+ )
|
||||
+
|
||||
+ def get_handlers(self):
|
||||
+ return []
|
||||
+
|
||||
+ def get_http_client(self):
|
||||
+ # simple_httpclient only: curl doesn't let you send a request starting
|
||||
+ # with two slashes.
|
||||
+ return SimpleAsyncHTTPClient()
|
||||
+
|
||||
+ def test_no_open_redirect(self):
|
||||
+ # This test verifies that the open redirect that affected some configurations
|
||||
+ # prior to Tornado 6.3.2 is no longer possible. The vulnerability required
|
||||
+ # a static_url_prefix of "/" and a default_filename (any value) to be set.
|
||||
+ # The absolute server-side path to the static directory must also be known.
|
||||
+ with ExpectLog(gen_log, ".*cannot redirect path with two initial slashes"):
|
||||
+ response = self.fetch(
|
||||
+ f"//evil.com/../{os.path.dirname(__file__)}/static/dir",
|
||||
+ follow_redirects=False,
|
||||
+ )
|
||||
+ self.assertEqual(response.code, 403)
|
||||
+
|
||||
+
|
||||
class StaticFileWithPathTest(WebTestCase):
|
||||
def get_app_kwargs(self):
|
||||
return dict(
|
||||
@@ -2837,7 +2866,7 @@ class XSRFTest(SimpleHandlerTestCase):
|
||||
body=b"",
|
||||
headers=dict(
|
||||
{"X-Xsrftoken": self.xsrf_token}, # type: ignore
|
||||
- **self.cookie_headers()
|
||||
+ **self.cookie_headers(),
|
||||
),
|
||||
)
|
||||
self.assertEqual(response.code, 200)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,41 @@
|
||||
From bcae82a6dd7bfed280559c8920dd89d4a48fa021 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Darnell <ben@bendarnell.com>
|
||||
Date: Tue, 25 Jul 2023 06:39:23 -0400
|
||||
Subject: [PATCH 2/2] [PATCH] web: Fix an open redirect in StaticFileHandler
|
||||
|
||||
Under some configurations the default_filename redirect could be exploited
|
||||
to redirect to an attacker-controlled site. This change refuses to redirect
|
||||
to URLs that could be misinterpreted.
|
||||
|
||||
A test case for the specific vulnerable configuration will follow after the
|
||||
patch has been available.
|
||||
|
||||
Originally from upstream:
|
||||
- https://github.com/tornadoweb/tornado/commit/8f35b31ab
|
||||
---
|
||||
tornado/web.py | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/tornado/web.py b/tornado/web.py
|
||||
index 546e6ec..8410880 100644
|
||||
--- a/tornado/web.py
|
||||
+++ b/tornado/web.py
|
||||
@@ -2771,6 +2771,15 @@ class StaticFileHandler(RequestHandler):
|
||||
# but there is some prefix to the path that was already
|
||||
# trimmed by the routing
|
||||
if not self.request.path.endswith("/"):
|
||||
+ if self.request.path.startswith("//"):
|
||||
+ # A redirect with two initial slashes is a "protocol-relative" URL.
|
||||
+ # This means the next path segment is treated as a hostname instead
|
||||
+ # of a part of the path, making this effectively an open redirect.
|
||||
+ # Reject paths starting with two slashes to prevent this.
|
||||
+ # This is only reachable under certain configurations.
|
||||
+ raise HTTPError(
|
||||
+ 403, "cannot redirect path with two initial slashes"
|
||||
+ )
|
||||
self.redirect(self.request.path + "/", permanent=True)
|
||||
return None
|
||||
absolute_path = os.path.join(absolute_path, self.default_filename)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -1,8 +1,7 @@
|
||||
diff --git a/tornado/test/runtests.py b/tornado/test/runtests.py
|
||||
index 6075b1e..dc4fb89 100644
|
||||
--- a/tornado/test/runtests.py
|
||||
+++ b/tornado/test/runtests.py
|
||||
@@ -123,7 +123,6 @@ def main():
|
||||
diff -Nur tornado-6.0.4/tornado/test/runtests.py tornado-6.0.4-new/tornado/test/runtests.py
|
||||
--- tornado-6.0.4/tornado/test/runtests.py 2020-03-02 20:21:37.000000000 +0100
|
||||
+++ tornado-6.0.4-new/tornado/test/runtests.py 2020-09-14 09:21:31.818678680 +0200
|
||||
@@ -126,7 +126,6 @@
|
||||
# Tornado generally shouldn't use anything deprecated, but some of
|
||||
# our dependencies do (last match wins).
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
32
SOURCES/Increase-timeout-in-test_request_timeout.patch
Normal file
32
SOURCES/Increase-timeout-in-test_request_timeout.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From b99df6cf3328f8e4aeb9db178df19b2411894b8c Mon Sep 17 00:00:00 2001
|
||||
From: Sergio Correia <scorreia@redhat.com>
|
||||
Date: Wed, 15 Jun 2022 21:10:52 -0300
|
||||
Subject: [PATCH] Increase timeout in test_request_timeout
|
||||
|
||||
Some architectures -- e.g. ppc64le -- need an increased request timeout.
|
||||
This situation is similar to upstream change 2195f0b72, where the
|
||||
timeout was increased for Windows.
|
||||
|
||||
[1] https://github.com/tornadoweb/tornado/commit/2195f0b72249738
|
||||
---
|
||||
tornado/test/simple_httpclient_test.py | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/tornado/test/simple_httpclient_test.py b/tornado/test/simple_httpclient_test.py
|
||||
index eadd4ed..333de50 100644
|
||||
--- a/tornado/test/simple_httpclient_test.py
|
||||
+++ b/tornado/test/simple_httpclient_test.py
|
||||
@@ -293,9 +293,7 @@ class SimpleHTTPClientTestMixin(object):
|
||||
|
||||
@skipOnTravis
|
||||
def test_request_timeout(self: typing.Any):
|
||||
- timeout = 0.1
|
||||
- if os.name == "nt":
|
||||
- timeout = 0.5
|
||||
+ timeout = 0.5
|
||||
|
||||
with self.assertRaises(HTTPTimeoutError):
|
||||
self.fetch("/trigger?wake=false", request_timeout=timeout, raise_error=True)
|
||||
--
|
||||
2.35.1
|
||||
|
@ -10,23 +10,27 @@ handle thousands of simultaneous standing connections, which means it is
|
||||
ideal for real-time web services.}
|
||||
|
||||
Name: python-%{srcname}
|
||||
Version: 6.3.3
|
||||
Release: 5%{?dist}
|
||||
Version: 6.1.0
|
||||
Release: 9%{?dist}
|
||||
Summary: Scalable, non-blocking web server and tools
|
||||
|
||||
License: Apache-2.0
|
||||
License: ASL 2.0
|
||||
URL: https://www.tornadoweb.org
|
||||
Source0: https://github.com/tornadoweb/tornado/archive/v%{version}/%{srcname}-%{version}.tar.gz
|
||||
|
||||
# Do not turn DeprecationWarning in tornado module into Exception
|
||||
# fixes FTBFS with Python 3.8
|
||||
Patch1: Do-not-turn-DeprecationWarning-into-Exception.patch
|
||||
# Fixes for Python 3.12 - rebased
|
||||
# https://github.com/tornadoweb/tornado/pull/3288
|
||||
Patch2: python-tornado-Python-3.12.patch
|
||||
Patch: Do-not-turn-DeprecationWarning-into-Exception.patch
|
||||
# Fix timeout failure in architectures such as ppc64le.
|
||||
Patch: Increase-timeout-in-test_request_timeout.patch
|
||||
|
||||
# CVE-2023-28370
|
||||
Patch: 0001-Add-test-for-open-redirect-issue.patch
|
||||
Patch: 0002-PATCH-web-Fix-an-open-redirect-in-StaticFileHandler.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: python3-devel
|
||||
BuildRequires: python%{python3_pkgversion}-setuptools
|
||||
BuildRequires: python%{python3_pkgversion}-devel
|
||||
|
||||
%description %{common_description}
|
||||
|
||||
@ -44,81 +48,41 @@ This package contains some example applications.
|
||||
|
||||
%prep
|
||||
%autosetup -p1 -n %{srcname}-%{version}
|
||||
|
||||
%generate_buildrequires
|
||||
%pyproject_buildrequires
|
||||
# Remove shebang from files
|
||||
%{__sed} -i.orig -e '/^#!\//, 1d' *py tornado/*.py tornado/*/*.py
|
||||
|
||||
%build
|
||||
%pyproject_wheel
|
||||
%py3_build
|
||||
|
||||
%install
|
||||
%pyproject_install
|
||||
%pyproject_save_files %{srcname}
|
||||
%py3_install
|
||||
|
||||
%check
|
||||
# Skip the same timing-related tests that upstream skips when run in Travis CI.
|
||||
# https://github.com/tornadoweb/tornado/commit/abc5780a06a1edd0177a399a4dd4f39497cb0c57
|
||||
export TRAVIS=true
|
||||
export ASYNC_TEST_TIMEOUT=10
|
||||
%{__python3} -m tornado.test.runtests --verbose
|
||||
|
||||
# Increase timeout for tests on riscv64
|
||||
%ifarch riscv64
|
||||
export ASYNC_TEST_TIMEOUT=80
|
||||
%endif
|
||||
|
||||
%{py3_test_envvars} %{python3} -m tornado.test
|
||||
|
||||
%files -n python3-%{srcname} -f %{pyproject_files}
|
||||
%files -n python%{python3_pkgversion}-%{srcname}
|
||||
%license LICENSE
|
||||
%doc README.rst
|
||||
%{python3_sitearch}/%{srcname}/
|
||||
%{python3_sitearch}/%{srcname}-*.egg-info/
|
||||
|
||||
%files doc
|
||||
%license LICENSE
|
||||
%doc demos
|
||||
|
||||
%changelog
|
||||
* Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 6.3.3-5
|
||||
- Bump release for June 2024 mass rebuild
|
||||
* Tue Jul 25 2023 Sergio Correia <scorreia@redhat.com> - 6.1.0-9
|
||||
- Fix an open redirect in StaticFileHandler
|
||||
Resolves: CVE-2023-28370
|
||||
|
||||
* Fri Jun 07 2024 Richard W.M. Jones <rjones@redhat.com> - 6.3.3-4
|
||||
- Increase timeout for tests on riscv64
|
||||
* Wed Jun 15 2022 Sergio Correia <scorreia@redhat.com> - 6.1.0-8
|
||||
- Fix test failure in pcc64le
|
||||
Related: rhbz#2084553
|
||||
|
||||
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 6.3.3-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 6.3.3-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Fri Aug 11 2023 Gwyn Ciesla <gwync@protonmail.com> - 6.3.3-1
|
||||
- 6.3.3
|
||||
|
||||
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 6.3.2-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||
|
||||
* Thu Jun 22 2023 Orion Poplawski <orion@nwra.com> - 6.3.2-4
|
||||
- Add upstream patch for Python 3.12 support
|
||||
|
||||
* Tue Jun 13 2023 Python Maint <python-maint@redhat.com> - 6.3.2-3
|
||||
- Rebuilt for Python 3.12
|
||||
|
||||
* Tue May 23 2023 Yaakov Selkowitz <yselkowi@redhat.com> - 6.3.2-2
|
||||
- Avoid tox dependency
|
||||
|
||||
* Tue May 16 2023 Orion Poplawski <orion@nwra.com> - 6.3.2-1
|
||||
- Update to 6.3.2
|
||||
|
||||
* Wed Mar 08 2023 Gwyn Ciesla <gwync@protonmail.com> - 6.2.0-4
|
||||
- migrated to SPDX license
|
||||
|
||||
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 6.2.0-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||
|
||||
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 6.2.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
|
||||
|
||||
* Thu Jul 07 2022 Carl George <carl@george.computer> - 6.2.0-1
|
||||
- Latest upstream, resolves rhbz#1883858
|
||||
|
||||
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 6.1.0-7
|
||||
- Rebuilt for Python 3.11
|
||||
* Wed Jun 15 2022 Sergio Correia <scorreia@redhat.com> - 6.1.0-7
|
||||
- Add python-tornado to RHEL-9
|
||||
Resolves: rhbz#2084553
|
||||
|
||||
* Tue Feb 08 2022 Carl George <carl@george.computer> - 6.1.0-6
|
||||
- Convert to pyproject macros
|
16
ci.fmf
16
ci.fmf
@ -1,16 +0,0 @@
|
||||
/keylime-e2e:
|
||||
plan:
|
||||
import:
|
||||
url: https://github.com/RedHat-SP-Security/keylime-plans.git
|
||||
name: /generic/minimal-e2e
|
||||
|
||||
/internal-regression:
|
||||
enabled: false
|
||||
|
||||
adjust:
|
||||
- when: distro == rhel
|
||||
enabled: true
|
||||
plan:
|
||||
import:
|
||||
url: https://gitlab.cee.redhat.com/special-projects/tests/python-tornado.git
|
||||
name: /Plans/regression
|
@ -1,6 +0,0 @@
|
||||
--- !Policy
|
||||
product_versions:
|
||||
- rhel-10
|
||||
decision_context: osci_compose_gate
|
||||
rules:
|
||||
- !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}
|
@ -1,404 +0,0 @@
|
||||
diff --git a/requirements.txt b/requirements.txt
|
||||
index 349db21..e96c870 100644
|
||||
--- a/requirements.txt
|
||||
+++ b/requirements.txt
|
||||
@@ -12,7 +12,7 @@ black==22.12.0
|
||||
# via -r requirements.in
|
||||
build==0.10.0
|
||||
# via pip-tools
|
||||
-cachetools==5.2.1
|
||||
+cachetools==5.3.1
|
||||
# via tox
|
||||
certifi==2022.12.7
|
||||
# via requests
|
||||
@@ -32,7 +32,7 @@ docutils==0.17.1
|
||||
# via
|
||||
# sphinx
|
||||
# sphinx-rtd-theme
|
||||
-filelock==3.9.0
|
||||
+filelock==3.12.0
|
||||
# via
|
||||
# tox
|
||||
# virtualenv
|
||||
@@ -54,7 +54,7 @@ mypy-extensions==0.4.3
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
-packaging==23.0
|
||||
+packaging==23.1
|
||||
# via
|
||||
# build
|
||||
# pyproject-api
|
||||
@@ -64,7 +64,7 @@ pathspec==0.10.3
|
||||
# via black
|
||||
pip-tools==6.12.1
|
||||
# via -r requirements.in
|
||||
-platformdirs==2.6.2
|
||||
+platformdirs==3.5.1
|
||||
# via
|
||||
# black
|
||||
# tox
|
||||
@@ -77,7 +77,7 @@ pyflakes==3.0.1
|
||||
# via flake8
|
||||
pygments==2.14.0
|
||||
# via sphinx
|
||||
-pyproject-api==1.5.0
|
||||
+pyproject-api==1.5.1
|
||||
# via tox
|
||||
pyproject-hooks==1.0.0
|
||||
# via build
|
||||
@@ -115,7 +115,7 @@ tomli==2.0.1
|
||||
# mypy
|
||||
# pyproject-api
|
||||
# tox
|
||||
-tox==4.3.5
|
||||
+tox==4.6.0
|
||||
# via -r requirements.in
|
||||
types-pycurl==7.45.2.0
|
||||
# via -r requirements.in
|
||||
@@ -123,7 +123,7 @@ typing-extensions==4.4.0
|
||||
# via mypy
|
||||
urllib3==1.26.14
|
||||
# via requests
|
||||
-virtualenv==20.17.1
|
||||
+virtualenv==20.23.0
|
||||
# via tox
|
||||
wheel==0.38.4
|
||||
# via pip-tools
|
||||
diff --git a/tornado/httputil.py b/tornado/httputil.py
|
||||
index 9c341d4..b21d804 100644
|
||||
--- a/tornado/httputil.py
|
||||
+++ b/tornado/httputil.py
|
||||
@@ -856,7 +856,8 @@ def format_timestamp(
|
||||
|
||||
The argument may be a numeric timestamp as returned by `time.time`,
|
||||
a time tuple as returned by `time.gmtime`, or a `datetime.datetime`
|
||||
- object.
|
||||
+ object. Naive `datetime.datetime` objects are assumed to represent
|
||||
+ UTC; aware objects are converted to UTC before formatting.
|
||||
|
||||
>>> format_timestamp(1359312200)
|
||||
'Sun, 27 Jan 2013 18:43:20 GMT'
|
||||
diff --git a/tornado/locale.py b/tornado/locale.py
|
||||
index 55072af..c552670 100644
|
||||
--- a/tornado/locale.py
|
||||
+++ b/tornado/locale.py
|
||||
@@ -333,7 +333,7 @@ class Locale(object):
|
||||
shorter: bool = False,
|
||||
full_format: bool = False,
|
||||
) -> str:
|
||||
- """Formats the given date (which should be GMT).
|
||||
+ """Formats the given date.
|
||||
|
||||
By default, we return a relative time (e.g., "2 minutes ago"). You
|
||||
can return an absolute date string with ``relative=False``.
|
||||
@@ -343,10 +343,16 @@ class Locale(object):
|
||||
|
||||
This method is primarily intended for dates in the past.
|
||||
For dates in the future, we fall back to full format.
|
||||
+
|
||||
+ .. versionchanged:: 6.4
|
||||
+ Aware `datetime.datetime` objects are now supported (naive
|
||||
+ datetimes are still assumed to be UTC).
|
||||
"""
|
||||
if isinstance(date, (int, float)):
|
||||
- date = datetime.datetime.utcfromtimestamp(date)
|
||||
- now = datetime.datetime.utcnow()
|
||||
+ date = datetime.datetime.fromtimestamp(date, datetime.timezone.utc)
|
||||
+ if date.tzinfo is None:
|
||||
+ date = date.replace(tzinfo=datetime.timezone.utc)
|
||||
+ now = datetime.datetime.now(datetime.timezone.utc)
|
||||
if date > now:
|
||||
if relative and (date - now).seconds < 60:
|
||||
# Due to click skew, things are some things slightly
|
||||
diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py
|
||||
index a71ec0a..a41040e 100644
|
||||
--- a/tornado/test/httpclient_test.py
|
||||
+++ b/tornado/test/httpclient_test.py
|
||||
@@ -28,7 +28,7 @@ from tornado.iostream import IOStream
|
||||
from tornado.log import gen_log, app_log
|
||||
from tornado import netutil
|
||||
from tornado.testing import AsyncHTTPTestCase, bind_unused_port, gen_test, ExpectLog
|
||||
-from tornado.test.util import skipOnTravis
|
||||
+from tornado.test.util import skipOnTravis, ignore_deprecation
|
||||
from tornado.web import Application, RequestHandler, url
|
||||
from tornado.httputil import format_timestamp, HTTPHeaders
|
||||
|
||||
@@ -887,7 +887,15 @@ class HTTPRequestTestCase(unittest.TestCase):
|
||||
self.assertEqual(request.body, utf8("foo"))
|
||||
|
||||
def test_if_modified_since(self):
|
||||
- http_date = datetime.datetime.utcnow()
|
||||
+ http_date = datetime.datetime.now(datetime.timezone.utc)
|
||||
+ request = HTTPRequest("http://example.com", if_modified_since=http_date)
|
||||
+ self.assertEqual(
|
||||
+ request.headers, {"If-Modified-Since": format_timestamp(http_date)}
|
||||
+ )
|
||||
+
|
||||
+ def test_if_modified_since_naive_deprecated(self):
|
||||
+ with ignore_deprecation():
|
||||
+ http_date = datetime.datetime.utcnow()
|
||||
request = HTTPRequest("http://example.com", if_modified_since=http_date)
|
||||
self.assertEqual(
|
||||
request.headers, {"If-Modified-Since": format_timestamp(http_date)}
|
||||
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
|
||||
index 8424491..aa9b6ee 100644
|
||||
--- a/tornado/test/httputil_test.py
|
||||
+++ b/tornado/test/httputil_test.py
|
||||
@@ -13,6 +13,7 @@ from tornado.httputil import (
|
||||
from tornado.escape import utf8, native_str
|
||||
from tornado.log import gen_log
|
||||
from tornado.testing import ExpectLog
|
||||
+from tornado.test.util import ignore_deprecation
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
@@ -412,8 +413,29 @@ class FormatTimestampTest(unittest.TestCase):
|
||||
self.assertEqual(9, len(tup))
|
||||
self.check(tup)
|
||||
|
||||
- def test_datetime(self):
|
||||
- self.check(datetime.datetime.utcfromtimestamp(self.TIMESTAMP))
|
||||
+ def test_utc_naive_datetime(self):
|
||||
+ self.check(
|
||||
+ datetime.datetime.fromtimestamp(
|
||||
+ self.TIMESTAMP, datetime.timezone.utc
|
||||
+ ).replace(tzinfo=None)
|
||||
+ )
|
||||
+
|
||||
+ def test_utc_naive_datetime_deprecated(self):
|
||||
+ with ignore_deprecation():
|
||||
+ self.check(datetime.datetime.utcfromtimestamp(self.TIMESTAMP))
|
||||
+
|
||||
+ def test_utc_aware_datetime(self):
|
||||
+ self.check(
|
||||
+ datetime.datetime.fromtimestamp(self.TIMESTAMP, datetime.timezone.utc)
|
||||
+ )
|
||||
+
|
||||
+ def test_other_aware_datetime(self):
|
||||
+ # Other timezones are ignored; the timezone is always printed as GMT
|
||||
+ self.check(
|
||||
+ datetime.datetime.fromtimestamp(
|
||||
+ self.TIMESTAMP, datetime.timezone(datetime.timedelta(hours=-4))
|
||||
+ )
|
||||
+ )
|
||||
|
||||
|
||||
# HTTPServerRequest is mainly tested incidentally to the server itself,
|
||||
diff --git a/tornado/test/locale_test.py b/tornado/test/locale_test.py
|
||||
index ee74cb0..a2e0872 100644
|
||||
--- a/tornado/test/locale_test.py
|
||||
+++ b/tornado/test/locale_test.py
|
||||
@@ -91,45 +91,55 @@ class EnglishTest(unittest.TestCase):
|
||||
locale.format_date(date, full_format=True), "April 28, 2013 at 6:35 pm"
|
||||
)
|
||||
|
||||
- now = datetime.datetime.utcnow()
|
||||
-
|
||||
- self.assertEqual(
|
||||
- locale.format_date(now - datetime.timedelta(seconds=2), full_format=False),
|
||||
- "2 seconds ago",
|
||||
- )
|
||||
- self.assertEqual(
|
||||
- locale.format_date(now - datetime.timedelta(minutes=2), full_format=False),
|
||||
- "2 minutes ago",
|
||||
- )
|
||||
- self.assertEqual(
|
||||
- locale.format_date(now - datetime.timedelta(hours=2), full_format=False),
|
||||
- "2 hours ago",
|
||||
- )
|
||||
-
|
||||
- self.assertEqual(
|
||||
- locale.format_date(
|
||||
- now - datetime.timedelta(days=1), full_format=False, shorter=True
|
||||
- ),
|
||||
- "yesterday",
|
||||
- )
|
||||
-
|
||||
- date = now - datetime.timedelta(days=2)
|
||||
- self.assertEqual(
|
||||
- locale.format_date(date, full_format=False, shorter=True),
|
||||
- locale._weekdays[date.weekday()],
|
||||
- )
|
||||
-
|
||||
- date = now - datetime.timedelta(days=300)
|
||||
- self.assertEqual(
|
||||
- locale.format_date(date, full_format=False, shorter=True),
|
||||
- "%s %d" % (locale._months[date.month - 1], date.day),
|
||||
- )
|
||||
-
|
||||
- date = now - datetime.timedelta(days=500)
|
||||
- self.assertEqual(
|
||||
- locale.format_date(date, full_format=False, shorter=True),
|
||||
- "%s %d, %d" % (locale._months[date.month - 1], date.day, date.year),
|
||||
- )
|
||||
+ aware_dt = datetime.datetime.now(datetime.timezone.utc)
|
||||
+ naive_dt = aware_dt.replace(tzinfo=None)
|
||||
+ for name, now in {"aware": aware_dt, "naive": naive_dt}.items():
|
||||
+ with self.subTest(dt=name):
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(
|
||||
+ now - datetime.timedelta(seconds=2), full_format=False
|
||||
+ ),
|
||||
+ "2 seconds ago",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(
|
||||
+ now - datetime.timedelta(minutes=2), full_format=False
|
||||
+ ),
|
||||
+ "2 minutes ago",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(
|
||||
+ now - datetime.timedelta(hours=2), full_format=False
|
||||
+ ),
|
||||
+ "2 hours ago",
|
||||
+ )
|
||||
+
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(
|
||||
+ now - datetime.timedelta(days=1),
|
||||
+ full_format=False,
|
||||
+ shorter=True,
|
||||
+ ),
|
||||
+ "yesterday",
|
||||
+ )
|
||||
+
|
||||
+ date = now - datetime.timedelta(days=2)
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(date, full_format=False, shorter=True),
|
||||
+ locale._weekdays[date.weekday()],
|
||||
+ )
|
||||
+
|
||||
+ date = now - datetime.timedelta(days=300)
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(date, full_format=False, shorter=True),
|
||||
+ "%s %d" % (locale._months[date.month - 1], date.day),
|
||||
+ )
|
||||
+
|
||||
+ date = now - datetime.timedelta(days=500)
|
||||
+ self.assertEqual(
|
||||
+ locale.format_date(date, full_format=False, shorter=True),
|
||||
+ "%s %d, %d" % (locale._months[date.month - 1], date.day, date.year),
|
||||
+ )
|
||||
|
||||
def test_friendly_number(self):
|
||||
locale = tornado.locale.get("en_US")
|
||||
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
|
||||
index c2d057c..a9bce04 100644
|
||||
--- a/tornado/test/web_test.py
|
||||
+++ b/tornado/test/web_test.py
|
||||
@@ -404,10 +404,10 @@ class CookieTest(WebTestCase):
|
||||
match = re.match("foo=bar; expires=(?P<expires>.+); Path=/", header)
|
||||
assert match is not None
|
||||
|
||||
- expires = datetime.datetime.utcnow() + datetime.timedelta(days=10)
|
||||
- parsed = email.utils.parsedate(match.groupdict()["expires"])
|
||||
- assert parsed is not None
|
||||
- header_expires = datetime.datetime(*parsed[:6])
|
||||
+ expires = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
|
||||
+ days=10
|
||||
+ )
|
||||
+ header_expires = email.utils.parsedate_to_datetime(match.groupdict()["expires"])
|
||||
self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)
|
||||
|
||||
def test_set_cookie_false_flags(self):
|
||||
@@ -1697,11 +1697,10 @@ class DateHeaderTest(SimpleHandlerTestCase):
|
||||
|
||||
def test_date_header(self):
|
||||
response = self.fetch("/")
|
||||
- parsed = email.utils.parsedate(response.headers["Date"])
|
||||
- assert parsed is not None
|
||||
- header_date = datetime.datetime(*parsed[:6])
|
||||
+ header_date = email.utils.parsedate_to_datetime(response.headers["Date"])
|
||||
self.assertTrue(
|
||||
- header_date - datetime.datetime.utcnow() < datetime.timedelta(seconds=2)
|
||||
+ header_date - datetime.datetime.now(datetime.timezone.utc)
|
||||
+ < datetime.timedelta(seconds=2)
|
||||
)
|
||||
|
||||
|
||||
@@ -3010,10 +3009,12 @@ class XSRFCookieKwargsTest(SimpleHandlerTestCase):
|
||||
match = re.match(".*; expires=(?P<expires>.+);.*", header)
|
||||
assert match is not None
|
||||
|
||||
- expires = datetime.datetime.utcnow() + datetime.timedelta(days=2)
|
||||
- parsed = email.utils.parsedate(match.groupdict()["expires"])
|
||||
- assert parsed is not None
|
||||
- header_expires = datetime.datetime(*parsed[:6])
|
||||
+ expires = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
|
||||
+ days=2
|
||||
+ )
|
||||
+ header_expires = email.utils.parsedate_to_datetime(match.groupdict()["expires"])
|
||||
+ if header_expires.tzinfo is None:
|
||||
+ header_expires = header_expires.replace(tzinfo=datetime.timezone.utc)
|
||||
self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)
|
||||
|
||||
|
||||
diff --git a/tornado/web.py b/tornado/web.py
|
||||
index 5651404..439e02c 100644
|
||||
--- a/tornado/web.py
|
||||
+++ b/tornado/web.py
|
||||
@@ -647,7 +647,9 @@ class RequestHandler(object):
|
||||
if domain:
|
||||
morsel["domain"] = domain
|
||||
if expires_days is not None and not expires:
|
||||
- expires = datetime.datetime.utcnow() + datetime.timedelta(days=expires_days)
|
||||
+ expires = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
|
||||
+ days=expires_days
|
||||
+ )
|
||||
if expires:
|
||||
morsel["expires"] = httputil.format_timestamp(expires)
|
||||
if path:
|
||||
@@ -698,7 +700,9 @@ class RequestHandler(object):
|
||||
raise TypeError(
|
||||
f"clear_cookie() got an unexpected keyword argument '{excluded_arg}'"
|
||||
)
|
||||
- expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
|
||||
+ expires = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
|
||||
+ days=365
|
||||
+ )
|
||||
self.set_cookie(name, value="", expires=expires, **kwargs)
|
||||
|
||||
def clear_all_cookies(self, **kwargs: Any) -> None:
|
||||
@@ -2812,12 +2816,12 @@ class StaticFileHandler(RequestHandler):
|
||||
# content has not been modified
|
||||
ims_value = self.request.headers.get("If-Modified-Since")
|
||||
if ims_value is not None:
|
||||
- date_tuple = email.utils.parsedate(ims_value)
|
||||
- if date_tuple is not None:
|
||||
- if_since = datetime.datetime(*date_tuple[:6])
|
||||
- assert self.modified is not None
|
||||
- if if_since >= self.modified:
|
||||
- return True
|
||||
+ if_since = email.utils.parsedate_to_datetime(ims_value)
|
||||
+ if if_since.tzinfo is None:
|
||||
+ if_since = if_since.replace(tzinfo=datetime.timezone.utc)
|
||||
+ assert self.modified is not None
|
||||
+ if if_since >= self.modified:
|
||||
+ return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -2981,6 +2985,10 @@ class StaticFileHandler(RequestHandler):
|
||||
object or None.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
+
|
||||
+ .. versionchanged:: 6.4
|
||||
+ Now returns an aware datetime object instead of a naive one.
|
||||
+ Subclasses that override this method may return either kind.
|
||||
"""
|
||||
stat_result = self._stat()
|
||||
# NOTE: Historically, this used stat_result[stat.ST_MTIME],
|
||||
@@ -2991,7 +2999,9 @@ class StaticFileHandler(RequestHandler):
|
||||
# consistency with the past (and because we have a unit test
|
||||
# that relies on this), we truncate the float here, although
|
||||
# I'm not sure that's the right thing to do.
|
||||
- modified = datetime.datetime.utcfromtimestamp(int(stat_result.st_mtime))
|
||||
+ modified = datetime.datetime.fromtimestamp(
|
||||
+ int(stat_result.st_mtime), datetime.timezone.utc
|
||||
+ )
|
||||
return modified
|
||||
|
||||
def get_content_type(self) -> str:
|
Loading…
Reference in New Issue
Block a user