99from .router import MeshRouter
1010from .work import WorkManager
1111from .connection import Connection
12+ from .messages import envelope , directive
13+ from . import exceptions
1214
15+ RECEPTOR_DIRECTIVE_NAMESPACE = 'receptor'
1316logger = logging .getLogger (__name__ )
1417
1518
@@ -26,6 +29,7 @@ def __init__(self, config, node_id=None, router_cls=None,
2629 if not os .path .exists (self .base_path ):
2730 os .makedirs (os .path .join (self .config .default_data_dir , self .node_id ))
2831 self .connection_manifest_path = os .path .join (self .base_path , "connection_manifest" )
32+ self .buffer_mgr = self .config .components_buffer_manager
2933 self .stop = False
3034
3135 def _find_node_id (self ):
@@ -42,7 +46,7 @@ async def watch_expire(self):
4246 while True :
4347 current_manifest = self .get_connection_manifest ()
4448 for connection in current_manifest :
45- buffer = self .config . components_buffer_manager .get_buffer_for_node (connection ["id" ], self )
49+ buffer = self .buffer_mgr .get_buffer_for_node (connection ["id" ], self )
4650 for ident , message in buffer :
4751 message_actual = json .loads (message )
4852 if "expire_time" in message_actual and message_actual ['expire_time' ] < time .time ():
@@ -84,7 +88,7 @@ def update_connection_manifest(self, connection):
8488 manifest .append (dict (id = connection ,
8589 last = time .time ()))
8690 self .write_connection_manifest (manifest )
87-
91+
8892 def update_connections (self , connection ):
8993 self .router .register_edge (connection .id_ , self .node_id , 1 )
9094 if connection .id_ in self .connections :
@@ -93,11 +97,18 @@ def update_connections(self, connection):
9397 self .connections [connection .id_ ] = [connection ]
9498 self .update_connection_manifest (connection .id_ )
9599
100+ async def message_handler (self , buf ):
101+ while True :
102+ for data in buf .get ():
103+ if "cmd" in data and data ["cmd" ] == "ROUTE" :
104+ self .handle_route_advertisement (data )
105+ else :
106+ await self .handle_message (data )
107+ await asyncio .sleep (.1 )
108+
96109 def add_connection (self , id_ , meta , protocol_obj ):
97- buffer_mgr = self .config .components_buffer_manager
98- conn = Connection (id_ , meta , protocol_obj , buffer_mgr , self )
110+ conn = Connection (id_ , meta , protocol_obj )
99111 self .update_connections (conn )
100- return conn
101112
102113 def remove_connection (self , protocol_obj ):
103114 notify_connections = []
@@ -118,3 +129,84 @@ async def shutdown_handler(self):
118129 if self .stop :
119130 return
120131 await asyncio .sleep (1 )
132+
133+ def handle_route_advertisement (self , data ):
134+ self .router .add_edges (data ["edges" ])
135+ self .send_route_advertisement (data ["edges" ], data ["seen" ])
136+
137+ def send_route_advertisement (self , edges = None , seen = []):
138+ edges = edges or self .router .get_edges ()
139+ seen = set (seen )
140+ logger .debug ("Emitting Route Advertisements, excluding {}" .format (seen ))
141+ destinations = set (self .connections ) - seen
142+ seens = list (seen | destinations | {self .node_id })
143+
144+ # TODO: This should be a broadcast call to the connection manager
145+ for target in destinations :
146+ buf = self .buffer_mgr .get_buffer_for_node (target , self )
147+ try :
148+ buf .push (json .dumps ({
149+ "cmd" : "ROUTE" ,
150+ "id" : self .node_id ,
151+ "capabilities" : self .work_manager .get_capabilities (),
152+ "groups" : self .config .node_groups ,
153+ "edges" : edges ,
154+ "seen" : seens
155+ }).encode ("utf-8" ))
156+ except exceptions .ReceptorBufferError as e :
157+ logger .exception ("Receptor Buffer Write Error broadcasting routes and capabilities: {}" .format (e ))
158+ # TODO: This might should be a hard shutdown event
159+ except Exception as e :
160+ logger .exception ("Error trying to broadcast routes and capabilities: {}" .format (e ))
161+
162+ async def handle_message (self , msg ):
163+ outer_env = envelope .OuterEnvelope (** msg )
164+ next_hop = self .router .next_hop (outer_env .recipient )
165+ if next_hop :
166+ return await self .router .forward (outer_env , next_hop )
167+
168+ await outer_env .deserialize_inner (self )
169+
170+ if outer_env .inner_obj .message_type != 'directive' :
171+ raise exceptions .UnknownMessageType (
172+ f'Unknown message type: { outer_env .inner_obj .message_type } ' )
173+
174+ try :
175+ namespace , _ = outer_env .inner_obj .directive .split (':' , 1 )
176+ if namespace == RECEPTOR_DIRECTIVE_NAMESPACE :
177+ await directive .control (self .router , outer_env .inner_obj )
178+ else :
179+ # other namespace/work directives
180+ await self .work_manager .handle (outer_env .inner_obj )
181+ except ValueError :
182+ logger .error ("error in handle_message: Invalid directive -> '%s'. Sending failure response back." % (outer_env .inner_obj .directive ,))
183+ err_resp = outer_env .inner_obj .make_response (
184+ receptor = self ,
185+ recipient = outer_env .inner_obj .sender ,
186+ payload = "An invalid directive ('%s') was specified." % (outer_env .inner_obj .directive ,),
187+ in_response_to = outer_env .inner_obj .message_id ,
188+ serial = outer_env .inner_obj .serial + 1 ,
189+ ttl = 15 ,
190+ code = 1 ,
191+ )
192+ await self .router .send (err_resp )
193+ except Exception as e :
194+ logger .error ("error in handle_message: '%s'. Sending failure response back." % (str (e ),))
195+ err_resp = outer_env .inner_obj .make_response (
196+ receptor = self ,
197+ recipient = outer_env .inner_obj .sender ,
198+ payload = str (e ),
199+ in_response_to = outer_env .inner_obj .message_id ,
200+ serial = outer_env .inner_obj .serial + 1 ,
201+ ttl = 15 ,
202+ code = 1 ,
203+ )
204+ await self .router .send (err_resp )
205+ elif outer_env .inner_obj .message_type == 'response' :
206+ in_response_to = outer_env .inner_obj .in_response_to
207+ if in_response_to in self .router .response_registry :
208+ logger .info (f'Handling response to { in_response_to } with callback.' )
209+ for connection in self .controller_connections :
210+ connection .emit_response (outer_env .inner_obj )
211+ else :
212+ logger .warning (f'Received response to { in_response_to } but no record of sent message.' )
0 commit comments