2019-08-31 11:06:53 +02:00
|
|
|
"""
|
2019-11-03 13:09:00 +01:00
|
|
|
`BMConnectionPool` class definition
|
2019-08-31 11:06:53 +02:00
|
|
|
"""
|
2017-05-24 16:51:49 +02:00
|
|
|
import errno
|
2019-08-06 13:04:33 +02:00
|
|
|
import logging
|
2018-07-17 13:28:56 +02:00
|
|
|
import re
|
2017-05-24 16:51:49 +02:00
|
|
|
import socket
|
|
|
|
import time
|
|
|
|
|
2018-07-17 13:28:56 +02:00
|
|
|
import asyncore_pollchoose as asyncore
|
|
|
|
import helper_random
|
2018-10-10 17:42:58 +02:00
|
|
|
import knownnodes
|
2017-05-24 16:51:49 +02:00
|
|
|
import protocol
|
|
|
|
import state
|
2018-07-17 13:28:56 +02:00
|
|
|
from bmconfigparser import BMConfigParser
|
|
|
|
from connectionchooser import chooseConnection
|
|
|
|
from proxy import Proxy
|
|
|
|
from singleton import Singleton
|
|
|
|
from tcp import (
|
2019-07-20 10:41:49 +02:00
|
|
|
bootstrap, Socks4aBMConnection, Socks5BMConnection,
|
|
|
|
TCPConnection, TCPServer)
|
2018-07-17 13:28:56 +02:00
|
|
|
from udp import UDPSocket
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2019-08-06 13:04:33 +02:00
|
|
|
logger = logging.getLogger('default')
|
|
|
|
|
2018-10-10 17:42:58 +02:00
|
|
|
|
2017-05-24 16:51:49 +02:00
|
|
|
@Singleton
|
|
|
|
class BMConnectionPool(object):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Pool of all existing connections"""
|
2019-11-03 13:09:00 +01:00
|
|
|
# pylint: disable=too-many-instance-attributes
|
|
|
|
|
2017-05-24 16:51:49 +02:00
|
|
|
def __init__(self):
|
|
|
|
asyncore.set_rates(
|
2018-07-17 13:28:56 +02:00
|
|
|
BMConfigParser().safeGetInt(
|
|
|
|
"bitmessagesettings", "maxdownloadrate"),
|
|
|
|
BMConfigParser().safeGetInt(
|
|
|
|
"bitmessagesettings", "maxuploadrate")
|
|
|
|
)
|
2017-05-24 16:51:49 +02:00
|
|
|
self.outboundConnections = {}
|
|
|
|
self.inboundConnections = {}
|
|
|
|
self.listeningSockets = {}
|
2017-05-27 19:09:21 +02:00
|
|
|
self.udpSockets = {}
|
2017-05-24 16:51:49 +02:00
|
|
|
self.streams = []
|
2019-11-03 13:09:00 +01:00
|
|
|
self._lastSpawned = 0
|
|
|
|
self._spawnWait = 2
|
|
|
|
self._bootstrapped = False
|
|
|
|
|
|
|
|
def connections(self):
|
|
|
|
"""
|
|
|
|
Shortcut for combined list of connections from
|
|
|
|
`inboundConnections` and `outboundConnections` dicts
|
|
|
|
"""
|
|
|
|
return self.inboundConnections.values() + self.outboundConnections.values()
|
|
|
|
|
|
|
|
def establishedConnections(self):
|
|
|
|
"""Shortcut for list of connections having fullyEstablished == True"""
|
|
|
|
return [
|
|
|
|
x for x in self.connections() if x.fullyEstablished]
|
2017-05-24 16:51:49 +02:00
|
|
|
|
|
|
|
def connectToStream(self, streamNumber):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Connect to a bitmessage stream"""
|
2017-05-24 16:51:49 +02:00
|
|
|
self.streams.append(streamNumber)
|
|
|
|
|
2017-07-08 18:02:47 +02:00
|
|
|
def getConnectionByAddr(self, addr):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""
|
|
|
|
Return an (existing) connection object based on a `Peer` object
|
|
|
|
(IP and port)
|
|
|
|
"""
|
2018-07-17 13:28:56 +02:00
|
|
|
try:
|
2017-07-08 18:02:47 +02:00
|
|
|
return self.inboundConnections[addr]
|
2018-07-17 13:28:56 +02:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2018-01-02 22:23:03 +01:00
|
|
|
try:
|
2018-07-17 13:28:56 +02:00
|
|
|
return self.inboundConnections[addr.host]
|
|
|
|
except (KeyError, AttributeError):
|
2018-01-02 22:23:03 +01:00
|
|
|
pass
|
2018-07-17 13:28:56 +02:00
|
|
|
try:
|
2017-07-08 18:02:47 +02:00
|
|
|
return self.outboundConnections[addr]
|
2018-07-17 13:28:56 +02:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2018-01-02 22:23:03 +01:00
|
|
|
try:
|
2018-07-17 13:28:56 +02:00
|
|
|
return self.udpSockets[addr.host]
|
|
|
|
except (KeyError, AttributeError):
|
2018-01-02 22:23:03 +01:00
|
|
|
pass
|
2017-07-08 18:02:47 +02:00
|
|
|
raise KeyError
|
|
|
|
|
2017-07-10 07:10:05 +02:00
|
|
|
def isAlreadyConnected(self, nodeid):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Check if we're already connected to this peer"""
|
2019-11-03 13:09:00 +01:00
|
|
|
for i in self.connections():
|
2017-07-10 07:10:05 +02:00
|
|
|
try:
|
|
|
|
if nodeid == i.nodeid:
|
|
|
|
return True
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
return False
|
|
|
|
|
2017-05-24 16:51:49 +02:00
|
|
|
def addConnection(self, connection):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Add a connection object to our internal dict"""
|
2018-02-03 11:46:39 +01:00
|
|
|
if isinstance(connection, UDPSocket):
|
2017-05-27 19:09:21 +02:00
|
|
|
return
|
2017-05-24 16:51:49 +02:00
|
|
|
if connection.isOutbound:
|
|
|
|
self.outboundConnections[connection.destination] = connection
|
|
|
|
else:
|
|
|
|
if connection.destination.host in self.inboundConnections:
|
|
|
|
self.inboundConnections[connection.destination] = connection
|
|
|
|
else:
|
2018-07-17 13:28:56 +02:00
|
|
|
self.inboundConnections[connection.destination.host] = \
|
|
|
|
connection
|
2017-05-24 16:51:49 +02:00
|
|
|
|
|
|
|
def removeConnection(self, connection):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Remove a connection from our internal dict"""
|
2018-02-03 11:46:39 +01:00
|
|
|
if isinstance(connection, UDPSocket):
|
2017-06-11 14:11:39 +02:00
|
|
|
del self.udpSockets[connection.listening.host]
|
2018-02-03 11:46:39 +01:00
|
|
|
elif isinstance(connection, TCPServer):
|
2018-07-17 13:28:56 +02:00
|
|
|
del self.listeningSockets[state.Peer(
|
|
|
|
connection.destination.host, connection.destination.port)]
|
2017-05-27 19:09:21 +02:00
|
|
|
elif connection.isOutbound:
|
2017-05-24 16:51:49 +02:00
|
|
|
try:
|
|
|
|
del self.outboundConnections[connection.destination]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
del self.inboundConnections[connection.destination]
|
|
|
|
except KeyError:
|
|
|
|
try:
|
|
|
|
del self.inboundConnections[connection.destination.host]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2018-12-18 17:10:29 +01:00
|
|
|
connection.handle_close()
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2019-08-31 11:06:53 +02:00
|
|
|
@staticmethod
|
|
|
|
def getListeningIP():
|
2019-07-08 16:20:29 +02:00
|
|
|
"""What IP are we supposed to be listening on?"""
|
2018-07-17 13:28:56 +02:00
|
|
|
if BMConfigParser().safeGet(
|
|
|
|
"bitmessagesettings", "onionhostname").endswith(".onion"):
|
|
|
|
host = BMConfigParser().safeGet(
|
|
|
|
"bitmessagesettings", "onionbindip")
|
2017-05-24 16:51:49 +02:00
|
|
|
else:
|
|
|
|
host = '127.0.0.1'
|
2019-11-03 13:09:00 +01:00
|
|
|
if (
|
|
|
|
BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten")
|
|
|
|
or BMConfigParser().safeGet("bitmessagesettings", "socksproxytype")
|
|
|
|
== "none"
|
|
|
|
):
|
2017-05-27 19:09:21 +02:00
|
|
|
# python doesn't like bind + INADDR_ANY?
|
2018-07-17 13:28:56 +02:00
|
|
|
# host = socket.INADDR_ANY
|
2017-08-09 17:34:47 +02:00
|
|
|
host = BMConfigParser().get("network", "bind")
|
2017-05-27 19:09:21 +02:00
|
|
|
return host
|
|
|
|
|
2017-08-09 23:30:22 +02:00
|
|
|
def startListening(self, bind=None):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Open a listening socket and start accepting connections on it"""
|
2017-08-09 23:30:22 +02:00
|
|
|
if bind is None:
|
|
|
|
bind = self.getListeningIP()
|
2017-05-27 19:09:21 +02:00
|
|
|
port = BMConfigParser().safeGetInt("bitmessagesettings", "port")
|
2017-08-09 17:29:23 +02:00
|
|
|
# correct port even if it changed
|
2018-02-03 11:46:39 +01:00
|
|
|
ls = TCPServer(host=bind, port=port)
|
2017-08-09 17:29:23 +02:00
|
|
|
self.listeningSockets[ls.destination] = ls
|
2017-05-27 19:09:21 +02:00
|
|
|
|
|
|
|
def startUDPSocket(self, bind=None):
|
2019-07-08 16:20:29 +02:00
|
|
|
"""
|
|
|
|
Open an UDP socket. Depending on settings, it can either only
|
|
|
|
accept incoming UDP packets, or also be able to send them.
|
|
|
|
"""
|
2017-05-27 19:09:21 +02:00
|
|
|
if bind is None:
|
|
|
|
host = self.getListeningIP()
|
2018-02-03 11:46:39 +01:00
|
|
|
udpSocket = UDPSocket(host=host, announcing=True)
|
2017-05-27 19:09:21 +02:00
|
|
|
else:
|
2017-08-09 17:34:47 +02:00
|
|
|
if bind is False:
|
2018-02-03 11:46:39 +01:00
|
|
|
udpSocket = UDPSocket(announcing=False)
|
2017-08-09 17:34:47 +02:00
|
|
|
else:
|
2018-02-03 11:46:39 +01:00
|
|
|
udpSocket = UDPSocket(host=bind, announcing=True)
|
2017-06-11 14:11:39 +02:00
|
|
|
self.udpSockets[udpSocket.listening.host] = udpSocket
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2019-07-20 10:41:49 +02:00
|
|
|
def startBootstrappers(self):
|
|
|
|
"""Run the process of resolving bootstrap hostnames"""
|
|
|
|
proxy_type = BMConfigParser().safeGet(
|
|
|
|
'bitmessagesettings', 'socksproxytype')
|
|
|
|
# A plugins may be added here
|
2019-09-02 13:19:45 +02:00
|
|
|
hostname = None
|
2019-07-20 10:41:49 +02:00
|
|
|
if not proxy_type or proxy_type == 'none':
|
|
|
|
connection_base = TCPConnection
|
|
|
|
elif proxy_type == 'SOCKS5':
|
|
|
|
connection_base = Socks5BMConnection
|
2019-09-02 13:19:45 +02:00
|
|
|
hostname = helper_random.randomchoice([
|
|
|
|
'quzwelsuziwqgpt2.onion', None
|
|
|
|
])
|
2019-07-20 10:41:49 +02:00
|
|
|
elif proxy_type == 'SOCKS4a':
|
|
|
|
connection_base = Socks4aBMConnection # FIXME: I cannot test
|
|
|
|
else:
|
|
|
|
# This should never happen because socksproxytype setting
|
|
|
|
# is handled in bitmessagemain before starting the connectionpool
|
|
|
|
return
|
|
|
|
|
|
|
|
bootstrapper = bootstrap(connection_base)
|
2019-09-02 13:19:45 +02:00
|
|
|
if not hostname:
|
|
|
|
port = helper_random.randomchoice([8080, 8444])
|
|
|
|
hostname = 'bootstrap%s.bitmessage.org' % port
|
|
|
|
else:
|
|
|
|
port = 8444
|
2019-07-20 10:41:49 +02:00
|
|
|
self.addConnection(bootstrapper(hostname, port))
|
|
|
|
|
|
|
|
def loop(self): # pylint: disable=too-many-branches,too-many-statements
|
2019-07-08 16:20:29 +02:00
|
|
|
"""Main Connectionpool's loop"""
|
2017-05-24 16:51:49 +02:00
|
|
|
# defaults to empty loop if outbound connections are maxed
|
|
|
|
spawnConnections = False
|
|
|
|
acceptConnections = True
|
2018-07-17 13:28:56 +02:00
|
|
|
if BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'dontconnect'):
|
2017-05-24 16:51:49 +02:00
|
|
|
acceptConnections = False
|
2018-07-17 13:28:56 +02:00
|
|
|
elif BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'sendoutgoingconnections'):
|
2017-05-24 16:51:49 +02:00
|
|
|
spawnConnections = True
|
2019-05-28 15:28:35 +02:00
|
|
|
socksproxytype = BMConfigParser().safeGet(
|
|
|
|
'bitmessagesettings', 'socksproxytype', '')
|
|
|
|
onionsocksproxytype = BMConfigParser().safeGet(
|
|
|
|
'bitmessagesettings', 'onionsocksproxytype', '')
|
2019-11-03 13:09:00 +01:00
|
|
|
if (
|
|
|
|
socksproxytype[:5] == 'SOCKS'
|
|
|
|
and not BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'sockslisten')
|
|
|
|
and '.onion' not in BMConfigParser().safeGet(
|
|
|
|
'bitmessagesettings', 'onionhostname', '')
|
|
|
|
):
|
2017-05-24 16:51:49 +02:00
|
|
|
acceptConnections = False
|
|
|
|
|
2019-08-31 11:06:53 +02:00
|
|
|
# pylint: disable=too-many-nested-blocks
|
2017-05-24 16:51:49 +02:00
|
|
|
if spawnConnections:
|
2018-10-10 17:42:58 +02:00
|
|
|
if not knownnodes.knownNodesActual:
|
2019-07-20 10:41:49 +02:00
|
|
|
self.startBootstrappers()
|
|
|
|
knownnodes.knownNodesActual = True
|
2019-11-03 13:09:00 +01:00
|
|
|
if not self._bootstrapped:
|
|
|
|
self._bootstrapped = True
|
2018-07-17 13:28:56 +02:00
|
|
|
Proxy.proxy = (
|
|
|
|
BMConfigParser().safeGet(
|
2019-05-28 15:28:35 +02:00
|
|
|
'bitmessagesettings', 'sockshostname'),
|
2018-07-17 13:28:56 +02:00
|
|
|
BMConfigParser().safeGetInt(
|
2019-05-28 15:28:35 +02:00
|
|
|
'bitmessagesettings', 'socksport')
|
2018-07-17 13:28:56 +02:00
|
|
|
)
|
2018-02-04 21:03:54 +01:00
|
|
|
# TODO AUTH
|
|
|
|
# TODO reset based on GUI settings changes
|
|
|
|
try:
|
2019-05-28 15:28:35 +02:00
|
|
|
if not onionsocksproxytype.startswith("SOCKS"):
|
|
|
|
raise ValueError
|
|
|
|
Proxy.onion_proxy = (
|
|
|
|
BMConfigParser().safeGet(
|
|
|
|
'network', 'onionsockshostname', None),
|
|
|
|
BMConfigParser().safeGet(
|
|
|
|
'network', 'onionsocksport', None)
|
2018-07-17 13:28:56 +02:00
|
|
|
)
|
2019-05-28 15:28:35 +02:00
|
|
|
except ValueError:
|
|
|
|
Proxy.onion_proxy = None
|
2018-07-17 13:28:56 +02:00
|
|
|
established = sum(
|
|
|
|
1 for c in self.outboundConnections.values()
|
|
|
|
if (c.connected and c.fullyEstablished))
|
2017-05-25 23:04:33 +02:00
|
|
|
pending = len(self.outboundConnections) - established
|
2018-07-17 13:28:56 +02:00
|
|
|
if established < BMConfigParser().safeGetInt(
|
2019-05-28 15:28:35 +02:00
|
|
|
'bitmessagesettings', 'maxoutboundconnections'):
|
2018-07-17 13:28:56 +02:00
|
|
|
for i in range(
|
|
|
|
state.maximumNumberOfHalfOpenConnections - pending):
|
2017-07-05 09:17:01 +02:00
|
|
|
try:
|
2018-07-17 13:28:56 +02:00
|
|
|
chosen = chooseConnection(
|
|
|
|
helper_random.randomchoice(self.streams))
|
2017-07-05 09:17:01 +02:00
|
|
|
except ValueError:
|
|
|
|
continue
|
2017-05-25 23:04:33 +02:00
|
|
|
if chosen in self.outboundConnections:
|
|
|
|
continue
|
|
|
|
if chosen.host in self.inboundConnections:
|
2017-05-24 16:51:49 +02:00
|
|
|
continue
|
2017-05-31 00:04:21 +02:00
|
|
|
# don't connect to self
|
2017-05-31 00:22:07 +02:00
|
|
|
if chosen in state.ownAddresses:
|
2017-05-31 00:04:21 +02:00
|
|
|
continue
|
2018-07-17 13:28:56 +02:00
|
|
|
|
2017-05-25 23:04:33 +02:00
|
|
|
try:
|
2019-11-03 13:09:00 +01:00
|
|
|
if chosen.host.endswith(".onion") and Proxy.onion_proxy:
|
2019-05-28 15:28:35 +02:00
|
|
|
if onionsocksproxytype == "SOCKS5":
|
2018-02-04 21:03:54 +01:00
|
|
|
self.addConnection(Socks5BMConnection(chosen))
|
2019-05-28 15:28:35 +02:00
|
|
|
elif onionsocksproxytype == "SOCKS4a":
|
2018-02-04 21:03:54 +01:00
|
|
|
self.addConnection(Socks4aBMConnection(chosen))
|
2019-05-28 15:28:35 +02:00
|
|
|
elif socksproxytype == "SOCKS5":
|
2018-02-03 11:46:39 +01:00
|
|
|
self.addConnection(Socks5BMConnection(chosen))
|
2019-05-28 15:28:35 +02:00
|
|
|
elif socksproxytype == "SOCKS4a":
|
2018-02-03 11:46:39 +01:00
|
|
|
self.addConnection(Socks4aBMConnection(chosen))
|
2018-02-04 21:03:54 +01:00
|
|
|
else:
|
2018-02-03 11:46:39 +01:00
|
|
|
self.addConnection(TCPConnection(chosen))
|
2017-05-25 23:04:33 +02:00
|
|
|
except socket.error as e:
|
|
|
|
if e.errno == errno.ENETUNREACH:
|
|
|
|
continue
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2019-11-03 13:09:00 +01:00
|
|
|
self._lastSpawned = time.time()
|
2018-01-30 12:55:01 +01:00
|
|
|
else:
|
2019-11-03 13:09:00 +01:00
|
|
|
for i in self.connections():
|
2018-01-30 12:55:01 +01:00
|
|
|
# FIXME: rating will be increased after next connection
|
|
|
|
i.handle_close()
|
2017-05-29 12:56:59 +02:00
|
|
|
|
2017-06-24 12:23:16 +02:00
|
|
|
if acceptConnections:
|
|
|
|
if not self.listeningSockets:
|
2019-05-28 15:28:35 +02:00
|
|
|
if BMConfigParser().safeGet('network', 'bind') == '':
|
2017-08-09 23:30:22 +02:00
|
|
|
self.startListening()
|
|
|
|
else:
|
2018-07-17 13:28:56 +02:00
|
|
|
for bind in re.sub(
|
2019-11-03 13:09:00 +01:00
|
|
|
r'[^\w.]+', ' ',
|
|
|
|
BMConfigParser().safeGet('network', 'bind')
|
2018-07-17 13:28:56 +02:00
|
|
|
).split():
|
2017-08-09 23:30:22 +02:00
|
|
|
self.startListening(bind)
|
2017-06-24 12:23:16 +02:00
|
|
|
logger.info('Listening for incoming connections.')
|
|
|
|
if not self.udpSockets:
|
2019-05-28 15:28:35 +02:00
|
|
|
if BMConfigParser().safeGet('network', 'bind') == '':
|
2017-06-24 12:23:16 +02:00
|
|
|
self.startUDPSocket()
|
|
|
|
else:
|
2018-07-17 13:28:56 +02:00
|
|
|
for bind in re.sub(
|
2019-11-03 13:09:00 +01:00
|
|
|
r'[^\w.]+', ' ',
|
|
|
|
BMConfigParser().safeGet('network', 'bind')
|
2018-07-17 13:28:56 +02:00
|
|
|
).split():
|
2017-06-24 12:23:16 +02:00
|
|
|
self.startUDPSocket(bind)
|
2017-08-09 17:34:47 +02:00
|
|
|
self.startUDPSocket(False)
|
2017-06-24 12:23:16 +02:00
|
|
|
logger.info('Starting UDP socket(s).')
|
|
|
|
else:
|
|
|
|
if self.listeningSockets:
|
2017-08-09 17:34:47 +02:00
|
|
|
for i in self.listeningSockets.values():
|
2017-11-17 13:37:51 +01:00
|
|
|
i.close_reason = "Stopping listening"
|
|
|
|
i.accepting = i.connecting = i.connected = False
|
2017-06-24 12:23:16 +02:00
|
|
|
logger.info('Stopped listening for incoming connections.')
|
|
|
|
if self.udpSockets:
|
2017-08-09 17:34:47 +02:00
|
|
|
for i in self.udpSockets.values():
|
2017-11-17 13:37:51 +01:00
|
|
|
i.close_reason = "Stopping UDP socket"
|
|
|
|
i.accepting = i.connecting = i.connected = False
|
2017-06-24 12:23:16 +02:00
|
|
|
logger.info('Stopped udp sockets.')
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2019-11-03 13:09:00 +01:00
|
|
|
loopTime = float(self._spawnWait)
|
|
|
|
if self._lastSpawned < time.time() - self._spawnWait:
|
2017-07-06 19:45:36 +02:00
|
|
|
loopTime = 2.0
|
|
|
|
asyncore.loop(timeout=loopTime, count=1000)
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2017-05-30 23:53:43 +02:00
|
|
|
reaper = []
|
2019-11-03 13:09:00 +01:00
|
|
|
for i in self.connections():
|
2017-05-24 16:51:49 +02:00
|
|
|
minTx = time.time() - 20
|
2017-05-25 23:04:33 +02:00
|
|
|
if i.fullyEstablished:
|
2017-05-24 16:51:49 +02:00
|
|
|
minTx -= 300 - 20
|
|
|
|
if i.lastTx < minTx:
|
2017-05-25 23:04:33 +02:00
|
|
|
if i.fullyEstablished:
|
2017-07-06 19:45:36 +02:00
|
|
|
i.append_write_buf(protocol.CreatePacket('ping'))
|
2017-05-25 14:59:18 +02:00
|
|
|
else:
|
2018-07-17 13:28:56 +02:00
|
|
|
i.close_reason = "Timeout (%is)" % (
|
|
|
|
time.time() - i.lastTx)
|
2017-11-14 23:43:05 +01:00
|
|
|
i.set_state("close")
|
2018-07-17 13:28:56 +02:00
|
|
|
for i in (
|
2019-11-03 13:09:00 +01:00
|
|
|
self.connections()
|
|
|
|
+ self.listeningSockets.values() + self.udpSockets.values()
|
2018-07-17 13:28:56 +02:00
|
|
|
):
|
2017-05-30 23:53:43 +02:00
|
|
|
if not (i.accepting or i.connecting or i.connected):
|
|
|
|
reaper.append(i)
|
2017-11-17 19:50:39 +01:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
if i.state == "close":
|
|
|
|
reaper.append(i)
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2017-05-30 23:53:43 +02:00
|
|
|
for i in reaper:
|
|
|
|
self.removeConnection(i)
|