stackless_response_router.py (3743B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\stackless_response_router\stackless_response_router.py 2 from __future__ import absolute_import 3 import logging 4 import sentry_sdk 5 import time 6 import uthread2 7 import uuid 8 from .exceptions import TimeoutException, UnpackException 9 DEFAULT_TIMEOUT_SECONDS = 10 10 MIN_TIMEOUT_SECONDS = 0.1 11 logger = logging.getLogger(__name__) 12 13 class StacklessResponseRouter(object): 14 15 def __init__(self, span_manager): 16 self.response_handlers = {} 17 self.span_manager = span_manager 18 19 def add_response_handler(self, request, expected_response_class, timeout_seconds = None): 20 if timeout_seconds is None: 21 timeout_seconds = DEFAULT_TIMEOUT_SECONDS 22 elif timeout_seconds < MIN_TIMEOUT_SECONDS: 23 timeout_seconds = MIN_TIMEOUT_SECONDS 24 rh = ResponseHandler(request, expected_response_class, timeout_seconds) 25 self.response_handlers[request.correlation_uuid] = rh 26 return rh.response_channel 27 28 def handle_response(self, response_id_bytes, response): 29 try: 30 rh = self.response_handlers.pop(response_id_bytes) 31 if rh is None: 32 return 33 rh.handle(response) 34 except KeyError: 35 return 36 37 def handle_request_timeouts(self): 38 try: 39 timed_out_request_ids = [] 40 now = time.time() 41 for request_id, rh in self.response_handlers.items(): 42 if now > rh.deadline: 43 timed_out_request_ids.append(request_id) 44 45 for request_id_bytes in timed_out_request_ids: 46 rh = self.response_handlers.pop(request_id_bytes) 47 rh.timeout() 48 self.span_manager.timeout_request_trace(request_id_bytes) 49 50 except Exception as e: 51 logger.error(e) 52 53 @staticmethod 54 def report_timeout_exception(request_id_bytes, details): 55 with sentry_sdk.push_scope() as scope: 56 request_uuid = uuid.UUID(bytes=request_id_bytes) 57 scope.set_tag('request_uuid', request_uuid) 58 for key, val in details.items(): 59 scope.set_tag(key, val) 60 61 sentry_sdk.capture_message('request timed out', 'error') 62 63 64 class ResponseHandler(object): 65 __slots__ = ('request', 'response_channel', 'deadline', 'expected_response_class') 66 67 def __init__(self, request, expected_response_class, timeout_seconds): 68 self.request = request 69 self.response_channel = uthread2.queue_channel() 70 self.deadline = time.time() + timeout_seconds 71 self.expected_response_class = expected_response_class 72 73 def unpack_failure(self, payload_type_url, exception): 74 msg = 'Delivered Response Type: {} Expected Response Class: {} Exception: {}'.format(payload_type_url, self.expected_response_class, exception) 75 self.response_channel.send_exception(UnpackException, msg) 76 self.response_channel.close() 77 78 def handle(self, response_primitive): 79 payload = None 80 if response_primitive.payload.type_url: 81 try: 82 payload = self.expected_response_class() 83 unpacked = response_primitive.payload.Unpack(payload) 84 if not unpacked: 85 self.unpack_failure(response_primitive.payload.type_url, None) 86 return 87 except Exception as e: 88 self.unpack_failure(response_primitive.payload.type_url, e) 89 return 90 91 self.response_channel.send((response_primitive, payload)) 92 self.response_channel.close() 93 94 def timeout(self): 95 self.response_channel.send_exception(TimeoutException) 96 self.response_channel.close()