def main(): platform = Platform() # you can register a method of a class platform.register('hello.world.1', Hi().hello, 5) # or register a function platform.register('hello.world.2', hello, 10) po = PostOffice() # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings po.send('hello.world.1', headers={'one': 1}, body='hello world one') po.send('hello.world.2', headers={'two': 2}, body='hello world two') # demonstrate a RPC request try: result = po.request('hello.world.2', 2.0, headers={'some_key': 'some_value'}, body='hello world') if isinstance(result, EventEnvelope): print('Received RPC response:') print("HEADERS =", result.get_headers(), ", BODY =", result.get_body(), ", STATUS =", result.get_status(), ", EXEC =", result.get_exec_time(), ", ROUND TRIP =", result.get_round_trip(), "ms") except TimeoutError as e: print("Exception: ", str(e)) # illustrate parallel RPC requests event_list = list() event_list.append(EventEnvelope().set_to('hello.world.1').set_body("first request")) event_list.append(EventEnvelope().set_to('hello.world.2').set_body("second request")) try: result = po.parallel_request(event_list, 2.0) if isinstance(result, list): print('Received', len(result), 'RPC responses:') for res in result: print("HEADERS =", res.get_headers(), ", BODY =", res.get_body(), ", STATUS =", res.get_status(), ", EXEC =", res.get_exec_time(), ", ROUND TRIP =", res.get_round_trip(), "ms") except TimeoutError as e: print("Exception: ", str(e)) # connect to the network platform.connect_to_cloud() # wait until connected while not platform.cloud_ready(): try: time.sleep(0.1) except KeyboardInterrupt: # this allows us to stop the application while waiting for cloud connection platform.stop() return # Demonstrate broadcast feature # the event will be broadcast to multiple application instances that serve the same route po.broadcast("hello.world.1", body="this is a broadcast message from "+platform.get_origin()) # demonstrate deferred delivery po.send_later('hello.world.1', headers={'hello': 'world'}, body='this message arrives 5 seconds later', seconds=5.0) # # this will keep the main thread running in the background # so we can use Control-C or KILL signal to stop the application platform.run_forever()
def send_event(self, event: EventEnvelope, broadcast=False) -> None: if not isinstance(event, EventEnvelope): raise ValueError("event object must be an EventEnvelope class") # restore distributed tracing info from current thread trace_info = self.get_trace() if trace_info: event.set_trace(trace_info.get_id(), trace_info.get_path()) # regulate rate for best performance self._seq += 1 self._throttle.regulate_rate(self._seq) route = event.get_to() if broadcast: event.set_broadcast(True) reply_to = event.get_reply_to() if reply_to: target = reply_to[2:] if reply_to.startswith('->') else reply_to if route == target: raise ValueError("route and reply_to must not be the same") if route in self._function_queues: if event.is_broadcast() and self._cloud.is_connected(): self._cloud.send_payload({ 'type': 'event', 'event': event.to_map() }) else: self._loop.call_soon_threadsafe(self._send, route, event.to_map()) else: if self._cloud.is_connected(): self._cloud.send_payload({ 'type': 'event', 'event': event.to_map() }) else: raise ValueError("route " + route + " not found")
def send_keep_alive(self): message = "Keep-Alive " + self.util.get_iso_8601(time.time(), show_ms=True) envelope = EventEnvelope() envelope.set_to(self.OUTGOING_WS_PATH).set_header( 'type', 'text').set_body(message) self.platform.send_event(envelope)
def main(): # register a function platform.register('hello.world', hello, 10) platform.register('distributed.tracing', tracing, 10) # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings po.send('hello.world', headers={'one': 1}, body='hello world one') po.send('hello.world', headers={'two': 2}, body='hello world two') # demonstrate a RPC request try: trace_id = str(uuid.uuid4()).replace('-', '') trace_path = 'GET /api/hello/world' event = EventEnvelope().set_to("hello.world").set_header('some_key', 'some_value').set_body('hello world') event.set_trace(trace_id, trace_path).set_from('this.demo') result = po.single_request(event, 2.0) if isinstance(result, EventEnvelope): log.info('Received RPC response:') log.info(f'HEADERS = {result.get_headers()}, BODY = {result.get_body()}, STATUS = {result.get_status()}, ' f'EXEC = {result.get_exec_time()} ms, ROUND TRIP = {result.get_round_trip()} ms') except TimeoutError as e: log.error(f'Exception: {e}') # demonstrate drop-n-forget for n in range(20): po.send('hello.world', body=f'drop-n-forget message {n}') # demonstrate deferred delivery po.send_later('hello.world', headers={'hello': 'world'}, body='this message arrives 5 seconds later', seconds=5.0) # # This will keep the main thread running in the background. # We can use Control-C or KILL signal to stop the application. platform.run_forever()
def send_event_later(self, event: EventEnvelope, seconds: float = 1.0, me=True): if event.get_reply_to() is not None: event.set_reply_to(event.get_reply_to(), me) self.platform.send_event_later(event, seconds)
def exists(self, routes: any): if isinstance(routes, str): single_route = routes if self.has_route(single_route): return True if self.cloud_ready(): event = EventEnvelope() event.set_to(self.SERVICE_QUERY).set_header( 'type', 'find').set_header('route', single_route) result = self.request(event, 8.0) if isinstance(result, EventEnvelope): if result.get_body() is not None: return result.get_body() if isinstance(routes, list): if len(routes) > 0: remote_routes = list() for r in routes: if not self.platform.has_route(r): remote_routes.append(r) if len(remote_routes) == 0: return True if self.platform.cloud_ready(): # tell service query to use the route list in body event = EventEnvelope() event.set_to(self.SERVICE_QUERY).set_header('type', 'find') event.set_header('route', '*').set_body(routes) result = self.request(event, 8.0) if isinstance( result, EventEnvelope) and result.get_body() is not None: return result.get_body() return False
def hello(headers: dict, body: any, instance: int): # regular function signature (headers: dict, body: any, instance: int) Platform().log.info("#"+str(instance)+" "+str(headers)+" body="+str(body)) # to set status, headers and body, return them in an event envelope result = EventEnvelope().set_header('hello', 'world').set_body(body) for h in headers: result.set_header(h, headers[h]) return result
def hello(headers: dict, body: any, instance: int): # regular function signature (headers: dict, body: any, instance: int) log.info(f'#{instance} {headers} body={body}') # to set status, headers and body, return them in an event envelope result = EventEnvelope().set_header('hello', 'world').set_body(body) for h in headers: result.set_header(h, headers[h]) return result
def _life_cycle(self, headers: dict, body: any): for subscriber in self._subscription: try: event = EventEnvelope() event.set_to(subscriber).set_headers(headers).set_body(body) self.platform.send_event(event) except ValueError as e: self.log.warn( f'Unable to relay life cycle event {headers} to {subscriber} - {e}' )
def request(self, route: str, timeout_seconds: float, headers: dict = None, body: any = None, correlation_id: str = None) -> EventEnvelope: self.util.validate_service_name(route, True) if headers is None and body is None: raise ValueError( 'Unable to make RPC call because both headers and body are missing' ) timeout_value = self.util.get_float(timeout_seconds) if timeout_value <= 0: raise ValueError( 'timeout value in seconds must be positive number') event = EventEnvelope().set_to(route) if headers is not None: if not isinstance(headers, dict): raise ValueError('headers must be dict') for h in headers: event.set_header(h, str(headers[h])) if body is not None: event.set_body(body) if correlation_id is not None: event.set_correlation_id(str(correlation_id)) response = self.platform.request(event, timeout_seconds) if isinstance(response, EventEnvelope): if response.get_tag('exception') is None: return response else: raise AppException(response.get_status(), response.get_body()) raise ValueError( f'Expect response is EventEnvelope, actual: ({response})')
def send_later(self, route: str, headers: dict = None, body: any = None, reply_to: str = None, me=True, seconds: float = 1.0) -> None: self.util.validate_service_name(route, True) if isinstance(seconds, float) or isinstance(seconds, int): if seconds > 0: if headers is None and body is None: raise ValueError( 'Unable to send because both headers and body are missing' ) event = EventEnvelope().set_to(route) if headers is not None: if not isinstance(headers, dict): raise ValueError('headers must be dict') for h in headers: event.set_header(str(h), str(headers[h])) if body is not None: event.set_body(body) if reply_to is not None: if not isinstance(reply_to, str): raise ValueError('reply_to must be str') # encode 'me' in the "call back" if replying to this instance event.set_reply_to(reply_to, me) self.platform.send_event_later(event, seconds) else: raise ValueError('delay in seconds must be larger than zero') else: raise ValueError('delay in seconds must be int or float')
def request(self, route: str, timeout_seconds: float, headers: dict = None, body: any = None, correlation_id: str = None) -> EventEnvelope: self.util.validate_service_name(route, True) if headers is None and body is None: raise ValueError( 'Unable to make RPC call because both headers and body are missing' ) timeout_value = self.util.get_float(timeout_seconds) if timeout_value <= 0: raise ValueError( "timeout value in seconds must be positive number") event = EventEnvelope().set_to(route) if headers is not None: if not isinstance(headers, dict): raise ValueError('headers must be dict') for h in headers: event.set_header(h, str(headers[h])) if body is not None: event.set_body(body) if correlation_id is not None: event.set_correlation_id(str(correlation_id)) return self.platform.request(event, timeout_seconds)
def request(self, event: EventEnvelope, timeout_seconds: float): timeout_value = self.util.get_float(timeout_seconds) if timeout_value <= 0: raise ValueError( 'timeout value in seconds must be positive number') if not isinstance(event, EventEnvelope): raise ValueError('event object must be an EventEnvelope') # restore distributed tracing info from current thread trace_info = self.get_trace() if trace_info: if trace_info.get_route() is not None and event.get_from() is None: event.set_from(trace_info.get_route()) if trace_info.get_id() is not None and trace_info.get_path( ) is not None: event.set_trace(trace_info.get_id(), trace_info.get_path()) # emulate RPC inbox = Inbox(self) temp_route = inbox.get_route() inbox_queue = inbox.get_queue() try: route = event.get_to() event.set_reply_to(temp_route, me=True) if route in self._function_queues: self._loop.call_soon_threadsafe(self._send, route, event.to_map()) else: if self._cloud.is_connected(): self._cloud.send_payload({ 'type': 'event', 'event': event.to_map() }) else: raise ValueError(f'route {route} not found') # wait until response event is delivered to the inbox return inbox_queue.get(True, timeout_value) except Empty: raise TimeoutError( f'Route {event.get_to()} timeout for {round(timeout_value, 3)} seconds' ) finally: inbox.close()
def logger(self, event: EventEnvelope): if isinstance(event, EventEnvelope): self.log.info( f'trace={event.get_headers()}, annotations={event.get_body()}') if self.platform.is_trace_supported(): # forward to user provided distributed trace logger if any current_time = time.time() if self._dt_last_check is None or current_time - self._dt_last_check > 5.0: self._dt_last_check = current_time self._dt_found = self.platform.exists(self._dt_processor) if self._dt_found: trace_event = EventEnvelope() trace_event.set_to(self._dt_processor).set_body( {'annotations': event.get_body()}) for h in event.get_headers(): trace_event.set_header(h, event.get_header(h)) self.platform.send_event(trace_event)
def _normalize_result(result: EventEnvelope, result_obj: any): if isinstance(result, EventEnvelope): if result.get_status() == 200: if isinstance(result.get_body(), type(result_obj)): return result.get_body() else: raise AppException(500, str(result.get_body())) else: raise AppException(result.get_status(), str(result.get_body()))
def _get_server_config(self, headers: dict, body: any): if 'type' in headers: # at this point, login is successful if headers['type'] == 'system.config' and isinstance(body, dict): if self.MAX_PAYLOAD in body: self.max_ws_payload = body[self.MAX_PAYLOAD] self.log.info( 'Automatic segmentation when event payload exceeds ' + format(self.max_ws_payload, ',d')) # advertise public routes to language connector for r in self.platform.get_routes('public'): self.send_payload({'type': 'add', 'route': r}) # tell server that I am ready self.send_payload({'type': 'ready'}) # server acknowledges my ready signal if headers['type'] == 'ready': self.ready = True self.log.info('Ready') # redo subscription if any if self.platform.has_route('pub.sub.sync'): event = EventEnvelope() event.set_to('pub.sub.sync').set_header( 'type', 'subscription_sync') self.platform.send_event(event)
def _incoming(self, headers: dict, body: any): """ This function handles incoming messages from the websocket connection with the Mercury language connector. It must be invoked using events. It should not be called directly to guarantee proper event sequencing. :param headers: type is open, close, text or bytes :param body: string or bytes :return: None """ if self.ws and 'type' in headers: if headers['type'] == 'open': self.ready = False self.log.info("Login to language connector") self.send_payload({'type': 'login', 'api_key': self.api_key}) if headers['type'] == 'close': self.ready = False self.log.info("Closed") if headers['type'] == 'text': self.log.debug(body) if headers['type'] == 'bytes': event = msgpack.unpackb(body, raw=False) if 'type' in event: event_type = event['type'] if event_type == 'block' and 'block' in event: envelope = EventEnvelope() inner_event = envelope.from_map(event['block']) inner_headers = inner_event.get_headers() if 'id' in inner_headers and 'count' in inner_headers and 'total' in inner_headers: msg_id = inner_headers['id'] msg_count = inner_headers['count'] msg_total = inner_headers['total'] data = inner_event.get_body() if isinstance(data, bytes): buffer = self.cache.get(msg_id) if buffer is None: buffer = io.BytesIO() buffer.write(data) self.cache.put(msg_id, buffer) if msg_count == msg_total: buffer.seek(0) # reconstruct event for processing event = msgpack.unpackb(buffer.read(), raw=False) event_type = 'event' self.cache.remove(msg_id) if event_type == 'event' and 'event' in event: envelope = EventEnvelope() inner_event = envelope.from_map(event['event']) if self.platform.has_route(inner_event.get_to()): self.platform.send_event(inner_event) else: self.log.warn('Incoming event dropped because ' + str(inner_event.get_to()) + ' not found')
def broadcast(self, route: str, headers: dict = None, body: any = None) -> None: self.util.validate_service_name(route) if headers is None and body is None: raise ValueError('Unable to broadcast because both headers and body are missing') event = EventEnvelope().set_to(route) if headers is not None: if not isinstance(headers, dict): raise ValueError('headers must be dict') for h in headers: event.set_header(h, str(headers[h])) if body is not None: event.set_body(body) self.platform.send_event(event, True)
def _send_life_cycle_event(self, headers: dict): event = EventEnvelope() event.set_to(self.CONNECTOR_LIFECYCLE).set_headers(headers) self.platform.send_event(event)
def listener(self, event: EventEnvelope): event.set_round_trip((time.time() - self.begin) * 1000) self.inbox_queue.put(event)
def handle_event(self, event, instance): headers = dict() if 'headers' not in event else event['headers'] body = None if 'body' not in event else event['body'] result = None error_code = None error_msg = None # start distributed tracing if the event contains trace_id and trace_path if 'trace_id' in event and 'trace_path' in event: self.platform.start_tracing(self.route, trace_id=event['trace_id'], trace_path=event['trace_path']) else: self.platform.start_tracing(self.route) # execute user function begin = end = time.time() try: if instance == 0: # service is an interceptor. e.g. inbox for RPC call self.user_function(EventEnvelope().from_map(event)) elif instance == -1: # service is a singleton result = self.user_function(headers, body) else: # service with multiple instances result = self.user_function(headers, body, instance) end = time.time() except AppException as e: error_code = e.get_status() error_msg = e.get_message() except ValueError as e: error_code = 400 error_msg = str(e) except Exception as e: error_code = 500 error_msg = str(e) # execution time is rounded to 3 decimal points exec_time = float(format((end - begin) * 1000, '.3f')) if error_code: if 'reply_to' in event: # set exception as result result = EventEnvelope().set_status(error_code).set_body( error_msg) else: self.log.warn("Unhandled exception for " + self.route + " - code=" + str(error_code) + ", message=" + error_msg) # # interceptor should not send regular response because it will forward the request to another function. # However, if error_code exists, the system will send the exception response. # This allows interceptor to simply throw exception to indicate an error case. # if 'reply_to' in event and (error_code or not self.interceptor): reply_to = event['reply_to'] # in case this is a RPC call from within if reply_to.startswith('->'): reply_to = reply_to[2:] response = EventEnvelope().set_to(reply_to) if not error_code: response.set_exec_time(exec_time, False) if 'extra' in event: response.set_extra(event['extra']) if 'cid' in event: response.set_correlation_id(event['cid']) if 'trace_id' in event and 'trace_path' in event: response.set_trace(event['trace_id'], event['trace_path']) if isinstance(result, EventEnvelope): for h in result.get_headers(): response.set_header(h, result.get_header(h)) response.set_body(result.get_body()) response.set_status(result.get_status()) else: response.set_body(result) try: self.platform.send_event(response.set_from(self.route)) except Exception as e: self.log.warn("Event dropped because " + str(e)) # send tracing info to distributed trace logger trace_info = self.platform.stop_tracing() if self.tracing and trace_info is not None and isinstance(trace_info, TraceInfo) \ and trace_info.get_id() is not None and trace_info.get_path() is not None: dt = EventEnvelope().set_to(self.DISTRIBUTED_TRACING).set_body( trace_info.get_annotations()) dt.set_header('origin', self.platform.get_origin()) dt.set_header('id', trace_info.get_id()).set_header( 'path', trace_info.get_path()) dt.set_header('service', self.route).set_header('start', trace_info.get_start_time()) if not error_code: dt.set_header('success', 'true') dt.set_header('exec_time', exec_time) else: dt.set_header('success', 'false') dt.set_header('status', error_code) dt.set_header('exception', error_msg) self.platform.send_event(dt) self._loop.call_soon_threadsafe(self._ack)
def _incoming(self, headers: dict, body: any): """ This function handles incoming messages from the websocket connection with the Mercury language connector. It must be invoked using events. It should not be called directly to guarantee proper event sequencing. Args: headers: type is open, close, text or bytes body: string or bytes Returns: None """ if self.ws and 'type' in headers: if headers['type'] == 'open': self.ready = False self.log.info("Login to language connector") self.send_payload({'type': 'login', 'api_key': self.api_key}) self._send_life_cycle_event(headers) if headers['type'] == 'close': self.ready = False self.log.info("Closed") self._send_life_cycle_event(headers) if headers['type'] == 'text': self.log.debug(body) if headers['type'] == 'bytes': event = msgpack.unpackb(body, raw=False) if 'type' in event: event_type = event['type'] if event_type == 'block' and 'block' in event: envelope = EventEnvelope() inner_event = envelope.from_map(event['block']) inner_headers = inner_event.get_headers() if self.MSG_ID in inner_headers and self.COUNT in inner_headers and self.TOTAL in inner_headers: msg_id = inner_headers[self.MSG_ID] msg_count = inner_headers[self.COUNT] msg_total = inner_headers[self.TOTAL] data = inner_event.get_body() if isinstance(data, bytes): buffer = self.cache.get(msg_id) if buffer is None: buffer = io.BytesIO() buffer.write(data) if msg_count == msg_total: self.cache.remove(msg_id) # reconstruct event for processing buffer.seek(0) envelope = EventEnvelope() unpacked = msgpack.unpackb(buffer.read(), raw=False) restored = envelope.from_map(unpacked) target = restored.get_to() if self.platform.has_route(target): self.platform.send_event(restored) else: self.log.warn( f'Incoming event dropped because {target} not found' ) else: self.cache.put(msg_id, buffer) if event_type == 'event' and 'event' in event: unpacked = msgpack.unpackb(event['event'], raw=False) envelope = EventEnvelope() inner_event = envelope.from_map(unpacked) if self.platform.has_route(inner_event.get_to()): self.platform.send_event(inner_event) else: self.log.warn( f'Incoming event dropped because {inner_event.get_to()} not found' )
def hello(self, headers: dict, body: any): # singleton function signature (headers: dict, body: any) log.info(f'{self.MY_NAME} {headers}, {body}') return EventEnvelope().set_body({'body': body, 'origin': platform.get_origin()}) \ .set_header('x-notes', 'I am a python app')
def main(): # you can register a method of a class platform.register('hello.world.1', Hi().hello, 5) # or register a function platform.register('hello.world.2', hello, 10) # you can also create an alias for the same service platform.register('hello.world', Hi().hello, 5) # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings po.send('hello.world.1', headers={'one': 1}, body='hello world one') po.send('hello.world.2', headers={'two': 2}, body='hello world two') # demonstrate a RPC request try: result = po.request('hello.world.2', 2.0, headers={'some_key': 'some_value'}, body='hello world') if isinstance(result, EventEnvelope): log.info('Received RPC response:') log.info( f'HEADERS = {result.get_headers()}, BODY = {result.get_body()}, STATUS = {result.get_status()}, ' f'EXEC = {result.get_exec_time()} ms, ROUND TRIP = {result.get_round_trip()} ms' ) except TimeoutError as e: log.error(f'Exception: {e}') # illustrate parallel RPC requests event_list = list() event_list.append( EventEnvelope().set_to('hello.world.1').set_body("first request")) event_list.append( EventEnvelope().set_to('hello.world.2').set_body("second request")) try: result = po.parallel_request(event_list, 2.0) if isinstance(result, list): log.info(f'Received {len(result)} parallel RPC responses:') for res in result: log.info( f'HEADERS = {res.get_headers()}, BODY = {res.get_body()}, STATUS = {res.get_status()}, ' f'EXEC = {res.get_exec_time()} ms, ROUND TRIP = {res.get_round_trip()} ms' ) except TimeoutError as e: log.error(f'Exception: {e}') # demonstrate deferred delivery po.send_later('hello.world.1', headers={'hello': 'world'}, body='this message arrives 5 seconds later', seconds=5.0) def life_cycle_listener(headers: dict, body: any): # Detect when cloud is ready log.info(f'Cloud life cycle event - {headers}') if 'type' in headers and 'ready' == headers['type']: # Demonstrate broadcast feature: # To test this feature, please run multiple instances of this demo po.broadcast("hello.world", body="this is a broadcast message from " + platform.get_origin()) platform.register('my.cloud.status', life_cycle_listener, is_private=True) platform.subscribe_life_cycle('my.cloud.status') # Connect to the network platform.connect_to_cloud() # # This will keep the main thread running in the background. # We can use Control-C or KILL signal to stop the application. platform.run_forever()
def send_event(self, event: EventEnvelope, me=True): if event.get_reply_to() is not None: event.set_reply_to(event.get_reply_to(), me) self.platform.send_event(event)
def listener(self, event: EventEnvelope): diff = time.perf_counter() - self.start_time event.set_round_trip(diff * 1000) self.inbox_queue.put(event)
async def connection_handler(self, url): try: async with aiohttp.ClientSession( loop=self._loop, timeout=aiohttp.ClientTimeout(total=10)) as session: full_path = url + '/' + self.origin self.ws = await session.ws_connect(full_path) envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_header( 'type', 'open') self.platform.send_event(envelope) self.log.info("Connected to " + full_path) closed = False self.last_active = time.time() while self.normal: try: msg = await self.ws.receive(timeout=1) except asyncio.TimeoutError: if not self.normal: break else: # idle - send keep-alive now = time.time() if self.is_connected( ) and now - self.last_active > 30: self.last_active = now self.send_keep_alive() continue # receive incoming event self.last_active = time.time() if msg.type == aiohttp.WSMsgType.TEXT: if self.platform.has_route(self.INCOMING_WS_PATH): envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_header( 'type', 'text').set_body(msg.data) self.platform.send_event(envelope) else: break elif msg.type == aiohttp.WSMsgType.BINARY: if self.platform.has_route(self.INCOMING_WS_PATH): envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_header( 'type', 'bytes').set_body(msg.data) self.platform.send_event(envelope) else: break else: if msg.type == aiohttp.WSMsgType.ERROR: self.log.error("Unexpected connection error") if msg.type == aiohttp.WSMsgType.CLOSING: # closing signal received - close the connection now self.log.info("Disconnected, status=" + str(self.close_code) + ", message=" + self.close_message) await self.ws.close(code=self.close_code, message=bytes( self.close_message, 'utf-8')) if self.platform.has_route(self.INCOMING_WS_PATH): envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_body(self.close_message)\ .set_header('type', 'close').set_header('status', self.close_code) self.platform.send_event(envelope) closed = True if msg.type == aiohttp.WSMsgType.CLOSE or msg.type == aiohttp.WSMsgType.CLOSED: self.close_code = 1001 if msg.data is None else msg.data self.close_message = 'OK' if msg.extra is None else str( msg.extra) self.log.info("Disconnected, status=" + str(self.close_code) + ", message=" + self.close_message) if self.platform.has_route(self.INCOMING_WS_PATH): envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_body(self.close_message)\ .set_header('type', 'close').set_header('status', self.close_code) self.platform.send_event(envelope) closed = True break if not closed: await self.ws.close(code=1000, message=b'OK') self.ws = None if self.platform.has_route(self.INCOMING_WS_PATH): envelope = EventEnvelope() envelope.set_to(self.INCOMING_WS_PATH).set_body('OK')\ .set_header('type', 'close').set_header('status', 1000) self.platform.send_event(envelope) except aiohttp.ClientConnectorError: self._skip_url() self.log.warn("Unreachable " + url)
def send_payload(self, data: dict): payload = msgpack.packb(data, use_bin_type=True) payload_len = len(payload) if 'type' in data and data[ 'type'] == 'event' and 'event' in data and payload_len > self.max_ws_payload: evt = data['event'] if 'id' in evt: msg_id = evt['id'] total = int(payload_len / self.max_ws_payload) if payload_len > total: total += 1 buffer = io.BytesIO(payload) count = 0 for i in range(total): count += 1 block = EventEnvelope() block.set_header('id', msg_id) block.set_header('count', count) block.set_header('total', total) block.set_body(buffer.read(self.max_ws_payload)) block_map = dict() block_map['type'] = 'block' block_map['block'] = block.to_map() block_payload = msgpack.packb(block_map, use_bin_type=True) envelope = EventEnvelope() envelope.set_to(self.OUTGOING_WS_PATH).set_header( 'type', 'bytes').set_body(block_payload) self.platform.send_event(envelope) else: envelope = EventEnvelope() envelope.set_to(self.OUTGOING_WS_PATH).set_header( 'type', 'bytes').set_body(payload) self.platform.send_event(envelope)
def logger(self, event: EventEnvelope): if isinstance(event, EventEnvelope): self.log.info('trace=' + str(event.get_headers()) + ', annotations=' + str(event.get_body())) # forward to user provided distributed trace logger if any current_time = time.time() if self._dt_last_check is None or current_time - self._dt_last_check > 5.0: self._dt_last_check = current_time self._dt_found = self.platform.exists(self._dt_processor) if self._dt_found: te = EventEnvelope() te.set_to(self._dt_processor).set_body(event.get_body()) for h in event.get_headers(): te.set_header(h, event.get_header(h)) self.platform.send_event(te)