def _test_dreply(self, gen_dreply, good, diff_ips_test=False): for i in range(self.NLISTENERS): print >> sys.stderr, "test: waiting for #", i, "listenport", self.mylistenport[ i] conn, addr = self.myss[i].accept() s = OLConnection(self.mykeypairs[i], '', 0, conn, self.mylistenport[i]) while True: msg = s.recv() self.assert_(len(msg) > 0) print >> sys.stderr, "test: Received overlay message", getMessageName( msg[0]) if msg[0] == DIALBACK_REQUEST: break self.assert_(msg[0] == DIALBACK_REQUEST) self.check_drequest(msg[1:]) # Proper behaviour is to try to send a reply using a new return connection s2 = BTConnection('localhost', self.hisport, mylistenport=self.mylistenport[i], user_infohash=dialback_infohash) s2.read_handshake_medium_rare(close_ok=True) if gen_dreply is not None: resp = gen_dreply(i) print >> sys.stderr, "test: sending DIALBACK_REPLY #", i s2.send(resp) time.sleep(2) # the other side should always close the # connection, either because we're done or he didn't like our # bad DIALBACK_REPLY message msg = s2.recv() if len(msg) > 0: print >> sys.stderr, "test: Received unexpected data", getMessageName( msg[0]) self.assert_(len(msg) == 0) s2.close() # Not really necessary, but helps with test_dialback_active2 s.close() ext_ip = self.session.get_external_ip() print >> sys.stderr, "test: External IP address after test is", ext_ip if diff_ips_test: if self.config.sessconfig['dialback_trust_superpeers'] == 1: good = True else: good = False if good: self.assert_(ext_ip == REPLY_IP) else: self.assert_(ext_ip == self.myoriginalip)
def _test_dreply(self,gen_dreply,good,diff_ips_test=False): for i in range(self.NLISTENERS): print >> sys.stderr,"test: waiting for #",i,"listenport",self.mylistenport[i] conn, addr = self.myss[i].accept() s = OLConnection(self.mykeypairs[i],'',0,conn,self.mylistenport[i]) while True: msg = s.recv() self.assert_(len(msg) > 0) print >> sys.stderr,"test: Received overlay message",getMessageName(msg[0]) if msg[0] == DIALBACK_REQUEST: break self.assert_(msg[0] == DIALBACK_REQUEST) self.check_drequest(msg[1:]) # Proper behaviour is to try to send a reply using a new return connection s2 = BTConnection('localhost',self.hisport,mylistenport=self.mylistenport[i],user_infohash=dialback_infohash) s2.read_handshake_medium_rare(close_ok = True) if gen_dreply is not None: resp = gen_dreply(i) print >> sys.stderr,"test: sending DIALBACK_REPLY #",i s2.send(resp) time.sleep(2) # the other side should always close the # connection, either because we're done or he didn't like our # bad DIALBACK_REPLY message msg = s2.recv() if len(msg) > 0: print >> sys.stderr,"test: Received unexpected data",getMessageName(msg[0]) self.assert_(len(msg)==0) s2.close() # Not really necessary, but helps with test_dialback_active2 s.close() ext_ip = self.session.get_external_ip() print >>sys.stderr,"test: External IP address after test is",ext_ip if diff_ips_test: if self.config.sessconfig['dialback_trust_superpeers'] == 1: good = True else: good = False if good: self.assert_(ext_ip == REPLY_IP) else: self.assert_(ext_ip == self.myoriginalip)
def receive_crawler_reply(self, sock, message_id, channel_id): # Sending a reply from a Tribler peer to a Crawler # SIZE INDEX # 1 byte: 0 CRAWLER_REPLY (from BaseLib.Core.BitTornado.BT1.MessageID) # 1 byte: 1 --MESSAGE-SPECIFIC-ID-- # 1 byte: 2 Channel id # 1 byte: 3 Parts left # 1 byte: 4 Indicating success (0) or failure (non 0) # n byte: 5... Reply payload if DEBUG: print >>sys.stderr, "test_crawler: receive_crawler_reply: waiting for channel",channel_id parts = [] while True: response = sock.recv() if response: if response[0] == CRAWLER_REPLY and response[1] == message_id and ord(response[2]) == channel_id: parts.append(response[5:]) if DEBUG: print >>sys.stderr, "test_crawler: received", getMessageName(response[0:2]), "channel", channel_id, "length", sum([len(part) for part in parts]), "parts left", ord(response[3]) if ord(response[3]): # there are parts left continue return ord(response[4]), "".join(parts) return -1, ""
def handleMessage(self, permid, selversion, message): """ Called by NetworkThread """ if DEBUG: print >> sys.stderr, "olbridge: handleMessage", show_permid_short( permid), selversion, getMessageName( message[0]), currentThread().getName() def olbridge_handle_msg_func(): # Called by OverlayThread if DEBUG: print >> sys.stderr, "olbridge: handle_msg_func", show_permid_short( permid), selversion, getMessageName( message[0]), currentThread().getName() try: if self.olappsmsghandler is None: ret = True else: ret = self.olappsmsghandler(permid, selversion, message) except: print_exc() ret = False if ret == False: self.close(permid) self.tqueue.add_task(olbridge_handle_msg_func, 0) return True
def _send_request(self, permid, message_id, channel_id, payload, frequency=3600, callback=None): """ Send a CRAWLER_REQUEST message to permid. This method assumes that connection exists to the permid. @param permid The destination peer @param message_id The message id @param payload The message content @param frequency Destination peer will return a frequency-error when this message_id has been received within the last frequency seconds @param callback Callable function/method is called when request is send with 2 paramaters (exc, permid) @return The message channel-id > 0 on success, and 0 on failure """ # Sending a request from a Crawler to a Tribler peer # SIZE INDEX # 1 byte: 0 CRAWLER_REQUEST (from BaseLib.Core.BitTornado.BT1.MessageID) # 1 byte: 1 --MESSAGE-SPECIFIC-ID-- # 1 byte: 2 Channel id # 2 byte: 3+4 Frequency # n byte: 5... Request payload def _after_send_request(exc, permid): if DEBUG: if exc: print >> sys.stderr, "crawler: could not send request to", show_permid_short( permid), exc if exc: self._release_channel_id(permid, channel_id) # call the optional callback supplied with send_request if callback: callback(exc, permid) if DEBUG: print >> sys.stderr, "crawler: sending", getMessageName( CRAWLER_REQUEST + message_id), "with", len( payload), "bytes payload to", show_permid_short(permid) self._overlay_bridge.send( permid, "".join( (CRAWLER_REQUEST, message_id, chr(channel_id & 0xFF), chr((frequency >> 8) & 0xFF) + chr(frequency & 0xFF), str(payload))), _after_send_request) return channel_id
def olbridge_handle_msg_func(): # Called by OverlayThread if DEBUG: print >> sys.stderr, "olbridge: handle_msg_func", show_permid_short( permid), selversion, getMessageName( message[0]), currentThread().getName() try: if self.olappsmsghandler is None: ret = True else: ret = self.olappsmsghandler(permid, selversion, message) except: print_exc() ret = False if ret == False: self.close(permid)
def got_message(self,dns,message): """ received message from peer, pass to upper layer """ if DEBUG: print >> sys.stderr,"dlbreturn: got_message",getMessageName(message[0]) if self.usermsghandler is None: if DEBUG: print >> sys.stderr,"dlbreturn: User receive callback not set" return try: ret = self.usermsghandler(dns,message) if ret is None: if DEBUG: print >> sys.stderr,"dlbreturn: INTERNAL ERROR:", \ "User receive callback returned None, not True or False" ret = False return ret except: # Catch all print_exc(file=sys.stderr) return False
def got_message(self, dns, message): """ received message from peer, pass to upper layer """ if DEBUG: print >> sys.stderr, "dlbreturn: got_message", getMessageName( message[0]) if self.usermsghandler is None: if DEBUG: print >> sys.stderr, "dlbreturn: User receive callback not set" return try: ret = self.usermsghandler(dns, message) if ret is None: if DEBUG: print >> sys.stderr,"dlbreturn: INTERNAL ERROR:", \ "User receive callback returned None, not True or False" ret = False return ret except: # Catch all print_exc(file=sys.stderr) return False
def test_good_get_metadata_url(self): # 1. Establish overlay connection to Tribler s = OLConnection(self.my_keypair,'localhost',self.hisport) for tdef in [self.tdef1,self.tdef2]: msg = self.create_good_get_metadata(tdef.get_infohash()) s.send(msg) try: s.b.s.settimeout(10.0) resp = s.recv() self.assert_(len(resp) > 0) print >>sys.stderr,"test: Got reply",getMessageName(resp[0]) self.assert_(resp[0] == METADATA) self.check_metadata(resp[1:],tdef) except socket.timeout: print >> sys.stderr,"test: Timeout, bad, peer didn't reply with METADATA message" self.assert_(False) s.close()
def test_good_get_metadata_url(self): # 1. Establish overlay connection to Tribler s = OLConnection(self.my_keypair,'localhost',self.hisport) for tdef in [self.tdef1,self.tdef2]: msg = self.create_good_get_metadata(tdef.get_infohash()) s.send(msg) try: s.b.s.settimeout(10.0) resp = s.recv() self.assert_(len(resp) > 0) print >>sys.stderr,time.asctime(),'-', "test: Got reply",getMessageName(resp[0]) self.assert_(resp[0] == METADATA) self.check_metadata(resp[1:],tdef) except socket.timeout: print >> sys.stderr,time.asctime(),'-', "test: Timeout, bad, peer didn't reply with METADATA message" self.assert_(False) s.close()
def subtest_invalid_messageid(self): """ Send an invalid message-id from a registered crawler peer """ print >>sys.stderr, "-"*80, "\ntest: invalid_messageid" # make sure that the OLConnection IS in the crawler_db crawler_db = CrawlerDBHandler.getInstance() crawler_db.temporarilyAddCrawler(self.my_permid) # We are a registered crawler, start sending invalid messages messages = [CRAWLER_REQUEST, CRAWLER_REQUEST + chr(0), CRAWLER_REPLY, CRAWLER_REPLY + chr(0)] for msg in messages: s = OLConnection(self.my_keypair, "localhost", self.hisport) s.send(msg) response = s.recv() assert response == "", "response type is %s" % getMessageName(response[0]) time.sleep(1) s.close()
def _test_qreply(self,gen_qreply,good): print >> sys.stderr,"test: waiting for reply" s = self.openconn msg = s.recv() self.assert_(len(msg) > 0) print >> sys.stderr,"test: Received overlay message",getMessageName(msg[0]) self.assert_(msg[0] == QUERY) id = self.check_rquery(msg[1:]) resp = gen_qreply(id) print >> sys.stderr,"test: sending QUERY_REPLY" s.send(resp) if good: time.sleep(10) # the other side should not have closed the connection, as # this is all valid, so this should not throw an exception: s.send('bla') s.close() else: # the other side should not like this and close the connection self.assert_(len(s.recv())==0) s.close()
def _send_request(self, permid, message_id, channel_id, payload, frequency=3600, callback=None): """ Send a CRAWLER_REQUEST message to permid. This method assumes that connection exists to the permid. @param permid The destination peer @param message_id The message id @param payload The message content @param frequency Destination peer will return a frequency-error when this message_id has been received within the last frequency seconds @param callback Callable function/method is called when request is send with 2 paramaters (exc, permid) @return The message channel-id > 0 on success, and 0 on failure """ # Sending a request from a Crawler to a Tribler peer # SIZE INDEX # 1 byte: 0 CRAWLER_REQUEST (from BaseLib.Core.BitTornado.BT1.MessageID) # 1 byte: 1 --MESSAGE-SPECIFIC-ID-- # 1 byte: 2 Channel id # 2 byte: 3+4 Frequency # n byte: 5... Request payload def _after_send_request(exc, permid): if DEBUG: if exc: print >> sys.stderr, "crawler: could not send request to", show_permid_short(permid), exc if exc: self._release_channel_id(permid, channel_id) # call the optional callback supplied with send_request if callback: callback(exc, permid) if DEBUG: print >> sys.stderr, "crawler: sending", getMessageName(CRAWLER_REQUEST+message_id), "with", len(payload), "bytes payload to", show_permid_short(permid) self._overlay_bridge.send(permid, "".join((CRAWLER_REQUEST, message_id, chr(channel_id & 0xFF), chr((frequency >> 8) & 0xFF) + chr(frequency & 0xFF), str(payload))), _after_send_request) return channel_id
def subtest_invalid_permid(self): """ Send crawler messages from a non-crawler peer """ print >>sys.stderr, "-"*80, "\ntest: invalid_permid" # make sure that the OLConnection is NOT in the crawler_db crawler_db = CrawlerDBHandler.getInstance() assert not self.my_permid in crawler_db.getCrawlers() # We are not a registered crawler, any request from us should # be denied messages = [CRAWLER_REQUEST, CRAWLER_REQUEST + CRAWLER_DATABASE_QUERY, CRAWLER_REQUEST + CRAWLER_DATABASE_QUERY, CRAWLER_REQUEST + chr(0)] for msg in messages: s = OLConnection(self.my_keypair, "localhost", self.hisport) s.send(msg) response = s.recv() assert response == "", "response type is %s" % getMessageName(response[0]) time.sleep(1) s.close()
def _test_qreply(self, gen_qreply, good): print >> sys.stderr, "test: waiting for reply" s = self.openconn msg = s.recv() self.assert_(len(msg) > 0) print >> sys.stderr, "test: Received overlay message", getMessageName( msg[0]) self.assert_(msg[0] == QUERY) id = self.check_rquery(msg[1:]) resp = gen_qreply(id) print >> sys.stderr, "test: sending QUERY_REPLY" s.send(resp) if good: time.sleep(10) # the other side should not have closed the connection, as # this is all valid, so this should not throw an exception: s.send('bla') s.close() else: # the other side should not like this and close the connection self.assert_(len(s.recv()) == 0) s.close()
def handle_reply(self, permid, selversion, message): """ Received CRAWLER_REPLY message from OverlayApps """ if selversion >= OLPROTO_VER_SEVENTH and len(message) >= 5 and message[1] in self._message_handlers: message_id = message[1] channel_id = ord(message[2]) parts_left = ord(message[3]) error = ord(message[4]) # A request must exist in self._channels, otherwise we did # not request this reply if permid in self._channels and channel_id in self._channels[permid]: # add part to buffer self._channels[permid][channel_id][1] += message[5:] if parts_left: # todo: register some event to remove the buffer # after a time (in case connection is lost before # all parts are received) if DEBUG: print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(message), "bytes payload from", show_permid_short(permid), "with", parts_left, "parts left" # Can't do anything until all parts have been received return True else: timestamp, payload, channel_data = self._channels[permid].pop(channel_id) if DEBUG: if error == 253: # unknown message error (probably because # the crawler is newer than the peer) print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(message), "bytes payload from", show_permid_short(permid), "indicating an unknown message error" if error == 254: # frequency error (we did this request recently) print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(message), "bytes payload from", show_permid_short(permid), "indicating a frequency error" else: print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(payload), "bytes payload from", show_permid_short(permid) if not self._channels[permid]: del self._channels[permid] def send_request_helper(message_id, payload, frequency=3600, callback=None, channel_data=None): return self.send_request(permid, message_id, payload, frequency=frequency, callback=callback, channel_data=channel_data) # 20/10/08. Boudewijn: We will no longer # disconnect based on the return value from the # message handler try: self._message_handlers[message_id][1](permid, selversion, channel_id, channel_data, error, payload, send_request_helper) except: print_exc() return True else: # reply from unknown permid or channel if DEBUG: print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(payload), "bytes payload from", show_permid_short(permid), "from unknown peer or unused channel" if DEBUG: if len(message) >= 2: message_id = message[1] else: message_id = "" print >> sys.stderr, "crawler: received", getMessageName(CRAWLER_REPLY+message_id), "with", len(message), "bytes from", show_permid_short(permid), "from unknown peer or unused channel" return False
def _send_reply(self, permid, message_id, channel_id, payload, error=0, callback=None): """ Send a CRAWLER_REPLY message to permid. This method assumes that connection exists to the permid. @param permid The destination peer @param message_id The message id @param channel_id The channel id. Used to match replies to requests @param payload The message content @param error The error code. (0: no-error, 253: unknown-message, 254: frequency-error, 255: reserved) @param callback Callable function/method is called when request is send with 2 paramaters (exc, permid) @return The message channel-id > 0 on success, and 0 on failure """ # Sending a reply from a Tribler peer to a Crawler # SIZE INDEX # 1 byte: 0 CRAWLER_REPLY (from BaseLib.Core.BitTornado.BT1.MessageID) # 1 byte: 1 --MESSAGE-SPECIFIC-ID-- # 1 byte: 2 Channel id # 1 byte: 3 Parts left # 1 byte: 4 Indicating success (0) or failure (non 0) # n byte: 5... Reply payload if len(payload) > MAX_PAYLOAD_LENGTH: remaining_payload = payload[MAX_PAYLOAD_LENGTH:] def _after_send_reply(exc, permid): """ Called after the overlay attempted to send a reply message """ if DEBUG: print >> sys.stderr, "crawler: _after_send_reply", show_permid_short(permid), exc if not exc: self.send_reply(permid, message_id, channel_id, remaining_payload, error=error) # call the optional callback supplied with send_request if callback: callback(exc, permid) # 03/06/09 boudewijn: parts_left may be no larger than 255 # because we only use one byte to store the 'parts # left'. This does not mean that there can't be more than # 255 parts! parts_left = min(255, int(len(payload) / MAX_PAYLOAD_LENGTH)) payload = payload[:MAX_PAYLOAD_LENGTH] else: def _after_send_reply(exc, permid): if DEBUG: if exc: print >> sys.stderr, "crawler: could not send request", show_permid_short(permid), exc # call the optional callback supplied with send_request if callback: callback(exc, permid) parts_left = 0 # remove from self._channels if it is still there (could # have been remove during periodic timeout check) if permid in self._channels and channel_id in self._channels[permid]: del self._channels[permid][channel_id] if not self._channels[permid]: del self._channels[permid] if DEBUG: print >> sys.stderr, "crawler: sending", getMessageName(CRAWLER_REPLY+message_id), "with", len(payload), "bytes payload to", show_permid_short(permid) self._overlay_bridge.send(permid, "".join((CRAWLER_REPLY, message_id, chr(channel_id & 0xFF), chr(parts_left & 0xFF), chr(error & 0xFF), str(payload))), _after_send_reply) return channel_id
def olbridge_handle_msg_func(): # Called by OverlayThread if DEBUG: print >>sys.stderr,"olbridge: handle_msg_func",show_permid_short(permid),selversion,getMessageName(message[0]),currentThread().getName() try: if self.olappsmsghandler is None: ret = True else: ret = self.olappsmsghandler(permid,selversion,message) except: print_exc() ret = False if ret == False: if DEBUG: print >>sys.stderr,"olbridge: olbridge_handle_msg_func closing!",show_permid_short(permid),selversion,getMessageName(message[0]),currentThread().getName() self.close(permid)
def handleMessage(self,permid,selversion,message): """ Called by NetworkThread """ #ProxyService_ # # DEBUG #print "### olbridge: handleMessage", show_permid_short(permid), selversion, getMessageName(message[0]), currentThread().getName() # #_ProxyService if DEBUG: print >>sys.stderr,"olbridge: handleMessage",show_permid_short(permid),selversion,getMessageName(message[0]),currentThread().getName() def olbridge_handle_msg_func(): # Called by OverlayThread if DEBUG: print >>sys.stderr,"olbridge: handle_msg_func",show_permid_short(permid),selversion,getMessageName(message[0]),currentThread().getName() try: if self.olappsmsghandler is None: ret = True else: ret = self.olappsmsghandler(permid,selversion,message) except: print_exc() ret = False if ret == False: if DEBUG: print >>sys.stderr,"olbridge: olbridge_handle_msg_func closing!",show_permid_short(permid),selversion,getMessageName(message[0]),currentThread().getName() self.close(permid) self.tqueue.add_task(olbridge_handle_msg_func,0) return True
def handle_reply(self, permid, selversion, message): """ Received CRAWLER_REPLY message from OverlayApps """ if selversion >= OLPROTO_VER_SEVENTH and len( message) >= 5 and message[1] in self._message_handlers: message_id = message[1] channel_id = ord(message[2]) parts_left = ord(message[3]) error = ord(message[4]) # A request must exist in self._channels, otherwise we did # not request this reply if permid in self._channels and channel_id in self._channels[ permid]: # add part to buffer self._channels[permid][channel_id][1] += message[5:] if parts_left: # todo: register some event to remove the buffer # after a time (in case connection is lost before # all parts are received) if DEBUG: print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( message ), "bytes payload from", show_permid_short( permid), "with", parts_left, "parts left" # Can't do anything until all parts have been received return True else: timestamp, payload, channel_data = self._channels[ permid].pop(channel_id) if DEBUG: if error == 253: # unknown message error (probably because # the crawler is newer than the peer) print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( message ), "bytes payload from", show_permid_short( permid ), "indicating an unknown message error" if error == 254: # frequency error (we did this request recently) print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( message ), "bytes payload from", show_permid_short( permid), "indicating a frequency error" else: print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( payload ), "bytes payload from", show_permid_short( permid) if not self._channels[permid]: del self._channels[permid] def send_request_helper(message_id, payload, frequency=3600, callback=None, channel_data=None): return self.send_request(permid, message_id, payload, frequency=frequency, callback=callback, channel_data=channel_data) # 20/10/08. Boudewijn: We will no longer # disconnect based on the return value from the # message handler try: self._message_handlers[message_id][1]( permid, selversion, channel_id, channel_data, error, payload, send_request_helper) except: print_exc() return True else: # reply from unknown permid or channel if DEBUG: print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( payload), "bytes payload from", show_permid_short( permid), "from unknown peer or unused channel" if DEBUG: if len(message) >= 2: message_id = message[1] else: message_id = "" print >> sys.stderr, "crawler: received", getMessageName( CRAWLER_REPLY + message_id), "with", len( message), "bytes from", show_permid_short( permid), "from unknown peer or unused channel" return False
def _send_reply(self, permid, message_id, channel_id, payload, error=0, callback=None): """ Send a CRAWLER_REPLY message to permid. This method assumes that connection exists to the permid. @param permid The destination peer @param message_id The message id @param channel_id The channel id. Used to match replies to requests @param payload The message content @param error The error code. (0: no-error, 253: unknown-message, 254: frequency-error, 255: reserved) @param callback Callable function/method is called when request is send with 2 paramaters (exc, permid) @return The message channel-id > 0 on success, and 0 on failure """ # Sending a reply from a Tribler peer to a Crawler # SIZE INDEX # 1 byte: 0 CRAWLER_REPLY (from BaseLib.Core.BitTornado.BT1.MessageID) # 1 byte: 1 --MESSAGE-SPECIFIC-ID-- # 1 byte: 2 Channel id # 1 byte: 3 Parts left # 1 byte: 4 Indicating success (0) or failure (non 0) # n byte: 5... Reply payload if len(payload) > MAX_PAYLOAD_LENGTH: remaining_payload = payload[MAX_PAYLOAD_LENGTH:] def _after_send_reply(exc, permid): """ Called after the overlay attempted to send a reply message """ if DEBUG: print >> sys.stderr, "crawler: _after_send_reply", show_permid_short( permid), exc if not exc: self.send_reply(permid, message_id, channel_id, remaining_payload, error=error) # call the optional callback supplied with send_request if callback: callback(exc, permid) # 03/06/09 boudewijn: parts_left may be no larger than 255 # because we only use one byte to store the 'parts # left'. This does not mean that there can't be more than # 255 parts! parts_left = min(255, int(len(payload) / MAX_PAYLOAD_LENGTH)) payload = payload[:MAX_PAYLOAD_LENGTH] else: def _after_send_reply(exc, permid): if DEBUG: if exc: print >> sys.stderr, "crawler: could not send request", show_permid_short( permid), exc # call the optional callback supplied with send_request if callback: callback(exc, permid) parts_left = 0 # remove from self._channels if it is still there (could # have been remove during periodic timeout check) if permid in self._channels and channel_id in self._channels[ permid]: del self._channels[permid][channel_id] if not self._channels[permid]: del self._channels[permid] if DEBUG: print >> sys.stderr, "crawler: sending", getMessageName( CRAWLER_REPLY + message_id), "with", len( payload), "bytes payload to", show_permid_short(permid) self._overlay_bridge.send( permid, "".join( (CRAWLER_REPLY, message_id, chr(channel_id & 0xFF), chr(parts_left & 0xFF), chr(error & 0xFF), str(payload))), _after_send_reply) return channel_id