From 083ddbf61bf83c49c72e37da2cb8fe3891b40a39 Mon Sep 17 00:00:00 2001 From: Eujin Ong <62314480+euj1n0ng@users.noreply.github.com> Date: Wed, 28 Sep 2022 23:34:05 +0700 Subject: [PATCH 1/3] Replace obsolete JS media type --- falcon/constants.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/falcon/constants.py b/falcon/constants.py index e3585aece..4cd42a677 100644 --- a/falcon/constants.py +++ b/falcon/constants.py @@ -105,14 +105,10 @@ # contrary to the RFCs. MEDIA_XML = 'application/xml' -# NOTE(kgriffs): RFC 4329 recommends application/* over text/. -# furthermore, parsers are required to respect the Unicode -# encoding signature, if present in the document, and to default -# to UTF-8 when not present. Note, however, that implementations -# are not required to support anything besides UTF-8, so it is -# unclear how much utility an encoding signature (or the charset -# parameter for that matter) has in practice. -MEDIA_JS = 'application/javascript' +# NOTE: According to RFC 9239, Changed the intended usage of the +# media type "text/javascript" from OBSOLETE to COMMON. Changed +# the intended usage for all other script media types to obsolete. +MEDIA_JS = 'text/javascript' # NOTE(kgriffs): According to RFC 6838, most text media types should # include the charset parameter. From 59917dc9c6a5d6d091c78a7dbb4021a5f79dd1af Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Sat, 3 Jun 2023 18:00:43 +0200 Subject: [PATCH 2/3] feat(response): prefer `falcon.MEDIA_*` constants to stdlib's `mimetypes` --- docs/_newsfragments/2110.newandimproved.rst | 6 +++++ falcon/constants.py | 28 ++++++++++++++++++--- falcon/response.py | 4 ++- tests/test_response.py | 5 ++-- 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 docs/_newsfragments/2110.newandimproved.rst diff --git a/docs/_newsfragments/2110.newandimproved.rst b/docs/_newsfragments/2110.newandimproved.rst new file mode 100644 index 000000000..a8cc0f8da --- /dev/null +++ b/docs/_newsfragments/2110.newandimproved.rst @@ -0,0 +1,6 @@ +Following the recommendation from +`RFC 9239 `__, the +:ref:`MEDIA_JS ` constant has been updated to +``text/javascript``. Furthermore, this and other media type constants are now +preferred to the stdlib's :mod:`mimetypes` for the initialization of +:attr:`~falcon.ResponseOptions.static_media_types`. diff --git a/falcon/constants.py b/falcon/constants.py index dcb35e596..24057f2b7 100644 --- a/falcon/constants.py +++ b/falcon/constants.py @@ -105,9 +105,9 @@ # contrary to the RFCs. MEDIA_XML = 'application/xml' -# NOTE: According to RFC 9239, Changed the intended usage of the -# media type "text/javascript" from OBSOLETE to COMMON. Changed -# the intended usage for all other script media types to obsolete. +# NOTE(euj1n0ng): According to RFC 9239, Changed the intended usage of the +# media type "text/javascript" from OBSOLETE to COMMON. Changed +# the intended usage for all other script media types to obsolete. MEDIA_JS = 'text/javascript' # NOTE(kgriffs): According to RFC 6838, most text media types should @@ -137,6 +137,28 @@ ] ) +# NOTE(vytas): We strip the preferred charsets from the default static file +# type mapping as it is hard to make any assumptions without knowing which +# files are going to be served. Moreover, the popular web servers (like +# Nginx) do not try to guess either. +_DEFAULT_STATIC_MEDIA_TYPES = tuple( + (ext, media_type.split(';', 1)[0]) + for ext, media_type in ( + ('.bmp', MEDIA_BMP), + ('.gif', MEDIA_GIF), + ('.htm', MEDIA_HTML), + ('.html', MEDIA_HTML), + ('.jpeg', MEDIA_JPEG), + ('.jpg', MEDIA_JPEG), + ('.js', MEDIA_JS), + ('.png', MEDIA_PNG), + ('.txt', MEDIA_TEXT), + ('.xml', MEDIA_XML), + ('.yaml', MEDIA_YAML), + ('.yml', MEDIA_YAML), + ) +) + # NOTE(kgriffs): Special singleton to be used internally whenever using # None would be ambiguous. _UNSET = object() diff --git a/falcon/response.py b/falcon/response.py index 37941991f..f69ff082f 100644 --- a/falcon/response.py +++ b/falcon/response.py @@ -17,6 +17,7 @@ import functools import mimetypes +from falcon.constants import _DEFAULT_STATIC_MEDIA_TYPES from falcon.constants import _UNSET from falcon.constants import DEFAULT_MEDIA_TYPE from falcon.errors import HeaderNotSupported @@ -1246,4 +1247,5 @@ def __init__(self): if not mimetypes.inited: mimetypes.init() - self.static_media_types = mimetypes.types_map + self.static_media_types = mimetypes.types_map.copy() + self.static_media_types.update(_DEFAULT_STATIC_MEDIA_TYPES) diff --git a/tests/test_response.py b/tests/test_response.py index 697197506..1116be3a3 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -65,13 +65,14 @@ def test_response_removed_stream_len(resp): def test_response_option_mimetype_init(monkeypatch): mock = MagicMock() mock.inited = False + mock.types_map = {'.js': 'application/javascript'} monkeypatch.setattr('falcon.response.mimetypes', mock) ro = ResponseOptions() - assert ro.static_media_types is mock.types_map + assert ro.static_media_types['.js'] == 'text/javascript' + mock.reset_mock() mock.inited = True ro = ResponseOptions() - assert ro.static_media_types is mock.types_map mock.init.assert_not_called() From 5dda080cdfa2947ed1f677811b560f3087fba203 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Sun, 4 Jun 2023 11:05:15 +0200 Subject: [PATCH 3/3] feat(static): add static media type mapping for `.mjs` --- falcon/constants.py | 2 ++ tests/test_response.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/falcon/constants.py b/falcon/constants.py index 24057f2b7..9bd71f373 100644 --- a/falcon/constants.py +++ b/falcon/constants.py @@ -151,6 +151,8 @@ ('.jpeg', MEDIA_JPEG), ('.jpg', MEDIA_JPEG), ('.js', MEDIA_JS), + ('.json', MEDIA_JSON), + ('.mjs', MEDIA_JS), ('.png', MEDIA_PNG), ('.txt', MEDIA_TEXT), ('.xml', MEDIA_XML), diff --git a/tests/test_response.py b/tests/test_response.py index 1116be3a3..fe2b73d93 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -71,8 +71,14 @@ def test_response_option_mimetype_init(monkeypatch): ro = ResponseOptions() assert ro.static_media_types['.js'] == 'text/javascript' + assert ro.static_media_types['.json'] == 'application/json' + assert ro.static_media_types['.mjs'] == 'text/javascript' mock.reset_mock() mock.inited = True ro = ResponseOptions() mock.init.assert_not_called() + + assert ro.static_media_types['.js'] == 'text/javascript' + assert ro.static_media_types['.json'] == 'application/json' + assert ro.static_media_types['.mjs'] == 'text/javascript'