Skip to content

Commit

Permalink
Merge branch 'main' into issue2677
Browse files Browse the repository at this point in the history
  • Loading branch information
ahopkins authored Mar 20, 2023
2 parents ab30a43 + ac1f561 commit 9a82246
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 4 deletions.
4 changes: 3 additions & 1 deletion sanic/http/http1.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,9 @@ async def error_response(self, exception: Exception) -> None:
if self.request is None:
self.create_empty_request()

request_middleware = not isinstance(exception, ServiceUnavailable)
request_middleware = not isinstance(
exception, (ServiceUnavailable, RequestCancelled)
)
try:
await app.handle_exception(
self.request, exception, request_middleware
Expand Down
2 changes: 1 addition & 1 deletion sanic/http/tls/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def __new__(cls, cert, key, **kw):
# try common aliases, rename to cert/key
certfile = kw["cert"] = kw.pop("certificate", None) or cert
keyfile = kw["key"] = kw.pop("keyfile", None) or key
password = kw.pop("password", None)
password = kw.get("password", None)
if not certfile or not keyfile:
raise ValueError("SSL dict needs filenames for cert and key.")
subject = {}
Expand Down
2 changes: 1 addition & 1 deletion sanic/mixins/startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ def serve(
ssl = kwargs.get("ssl")

if isinstance(ssl, SanicSSLContext):
kwargs["ssl"] = kwargs["ssl"].sanic
kwargs["ssl"] = ssl.sanic

manager = WorkerManager(
primary.state.workers,
Expand Down
21 changes: 20 additions & 1 deletion sanic/response/convenience.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,26 @@ async def validate_file(
last_modified = datetime.fromtimestamp(
float(last_modified), tz=timezone.utc
).replace(microsecond=0)
if last_modified <= if_modified_since:

if (
last_modified.utcoffset() is None
and if_modified_since.utcoffset() is not None
):
logger.warning(
"Cannot compare tz-aware and tz-naive datetimes. To avoid "
"this conflict Sanic is converting last_modified to UTC."
)
last_modified.replace(tzinfo=timezone.utc)
elif (
last_modified.utcoffset() is not None
and if_modified_since.utcoffset() is None
):
logger.warning(
"Cannot compare tz-aware and tz-naive datetimes. To avoid "
"this conflict Sanic is converting if_modified_since to UTC."
)
if_modified_since.replace(tzinfo=timezone.utc)
if last_modified.timestamp() <= if_modified_since.timestamp():
return HTTPResponse(status=304)


Expand Down
19 changes: 19 additions & 0 deletions tests/certs/password/fullchain.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUa7OOlAGQfXOgUgRENJ9GbUgO7kwwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTIzMDMyMDA3MzE1M1oXDTIzMDQx
OTA3MzE1M1owFDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAn2/RqVpzO7GFrgVGiowR5CzcFzf1tSFti1K/WIGr/jsu
NP+1R3sim17pgg6SCOFnUMRS0KnDihkzoeP6z+0tFsrbCH4V1+fq0iud8WgYQrgD
3ttUcHrz04p7wsMoeqndUQoLbyJzP8MpA2XJsoacdIVkuLv2AESGXLhJym/e9HGN
g8bqdz25X0hVTczZW1FN9AZyWWVf9Go6jqC7LCaOnYXAnOkEy2/JHdkeNXYFZHB3
71UemfkCjfp0vlRV8pVpkBGMhRNFphBTfxdqeWiGQwVqrhaJO4M7DJlQHCAPY16P
o9ywnhLDhFHD7KIfTih9XxrdgTowqcwyGX3e3aJpTwIDAQABo1MwUTAdBgNVHQ4E
FgQU5NogMq6mRBeGl4i6hIuUlcR2bVEwHwYDVR0jBBgwFoAU5NogMq6mRBeGl4i6
hIuUlcR2bVEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYW34
JY1kd0UO5HE41oxJD4PioQboXXX0al4RgKaUUsPykeHQbK0q0TSYAZLwRjooTVUO
Wvna5bU2mzyULqA2r/Cr/w4zb9xybO3SiHFHcU1RacouauHXROHwRm98i8A73xnH
vHws5BADr2ggnVcPNh4VOQ9ZvBlC7jhgpvMjqOEu5ZPCovhfZYfSsvBDHcD74ZYm
Di9DvqsJmrb23Dv3SUykm3W+Ql2q+JyjFj30rhD89CFwJ9iSlFwTYEwZLHA+mV6p
UKy3I3Fiht1Oc+nIivX5uhRSMbDVvDTVHbjjPujxxFjkiHXMjtwvwfg4Sb6du61q
AjBRFyXbNu4hZkkHOA==
-----END CERTIFICATE-----
30 changes: 30 additions & 0 deletions tests/certs/password/privkey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI94UBqjaZlG4CAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCvJhEy+3/+0Ec0gpd5dkP6BIIE
0E7rLplTe9rxK3sR9V0cx8Xn6V+uFhG3p7dzeMDCCKpGo9MEaacF5m+paGnBkMlH
Pz3rRoLA5jqzwXl4US/C5E1Or//2YBgF1XXKi3BPF/bVx/g6vR+xeobf9kQGbqQk
FNPYtP7mpg2dekp5BUsKSosIt8BkknWFvhBeNuGZT/zlMUuq1WpMe4KIh/W9IdNr
HolcuZJWBhQAwGPciWIZRyq48wKa++W7Jdg/aG8FviJQnjaAUv4CyZJHUJnaNwUx
iHOETpzIC+bhF2K+s4g5w68VCj6Jtz78sIBEZKzo7LI5QHdRHqYB5SJ/dGiV+h09
R/rQ/M+24mwHDlRSCxxq0yuDwUuGBlHyATeDCFeE3L5OX8yTLuqYJ6vUa6UbzMYA
8H4l5zfu9RrAhKYa9tD+4ONxMmHziIgmn5zvSXeBwJKfeUbnN4IKWLsSoSVspBRh
zLl51DMAnem4NEjLfIW8WYjhsvSYwd9BYqxXaAiv4Wjx9ZV1yLqFICC7tejpVdRT
afI0qMOfWu4ma6xVBg1ezLgF1wHIPrq6euTvWdnifYQopVICALlltEo5oxQ2i/OM
NY8RyovWujiGNsa3pId9HmZXiLyLXjKPstGWRK4liMyc2EiP099gTdBvrb+VQp+I
EyPavmh3WNhgZGOh3qah39X8HrBprc0PPfSPlxpaWdNMIIMSbcIWWdJEA/e4tcy/
uBaV4H3sNCtBApgrb6B9YUbS9CXNUburJo19T1sk2uCaO12qYfdu2IDEnFf8JiF3
i7nyftotRuoKq2D+V8d0PeMi/vJSo6+eZIn7VNe6ejYf+w0s7sxlpiKVzkslyOhq
n0T4M3ZkSwGIETzgkRRuTY1OK7slhglMgXlQ2FuIUUo6CRg9WjRJvI5rujLzLWfB
hkgP8STirjTV0DUWPFGtUcenvEcZPkYIQcoPHxOJGNW3ZPXNpt4RjbvPLeVzDm0O
WJiay/qhag/bXGqKraO3b6Y7FOzJa8kG4G0XrcFY1s2oCXRqRqYJAtwaEeVCjCSJ
Qy0OZkqcJEU7pv98pLMpG9OWz4Gle77g4KoQUJjQGtmg0MUMoPd0iPRmvkxsYg8E
Q9uZS3m6PpWmmYDY0Ik1w/4avs3skl2mW3dqcZGLEepkjiQSnFABsuvxKd+uIEQy
lyf9FrynXVcUI87LUkuniLRKwZZzFALVuc+BwtO3SA5mvEK22ZEq9QOysbwlpN54
G5xXJKJEeexUSjEUIij4J89RLsXldibhp7YYZ7rFviR6chIqC0V7G6VqAM9TOCrV
PWZXr3ZY5/pCZYs5DYKFJBFMSQ2UT/++VYxdZCeBH75vaxugbS8RdUM+iVDevWpQ
/AnP1FolNAgkVhi3Rw4L16SibkqpEzIi1svPWKMwXdvewA32UidLElhuTWWjI2Wm
veXhmEqwk/7ML4JMI7wHcDQdvSKen0mCL2J9tB7A/pewYyDE0ffIUmjxglOtw30f
ZOlQKhMaKJGXp00U2zsHA2NJRI/hThbJncsnZyvuLei0P42RrF+r64b/0gUH6IZ5
wPUttT815KSNoy+XXXum9YGDYYFoAL+6WVEkl6dgo+X0hcH7DDf5Nkewiq8UcJGh
/69vFIfp+JlpicXzZ+R42LO3T3luC907aFBywF3pmi//
-----END ENCRYPTED PRIVATE KEY-----
55 changes: 55 additions & 0 deletions tests/test_response_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from datetime import datetime, timezone
from logging import INFO

import pytest

from sanic.compat import Header
from sanic.response.convenience import validate_file


@pytest.mark.parametrize(
"ifmod,lastmod,expected",
(
("Sat, 01 Apr 2023 00:00:00 GMT", 1672524000, None),
(
"Sat, 01 Apr 2023 00:00:00",
1672524000,
"converting if_modified_since",
),
(
"Sat, 01 Apr 2023 00:00:00 GMT",
datetime(2023, 1, 1, 0, 0, 0),
"converting last_modified",
),
(
"Sat, 01 Apr 2023 00:00:00",
datetime(2023, 1, 1, 0, 0, 0),
None,
),
(
"Sat, 01 Apr 2023 00:00:00 GMT",
datetime(2023, 1, 1, 0, 0, 0).replace(tzinfo=timezone.utc),
None,
),
(
"Sat, 01 Apr 2023 00:00:00",
datetime(2023, 1, 1, 0, 0, 0).replace(tzinfo=timezone.utc),
"converting if_modified_since",
),
),
)
@pytest.mark.asyncio
async def test_file_timestamp_validation(
lastmod, ifmod, expected, caplog: pytest.LogCaptureFixture
):
headers = Header([["If-Modified-Since", ifmod]])

with caplog.at_level(INFO):
response = await validate_file(headers, lastmod)
assert response.status == 304
records = caplog.records
if not expected:
assert len(records) == 0
else:
record = records[0]
assert expected in record.message
38 changes: 38 additions & 0 deletions tests/test_tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,19 @@

current_dir = os.path.dirname(os.path.realpath(__file__))
localhost_dir = os.path.join(current_dir, "certs/localhost")
password_dir = os.path.join(current_dir, "certs/password")
sanic_dir = os.path.join(current_dir, "certs/sanic.example")
invalid_dir = os.path.join(current_dir, "certs/invalid.nonexist")
localhost_cert = os.path.join(localhost_dir, "fullchain.pem")
localhost_key = os.path.join(localhost_dir, "privkey.pem")
sanic_cert = os.path.join(sanic_dir, "fullchain.pem")
sanic_key = os.path.join(sanic_dir, "privkey.pem")
password_dict = {
"cert": os.path.join(password_dir, "fullchain.pem"),
"key": os.path.join(password_dir, "privkey.pem"),
"password": "password",
"names": ["localhost"],
}


@pytest.fixture
Expand Down Expand Up @@ -677,3 +684,34 @@ async def shutdown(app):
logging.INFO,
"Goin' Fast @ https://127.0.0.1:8000",
) in caplog.record_tuples


@pytest.mark.skipif(
sys.platform not in ("linux", "darwin"),
reason="This test requires fork context",
)
def test_ssl_in_multiprocess_mode_password(
app: Sanic, caplog: pytest.LogCaptureFixture
):
event = Event()

@app.main_process_start
async def main_start(app: Sanic):
app.shared_ctx.event = event

@app.after_server_start
async def shutdown(app):
app.shared_ctx.event.set()
app.stop()

assert not event.is_set()
with use_context("fork"):
with caplog.at_level(logging.INFO):
app.run(ssl=password_dict)
assert event.is_set()

assert (
"sanic.root",
logging.INFO,
"Goin' Fast @ https://127.0.0.1:8000",
) in caplog.record_tuples

0 comments on commit 9a82246

Please sign in to comment.