Gracefully handle transcoder encoding failures.

Since the form encoder refuses to handle nested sequences, the content
mixin explicitly handles this case instead of letting the unhandled
exception bubble up.
This commit is contained in:
Dave Shawley 2021-10-14 07:06:07 -04:00
parent 2df3aade3c
commit 1fe22df719
No known key found for this signature in database
GPG key ID: F41A8A99298F8EED
3 changed files with 43 additions and 5 deletions

View file

@ -9,6 +9,8 @@ Version History
and there is no default content type configured
- Deprecate not having a default content type configured
- Fail gracefully when a transcoder does not exist for the default content type
- Fail gracefully when a transcoder raises a :exc:`TypeError` or :exc:`ValueError` when encoding
the response
.. _application/x-www-formurlencoded: https://url.spec.whatwg.org/#application/x-www-form-urlencoded

View file

@ -423,8 +423,15 @@ class ContentMixin(web.RequestHandler):
settings.default_content_type)
raise web.HTTPError(500)
else:
content_type, data_bytes = handler.to_bytes(body)
if set_content_type:
self.set_header('Content-Type', content_type)
self.add_header('Vary', 'Accept')
self.write(data_bytes)
try:
content_type, data_bytes = handler.to_bytes(body)
except (TypeError, ValueError) as e:
self._logger.error(
'selected transcoder (%s) failed to encode response '
'body: %s', handler.__class__.__name__, e)
raise web.HTTPError(500, reason='Response Encoding Failure')
else:
if set_content_type:
self.set_header('Content-Type', content_type)
self.add_header('Vary', 'Accept')
self.write(data_bytes)

View file

@ -177,6 +177,35 @@ class SendResponseTests(testing.AsyncHTTPTestCase):
self.assertEqual('application/foo+json',
response.headers.get('Content-Type'))
def test_that_transcoder_failures_result_in_500(self):
class FailingTranscoder:
content_type = 'application/vnd.com.example.bad'
def __init__(self):
self.exc_class = TypeError
def to_bytes(self, inst_data, encoding=None):
raise self.exc_class('I always fail at this')
def from_bytes(self, data_bytes, encoding=None):
return {}
transcoder = FailingTranscoder()
content.add_transcoder(self.application, transcoder)
for _ in range(2):
response = self.fetch(
'/',
method='POST',
body=b'{}',
headers={
'Accept': 'application/vnd.com.example.bad',
'Content-Type': 'application/json',
},
)
self.assertEqual(500, response.code)
self.assertEqual('Response Encoding Failure', response.reason)
transcoder.exc_class = ValueError
class GetRequestBodyTests(testing.AsyncHTTPTestCase):
def setUp(self):