__init__.py 21 KB

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