def test_successful_get_peers(self): ts, datagrams = self.controller.main_loop() ping_timeout_ts = ts #FIXME: assert_almost_equal(ts, time.time()+2) ping = datagrams[0].data addr = datagrams[0].addr #fabricate response ping = self.servers_msg_f.incoming_msg(Datagram(ping, addr)) pong = self.servers_msg_f.outgoing_ping_response(tc.CLIENT_NODE) data = pong.stamp(ping.tid) # get a node in the routing table self.controller.on_datagram_received(message.Datagram(data, addr)) #The lookup starts with a single node lookup_result = [] datagrams = self.controller.get_peers(lookup_result, tc.INFO_HASH, lambda x, y: x.append(y), 0) #FIXME: assert_almost_equal(ts, ping_timeout_ts)#time.time()+2) #FIXME: eq_(len(datagrams), 1) # Now a get_peers with local results info_hash = identifier.Id('info_hash info_hash ') self.controller._tracker.put(info_hash, tc.CLIENT_ADDR) lookup_result = [] self.controller.get_peers(lookup_result, info_hash, lambda x, y: x.append(y), 0)
def _old(self): # controller.start() starts reactor (we don't want to use reactor in # tests), sets _running, and calls main_loop self.controller._running = True # controller.start calls main_loop, which does maintenance (bootstrap) self.controller.main_loop() # minitwisted informs of a response data = message.OutgoingPingResponse(tc.CLIENT_NODE, tc.SERVER_ID).stamp('\0\0') self.controller.on_datagram_received( message.Datagram(data, tc.SERVER_ADDR)) self.controller.main_loop() # maintenance (maintenance lookup)
def test_adding_and_removing_node(self): # The routing table is initially empty eq_(self.controller._routing_m.get_main_rnodes(), []) q = self.controller.msg_f.outgoing_ping_query(tc.SERVER_NODE) expected_ts, expected_datagrams = self.querier2.register_queries([q]) # main_loop is called by reactor.start() # It returns a maintenance ping ts, datagrams = self.controller.main_loop() #FIXME: assert_almost_equal(ts, expected_ts) eq_(len(datagrams), 1) eq_(datagrams[0], expected_datagrams[0]) time.sleep((ts - time.time()) / 2) # SERVER_NODE gets msg and replies before the timeout tid = self.servers_msg_f.incoming_msg( Datagram(datagrams[0].data, tc.CLIENT_ADDR)).tid data = self.servers_msg_f.outgoing_ping_response( tc.CLIENT_NODE).stamp(tid) eq_(self.controller._routing_m.get_main_rnodes(), []) datagram = message.Datagram(data, tc.SERVER_ADDR) self.controller.on_datagram_received(datagram) # SERVER_NODE is added to the routing table eq_(self.controller._routing_m.get_main_rnodes(), [tc.SERVER_NODE]) time.sleep((ts - time.time())) # main_loop is called to trigger timeout # It returns a maintenance lookup ts, datagrams = self.controller.main_loop() q = self.controller.msg_f.outgoing_find_node_query( tc.SERVER_NODE, self.my_id, None) expected_ts, expected_datagrams = self.querier2.register_queries([q]) #FIXME: assert_almost_equal(ts, expected_ts) #FIXME: eq_(len(datagrams), 1) #FIXME: eq_(datagrams[0], expected_datagrams[0]) time.sleep(ts - time.time()) # main_loop is called to trigger timeout # It triggers a timeout (removing SERVER_NODE from the routing table # returns a maintenance ping ts, datagrams = self.controller.main_loop() #FIXME: eq_(self.controller._routing_m.get_main_rnodes(), []) # No reply for this query #this call should trigger timeout self.controller.main_loop()
def register_queries(self, queries): """ A Querier object keeps a registry of sent queries while waiting for responses. """ assert len(queries) datagrams = [] current_ts = time.time() timeout_ts = current_ts + TIMEOUT_DELAY for i, query in enumerate(queries): msg = query tid = self._next_tid() logger.debug('registering query %d to node: %r\n%r' % (i, query.dst_node, msg)) self._timeouts.append((timeout_ts, msg)) # if node is not in the dictionary, it will create an empty list self._pending.setdefault(query.dst_node.addr, []).append(msg) datagrams.append( message.Datagram(msg.stamp(tid), query.dst_node.addr)) return timeout_ts, datagrams
def test_retry_get_peers(self): ts, datagrams = self.controller.main_loop() ping_timeout_ts = ts #FIXME: assert_almost_equal(ts, time.time()+2) eq_(len(datagrams), 1) ping = datagrams[0].data addr = datagrams[0].addr #this get_peers fails because there are no nodes in the routing table datagrams = self.controller.get_peers(None, tc.INFO_HASH, None, 0) eq_(len(datagrams), 0) #fabricate response ping = self.servers_msg_f.incoming_msg(Datagram(ping, addr)) pong = self.servers_msg_f.outgoing_ping_response(tc.CLIENT_NODE) data = pong.stamp(ping.tid) # get a node in the routing table self.controller.on_datagram_received(message.Datagram(data, addr)) # This call does nothing because it's too early ts, datagrams = self.controller.main_loop() #eq_(ts, ping_timeout_ts) eq_(datagrams, []) # Controller retries lookup get_peers time.sleep(ts - time.time()) ts, datagrams = self.controller.main_loop()
def test_bad_datagram_received(self): ts, datagrams = self.controller.on_datagram_received( message.Datagram('aa', tc.CLIENT_ADDR)) assert not datagrams
def on_datagram_received(self, datagram): """ Perform the actions associated to the arrival of the given datagram. The datagram will be ignored in cases such as invalid format. Otherwise, the datagram will be decoded and different modules will be informed to take action on it. For instance, if the datagram contains a response to a lookup query, both routing and lookup manager will be informed. Additionally, if that response contains peers, the lookup's handler will be called (see get\_peers above). This method is designed to be used as minitwisted's networking handler. """ exp_queries_to_send = [] data = datagram.data addr = datagram.addr datagrams_to_send = [] try: msg = self.msg_f.incoming_msg(datagram) except (message.MsgError): # ignore message return self._next_main_loop_call_ts, datagrams_to_send if msg.type == message.QUERY: if msg.src_node.id == self._my_id: logger.debug('Got a msg from myself:\n%r', msg) return self._next_main_loop_call_ts, datagrams_to_send #zinat: inform experimental_module exp_queries_to_send = self._experimental_m.on_query_received(msg) response_msg = self._responder.get_response(msg) if response_msg: bencoded_response = response_msg.stamp(msg.tid) datagrams_to_send.append( message.Datagram(bencoded_response, addr)) maintenance_queries_to_send = self._routing_m.on_query_received( msg.src_node) elif msg.type == message.RESPONSE: related_query = self._querier.get_related_query(msg) if not related_query: # Query timed out or unrequested response return self._next_main_loop_call_ts, datagrams_to_send ## zinat: if related_query.experimental_obj: exp_queries_to_send = self._experimental_m.on_response_received( msg, related_query) #TODO: you need to get datagrams to be able to send messages (raul) # lookup related tasks if related_query.lookup_obj: (lookup_queries_to_send, peers, num_parallel_queries, lookup_done) = related_query.lookup_obj.on_response_received( msg, msg.src_node) datagrams = self._register_queries(lookup_queries_to_send) datagrams_to_send.extend(datagrams) lookup_obj = related_query.lookup_obj lookup_id = lookup_obj.lookup_id callback_f = lookup_obj.callback_f if peers: self._add_cache_peers(lookup_obj.info_hash, peers) if callback_f and callable(callback_f): callback_f(lookup_id, peers, msg.src_node) if lookup_done: if callback_f and callable(callback_f): callback_f(lookup_id, None, msg.src_node) queries_to_send = self._announce(related_query.lookup_obj) datagrams = self._register_queries(queries_to_send) datagrams_to_send.extend(datagrams) # maintenance related tasks maintenance_queries_to_send = \ self._routing_m.on_response_received( msg.src_node, related_query.rtt, msg.all_nodes) elif msg.type == message.ERROR: related_query = self._querier.get_related_query(msg) if not related_query: # Query timed out or unrequested response return self._next_main_loop_call_ts, datagrams_to_send #TODO: zinat: same as response exp_queries_to_send = self._experimental_m.on_error_received( msg, related_query) # lookup related tasks if related_query.lookup_obj: peers = None # an error msg doesn't have peers (lookup_queries_to_send, num_parallel_queries, lookup_done) = related_query.lookup_obj.on_error_received( msg, addr) datagrams = self._register_queries(lookup_queries_to_send) datagrams_to_send.extend(datagrams) callback_f = related_query.lookup_obj.callback_f if callback_f and callable(callback_f): lookup_id = related_query.lookup_obj.lookup_id if lookup_done: callback_f(lookup_id, None, msg.src_node) if lookup_done: datagrams = self._announce(related_query.lookup_obj) datagrams_to_send.extend(datagrams) # maintenance related tasks maintenance_queries_to_send = \ self._routing_m.on_error_received(addr) else: # unknown type return self._next_main_loop_call_ts, datagrams_to_send # we are done with the plugins # now we have maintenance_queries_to_send, let's send them! datagrams = self._register_queries(maintenance_queries_to_send) datagrams_to_send.extend(datagrams) if exp_queries_to_send: datagrams = self._register_queries(exp_queries_to_send) datagrams_to_send.extend(datagrams) return self._next_main_loop_call_ts, datagrams_to_send