diff options
author | James Taylor <user234683@users.noreply.github.com> | 2018-07-12 23:40:30 -0700 |
---|---|---|
committer | James Taylor <user234683@users.noreply.github.com> | 2018-07-12 23:41:07 -0700 |
commit | c3b9f8c4582882cd1f768b0727eca75475bb4f94 (patch) | |
tree | 5b4a1c693fd5b7416f1d5a75862e633502e77ca7 /python/gevent/server.py | |
parent | fe9fe8257740529f5880693992e4eeca35c7ea3e (diff) | |
download | yt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.tar.lz yt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.tar.xz yt-local-c3b9f8c4582882cd1f768b0727eca75475bb4f94.zip |
track embedded python distribution
Diffstat (limited to 'python/gevent/server.py')
-rw-r--r-- | python/gevent/server.py | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/python/gevent/server.py b/python/gevent/server.py new file mode 100644 index 0000000..f2afab7 --- /dev/null +++ b/python/gevent/server.py @@ -0,0 +1,255 @@ +# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details. +"""TCP/SSL server""" +import sys +import _socket +from gevent.baseserver import BaseServer +from gevent.socket import EWOULDBLOCK, socket +from gevent._compat import PYPY, PY3 + +__all__ = ['StreamServer', 'DatagramServer'] + + +if sys.platform == 'win32': + # SO_REUSEADDR on Windows does not mean the same thing as on *nix (issue #217) + DEFAULT_REUSE_ADDR = None +else: + DEFAULT_REUSE_ADDR = 1 + + +class StreamServer(BaseServer): + """ + A generic TCP server. + + Accepts connections on a listening socket and spawns user-provided + *handle* function for each connection with 2 arguments: the client + socket and the client address. + + Note that although the errors in a successfully spawned handler + will not affect the server or other connections, the errors raised + by :func:`accept` and *spawn* cause the server to stop accepting + for a short amount of time. The exact period depends on the values + of :attr:`min_delay` and :attr:`max_delay` attributes. + + The delay starts with :attr:`min_delay` and doubles with each + successive error until it reaches :attr:`max_delay`. A successful + :func:`accept` resets the delay to :attr:`min_delay` again. + + See :class:`~gevent.baseserver.BaseServer` for information on defining the *handle* + function and important restrictions on it. + + **SSL Support** + + The server can optionally work in SSL mode when given the correct + keyword arguments. (That is, the presence of any keyword arguments + will trigger SSL mode.) On Python 2.7.9 and later (any Python + version that supports the :class:`ssl.SSLContext`), this can be + done with a configured ``SSLContext``. On any Python version, it + can be done by passing the appropriate arguments for + :func:`ssl.wrap_socket`. + + The incoming socket will be wrapped into an SSL socket before + being passed to the *handle* function. + + If the *ssl_context* keyword argument is present, it should + contain an :class:`ssl.SSLContext`. The remaining keyword + arguments are passed to the :meth:`ssl.SSLContext.wrap_socket` + method of that object. Depending on the Python version, supported arguments + may include: + + - server_hostname + - suppress_ragged_eofs + - do_handshake_on_connect + + .. caution:: When using an SSLContext, it should either be + imported from :mod:`gevent.ssl`, or the process needs to be monkey-patched. + If the process is not monkey-patched and you pass the standard library + SSLContext, the resulting client sockets will not cooperate with gevent. + + Otherwise, keyword arguments are assumed to apply to :func:`ssl.wrap_socket`. + These keyword arguments bay include: + + - keyfile + - certfile + - cert_reqs + - ssl_version + - ca_certs + - suppress_ragged_eofs + - do_handshake_on_connect + - ciphers + + .. versionchanged:: 1.2a2 + Add support for the *ssl_context* keyword argument. + + """ + # the default backlog to use if none was provided in __init__ + backlog = 256 + + reuse_addr = DEFAULT_REUSE_ADDR + + def __init__(self, listener, handle=None, backlog=None, spawn='default', **ssl_args): + BaseServer.__init__(self, listener, handle=handle, spawn=spawn) + try: + if ssl_args: + ssl_args.setdefault('server_side', True) + if 'ssl_context' in ssl_args: + ssl_context = ssl_args.pop('ssl_context') + self.wrap_socket = ssl_context.wrap_socket + self.ssl_args = ssl_args + else: + from gevent.ssl import wrap_socket + self.wrap_socket = wrap_socket + self.ssl_args = ssl_args + else: + self.ssl_args = None + if backlog is not None: + if hasattr(self, 'socket'): + raise TypeError('backlog must be None when a socket instance is passed') + self.backlog = backlog + except: + self.close() + raise + + @property + def ssl_enabled(self): + return self.ssl_args is not None + + def set_listener(self, listener): + BaseServer.set_listener(self, listener) + try: + self.socket = self.socket._sock + except AttributeError: + pass + + def init_socket(self): + if not hasattr(self, 'socket'): + # FIXME: clean up the socket lifetime + # pylint:disable=attribute-defined-outside-init + self.socket = self.get_listener(self.address, self.backlog, self.family) + self.address = self.socket.getsockname() + if self.ssl_args: + self._handle = self.wrap_socket_and_handle + else: + self._handle = self.handle + + @classmethod + def get_listener(cls, address, backlog=None, family=None): + if backlog is None: + backlog = cls.backlog + return _tcp_listener(address, backlog=backlog, reuse_addr=cls.reuse_addr, family=family) + + if PY3: + + def do_read(self): + sock = self.socket + try: + fd, address = sock._accept() + except BlockingIOError: # python 2: pylint: disable=undefined-variable + if not sock.timeout: + return + raise + sock = socket(sock.family, sock.type, sock.proto, fileno=fd) + # XXX Python issue #7995? + return sock, address + + else: + + def do_read(self): + try: + client_socket, address = self.socket.accept() + except _socket.error as err: + if err.args[0] == EWOULDBLOCK: + return + raise + sockobj = socket(_sock=client_socket) + if PYPY: + client_socket._drop() + return sockobj, address + + def do_close(self, sock, *args): + # pylint:disable=arguments-differ + sock.close() + + def wrap_socket_and_handle(self, client_socket, address): + # used in case of ssl sockets + ssl_socket = self.wrap_socket(client_socket, **self.ssl_args) + return self.handle(ssl_socket, address) + + +class DatagramServer(BaseServer): + """A UDP server""" + + reuse_addr = DEFAULT_REUSE_ADDR + + def __init__(self, *args, **kwargs): + # The raw (non-gevent) socket, if possible + self._socket = None + BaseServer.__init__(self, *args, **kwargs) + from gevent.lock import Semaphore + self._writelock = Semaphore() + + def init_socket(self): + if not hasattr(self, 'socket'): + # FIXME: clean up the socket lifetime + # pylint:disable=attribute-defined-outside-init + self.socket = self.get_listener(self.address, self.family) + self.address = self.socket.getsockname() + self._socket = self.socket + try: + self._socket = self._socket._sock + except AttributeError: + pass + + @classmethod + def get_listener(cls, address, family=None): + return _udp_socket(address, reuse_addr=cls.reuse_addr, family=family) + + def do_read(self): + try: + data, address = self._socket.recvfrom(8192) + except _socket.error as err: + if err.args[0] == EWOULDBLOCK: + return + raise + return data, address + + def sendto(self, *args): + self._writelock.acquire() + try: + self.socket.sendto(*args) + finally: + self._writelock.release() + + +def _tcp_listener(address, backlog=50, reuse_addr=None, family=_socket.AF_INET): + """A shortcut to create a TCP socket, bind it and put it into listening state.""" + sock = socket(family=family) + if reuse_addr is not None: + sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, reuse_addr) + try: + sock.bind(address) + except _socket.error as ex: + strerror = getattr(ex, 'strerror', None) + if strerror is not None: + ex.strerror = strerror + ': ' + repr(address) + raise + sock.listen(backlog) + sock.setblocking(0) + return sock + + +def _udp_socket(address, backlog=50, reuse_addr=None, family=_socket.AF_INET): + # backlog argument for compat with tcp_listener + # pylint:disable=unused-argument + + # we want gevent.socket.socket here + sock = socket(family=family, type=_socket.SOCK_DGRAM) + if reuse_addr is not None: + sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, reuse_addr) + try: + sock.bind(address) + except _socket.error as ex: + strerror = getattr(ex, 'strerror', None) + if strerror is not None: + ex.strerror = strerror + ': ' + repr(address) + raise + return sock |