Skip to content

Commit

Permalink
fix: properly account for q= when parting the accept header
Browse files Browse the repository at this point in the history
  • Loading branch information
CaselIT committed Sep 30, 2024
1 parent 55a9412 commit 9414ee2
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 7 deletions.
11 changes: 6 additions & 5 deletions falcon/app_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,12 @@ def default_serialize_error(req: Request, resp: Response, exception: HTTPError)
resp: Instance of ``falcon.Response``
exception: Instance of ``falcon.HTTPError``
"""
preferred = req.client_prefers((MEDIA_XML, 'text/xml', MEDIA_JSON))
predefined = [MEDIA_XML, 'text/xml', MEDIA_JSON]
media_handlers = [mt for mt in resp.options.media_handlers if mt not in predefined]
# NOTE(caselit) add all the registered before the predefined ones. This ensures that
# in case of equal match the last one (json) is selected and that the q= is taken
# into consideration when selecting the media
preferred = req.client_prefers(media_handlers + predefined)

if preferred is None:
# NOTE(kgriffs): See if the client expects a custom media
Expand All @@ -311,10 +316,6 @@ def default_serialize_error(req: Request, resp: Response, exception: HTTPError)
preferred = MEDIA_JSON
elif '+xml' in accept:
preferred = MEDIA_XML
else:
# NOTE(caselit): if nothing else matchers try using the media handlers
# registered in the response
preferred = req.client_prefers(resp.options.media_handlers)

if preferred is not None:
handler, _, _ = resp.options.media_handlers._resolve(
Expand Down
1 change: 0 additions & 1 deletion falcon/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
Mapping,
Optional,
overload,
Sequence,
TextIO,
Tuple,
Type,
Expand Down
33 changes: 32 additions & 1 deletion tests/test_httperror.py
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ def test_kw_only(self):
XML = (MEDIA_XML, MEDIA_XML, XML_CONTENT)
CUSTOM_XML = ('custom/any+xml', MEDIA_XML, XML_CONTENT)

YAML = (MEDIA_YAML, MEDIA_YAML, (b'title: 410 Gone!'))
YAML = (MEDIA_YAML, MEDIA_YAML, b'title: 410 Gone!')
ASYNC_ONLY = ('application/only_async', 'application/only_async', b'this is async')
ASYNC_WITH_SYNC = (
'application/async_with_sync',
Expand Down Expand Up @@ -1040,3 +1040,34 @@ def test_json_async_only_error(self, util):
client = testing.TestClient(app)
with pytest.raises(NotImplementedError, match='requires the sync interface'):
client.simulate_get()

def test_add_xml_handler(self, client):
client.app.resp_options.media_handlers[MEDIA_XML] = FakeYamlMediaHandler()
res = client.simulate_get(headers={'Accept': 'application/xhtml+xml'})
assert res.content_type == MEDIA_XML
assert res.content == YAML[-1]

@pytest.mark.parametrize(
'accept, content_type',
[
(
# firefox
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,'
'image/webp,image/png,image/svg+xml,*/*;q=0.8',
MEDIA_XML,
),
(
# safari / chrome
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,'
'image/apng,*/*;q=0.8',
MEDIA_XML,
),
('text/html, application/xhtml+xml, image/jxr, */*', MEDIA_JSON), # edge
(f'text/html,{MEDIA_YAML};q=0.8,*/*;q=0.7', MEDIA_YAML),
(f'text/html,{MEDIA_YAML};q=0.8,{MEDIA_JSON};q=0.8', MEDIA_JSON),
],
)
def test_hard_content_types(self, client, accept, content_type):
client.app.resp_options.media_handlers[MEDIA_YAML] = FakeYamlMediaHandler()
res = client.simulate_get(headers={'Accept': accept})
assert res.content_type == content_type

0 comments on commit 9414ee2

Please sign in to comment.