From d715fa0efd10c436f786197e9d2e422f9bc372f9 Mon Sep 17 00:00:00 2001 From: Eliot Berriot <contact@eliotberriot.com> Date: Tue, 4 Jun 2019 12:20:44 +0200 Subject: [PATCH] Fixed error handling --- retribute_api/search/consumers.py | 72 ++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/retribute_api/search/consumers.py b/retribute_api/search/consumers.py index f5ff473..eb3867a 100644 --- a/retribute_api/search/consumers.py +++ b/retribute_api/search/consumers.py @@ -1,6 +1,7 @@ import asyncio import aiohttp.client import json +import ssl from channels.generic.http import AsyncHttpConsumer @@ -33,11 +34,58 @@ def wrapper_500(callback): return callback +def ignore_aiohttp_ssl_eror(loop, aiohttpversion="3.5.4"): + """Ignore aiohttp #3535 issue with SSL data after close + + There appears to be an issue on Python 3.7 and aiohttp SSL that throws a + ssl.SSLError fatal error (ssl.SSLError: [SSL: KRB5_S_INIT] application data + after close notify (_ssl.c:2609)) after we are already done with the + connection. See GitHub issue aio-libs/aiohttp#3535 + + Given a loop, this sets up a exception handler that ignores this specific + exception, but passes everything else on to the previous exception handler + this one replaces. + + If the current aiohttp version is not exactly equal to aiohttpversion + nothing is done, assuming that the next version will have this bug fixed. + This can be disabled by setting this parameter to None + + """ + if aiohttpversion is not None and aiohttp.__version__ != aiohttpversion: + return + + orig_handler = loop.get_exception_handler() + + def ignore_ssl_error(loop, context): + if context.get("message") == "SSL error in data received": + # validate we have the right exception, transport and protocol + exception = context.get("exception") + protocol = context.get("protocol") + if ( + isinstance(exception, ssl.SSLError) + and exception.reason == "KRB5_S_INIT" + and isinstance(protocol, asyncio.sslproto.SSLProtocol) + and isinstance( + protocol._app_protocol, aiohttp.client_proto.ResponseHandler + ) + ): + if loop.get_debug(): + asyncio.log.logger.debug("Ignoring aiohttp SSL KRB5_S_INIT error") + return + if orig_handler is not None: + orig_handler(loop, context) + else: + loop.default_exception_handler(context) + + loop.set_exception_handler(ignore_ssl_error) + + class SearchSingleConsumer(AsyncHttpConsumer): @wrapper_500 async def handle(self, body): lookup_type = self.scope["url_route"]["kwargs"]["lookup_type"] lookup = self.scope["url_route"]["kwargs"]["lookup"] + ignore_aiohttp_ssl_eror(asyncio.get_running_loop()) try: source = sources.registry._data[lookup_type] except KeyError: @@ -59,16 +107,35 @@ async def do_lookup(lookup, lookup_type, session, source, results): try: data = await source.get(lookup, session) profile = sources.result_to_retribute_profile(lookup_type, lookup, data) - except (exceptions.SearchError, aiohttp.ClientError) as e: + except ( + exceptions.SearchError, + aiohttp.ClientError, + serializers.serializers.ValidationError, + asyncio.TimeoutError, + ) as e: + results[":".join([lookup_type, lookup])] = None + return + except Exception as e: + print("ERROR: unhandled - {}, {}".format(e.__class__, e)) results[":".join([lookup_type, lookup])] = None return - results[":".join([lookup_type, lookup])] = profile class SearchMultipleConsumer(AsyncHttpConsumer): @wrapper_500 async def handle(self, body): + if self.scope["method"] == "OPTIONS": + return await self.send_response( + 200, + b"", + headers=[ + (b"Content-Type", b"application/json"), + (b"Access-Control-Allow-Origin", b"*"), + (b"Access-Control-Allow-Headers", b"content-type"), + ], + ) + ignore_aiohttp_ssl_eror(asyncio.get_running_loop()) if self.scope["method"] not in ["POST"]: return await self.send_response(405, b"") @@ -103,6 +170,7 @@ class SearchMultipleConsumer(AsyncHttpConsumer): try: await asyncio.gather(*tasks) except Exception as e: + print("ERROR: unhandled - {}, {}".format(e.__class__, e)) await json_response(self, 500, {}) raise await json_response(self, 200, results) -- GitLab