__init__.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. #! /usr/bin/python3
  2. #
  3. ## LICENSE
  4. # This file is part of DigiLib.
  5. #
  6. # DigiLib is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # DigiLib is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with DigiLib. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import logging
  20. import logging.handlers
  21. import os
  22. import queue
  23. import select
  24. import socket
  25. import sys
  26. import threading
  27. import time
  28. import traceback
  29. import trio
  30. lclient = logging.getLogger(__name__+".client")
  31. lserver = logging.getLogger(__name__+".server")
  32. lchat = logging.getLogger(__name__+".chat")
  33. class ConnHandlerBase(object):
  34. def __init__(self, socket, addr, server):
  35. self.status = "init"
  36. super(ConnHandlerBase, self).__init__()
  37. self.socket = socket
  38. self.addr = addr
  39. self.server = server
  40. self.block_size = 1024
  41. self.welcome_client()
  42. self.status="connected"
  43. def welcome_client(self):
  44. pass
  45. def handle(self, data_decoded):
  46. return
  47. def recv(self):
  48. try:
  49. data_received = self.socket.recv(self.block_size)
  50. data_decoded = data_received.decode("utf-8")
  51. if data_decoded:
  52. lchat.info(data_decoded.strip())
  53. self.handle(data_decoded)
  54. return True
  55. else:
  56. lserver.debug("connection corrupted")
  57. return False
  58. except Exception as e:
  59. lserver.error(e, exc_info=True)
  60. return False
  61. def send(self, msg):
  62. msg_encoded = bytes(msg, "utf-8")
  63. lchat.info("Server:"+msg)
  64. try:
  65. self.socket.send(msg_encoded)
  66. return True
  67. except Exception as e:
  68. lserver.error(e, exc_info=True)
  69. return False
  70. def close(self):
  71. self.status = "closed"
  72. try:
  73. self.socket.shutdown(0)
  74. except:
  75. lserver.error("error during socket shutdown, ignoring")
  76. try:
  77. self.socket.close()
  78. except:
  79. lserver.error("error closing socket, maybe already closed")
  80. class ConnHandlerEcho(ConnHandlerBase):
  81. def __init__(self, socket, addr, server):
  82. self.status = "init"
  83. self.super_class = super(ConnHandlerEcho, self)
  84. self.super_class.__init__(socket, addr, server)
  85. self.server = server
  86. def welcome_client(self):
  87. self.send("welcome to the client")
  88. def handle(self, data_decoded):
  89. lchat.info("Client:"+data_decoded)
  90. for h in list(set(self.server.connection_handler)-{self}):
  91. h.send(data_decoded)
  92. class Server(object):
  93. """docstring for SocketHandler"""
  94. def __init__(self,
  95. host,
  96. port=None,
  97. af_family="AF_INET",
  98. max_allowed_clients=5,
  99. handler=None,
  100. handler_kwargs={},
  101. ):
  102. super(Server, self).__init__()
  103. self.exit_event = False
  104. self.host=host
  105. self.port=port
  106. self.af_family = af_family
  107. self.handler_kwargs = handler_kwargs
  108. self.handler = handler
  109. self.max_allowed_clients = max_allowed_clients
  110. self.socket = self.make_socket()
  111. self.connection_handler = []
  112. self.conn_to_addr = {}
  113. self.addr_to_conn = {}
  114. self.conn_to_handler ={}
  115. self.handler_to_conn = {}
  116. self.read_sockets_expected = [self.socket]
  117. self.write_sockets_expected = []
  118. self.exc_sockets_expected = []
  119. def make_socket(self):
  120. lserver.debug("making a {} socket".format(self.af_family))
  121. if self.af_family == "AF_INET":
  122. return socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  123. elif self.af_family == "AF_UNIX":
  124. return socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  125. else:
  126. raise ValueError(
  127. "AF_FAMILY '{}' not supported!".format(
  128. self.af_family
  129. )
  130. )
  131. def make_handler(self, conn, addr):
  132. return self.handler(conn, addr, self, **self.handler_kwargs)
  133. def register_conn(self, conn, addr):
  134. lserver.info(
  135. "New Connection, addr: '{}', socket: '{}'".format(addr,conn)
  136. )
  137. self.read_sockets_expected.append(conn)
  138. if addr:
  139. self.conn_to_addr[conn] = addr
  140. self.addr_to_conn[addr] = conn
  141. def unregister_conn(self, conn):
  142. self.read_sockets_expected.remove(conn)
  143. addr = self.conn_to_addr.get(conn, False)
  144. if addr:
  145. del self.addr_to_conn[addr]
  146. del self.conn_to_addr[conn]
  147. def register_handler(self, handler, conn):
  148. self.connection_handler.append(handler)
  149. self.conn_to_handler[conn] = handler
  150. self.handler_to_conn[handler] = conn
  151. def unregister_handler(self, handler, conn):
  152. self.connection_handler.remove(handler)
  153. del self.conn_to_handler[conn]
  154. del self.handler_to_conn[handler]
  155. def setup(self):
  156. lserver.info("setting up socket")
  157. if self.af_family == "AF_INET":
  158. self.socket.bind((self.host, self.port))
  159. elif self.af_family == "AF_UNIX":
  160. if os.path.exists(self.host):
  161. lserver.debug("file already exists")
  162. lserver.debug("attempting to remove it")
  163. os.remove(self.host)
  164. self.socket.bind(self.host)
  165. self.socket.listen(self.max_allowed_clients)
  166. # self.socket.settimeout(1)
  167. def run(self):
  168. self.setup()
  169. lserver.debug("entering main loop")
  170. while ( not self.exit_event ):
  171. # lserver.debug(self.read_sockets_expected)
  172. # lserver.debug(self.write_sockets_expected)
  173. # lserver.debug(self.exc_sockets_expected)
  174. read_sockets_confirmed, \
  175. write_sockets_confirmed, \
  176. exc_sockets_confirmed \
  177. = select.select(self.read_sockets_expected,
  178. self.write_sockets_expected,
  179. self.exc_sockets_expected,
  180. )
  181. for s in read_sockets_confirmed:
  182. socket_handler = self.conn_to_handler.get(s, None)
  183. if ( s == self.socket ):
  184. lserver.debug("handling new client")
  185. conn, addr = self.socket.accept()
  186. handler = self.make_handler(conn, addr)
  187. self.register_conn(conn, addr)
  188. self.register_handler(handler, conn)
  189. elif ( socket_handler
  190. and (socket_handler in self.connection_handler) ):
  191. lserver.debug("handling client connection")
  192. try:
  193. if not socket_handler.recv():
  194. lserver.info("connection is broken, closing socket and removing it")
  195. self.unregister_handler(socket_handler, s)
  196. self.unregister_conn(conn)
  197. socket_handler.close()
  198. except Exception as e:
  199. lserver.error(e, exc_info=True)
  200. else:
  201. lserver.debug("else!")
  202. lserver.debug(socket_handler)
  203. time.sleep(1)
  204. def cleanup(self):
  205. pass
  206. class Client(threading.Thread):
  207. """docstring for Client"""
  208. is_connecting = False
  209. is_connected = False
  210. status = "uninitialized"
  211. def __init__(self,
  212. host,
  213. port=None,
  214. af_family="AF_INET",
  215. handle_data_func=None,
  216. error_handler=None,
  217. block_size=1024,
  218. ):
  219. self.super_class = super(Client, self)
  220. self.super_class.__init__()
  221. self.name = "Client"
  222. self.exit_event = False
  223. self.host = host
  224. self.port = port
  225. self.af_family = af_family
  226. self.block_size = block_size
  227. self.handle_data_func = handle_data_func
  228. self.is_connected = False
  229. self.error_handler = error_handler
  230. # self.socket = self.make_socket()
  231. self.socket = None
  232. self.status = "disconnected"
  233. def connect(self):
  234. self.status = "connecting"
  235. self.socket = self.make_socket()
  236. lclient.info(
  237. "connecting to socket '{}' of type {}".format(
  238. self.host,
  239. self.af_family
  240. )
  241. )
  242. try:
  243. if self.af_family == "AF_INET":
  244. self.socket.connect((self.host, self.port))
  245. elif self.af_family == "AF_UNIX":
  246. if os.path.exists(self.host):
  247. self.socket.connect(self.host)
  248. else:
  249. lclient.warn("File not found. Aborting.")
  250. return
  251. self.is_connected = True
  252. self.status = "connected"
  253. lclient.info("connected")
  254. return True
  255. except Exception as e:
  256. lclient.debug(e, exc_info=True)
  257. if type(e) is ConnectionRefusedError:
  258. lclient.info("failed to connect to socket '{}'".format(self.host))
  259. self.disconnect()
  260. return False
  261. def disconnect(self):
  262. lclient.info("disconnecting from socket '{}'".format(self.host))
  263. self.is_connected = False
  264. self.status = "disconnected"
  265. if self.socket:
  266. try:
  267. self.socket.shutdown(socket.SHUT_RDWR)
  268. except Exception as e:
  269. lclient.error(e)
  270. try:
  271. self.socket.close()
  272. except Exception as e:
  273. lclient.error("error occured while closing the socket, " +
  274. "maybe it is already closed",exc_info=e)
  275. del self.socket
  276. self.socket = None
  277. def handle_data(self, data_received):
  278. data_decoded = data_received.decode("utf-8")
  279. lchat.info("Server: "+data_decoded)
  280. if self.handle_data_func:
  281. try:
  282. self.handle_data_func(data_decoded)
  283. except Exception as e:
  284. lclient.error(e, exc_info=True)
  285. def is_running(self):
  286. return (self in threading.enumerate())
  287. def make_socket(self):
  288. lclient.info("creating a {} socket".format(self.af_family))
  289. if self.af_family == "AF_INET":
  290. s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  291. elif self.af_family == "AF_UNIX":
  292. s = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  293. else:
  294. raise ValueError(
  295. "AF_FAMILY '{}' not supported!".format(
  296. self.af_family
  297. )
  298. )
  299. return s
  300. def main_loop(self):
  301. lclient.debug("starting main loop")
  302. while ( not self.exit_event ):
  303. if not self.status in ["connected"]:
  304. time.sleep(0.1)
  305. continue
  306. # print(0)
  307. read_confirmed, write_confirmed, exc_confirmed \
  308. = select.select(
  309. [self.socket],
  310. [],
  311. [self.socket],
  312. 1
  313. )
  314. if self.socket in exc_confirmed:
  315. self.is_connected = False
  316. lclient.warning("socket is expected to corrupt, exiting")
  317. self.disconnect()
  318. # self.stop()
  319. break
  320. elif self.socket in read_confirmed:
  321. try:
  322. data_received = self.read_from_socket()
  323. if data_received == b'':
  324. lclient.info("connection is broken, closing socket exiting")
  325. self.disconnect()
  326. # self.stop()
  327. # break
  328. else:
  329. self.handle_data(data_received)
  330. except Exception as e:
  331. lclient.error(e, exc_info=True)
  332. if type(e) is OSError:
  333. self.is_connected = False
  334. lclient.warn("connection broken, exiting")
  335. self.disconnect()
  336. # self.stop()
  337. # break
  338. else:
  339. raise
  340. else:
  341. time.sleep(0.1)
  342. def read_from_socket(self):
  343. data_received = self.socket.recv(self.block_size)
  344. return data_received
  345. def run(self):
  346. # self.connect()
  347. if self.error_handler:
  348. self.error_handler(self.main_loop)
  349. else:
  350. self.main_loop()
  351. def send(self, msg):
  352. msg = msg.rstrip()
  353. msg_encoded = bytes(msg+"\r\n", "utf-8")
  354. try:
  355. lchat.info("Client: "+msg)
  356. self.socket.send(msg_encoded)
  357. except Exception as e:
  358. self.is_connected = False
  359. lclient.error(e, exc_info=True)
  360. self.status = "shutdown"
  361. def setup(self):
  362. pass
  363. def stop(self,reason=None):
  364. self.disconnect()
  365. self.exit_event = True
  366. if reason:
  367. print(reason)
  368. class AsyncClient(object):
  369. """docstring for Client"""
  370. is_connecting = False
  371. is_connected = False
  372. status = "uninitialized"
  373. def __init__(self,
  374. host,
  375. port=None,
  376. af_family="AF_INET",
  377. handle_data_func=None,
  378. error_handler=None,
  379. block_size=1024,
  380. ):
  381. self.super_class = super(AsyncClient, self)
  382. self.super_class.__init__()
  383. self.name = "Client"
  384. self.exit_event = False
  385. self.host = host
  386. self.port = port
  387. self.af_family = af_family
  388. self.block_size = block_size
  389. self.handle_data_func = handle_data_func
  390. self.is_connected = False
  391. self.error_handler = error_handler
  392. self.socket = None
  393. self.status = "disconnected"
  394. def connect(self):
  395. self.status = "connecting"
  396. self.socket = self.make_socket()
  397. lclient.info("connecting to socket '{}' of type {}".format(
  398. self.host,self.af_family))
  399. try:
  400. if self.af_family == "AF_INET":
  401. self.socket.connect((self.host, self.port))
  402. elif self.af_family == "AF_UNIX":
  403. self.socket.connect(self.host)
  404. # if os.path.exists(self.host):
  405. # pass
  406. # else:
  407. # lclient.warn("File not found. Aborting.")
  408. # return
  409. self.is_connected = True
  410. self.status = "connected"
  411. lclient.info("connected")
  412. return True
  413. except Exception as e:
  414. lclient.debug(e, exc_info=True)
  415. if type(e) is ConnectionRefusedError:
  416. lclient.info("failed to connect to socket '{}'".format(self.host))
  417. self.disconnect()
  418. return False
  419. def disconnect(self):
  420. lclient.info("disconnecting from socket '{}'".format(self.host))
  421. self.is_connected = False
  422. self.status = "disconnected"
  423. if self.socket:
  424. try:
  425. self.socket.shutdown(socket.SHUT_RDWR)
  426. except Exception as e:
  427. lclient.error(e)
  428. try:
  429. self.socket.close()
  430. except Exception as e:
  431. lclient.error("error occured while closing the socket, " +
  432. "maybe it is already closed",exc_info=e)
  433. del self.socket
  434. self.socket = None
  435. def handle_data(self, data_received):
  436. data_decoded = data_received.decode("utf-8")
  437. lchat.info("Server: "+data_decoded)
  438. if self.handle_data_func:
  439. try:
  440. self.handle_data_func(data_decoded)
  441. except Exception as e:
  442. lclient.error(e, exc_info=True)
  443. def is_running(self):
  444. return (self in threading.enumerate())
  445. def make_socket(self):
  446. lclient.info("creating a {} socket".format(self.af_family))
  447. if self.af_family == "AF_INET":
  448. s = trio.socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  449. elif self.af_family == "AF_UNIX":
  450. s = trio.socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  451. else:
  452. raise ValueError(
  453. "AF_FAMILY '{}' not supported!".format(
  454. self.af_family
  455. )
  456. )
  457. return s
  458. def main_loop(self):
  459. lclient.debug("starting main loop")
  460. while ( not self.exit_event ):
  461. if not self.status in ["connected"]:
  462. time.sleep(0.1)
  463. continue
  464. # print(0)
  465. read_confirmed, write_confirmed, exc_confirmed \
  466. = select.select(
  467. [self.socket],
  468. [],
  469. [self.socket],
  470. 1
  471. )
  472. if self.socket in exc_confirmed:
  473. self.is_connected = False
  474. lclient.warning("socket is expected to corrupt, exiting")
  475. self.disconnect()
  476. # self.stop()
  477. break
  478. elif self.socket in read_confirmed:
  479. try:
  480. data_received = self.read_from_socket()
  481. if data_received == b'':
  482. lclient.info("connection is broken, closing socket exiting")
  483. self.disconnect()
  484. # self.stop()
  485. # break
  486. else:
  487. self.handle_data(data_received)
  488. except Exception as e:
  489. lclient.error(e, exc_info=True)
  490. if type(e) is OSError:
  491. self.is_connected = False
  492. lclient.warn("connection broken, exiting")
  493. self.disconnect()
  494. # self.stop()
  495. # break
  496. else:
  497. raise
  498. else:
  499. time.sleep(0.1)
  500. def read_from_socket(self):
  501. data_received = self.socket.recv(self.block_size)
  502. return data_received
  503. def run(self,connect=False):
  504. if connect:
  505. self.connect()
  506. if self.error_handler:
  507. self.error_handler(self.main_loop)
  508. else:
  509. self.main_loop()
  510. def send(self, msg):
  511. msg = msg.rstrip()
  512. msg_encoded = bytes(msg+"\r\n", "utf-8")
  513. try:
  514. lchat.info("Client: "+msg)
  515. self.socket.send(msg_encoded)
  516. except Exception as e:
  517. self.is_connected = False
  518. lclient.error(e, exc_info=True)
  519. self.status = "shutdown"
  520. def setup(self):
  521. pass
  522. def stop(self,reason=None):
  523. self.disconnect()
  524. self.exit_event = True
  525. if reason:
  526. print(reason)
  527. #