263 lines
9.6 KiB
Python
263 lines
9.6 KiB
Python
# uncompyle6 version 3.9.2
|
|
# Python bytecode version base 3.7.0 (3394)
|
|
# Decompiled from: Python 3.8.19 (default, Mar 20 2024, 15:27:52)
|
|
# [Clang 14.0.6 ]
|
|
# Embedded file name: /var/user/app/device_supervisorbak/device_supervisor/lib/Pyro4/socketserver/threadpoolserver.py
|
|
# Compiled at: 2024-04-18 03:12:55
|
|
# Size of source mod 2**32: 10057 bytes
|
|
"""
|
|
Socket server based on a worker thread pool. Doesn't use select.
|
|
|
|
Uses a single worker thread per client connection.
|
|
|
|
Pyro - Python Remote Objects. Copyright by Irmen de Jong (irmen@razorvine.net).
|
|
"""
|
|
from __future__ import print_function
|
|
import socket, logging, sys, time, threading, os
|
|
from Pyro4 import socketutil, errors, util
|
|
from Pyro4.configuration import config
|
|
from .threadpool import Pool, NoFreeWorkersError
|
|
from .multiplexserver import selectors
|
|
log = logging.getLogger("Pyro4.threadpoolserver")
|
|
_client_disconnect_lock = threading.Lock()
|
|
|
|
class ClientConnectionJob(object):
|
|
__doc__ = "\n Takes care of a single client connection and all requests\n that may arrive during its life span.\n "
|
|
|
|
def __init__(self, clientSocket, clientAddr, daemon):
|
|
self.csock = socketutil.SocketConnection(clientSocket)
|
|
self.caddr = clientAddr
|
|
self.daemon = daemon
|
|
|
|
def __call__(self):
|
|
if self.handleConnection():
|
|
try:
|
|
while True:
|
|
try:
|
|
self.daemon.handleRequest(self.csock)
|
|
except (socket.error, errors.ConnectionClosedError):
|
|
log.debug("disconnected %s", self.caddr)
|
|
break
|
|
except errors.SecurityError:
|
|
log.debug("security error on client %s", self.caddr)
|
|
break
|
|
except errors.TimeoutError as x:
|
|
try:
|
|
log.warning("error during handleRequest: %s" % x)
|
|
break
|
|
finally:
|
|
x = None
|
|
del x
|
|
|
|
except:
|
|
ex_t, ex_v, ex_tb = sys.exc_info()
|
|
tb = util.formatTraceback(ex_t, ex_v, ex_tb)
|
|
msg = "error during handleRequest: %s; %s" % (ex_v, "".join(tb))
|
|
log.warning(msg)
|
|
break
|
|
|
|
finally:
|
|
with _client_disconnect_lock:
|
|
try:
|
|
self.daemon._clientDisconnect(self.csock)
|
|
except Exception as x:
|
|
try:
|
|
log.warning("Error in clientDisconnect: " + str(x))
|
|
finally:
|
|
x = None
|
|
del x
|
|
|
|
self.csock.close()
|
|
|
|
def handleConnection(self):
|
|
try:
|
|
if self.daemon._handshake(self.csock):
|
|
return True
|
|
self.csock.close()
|
|
except:
|
|
ex_t, ex_v, ex_tb = sys.exc_info()
|
|
tb = util.formatTraceback(ex_t, ex_v, ex_tb)
|
|
log.warning("error during connect/handshake: %s; %s", ex_v, "\n".join(tb))
|
|
self.csock.close()
|
|
|
|
return False
|
|
|
|
def denyConnection(self, reason):
|
|
log.warning("client connection was denied: " + reason)
|
|
self.daemon._handshake((self.csock), denied_reason=reason)
|
|
self.csock.close()
|
|
|
|
|
|
class Housekeeper(threading.Thread):
|
|
|
|
def __init__(self, daemon):
|
|
super(Housekeeper, self).__init__(name="housekeeper")
|
|
self.pyroDaemon = daemon
|
|
self.stop = threading.Event()
|
|
self.daemon = True
|
|
self.waittime = min(config.POLLTIMEOUT or 0, max(config.COMMTIMEOUT or 0, 5))
|
|
|
|
def run(self):
|
|
while True:
|
|
if self.stop.wait(self.waittime):
|
|
break
|
|
self.pyroDaemon._housekeeping()
|
|
|
|
|
|
class SocketServer_Threadpool(object):
|
|
__doc__ = "transport server for socket connections, worker thread pool version."
|
|
|
|
def __init__(self):
|
|
self.daemon = self.sock = self._socketaddr = self.locationStr = self.pool = None
|
|
self.shutting_down = False
|
|
self.housekeeper = None
|
|
self._selector = selectors.DefaultSelector() if selectors else None
|
|
|
|
def init(self, daemon, host, port, unixsocket=None):
|
|
log.info("starting thread pool socketserver")
|
|
self.daemon = daemon
|
|
self.sock = None
|
|
bind_location = unixsocket if unixsocket else (host, port)
|
|
if config.SSL:
|
|
sslContext = socketutil.getSSLcontext(servercert=(config.SSL_SERVERCERT), serverkey=(config.SSL_SERVERKEY),
|
|
keypassword=(config.SSL_SERVERKEYPASSWD),
|
|
cacerts=(config.SSL_CACERTS))
|
|
log.info("using SSL, cert=%s key=%s cacerts=%s", config.SSL_SERVERCERT, config.SSL_SERVERKEY, config.SSL_CACERTS)
|
|
else:
|
|
sslContext = None
|
|
log.info("not using SSL")
|
|
self.sock = socketutil.createSocket(bind=bind_location, reuseaddr=(config.SOCK_REUSE),
|
|
timeout=(config.COMMTIMEOUT),
|
|
noinherit=True,
|
|
nodelay=(config.SOCK_NODELAY),
|
|
sslContext=sslContext)
|
|
self._socketaddr = self.sock.getsockname()
|
|
if not unixsocket:
|
|
if self._socketaddr[0].startswith("127."):
|
|
if (host is None or host.lower()) != "localhost" and not host.startswith("127."):
|
|
log.warning("weird DNS setup: %s resolves to localhost (127.x.x.x)", host)
|
|
elif unixsocket:
|
|
self.locationStr = "./u:" + unixsocket
|
|
else:
|
|
host = host or self._socketaddr[0]
|
|
port = port or self._socketaddr[1]
|
|
if ":" in host:
|
|
self.locationStr = "[%s]:%d" % (host, port)
|
|
else:
|
|
self.locationStr = "%s:%d" % (host, port)
|
|
self.pool = Pool()
|
|
self.housekeeper = Housekeeper(daemon)
|
|
self.housekeeper.start()
|
|
if self._selector:
|
|
self._selector.register(self.sock, selectors.EVENT_READ, self)
|
|
|
|
def __del__(self):
|
|
if self.sock is not None:
|
|
self.sock.close()
|
|
self.sock = None
|
|
if self.pool is not None:
|
|
self.pool.close()
|
|
self.pool = None
|
|
if self.housekeeper:
|
|
self.housekeeper.stop.set()
|
|
self.housekeeper.join()
|
|
self.housekeeper = None
|
|
|
|
def __repr__(self):
|
|
return "<%s on %s; %d workers>" % (self.__class__.__name__, self.locationStr, self.pool.num_workers())
|
|
|
|
def loop(self, loopCondition=(lambda: True)):
|
|
log.debug("threadpool server requestloop")
|
|
while self.sock is not None and not self.shutting_down:
|
|
if loopCondition():
|
|
try:
|
|
self.events([self.sock])
|
|
except (socket.error, OSError) as x:
|
|
try:
|
|
if not loopCondition():
|
|
break
|
|
err = getattr(x, "errno", x.args[0])
|
|
log.warning("socket error '%s' with errno=%d, shouldn't happen", x, err)
|
|
continue
|
|
finally:
|
|
x = None
|
|
del x
|
|
|
|
except KeyboardInterrupt:
|
|
log.debug("stopping on break signal")
|
|
break
|
|
|
|
def combine_loop(self, server):
|
|
raise TypeError("You can't use the loop combiner on the threadpool server type")
|
|
|
|
def events(self, eventsockets):
|
|
"""used for external event loops: handle events that occur on one of the sockets of this server"""
|
|
assert self.sock in eventsockets
|
|
try:
|
|
if self._selector:
|
|
events = self._selector.select(config.POLLTIMEOUT)
|
|
if not events:
|
|
return
|
|
else:
|
|
csock, caddr = self.sock.accept()
|
|
if self.shutting_down:
|
|
csock.close()
|
|
return
|
|
if hasattr(csock, "getpeercert"):
|
|
log.debug("connected %s - SSL", caddr)
|
|
else:
|
|
log.debug("connected %s - unencrypted", caddr)
|
|
if config.COMMTIMEOUT:
|
|
csock.settimeout(config.COMMTIMEOUT)
|
|
else:
|
|
job = ClientConnectionJob(csock, caddr, self.daemon)
|
|
try:
|
|
self.pool.process(job)
|
|
except NoFreeWorkersError:
|
|
job.denyConnection("no free workers, increase server threadpool size")
|
|
|
|
except socket.timeout:
|
|
pass
|
|
|
|
def shutdown(self):
|
|
self.shutting_down = True
|
|
self.wakeup()
|
|
time.sleep(0.05)
|
|
self.close()
|
|
self.sock = None
|
|
|
|
def close(self):
|
|
if self.housekeeper:
|
|
self.housekeeper.stop.set()
|
|
self.housekeeper.join()
|
|
self.housekeeper = None
|
|
if self.sock:
|
|
sockname = None
|
|
try:
|
|
sockname = self.sock.getsockname()
|
|
except (socket.error, OSError):
|
|
pass
|
|
|
|
try:
|
|
self.sock.close()
|
|
if type(sockname) is str:
|
|
if os.path.exists(sockname):
|
|
os.remove(sockname)
|
|
except Exception:
|
|
pass
|
|
|
|
self.sock = None
|
|
self.pool.close()
|
|
|
|
@property
|
|
def sockets(self):
|
|
return [
|
|
self.sock]
|
|
|
|
@property
|
|
def selector(self):
|
|
raise TypeError("threadpool server doesn't have multiplexing selector")
|
|
|
|
def wakeup(self):
|
|
socketutil.interruptSocket(self._socketaddr)
|