17db96d56Sopenharmony_ci# -*- Mode: Python -*- 27db96d56Sopenharmony_ci# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp 37db96d56Sopenharmony_ci# Author: Sam Rushing <rushing@nightmare.com> 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ci# ====================================================================== 67db96d56Sopenharmony_ci# Copyright 1996 by Sam Rushing 77db96d56Sopenharmony_ci# 87db96d56Sopenharmony_ci# All Rights Reserved 97db96d56Sopenharmony_ci# 107db96d56Sopenharmony_ci# Permission to use, copy, modify, and distribute this software and 117db96d56Sopenharmony_ci# its documentation for any purpose and without fee is hereby 127db96d56Sopenharmony_ci# granted, provided that the above copyright notice appear in all 137db96d56Sopenharmony_ci# copies and that both that copyright notice and this permission 147db96d56Sopenharmony_ci# notice appear in supporting documentation, and that the name of Sam 157db96d56Sopenharmony_ci# Rushing not be used in advertising or publicity pertaining to 167db96d56Sopenharmony_ci# distribution of the software without specific, written prior 177db96d56Sopenharmony_ci# permission. 187db96d56Sopenharmony_ci# 197db96d56Sopenharmony_ci# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 207db96d56Sopenharmony_ci# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 217db96d56Sopenharmony_ci# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR 227db96d56Sopenharmony_ci# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 237db96d56Sopenharmony_ci# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 247db96d56Sopenharmony_ci# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 257db96d56Sopenharmony_ci# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 267db96d56Sopenharmony_ci# ====================================================================== 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ci"""Basic infrastructure for asynchronous socket service clients and servers. 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ciThere are only two ways to have a program on a single processor do "more 317db96d56Sopenharmony_cithan one thing at a time". Multi-threaded programming is the simplest and 327db96d56Sopenharmony_cimost popular way to do it, but there is another very different technique, 337db96d56Sopenharmony_cithat lets you have nearly all the advantages of multi-threading, without 347db96d56Sopenharmony_ciactually using multiple threads. it's really only practical if your program 357db96d56Sopenharmony_ciis largely I/O bound. If your program is CPU bound, then pre-emptive 367db96d56Sopenharmony_cischeduled threads are probably what you really need. Network servers are 377db96d56Sopenharmony_cirarely CPU-bound, however. 387db96d56Sopenharmony_ci 397db96d56Sopenharmony_ciIf your operating system supports the select() system call in its I/O 407db96d56Sopenharmony_cilibrary (and nearly all do), then you can use it to juggle multiple 417db96d56Sopenharmony_cicommunication channels at once; doing other work while your I/O is taking 427db96d56Sopenharmony_ciplace in the "background." Although this strategy can seem strange and 437db96d56Sopenharmony_cicomplex, especially at first, it is in many ways easier to understand and 447db96d56Sopenharmony_cicontrol than multi-threaded programming. The module documented here solves 457db96d56Sopenharmony_cimany of the difficult problems for you, making the task of building 467db96d56Sopenharmony_cisophisticated high-performance network servers and clients a snap. 477db96d56Sopenharmony_ci""" 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ciimport select 507db96d56Sopenharmony_ciimport socket 517db96d56Sopenharmony_ciimport sys 527db96d56Sopenharmony_ciimport time 537db96d56Sopenharmony_ciimport warnings 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ciimport os 567db96d56Sopenharmony_cifrom errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ 577db96d56Sopenharmony_ci ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ 587db96d56Sopenharmony_ci errorcode 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci_DEPRECATION_MSG = ('The {name} module is deprecated and will be removed in ' 617db96d56Sopenharmony_ci 'Python {remove}. The recommended replacement is asyncio') 627db96d56Sopenharmony_ciwarnings._deprecated(__name__, _DEPRECATION_MSG, remove=(3, 12)) 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, 667db96d56Sopenharmony_ci EBADF}) 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_citry: 697db96d56Sopenharmony_ci socket_map 707db96d56Sopenharmony_ciexcept NameError: 717db96d56Sopenharmony_ci socket_map = {} 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_cidef _strerror(err): 747db96d56Sopenharmony_ci try: 757db96d56Sopenharmony_ci return os.strerror(err) 767db96d56Sopenharmony_ci except (ValueError, OverflowError, NameError): 777db96d56Sopenharmony_ci if err in errorcode: 787db96d56Sopenharmony_ci return errorcode[err] 797db96d56Sopenharmony_ci return "Unknown error %s" %err 807db96d56Sopenharmony_ci 817db96d56Sopenharmony_ciclass ExitNow(Exception): 827db96d56Sopenharmony_ci pass 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ci_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_cidef read(obj): 877db96d56Sopenharmony_ci try: 887db96d56Sopenharmony_ci obj.handle_read_event() 897db96d56Sopenharmony_ci except _reraised_exceptions: 907db96d56Sopenharmony_ci raise 917db96d56Sopenharmony_ci except: 927db96d56Sopenharmony_ci obj.handle_error() 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_cidef write(obj): 957db96d56Sopenharmony_ci try: 967db96d56Sopenharmony_ci obj.handle_write_event() 977db96d56Sopenharmony_ci except _reraised_exceptions: 987db96d56Sopenharmony_ci raise 997db96d56Sopenharmony_ci except: 1007db96d56Sopenharmony_ci obj.handle_error() 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_cidef _exception(obj): 1037db96d56Sopenharmony_ci try: 1047db96d56Sopenharmony_ci obj.handle_expt_event() 1057db96d56Sopenharmony_ci except _reraised_exceptions: 1067db96d56Sopenharmony_ci raise 1077db96d56Sopenharmony_ci except: 1087db96d56Sopenharmony_ci obj.handle_error() 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_cidef readwrite(obj, flags): 1117db96d56Sopenharmony_ci try: 1127db96d56Sopenharmony_ci if flags & select.POLLIN: 1137db96d56Sopenharmony_ci obj.handle_read_event() 1147db96d56Sopenharmony_ci if flags & select.POLLOUT: 1157db96d56Sopenharmony_ci obj.handle_write_event() 1167db96d56Sopenharmony_ci if flags & select.POLLPRI: 1177db96d56Sopenharmony_ci obj.handle_expt_event() 1187db96d56Sopenharmony_ci if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): 1197db96d56Sopenharmony_ci obj.handle_close() 1207db96d56Sopenharmony_ci except OSError as e: 1217db96d56Sopenharmony_ci if e.errno not in _DISCONNECTED: 1227db96d56Sopenharmony_ci obj.handle_error() 1237db96d56Sopenharmony_ci else: 1247db96d56Sopenharmony_ci obj.handle_close() 1257db96d56Sopenharmony_ci except _reraised_exceptions: 1267db96d56Sopenharmony_ci raise 1277db96d56Sopenharmony_ci except: 1287db96d56Sopenharmony_ci obj.handle_error() 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_cidef poll(timeout=0.0, map=None): 1317db96d56Sopenharmony_ci if map is None: 1327db96d56Sopenharmony_ci map = socket_map 1337db96d56Sopenharmony_ci if map: 1347db96d56Sopenharmony_ci r = []; w = []; e = [] 1357db96d56Sopenharmony_ci for fd, obj in list(map.items()): 1367db96d56Sopenharmony_ci is_r = obj.readable() 1377db96d56Sopenharmony_ci is_w = obj.writable() 1387db96d56Sopenharmony_ci if is_r: 1397db96d56Sopenharmony_ci r.append(fd) 1407db96d56Sopenharmony_ci # accepting sockets should not be writable 1417db96d56Sopenharmony_ci if is_w and not obj.accepting: 1427db96d56Sopenharmony_ci w.append(fd) 1437db96d56Sopenharmony_ci if is_r or is_w: 1447db96d56Sopenharmony_ci e.append(fd) 1457db96d56Sopenharmony_ci if [] == r == w == e: 1467db96d56Sopenharmony_ci time.sleep(timeout) 1477db96d56Sopenharmony_ci return 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci r, w, e = select.select(r, w, e, timeout) 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci for fd in r: 1527db96d56Sopenharmony_ci obj = map.get(fd) 1537db96d56Sopenharmony_ci if obj is None: 1547db96d56Sopenharmony_ci continue 1557db96d56Sopenharmony_ci read(obj) 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci for fd in w: 1587db96d56Sopenharmony_ci obj = map.get(fd) 1597db96d56Sopenharmony_ci if obj is None: 1607db96d56Sopenharmony_ci continue 1617db96d56Sopenharmony_ci write(obj) 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci for fd in e: 1647db96d56Sopenharmony_ci obj = map.get(fd) 1657db96d56Sopenharmony_ci if obj is None: 1667db96d56Sopenharmony_ci continue 1677db96d56Sopenharmony_ci _exception(obj) 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_cidef poll2(timeout=0.0, map=None): 1707db96d56Sopenharmony_ci # Use the poll() support added to the select module in Python 2.0 1717db96d56Sopenharmony_ci if map is None: 1727db96d56Sopenharmony_ci map = socket_map 1737db96d56Sopenharmony_ci if timeout is not None: 1747db96d56Sopenharmony_ci # timeout is in milliseconds 1757db96d56Sopenharmony_ci timeout = int(timeout*1000) 1767db96d56Sopenharmony_ci pollster = select.poll() 1777db96d56Sopenharmony_ci if map: 1787db96d56Sopenharmony_ci for fd, obj in list(map.items()): 1797db96d56Sopenharmony_ci flags = 0 1807db96d56Sopenharmony_ci if obj.readable(): 1817db96d56Sopenharmony_ci flags |= select.POLLIN | select.POLLPRI 1827db96d56Sopenharmony_ci # accepting sockets should not be writable 1837db96d56Sopenharmony_ci if obj.writable() and not obj.accepting: 1847db96d56Sopenharmony_ci flags |= select.POLLOUT 1857db96d56Sopenharmony_ci if flags: 1867db96d56Sopenharmony_ci pollster.register(fd, flags) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci r = pollster.poll(timeout) 1897db96d56Sopenharmony_ci for fd, flags in r: 1907db96d56Sopenharmony_ci obj = map.get(fd) 1917db96d56Sopenharmony_ci if obj is None: 1927db96d56Sopenharmony_ci continue 1937db96d56Sopenharmony_ci readwrite(obj, flags) 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_cipoll3 = poll2 # Alias for backward compatibility 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_cidef loop(timeout=30.0, use_poll=False, map=None, count=None): 1987db96d56Sopenharmony_ci if map is None: 1997db96d56Sopenharmony_ci map = socket_map 2007db96d56Sopenharmony_ci 2017db96d56Sopenharmony_ci if use_poll and hasattr(select, 'poll'): 2027db96d56Sopenharmony_ci poll_fun = poll2 2037db96d56Sopenharmony_ci else: 2047db96d56Sopenharmony_ci poll_fun = poll 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci if count is None: 2077db96d56Sopenharmony_ci while map: 2087db96d56Sopenharmony_ci poll_fun(timeout, map) 2097db96d56Sopenharmony_ci 2107db96d56Sopenharmony_ci else: 2117db96d56Sopenharmony_ci while map and count > 0: 2127db96d56Sopenharmony_ci poll_fun(timeout, map) 2137db96d56Sopenharmony_ci count = count - 1 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ciclass dispatcher: 2167db96d56Sopenharmony_ci 2177db96d56Sopenharmony_ci debug = False 2187db96d56Sopenharmony_ci connected = False 2197db96d56Sopenharmony_ci accepting = False 2207db96d56Sopenharmony_ci connecting = False 2217db96d56Sopenharmony_ci closing = False 2227db96d56Sopenharmony_ci addr = None 2237db96d56Sopenharmony_ci ignore_log_types = frozenset({'warning'}) 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ci def __init__(self, sock=None, map=None): 2267db96d56Sopenharmony_ci if map is None: 2277db96d56Sopenharmony_ci self._map = socket_map 2287db96d56Sopenharmony_ci else: 2297db96d56Sopenharmony_ci self._map = map 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci self._fileno = None 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci if sock: 2347db96d56Sopenharmony_ci # Set to nonblocking just to make sure for cases where we 2357db96d56Sopenharmony_ci # get a socket from a blocking source. 2367db96d56Sopenharmony_ci sock.setblocking(False) 2377db96d56Sopenharmony_ci self.set_socket(sock, map) 2387db96d56Sopenharmony_ci self.connected = True 2397db96d56Sopenharmony_ci # The constructor no longer requires that the socket 2407db96d56Sopenharmony_ci # passed be connected. 2417db96d56Sopenharmony_ci try: 2427db96d56Sopenharmony_ci self.addr = sock.getpeername() 2437db96d56Sopenharmony_ci except OSError as err: 2447db96d56Sopenharmony_ci if err.errno in (ENOTCONN, EINVAL): 2457db96d56Sopenharmony_ci # To handle the case where we got an unconnected 2467db96d56Sopenharmony_ci # socket. 2477db96d56Sopenharmony_ci self.connected = False 2487db96d56Sopenharmony_ci else: 2497db96d56Sopenharmony_ci # The socket is broken in some unknown way, alert 2507db96d56Sopenharmony_ci # the user and remove it from the map (to prevent 2517db96d56Sopenharmony_ci # polling of broken sockets). 2527db96d56Sopenharmony_ci self.del_channel(map) 2537db96d56Sopenharmony_ci raise 2547db96d56Sopenharmony_ci else: 2557db96d56Sopenharmony_ci self.socket = None 2567db96d56Sopenharmony_ci 2577db96d56Sopenharmony_ci def __repr__(self): 2587db96d56Sopenharmony_ci status = [self.__class__.__module__+"."+self.__class__.__qualname__] 2597db96d56Sopenharmony_ci if self.accepting and self.addr: 2607db96d56Sopenharmony_ci status.append('listening') 2617db96d56Sopenharmony_ci elif self.connected: 2627db96d56Sopenharmony_ci status.append('connected') 2637db96d56Sopenharmony_ci if self.addr is not None: 2647db96d56Sopenharmony_ci try: 2657db96d56Sopenharmony_ci status.append('%s:%d' % self.addr) 2667db96d56Sopenharmony_ci except TypeError: 2677db96d56Sopenharmony_ci status.append(repr(self.addr)) 2687db96d56Sopenharmony_ci return '<%s at %#x>' % (' '.join(status), id(self)) 2697db96d56Sopenharmony_ci 2707db96d56Sopenharmony_ci def add_channel(self, map=None): 2717db96d56Sopenharmony_ci #self.log_info('adding channel %s' % self) 2727db96d56Sopenharmony_ci if map is None: 2737db96d56Sopenharmony_ci map = self._map 2747db96d56Sopenharmony_ci map[self._fileno] = self 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ci def del_channel(self, map=None): 2777db96d56Sopenharmony_ci fd = self._fileno 2787db96d56Sopenharmony_ci if map is None: 2797db96d56Sopenharmony_ci map = self._map 2807db96d56Sopenharmony_ci if fd in map: 2817db96d56Sopenharmony_ci #self.log_info('closing channel %d:%s' % (fd, self)) 2827db96d56Sopenharmony_ci del map[fd] 2837db96d56Sopenharmony_ci self._fileno = None 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_ci def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): 2867db96d56Sopenharmony_ci self.family_and_type = family, type 2877db96d56Sopenharmony_ci sock = socket.socket(family, type) 2887db96d56Sopenharmony_ci sock.setblocking(False) 2897db96d56Sopenharmony_ci self.set_socket(sock) 2907db96d56Sopenharmony_ci 2917db96d56Sopenharmony_ci def set_socket(self, sock, map=None): 2927db96d56Sopenharmony_ci self.socket = sock 2937db96d56Sopenharmony_ci self._fileno = sock.fileno() 2947db96d56Sopenharmony_ci self.add_channel(map) 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_ci def set_reuse_addr(self): 2977db96d56Sopenharmony_ci # try to re-use a server port if possible 2987db96d56Sopenharmony_ci try: 2997db96d56Sopenharmony_ci self.socket.setsockopt( 3007db96d56Sopenharmony_ci socket.SOL_SOCKET, socket.SO_REUSEADDR, 3017db96d56Sopenharmony_ci self.socket.getsockopt(socket.SOL_SOCKET, 3027db96d56Sopenharmony_ci socket.SO_REUSEADDR) | 1 3037db96d56Sopenharmony_ci ) 3047db96d56Sopenharmony_ci except OSError: 3057db96d56Sopenharmony_ci pass 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_ci # ================================================== 3087db96d56Sopenharmony_ci # predicates for select() 3097db96d56Sopenharmony_ci # these are used as filters for the lists of sockets 3107db96d56Sopenharmony_ci # to pass to select(). 3117db96d56Sopenharmony_ci # ================================================== 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci def readable(self): 3147db96d56Sopenharmony_ci return True 3157db96d56Sopenharmony_ci 3167db96d56Sopenharmony_ci def writable(self): 3177db96d56Sopenharmony_ci return True 3187db96d56Sopenharmony_ci 3197db96d56Sopenharmony_ci # ================================================== 3207db96d56Sopenharmony_ci # socket object methods. 3217db96d56Sopenharmony_ci # ================================================== 3227db96d56Sopenharmony_ci 3237db96d56Sopenharmony_ci def listen(self, num): 3247db96d56Sopenharmony_ci self.accepting = True 3257db96d56Sopenharmony_ci if os.name == 'nt' and num > 5: 3267db96d56Sopenharmony_ci num = 5 3277db96d56Sopenharmony_ci return self.socket.listen(num) 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci def bind(self, addr): 3307db96d56Sopenharmony_ci self.addr = addr 3317db96d56Sopenharmony_ci return self.socket.bind(addr) 3327db96d56Sopenharmony_ci 3337db96d56Sopenharmony_ci def connect(self, address): 3347db96d56Sopenharmony_ci self.connected = False 3357db96d56Sopenharmony_ci self.connecting = True 3367db96d56Sopenharmony_ci err = self.socket.connect_ex(address) 3377db96d56Sopenharmony_ci if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ 3387db96d56Sopenharmony_ci or err == EINVAL and os.name == 'nt': 3397db96d56Sopenharmony_ci self.addr = address 3407db96d56Sopenharmony_ci return 3417db96d56Sopenharmony_ci if err in (0, EISCONN): 3427db96d56Sopenharmony_ci self.addr = address 3437db96d56Sopenharmony_ci self.handle_connect_event() 3447db96d56Sopenharmony_ci else: 3457db96d56Sopenharmony_ci raise OSError(err, errorcode[err]) 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci def accept(self): 3487db96d56Sopenharmony_ci # XXX can return either an address pair or None 3497db96d56Sopenharmony_ci try: 3507db96d56Sopenharmony_ci conn, addr = self.socket.accept() 3517db96d56Sopenharmony_ci except TypeError: 3527db96d56Sopenharmony_ci return None 3537db96d56Sopenharmony_ci except OSError as why: 3547db96d56Sopenharmony_ci if why.errno in (EWOULDBLOCK, ECONNABORTED, EAGAIN): 3557db96d56Sopenharmony_ci return None 3567db96d56Sopenharmony_ci else: 3577db96d56Sopenharmony_ci raise 3587db96d56Sopenharmony_ci else: 3597db96d56Sopenharmony_ci return conn, addr 3607db96d56Sopenharmony_ci 3617db96d56Sopenharmony_ci def send(self, data): 3627db96d56Sopenharmony_ci try: 3637db96d56Sopenharmony_ci result = self.socket.send(data) 3647db96d56Sopenharmony_ci return result 3657db96d56Sopenharmony_ci except OSError as why: 3667db96d56Sopenharmony_ci if why.errno == EWOULDBLOCK: 3677db96d56Sopenharmony_ci return 0 3687db96d56Sopenharmony_ci elif why.errno in _DISCONNECTED: 3697db96d56Sopenharmony_ci self.handle_close() 3707db96d56Sopenharmony_ci return 0 3717db96d56Sopenharmony_ci else: 3727db96d56Sopenharmony_ci raise 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci def recv(self, buffer_size): 3757db96d56Sopenharmony_ci try: 3767db96d56Sopenharmony_ci data = self.socket.recv(buffer_size) 3777db96d56Sopenharmony_ci if not data: 3787db96d56Sopenharmony_ci # a closed connection is indicated by signaling 3797db96d56Sopenharmony_ci # a read condition, and having recv() return 0. 3807db96d56Sopenharmony_ci self.handle_close() 3817db96d56Sopenharmony_ci return b'' 3827db96d56Sopenharmony_ci else: 3837db96d56Sopenharmony_ci return data 3847db96d56Sopenharmony_ci except OSError as why: 3857db96d56Sopenharmony_ci # winsock sometimes raises ENOTCONN 3867db96d56Sopenharmony_ci if why.errno in _DISCONNECTED: 3877db96d56Sopenharmony_ci self.handle_close() 3887db96d56Sopenharmony_ci return b'' 3897db96d56Sopenharmony_ci else: 3907db96d56Sopenharmony_ci raise 3917db96d56Sopenharmony_ci 3927db96d56Sopenharmony_ci def close(self): 3937db96d56Sopenharmony_ci self.connected = False 3947db96d56Sopenharmony_ci self.accepting = False 3957db96d56Sopenharmony_ci self.connecting = False 3967db96d56Sopenharmony_ci self.del_channel() 3977db96d56Sopenharmony_ci if self.socket is not None: 3987db96d56Sopenharmony_ci try: 3997db96d56Sopenharmony_ci self.socket.close() 4007db96d56Sopenharmony_ci except OSError as why: 4017db96d56Sopenharmony_ci if why.errno not in (ENOTCONN, EBADF): 4027db96d56Sopenharmony_ci raise 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ci # log and log_info may be overridden to provide more sophisticated 4057db96d56Sopenharmony_ci # logging and warning methods. In general, log is for 'hit' logging 4067db96d56Sopenharmony_ci # and 'log_info' is for informational, warning and error logging. 4077db96d56Sopenharmony_ci 4087db96d56Sopenharmony_ci def log(self, message): 4097db96d56Sopenharmony_ci sys.stderr.write('log: %s\n' % str(message)) 4107db96d56Sopenharmony_ci 4117db96d56Sopenharmony_ci def log_info(self, message, type='info'): 4127db96d56Sopenharmony_ci if type not in self.ignore_log_types: 4137db96d56Sopenharmony_ci print('%s: %s' % (type, message)) 4147db96d56Sopenharmony_ci 4157db96d56Sopenharmony_ci def handle_read_event(self): 4167db96d56Sopenharmony_ci if self.accepting: 4177db96d56Sopenharmony_ci # accepting sockets are never connected, they "spawn" new 4187db96d56Sopenharmony_ci # sockets that are connected 4197db96d56Sopenharmony_ci self.handle_accept() 4207db96d56Sopenharmony_ci elif not self.connected: 4217db96d56Sopenharmony_ci if self.connecting: 4227db96d56Sopenharmony_ci self.handle_connect_event() 4237db96d56Sopenharmony_ci self.handle_read() 4247db96d56Sopenharmony_ci else: 4257db96d56Sopenharmony_ci self.handle_read() 4267db96d56Sopenharmony_ci 4277db96d56Sopenharmony_ci def handle_connect_event(self): 4287db96d56Sopenharmony_ci err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) 4297db96d56Sopenharmony_ci if err != 0: 4307db96d56Sopenharmony_ci raise OSError(err, _strerror(err)) 4317db96d56Sopenharmony_ci self.handle_connect() 4327db96d56Sopenharmony_ci self.connected = True 4337db96d56Sopenharmony_ci self.connecting = False 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci def handle_write_event(self): 4367db96d56Sopenharmony_ci if self.accepting: 4377db96d56Sopenharmony_ci # Accepting sockets shouldn't get a write event. 4387db96d56Sopenharmony_ci # We will pretend it didn't happen. 4397db96d56Sopenharmony_ci return 4407db96d56Sopenharmony_ci 4417db96d56Sopenharmony_ci if not self.connected: 4427db96d56Sopenharmony_ci if self.connecting: 4437db96d56Sopenharmony_ci self.handle_connect_event() 4447db96d56Sopenharmony_ci self.handle_write() 4457db96d56Sopenharmony_ci 4467db96d56Sopenharmony_ci def handle_expt_event(self): 4477db96d56Sopenharmony_ci # handle_expt_event() is called if there might be an error on the 4487db96d56Sopenharmony_ci # socket, or if there is OOB data 4497db96d56Sopenharmony_ci # check for the error condition first 4507db96d56Sopenharmony_ci err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) 4517db96d56Sopenharmony_ci if err != 0: 4527db96d56Sopenharmony_ci # we can get here when select.select() says that there is an 4537db96d56Sopenharmony_ci # exceptional condition on the socket 4547db96d56Sopenharmony_ci # since there is an error, we'll go ahead and close the socket 4557db96d56Sopenharmony_ci # like we would in a subclassed handle_read() that received no 4567db96d56Sopenharmony_ci # data 4577db96d56Sopenharmony_ci self.handle_close() 4587db96d56Sopenharmony_ci else: 4597db96d56Sopenharmony_ci self.handle_expt() 4607db96d56Sopenharmony_ci 4617db96d56Sopenharmony_ci def handle_error(self): 4627db96d56Sopenharmony_ci nil, t, v, tbinfo = compact_traceback() 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_ci # sometimes a user repr method will crash. 4657db96d56Sopenharmony_ci try: 4667db96d56Sopenharmony_ci self_repr = repr(self) 4677db96d56Sopenharmony_ci except: 4687db96d56Sopenharmony_ci self_repr = '<__repr__(self) failed for object at %0x>' % id(self) 4697db96d56Sopenharmony_ci 4707db96d56Sopenharmony_ci self.log_info( 4717db96d56Sopenharmony_ci 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( 4727db96d56Sopenharmony_ci self_repr, 4737db96d56Sopenharmony_ci t, 4747db96d56Sopenharmony_ci v, 4757db96d56Sopenharmony_ci tbinfo 4767db96d56Sopenharmony_ci ), 4777db96d56Sopenharmony_ci 'error' 4787db96d56Sopenharmony_ci ) 4797db96d56Sopenharmony_ci self.handle_close() 4807db96d56Sopenharmony_ci 4817db96d56Sopenharmony_ci def handle_expt(self): 4827db96d56Sopenharmony_ci self.log_info('unhandled incoming priority event', 'warning') 4837db96d56Sopenharmony_ci 4847db96d56Sopenharmony_ci def handle_read(self): 4857db96d56Sopenharmony_ci self.log_info('unhandled read event', 'warning') 4867db96d56Sopenharmony_ci 4877db96d56Sopenharmony_ci def handle_write(self): 4887db96d56Sopenharmony_ci self.log_info('unhandled write event', 'warning') 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ci def handle_connect(self): 4917db96d56Sopenharmony_ci self.log_info('unhandled connect event', 'warning') 4927db96d56Sopenharmony_ci 4937db96d56Sopenharmony_ci def handle_accept(self): 4947db96d56Sopenharmony_ci pair = self.accept() 4957db96d56Sopenharmony_ci if pair is not None: 4967db96d56Sopenharmony_ci self.handle_accepted(*pair) 4977db96d56Sopenharmony_ci 4987db96d56Sopenharmony_ci def handle_accepted(self, sock, addr): 4997db96d56Sopenharmony_ci sock.close() 5007db96d56Sopenharmony_ci self.log_info('unhandled accepted event', 'warning') 5017db96d56Sopenharmony_ci 5027db96d56Sopenharmony_ci def handle_close(self): 5037db96d56Sopenharmony_ci self.log_info('unhandled close event', 'warning') 5047db96d56Sopenharmony_ci self.close() 5057db96d56Sopenharmony_ci 5067db96d56Sopenharmony_ci# --------------------------------------------------------------------------- 5077db96d56Sopenharmony_ci# adds simple buffered output capability, useful for simple clients. 5087db96d56Sopenharmony_ci# [for more sophisticated usage use asynchat.async_chat] 5097db96d56Sopenharmony_ci# --------------------------------------------------------------------------- 5107db96d56Sopenharmony_ci 5117db96d56Sopenharmony_ciclass dispatcher_with_send(dispatcher): 5127db96d56Sopenharmony_ci 5137db96d56Sopenharmony_ci def __init__(self, sock=None, map=None): 5147db96d56Sopenharmony_ci dispatcher.__init__(self, sock, map) 5157db96d56Sopenharmony_ci self.out_buffer = b'' 5167db96d56Sopenharmony_ci 5177db96d56Sopenharmony_ci def initiate_send(self): 5187db96d56Sopenharmony_ci num_sent = 0 5197db96d56Sopenharmony_ci num_sent = dispatcher.send(self, self.out_buffer[:65536]) 5207db96d56Sopenharmony_ci self.out_buffer = self.out_buffer[num_sent:] 5217db96d56Sopenharmony_ci 5227db96d56Sopenharmony_ci def handle_write(self): 5237db96d56Sopenharmony_ci self.initiate_send() 5247db96d56Sopenharmony_ci 5257db96d56Sopenharmony_ci def writable(self): 5267db96d56Sopenharmony_ci return (not self.connected) or len(self.out_buffer) 5277db96d56Sopenharmony_ci 5287db96d56Sopenharmony_ci def send(self, data): 5297db96d56Sopenharmony_ci if self.debug: 5307db96d56Sopenharmony_ci self.log_info('sending %s' % repr(data)) 5317db96d56Sopenharmony_ci self.out_buffer = self.out_buffer + data 5327db96d56Sopenharmony_ci self.initiate_send() 5337db96d56Sopenharmony_ci 5347db96d56Sopenharmony_ci# --------------------------------------------------------------------------- 5357db96d56Sopenharmony_ci# used for debugging. 5367db96d56Sopenharmony_ci# --------------------------------------------------------------------------- 5377db96d56Sopenharmony_ci 5387db96d56Sopenharmony_cidef compact_traceback(): 5397db96d56Sopenharmony_ci t, v, tb = sys.exc_info() 5407db96d56Sopenharmony_ci tbinfo = [] 5417db96d56Sopenharmony_ci if not tb: # Must have a traceback 5427db96d56Sopenharmony_ci raise AssertionError("traceback does not exist") 5437db96d56Sopenharmony_ci while tb: 5447db96d56Sopenharmony_ci tbinfo.append(( 5457db96d56Sopenharmony_ci tb.tb_frame.f_code.co_filename, 5467db96d56Sopenharmony_ci tb.tb_frame.f_code.co_name, 5477db96d56Sopenharmony_ci str(tb.tb_lineno) 5487db96d56Sopenharmony_ci )) 5497db96d56Sopenharmony_ci tb = tb.tb_next 5507db96d56Sopenharmony_ci 5517db96d56Sopenharmony_ci # just to be safe 5527db96d56Sopenharmony_ci del tb 5537db96d56Sopenharmony_ci 5547db96d56Sopenharmony_ci file, function, line = tbinfo[-1] 5557db96d56Sopenharmony_ci info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) 5567db96d56Sopenharmony_ci return (file, function, line), t, v, info 5577db96d56Sopenharmony_ci 5587db96d56Sopenharmony_cidef close_all(map=None, ignore_all=False): 5597db96d56Sopenharmony_ci if map is None: 5607db96d56Sopenharmony_ci map = socket_map 5617db96d56Sopenharmony_ci for x in list(map.values()): 5627db96d56Sopenharmony_ci try: 5637db96d56Sopenharmony_ci x.close() 5647db96d56Sopenharmony_ci except OSError as x: 5657db96d56Sopenharmony_ci if x.errno == EBADF: 5667db96d56Sopenharmony_ci pass 5677db96d56Sopenharmony_ci elif not ignore_all: 5687db96d56Sopenharmony_ci raise 5697db96d56Sopenharmony_ci except _reraised_exceptions: 5707db96d56Sopenharmony_ci raise 5717db96d56Sopenharmony_ci except: 5727db96d56Sopenharmony_ci if not ignore_all: 5737db96d56Sopenharmony_ci raise 5747db96d56Sopenharmony_ci map.clear() 5757db96d56Sopenharmony_ci 5767db96d56Sopenharmony_ci# Asynchronous File I/O: 5777db96d56Sopenharmony_ci# 5787db96d56Sopenharmony_ci# After a little research (reading man pages on various unixen, and 5797db96d56Sopenharmony_ci# digging through the linux kernel), I've determined that select() 5807db96d56Sopenharmony_ci# isn't meant for doing asynchronous file i/o. 5817db96d56Sopenharmony_ci# Heartening, though - reading linux/mm/filemap.c shows that linux 5827db96d56Sopenharmony_ci# supports asynchronous read-ahead. So _MOST_ of the time, the data 5837db96d56Sopenharmony_ci# will be sitting in memory for us already when we go to read it. 5847db96d56Sopenharmony_ci# 5857db96d56Sopenharmony_ci# What other OS's (besides NT) support async file i/o? [VMS?] 5867db96d56Sopenharmony_ci# 5877db96d56Sopenharmony_ci# Regardless, this is useful for pipes, and stdin/stdout... 5887db96d56Sopenharmony_ci 5897db96d56Sopenharmony_ciif os.name == 'posix': 5907db96d56Sopenharmony_ci class file_wrapper: 5917db96d56Sopenharmony_ci # Here we override just enough to make a file 5927db96d56Sopenharmony_ci # look like a socket for the purposes of asyncore. 5937db96d56Sopenharmony_ci # The passed fd is automatically os.dup()'d 5947db96d56Sopenharmony_ci 5957db96d56Sopenharmony_ci def __init__(self, fd): 5967db96d56Sopenharmony_ci self.fd = os.dup(fd) 5977db96d56Sopenharmony_ci 5987db96d56Sopenharmony_ci def __del__(self): 5997db96d56Sopenharmony_ci if self.fd >= 0: 6007db96d56Sopenharmony_ci warnings.warn("unclosed file %r" % self, ResourceWarning, 6017db96d56Sopenharmony_ci source=self) 6027db96d56Sopenharmony_ci self.close() 6037db96d56Sopenharmony_ci 6047db96d56Sopenharmony_ci def recv(self, *args): 6057db96d56Sopenharmony_ci return os.read(self.fd, *args) 6067db96d56Sopenharmony_ci 6077db96d56Sopenharmony_ci def send(self, *args): 6087db96d56Sopenharmony_ci return os.write(self.fd, *args) 6097db96d56Sopenharmony_ci 6107db96d56Sopenharmony_ci def getsockopt(self, level, optname, buflen=None): 6117db96d56Sopenharmony_ci if (level == socket.SOL_SOCKET and 6127db96d56Sopenharmony_ci optname == socket.SO_ERROR and 6137db96d56Sopenharmony_ci not buflen): 6147db96d56Sopenharmony_ci return 0 6157db96d56Sopenharmony_ci raise NotImplementedError("Only asyncore specific behaviour " 6167db96d56Sopenharmony_ci "implemented.") 6177db96d56Sopenharmony_ci 6187db96d56Sopenharmony_ci read = recv 6197db96d56Sopenharmony_ci write = send 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ci def close(self): 6227db96d56Sopenharmony_ci if self.fd < 0: 6237db96d56Sopenharmony_ci return 6247db96d56Sopenharmony_ci fd = self.fd 6257db96d56Sopenharmony_ci self.fd = -1 6267db96d56Sopenharmony_ci os.close(fd) 6277db96d56Sopenharmony_ci 6287db96d56Sopenharmony_ci def fileno(self): 6297db96d56Sopenharmony_ci return self.fd 6307db96d56Sopenharmony_ci 6317db96d56Sopenharmony_ci class file_dispatcher(dispatcher): 6327db96d56Sopenharmony_ci 6337db96d56Sopenharmony_ci def __init__(self, fd, map=None): 6347db96d56Sopenharmony_ci dispatcher.__init__(self, None, map) 6357db96d56Sopenharmony_ci self.connected = True 6367db96d56Sopenharmony_ci try: 6377db96d56Sopenharmony_ci fd = fd.fileno() 6387db96d56Sopenharmony_ci except AttributeError: 6397db96d56Sopenharmony_ci pass 6407db96d56Sopenharmony_ci self.set_file(fd) 6417db96d56Sopenharmony_ci # set it to non-blocking mode 6427db96d56Sopenharmony_ci os.set_blocking(fd, False) 6437db96d56Sopenharmony_ci 6447db96d56Sopenharmony_ci def set_file(self, fd): 6457db96d56Sopenharmony_ci self.socket = file_wrapper(fd) 6467db96d56Sopenharmony_ci self._fileno = self.socket.fileno() 6477db96d56Sopenharmony_ci self.add_channel() 648