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