__init__.py 21 KB

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