whoami7 - Manager
:
/
home
/
fresvfqn
/
waterdamagerestorationandrepairsmithtown.com
/
Compressed
/
Upload File:
files >> /home/fresvfqn/waterdamagerestorationandrepairsmithtown.com/Compressed/asyncio.zip
PK [v��4f 4f events.pynu �[��� """Event loop and event loop policy.""" __all__ = ( 'AbstractEventLoopPolicy', 'AbstractEventLoop', 'AbstractServer', 'Handle', 'TimerHandle', 'get_event_loop_policy', 'set_event_loop_policy', 'get_event_loop', 'set_event_loop', 'new_event_loop', 'get_child_watcher', 'set_child_watcher', '_set_running_loop', 'get_running_loop', '_get_running_loop', ) import contextvars import os import socket import subprocess import sys import threading from . import format_helpers from . import exceptions class Handle: """Object returned by callback registration methods.""" __slots__ = ('_callback', '_args', '_cancelled', '_loop', '_source_traceback', '_repr', '__weakref__', '_context') def __init__(self, callback, args, loop, context=None): if context is None: context = contextvars.copy_context() self._context = context self._loop = loop self._callback = callback self._args = args self._cancelled = False self._repr = None if self._loop.get_debug(): self._source_traceback = format_helpers.extract_stack( sys._getframe(1)) else: self._source_traceback = None def _repr_info(self): info = [self.__class__.__name__] if self._cancelled: info.append('cancelled') if self._callback is not None: info.append(format_helpers._format_callback_source( self._callback, self._args)) if self._source_traceback: frame = self._source_traceback[-1] info.append(f'created at {frame[0]}:{frame[1]}') return info def __repr__(self): if self._repr is not None: return self._repr info = self._repr_info() return '<{}>'.format(' '.join(info)) def cancel(self): if not self._cancelled: self._cancelled = True if self._loop.get_debug(): # Keep a representation in debug mode to keep callback and # parameters. For example, to log the warning # "Executing <Handle...> took 2.5 second" self._repr = repr(self) self._callback = None self._args = None def cancelled(self): return self._cancelled def _run(self): try: self._context.run(self._callback, *self._args) except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: cb = format_helpers._format_callback_source( self._callback, self._args) msg = f'Exception in callback {cb}' context = { 'message': msg, 'exception': exc, 'handle': self, } if self._source_traceback: context['source_traceback'] = self._source_traceback self._loop.call_exception_handler(context) self = None # Needed to break cycles when an exception occurs. class TimerHandle(Handle): """Object returned by timed callback registration methods.""" __slots__ = ['_scheduled', '_when'] def __init__(self, when, callback, args, loop, context=None): assert when is not None super().__init__(callback, args, loop, context) if self._source_traceback: del self._source_traceback[-1] self._when = when self._scheduled = False def _repr_info(self): info = super()._repr_info() pos = 2 if self._cancelled else 1 info.insert(pos, f'when={self._when}') return info def __hash__(self): return hash(self._when) def __lt__(self, other): return self._when < other._when def __le__(self, other): if self._when < other._when: return True return self.__eq__(other) def __gt__(self, other): return self._when > other._when def __ge__(self, other): if self._when > other._when: return True return self.__eq__(other) def __eq__(self, other): if isinstance(other, TimerHandle): return (self._when == other._when and self._callback == other._callback and self._args == other._args and self._cancelled == other._cancelled) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def cancel(self): if not self._cancelled: self._loop._timer_handle_cancelled(self) super().cancel() def when(self): """Return a scheduled callback time. The time is an absolute timestamp, using the same time reference as loop.time(). """ return self._when class AbstractServer: """Abstract server returned by create_server().""" def close(self): """Stop serving. This leaves existing connections open.""" raise NotImplementedError def get_loop(self): """Get the event loop the Server object is attached to.""" raise NotImplementedError def is_serving(self): """Return True if the server is accepting connections.""" raise NotImplementedError async def start_serving(self): """Start accepting connections. This method is idempotent, so it can be called when the server is already being serving. """ raise NotImplementedError async def serve_forever(self): """Start accepting connections until the coroutine is cancelled. The server is closed when the coroutine is cancelled. """ raise NotImplementedError async def wait_closed(self): """Coroutine to wait until service is closed.""" raise NotImplementedError async def __aenter__(self): return self async def __aexit__(self, *exc): self.close() await self.wait_closed() class AbstractEventLoop: """Abstract event loop.""" # Running and stopping the event loop. def run_forever(self): """Run the event loop until stop() is called.""" raise NotImplementedError def run_until_complete(self, future): """Run the event loop until a Future is done. Return the Future's result, or raise its exception. """ raise NotImplementedError def stop(self): """Stop the event loop as soon as reasonable. Exactly how soon that is may depend on the implementation, but no more I/O callbacks should be scheduled. """ raise NotImplementedError def is_running(self): """Return whether the event loop is currently running.""" raise NotImplementedError def is_closed(self): """Returns True if the event loop was closed.""" raise NotImplementedError def close(self): """Close the loop. The loop should not be running. This is idempotent and irreversible. No other methods should be called after this one. """ raise NotImplementedError async def shutdown_asyncgens(self): """Shutdown all active asynchronous generators.""" raise NotImplementedError # Methods scheduling callbacks. All these return Handles. def _timer_handle_cancelled(self, handle): """Notification that a TimerHandle has been cancelled.""" raise NotImplementedError def call_soon(self, callback, *args): return self.call_later(0, callback, *args) def call_later(self, delay, callback, *args): raise NotImplementedError def call_at(self, when, callback, *args): raise NotImplementedError def time(self): raise NotImplementedError def create_future(self): raise NotImplementedError # Method scheduling a coroutine object: create a task. def create_task(self, coro, *, name=None): raise NotImplementedError # Methods for interacting with threads. def call_soon_threadsafe(self, callback, *args): raise NotImplementedError def run_in_executor(self, executor, func, *args): raise NotImplementedError def set_default_executor(self, executor): raise NotImplementedError # Network I/O methods returning Futures. async def getaddrinfo(self, host, port, *, family=0, type=0, proto=0, flags=0): raise NotImplementedError async def getnameinfo(self, sockaddr, flags=0): raise NotImplementedError async def create_connection( self, protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, happy_eyeballs_delay=None, interleave=None): raise NotImplementedError async def create_server( self, protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True): """A coroutine which creates a TCP server bound to host and port. The return value is a Server object which can be used to stop the service. If host is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely one for IPv4 and another one for IPv6). The host parameter can also be a sequence (e.g. list) of hosts to bind to. family can be set to either AF_INET or AF_INET6 to force the socket to use IPv4 or IPv6. If not set it will be determined from host (defaults to AF_UNSPEC). flags is a bitmask for getaddrinfo(). sock can optionally be specified in order to use a preexisting socket object. backlog is the maximum number of queued connections passed to listen() (defaults to 100). ssl can be set to an SSLContext to enable SSL over the accepted connections. reuse_address tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire. If not specified will automatically be set to True on UNIX. reuse_port tells the kernel to allow this endpoint to be bound to the same port as other existing endpoints are bound to, so long as they all set this flag when being created. This option is not supported on Windows. ssl_handshake_timeout is the time in seconds that an SSL server will wait for completion of the SSL handshake before aborting the connection. Default is 60s. start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, the user should await Server.start_serving() or Server.serve_forever() to make the server to start accepting connections. """ raise NotImplementedError async def sendfile(self, transport, file, offset=0, count=None, *, fallback=True): """Send a file through a transport. Return an amount of sent bytes. """ raise NotImplementedError async def start_tls(self, transport, protocol, sslcontext, *, server_side=False, server_hostname=None, ssl_handshake_timeout=None): """Upgrade a transport to TLS. Return a new transport that *protocol* should start using immediately. """ raise NotImplementedError async def create_unix_connection( self, protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None): raise NotImplementedError async def create_unix_server( self, protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True): """A coroutine which creates a UNIX Domain Socket server. The return value is a Server object, which can be used to stop the service. path is a str, representing a file systsem path to bind the server socket to. sock can optionally be specified in order to use a preexisting socket object. backlog is the maximum number of queued connections passed to listen() (defaults to 100). ssl can be set to an SSLContext to enable SSL over the accepted connections. ssl_handshake_timeout is the time in seconds that an SSL server will wait for the SSL handshake to complete (defaults to 60s). start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, the user should await Server.start_serving() or Server.serve_forever() to make the server to start accepting connections. """ raise NotImplementedError async def create_datagram_endpoint(self, protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None): """A coroutine which creates a datagram endpoint. This method will try to establish the endpoint in the background. When successful, the coroutine returns a (transport, protocol) pair. protocol_factory must be a callable returning a protocol instance. socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending on host (or family if specified), socket type SOCK_DGRAM. reuse_address tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire. If not specified it will automatically be set to True on UNIX. reuse_port tells the kernel to allow this endpoint to be bound to the same port as other existing endpoints are bound to, so long as they all set this flag when being created. This option is not supported on Windows and some UNIX's. If the :py:data:`~socket.SO_REUSEPORT` constant is not defined then this capability is unsupported. allow_broadcast tells the kernel to allow this endpoint to send messages to the broadcast address. sock can optionally be specified in order to use a preexisting socket object. """ raise NotImplementedError # Pipes and subprocesses. async def connect_read_pipe(self, protocol_factory, pipe): """Register read pipe in event loop. Set the pipe to non-blocking mode. protocol_factory should instantiate object with Protocol interface. pipe is a file-like object. Return pair (transport, protocol), where transport supports the ReadTransport interface.""" # The reason to accept file-like object instead of just file descriptor # is: we need to own pipe and close it at transport finishing # Can got complicated errors if pass f.fileno(), # close fd in pipe transport then close f and vise versa. raise NotImplementedError async def connect_write_pipe(self, protocol_factory, pipe): """Register write pipe in event loop. protocol_factory should instantiate object with BaseProtocol interface. Pipe is file-like object already switched to nonblocking. Return pair (transport, protocol), where transport support WriteTransport interface.""" # The reason to accept file-like object instead of just file descriptor # is: we need to own pipe and close it at transport finishing # Can got complicated errors if pass f.fileno(), # close fd in pipe transport then close f and vise versa. raise NotImplementedError async def subprocess_shell(self, protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs): raise NotImplementedError async def subprocess_exec(self, protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs): raise NotImplementedError # Ready-based callback registration methods. # The add_*() methods return None. # The remove_*() methods return True if something was removed, # False if there was nothing to delete. def add_reader(self, fd, callback, *args): raise NotImplementedError def remove_reader(self, fd): raise NotImplementedError def add_writer(self, fd, callback, *args): raise NotImplementedError def remove_writer(self, fd): raise NotImplementedError # Completion based I/O methods returning Futures. async def sock_recv(self, sock, nbytes): raise NotImplementedError async def sock_recv_into(self, sock, buf): raise NotImplementedError async def sock_sendall(self, sock, data): raise NotImplementedError async def sock_connect(self, sock, address): raise NotImplementedError async def sock_accept(self, sock): raise NotImplementedError async def sock_sendfile(self, sock, file, offset=0, count=None, *, fallback=None): raise NotImplementedError # Signal handling. def add_signal_handler(self, sig, callback, *args): raise NotImplementedError def remove_signal_handler(self, sig): raise NotImplementedError # Task factory. def set_task_factory(self, factory): raise NotImplementedError def get_task_factory(self): raise NotImplementedError # Error handlers. def get_exception_handler(self): raise NotImplementedError def set_exception_handler(self, handler): raise NotImplementedError def default_exception_handler(self, context): raise NotImplementedError def call_exception_handler(self, context): raise NotImplementedError # Debug flag management. def get_debug(self): raise NotImplementedError def set_debug(self, enabled): raise NotImplementedError class AbstractEventLoopPolicy: """Abstract policy for accessing the event loop.""" def get_event_loop(self): """Get the event loop for the current context. Returns an event loop object implementing the BaseEventLoop interface, or raises an exception in case no event loop has been set for the current context and the current policy does not specify to create one. It should never return None.""" raise NotImplementedError def set_event_loop(self, loop): """Set the event loop for the current context to loop.""" raise NotImplementedError def new_event_loop(self): """Create and return a new event loop object according to this policy's rules. If there's need to set this loop as the event loop for the current context, set_event_loop must be called explicitly.""" raise NotImplementedError # Child processes handling (Unix only). def get_child_watcher(self): "Get the watcher for child processes." raise NotImplementedError def set_child_watcher(self, watcher): """Set the watcher for child processes.""" raise NotImplementedError class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy): """Default policy implementation for accessing the event loop. In this policy, each thread has its own event loop. However, we only automatically create an event loop by default for the main thread; other threads by default have no event loop. Other policies may have different rules (e.g. a single global event loop, or automatically creating an event loop per thread, or using some other notion of context to which an event loop is associated). """ _loop_factory = None class _Local(threading.local): _loop = None _set_called = False def __init__(self): self._local = self._Local() def get_event_loop(self): """Get the event loop for the current context. Returns an instance of EventLoop or raises an exception. """ if (self._local._loop is None and not self._local._set_called and isinstance(threading.current_thread(), threading._MainThread)): self.set_event_loop(self.new_event_loop()) if self._local._loop is None: raise RuntimeError('There is no current event loop in thread %r.' % threading.current_thread().name) return self._local._loop def set_event_loop(self, loop): """Set the event loop.""" self._local._set_called = True assert loop is None or isinstance(loop, AbstractEventLoop) self._local._loop = loop def new_event_loop(self): """Create a new event loop. You must call set_event_loop() to make this the current event loop. """ return self._loop_factory() # Event loop policy. The policy itself is always global, even if the # policy's rules say that there is an event loop per thread (or other # notion of context). The default policy is installed by the first # call to get_event_loop_policy(). _event_loop_policy = None # Lock for protecting the on-the-fly creation of the event loop policy. _lock = threading.Lock() # A TLS for the running event loop, used by _get_running_loop. class _RunningLoop(threading.local): loop_pid = (None, None) _running_loop = _RunningLoop() def get_running_loop(): """Return the running event loop. Raise a RuntimeError if there is none. This function is thread-specific. """ # NOTE: this function is implemented in C (see _asynciomodule.c) loop = _get_running_loop() if loop is None: raise RuntimeError('no running event loop') return loop def _get_running_loop(): """Return the running event loop or None. This is a low-level function intended to be used by event loops. This function is thread-specific. """ # NOTE: this function is implemented in C (see _asynciomodule.c) running_loop, pid = _running_loop.loop_pid if running_loop is not None and pid == os.getpid(): return running_loop def _set_running_loop(loop): """Set the running event loop. This is a low-level function intended to be used by event loops. This function is thread-specific. """ # NOTE: this function is implemented in C (see _asynciomodule.c) _running_loop.loop_pid = (loop, os.getpid()) def _init_event_loop_policy(): global _event_loop_policy with _lock: if _event_loop_policy is None: # pragma: no branch from . import DefaultEventLoopPolicy _event_loop_policy = DefaultEventLoopPolicy() def get_event_loop_policy(): """Get the current event loop policy.""" if _event_loop_policy is None: _init_event_loop_policy() return _event_loop_policy def set_event_loop_policy(policy): """Set the current event loop policy. If policy is None, the default policy is restored.""" global _event_loop_policy assert policy is None or isinstance(policy, AbstractEventLoopPolicy) _event_loop_policy = policy def get_event_loop(): """Return an asyncio event loop. When called from a coroutine or a callback (e.g. scheduled with call_soon or similar API), this function will always return the running event loop. If there is no running event loop set, the function will return the result of `get_event_loop_policy().get_event_loop()` call. """ # NOTE: this function is implemented in C (see _asynciomodule.c) current_loop = _get_running_loop() if current_loop is not None: return current_loop return get_event_loop_policy().get_event_loop() def set_event_loop(loop): """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" get_event_loop_policy().set_event_loop(loop) def new_event_loop(): """Equivalent to calling get_event_loop_policy().new_event_loop().""" return get_event_loop_policy().new_event_loop() def get_child_watcher(): """Equivalent to calling get_event_loop_policy().get_child_watcher().""" return get_event_loop_policy().get_child_watcher() def set_child_watcher(watcher): """Equivalent to calling get_event_loop_policy().set_child_watcher(watcher).""" return get_event_loop_policy().set_child_watcher(watcher) # Alias pure-Python implementations for testing purposes. _py__get_running_loop = _get_running_loop _py__set_running_loop = _set_running_loop _py_get_running_loop = get_running_loop _py_get_event_loop = get_event_loop try: # get_event_loop() is one of the most frequently called # functions in asyncio. Pure Python implementation is # about 4 times slower than C-accelerated. from _asyncio import (_get_running_loop, _set_running_loop, get_running_loop, get_event_loop) except ImportError: pass else: # Alias C implementations for testing purposes. _c__get_running_loop = _get_running_loop _c__set_running_loop = _set_running_loop _c_get_running_loop = get_running_loop _c_get_event_loop = get_event_loop PK [��,��( �( transports.pynu �[��� """Abstract Transport class.""" __all__ = ( 'BaseTransport', 'ReadTransport', 'WriteTransport', 'Transport', 'DatagramTransport', 'SubprocessTransport', ) class BaseTransport: """Base class for transports.""" __slots__ = ('_extra',) def __init__(self, extra=None): if extra is None: extra = {} self._extra = extra def get_extra_info(self, name, default=None): """Get optional transport information.""" return self._extra.get(name, default) def is_closing(self): """Return True if the transport is closing or closed.""" raise NotImplementedError def close(self): """Close the transport. Buffered data will be flushed asynchronously. No more data will be received. After all buffered data is flushed, the protocol's connection_lost() method will (eventually) be called with None as its argument. """ raise NotImplementedError def set_protocol(self, protocol): """Set a new protocol.""" raise NotImplementedError def get_protocol(self): """Return the current protocol.""" raise NotImplementedError class ReadTransport(BaseTransport): """Interface for read-only transports.""" __slots__ = () def is_reading(self): """Return True if the transport is receiving.""" raise NotImplementedError def pause_reading(self): """Pause the receiving end. No data will be passed to the protocol's data_received() method until resume_reading() is called. """ raise NotImplementedError def resume_reading(self): """Resume the receiving end. Data received will once again be passed to the protocol's data_received() method. """ raise NotImplementedError class WriteTransport(BaseTransport): """Interface for write-only transports.""" __slots__ = () def set_write_buffer_limits(self, high=None, low=None): """Set the high- and low-water limits for write flow control. These two values control when to call the protocol's pause_writing() and resume_writing() methods. If specified, the low-water limit must be less than or equal to the high-water limit. Neither value can be negative. The defaults are implementation-specific. If only the high-water limit is given, the low-water limit defaults to an implementation-specific value less than or equal to the high-water limit. Setting high to zero forces low to zero as well, and causes pause_writing() to be called whenever the buffer becomes non-empty. Setting low to zero causes resume_writing() to be called only once the buffer is empty. Use of zero for either limit is generally sub-optimal as it reduces opportunities for doing I/O and computation concurrently. """ raise NotImplementedError def get_write_buffer_size(self): """Return the current size of the write buffer.""" raise NotImplementedError def write(self, data): """Write some data bytes to the transport. This does not block; it buffers the data and arranges for it to be sent out asynchronously. """ raise NotImplementedError def writelines(self, list_of_data): """Write a list (or any iterable) of data bytes to the transport. The default implementation concatenates the arguments and calls write() on the result. """ data = b''.join(list_of_data) self.write(data) def write_eof(self): """Close the write end after flushing buffered data. (This is like typing ^D into a UNIX program reading from stdin.) Data may still be received. """ raise NotImplementedError def can_write_eof(self): """Return True if this transport supports write_eof(), False if not.""" raise NotImplementedError def abort(self): """Close the transport immediately. Buffered data will be lost. No more data will be received. The protocol's connection_lost() method will (eventually) be called with None as its argument. """ raise NotImplementedError class Transport(ReadTransport, WriteTransport): """Interface representing a bidirectional transport. There may be several implementations, but typically, the user does not implement new transports; rather, the platform provides some useful transports that are implemented using the platform's best practices. The user never instantiates a transport directly; they call a utility function, passing it a protocol factory and other information necessary to create the transport and protocol. (E.g. EventLoop.create_connection() or EventLoop.create_server().) The utility function will asynchronously create a transport and a protocol and hook them up by calling the protocol's connection_made() method, passing it the transport. The implementation here raises NotImplemented for every method except writelines(), which calls write() in a loop. """ __slots__ = () class DatagramTransport(BaseTransport): """Interface for datagram (UDP) transports.""" __slots__ = () def sendto(self, data, addr=None): """Send data to the transport. This does not block; it buffers the data and arranges for it to be sent out asynchronously. addr is target socket address. If addr is None use target address pointed on transport creation. """ raise NotImplementedError def abort(self): """Close the transport immediately. Buffered data will be lost. No more data will be received. The protocol's connection_lost() method will (eventually) be called with None as its argument. """ raise NotImplementedError class SubprocessTransport(BaseTransport): __slots__ = () def get_pid(self): """Get subprocess id.""" raise NotImplementedError def get_returncode(self): """Get subprocess returncode. See also http://docs.python.org/3/library/subprocess#subprocess.Popen.returncode """ raise NotImplementedError def get_pipe_transport(self, fd): """Get transport for pipe with number fd.""" raise NotImplementedError def send_signal(self, signal): """Send signal to subprocess. See also: docs.python.org/3/library/subprocess#subprocess.Popen.send_signal """ raise NotImplementedError def terminate(self): """Stop the subprocess. Alias for close() method. On Posix OSs the method sends SIGTERM to the subprocess. On Windows the Win32 API function TerminateProcess() is called to stop the subprocess. See also: http://docs.python.org/3/library/subprocess#subprocess.Popen.terminate """ raise NotImplementedError def kill(self): """Kill the subprocess. On Posix OSs the function sends SIGKILL to the subprocess. On Windows kill() is an alias for terminate(). See also: http://docs.python.org/3/library/subprocess#subprocess.Popen.kill """ raise NotImplementedError class _FlowControlMixin(Transport): """All the logic for (write) flow control in a mix-in base class. The subclass must implement get_write_buffer_size(). It must call _maybe_pause_protocol() whenever the write buffer size increases, and _maybe_resume_protocol() whenever it decreases. It may also override set_write_buffer_limits() (e.g. to specify different defaults). The subclass constructor must call super().__init__(extra). This will call set_write_buffer_limits(). The user may call set_write_buffer_limits() and get_write_buffer_size(), and their protocol's pause_writing() and resume_writing() may be called. """ __slots__ = ('_loop', '_protocol_paused', '_high_water', '_low_water') def __init__(self, extra=None, loop=None): super().__init__(extra) assert loop is not None self._loop = loop self._protocol_paused = False self._set_write_buffer_limits() def _maybe_pause_protocol(self): size = self.get_write_buffer_size() if size <= self._high_water: return if not self._protocol_paused: self._protocol_paused = True try: self._protocol.pause_writing() except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: self._loop.call_exception_handler({ 'message': 'protocol.pause_writing() failed', 'exception': exc, 'transport': self, 'protocol': self._protocol, }) def _maybe_resume_protocol(self): if (self._protocol_paused and self.get_write_buffer_size() <= self._low_water): self._protocol_paused = False try: self._protocol.resume_writing() except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: self._loop.call_exception_handler({ 'message': 'protocol.resume_writing() failed', 'exception': exc, 'transport': self, 'protocol': self._protocol, }) def get_write_buffer_limits(self): return (self._low_water, self._high_water) def _set_write_buffer_limits(self, high=None, low=None): if high is None: if low is None: high = 64 * 1024 else: high = 4 * low if low is None: low = high // 4 if not high >= low >= 0: raise ValueError( f'high ({high!r}) must be >= low ({low!r}) must be >= 0') self._high_water = high self._low_water = low def set_write_buffer_limits(self, high=None, low=None): self._set_write_buffer_limits(high=high, low=low) self._maybe_pause_protocol() def get_write_buffer_size(self): raise NotImplementedError PK [#ƅ� h h streams.pynu �[��� __all__ = ( 'StreamReader', 'StreamWriter', 'StreamReaderProtocol', 'open_connection', 'start_server') import socket import sys import warnings import weakref if hasattr(socket, 'AF_UNIX'): __all__ += ('open_unix_connection', 'start_unix_server') from . import coroutines from . import events from . import exceptions from . import format_helpers from . import protocols from .log import logger from .tasks import sleep _DEFAULT_LIMIT = 2 ** 16 # 64 KiB async def open_connection(host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """A wrapper for create_connection() returning a (reader, writer) pair. The reader returned is a StreamReader instance; the writer is a StreamWriter instance. The arguments are all the usual arguments to create_connection() except protocol_factory; most common are positional host and port, with various optional keyword arguments following. Additional optional keyword arguments are loop (to set the event loop instance to use) and limit (to set the buffer limit passed to the StreamReader). (If you want to customize the StreamReader and/or StreamReaderProtocol classes, just copy the code -- there's really nothing special here except some convenience.) """ if loop is None: loop = events.get_event_loop() else: warnings.warn("The loop argument is deprecated since Python 3.8, " "and scheduled for removal in Python 3.10.", DeprecationWarning, stacklevel=2) reader = StreamReader(limit=limit, loop=loop) protocol = StreamReaderProtocol(reader, loop=loop) transport, _ = await loop.create_connection( lambda: protocol, host, port, **kwds) writer = StreamWriter(transport, protocol, reader, loop) return reader, writer async def start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Start a socket server, call back for each client connected. The first parameter, `client_connected_cb`, takes two parameters: client_reader, client_writer. client_reader is a StreamReader object, while client_writer is a StreamWriter object. This parameter can either be a plain callback function or a coroutine; if it is a coroutine, it will be automatically converted into a Task. The rest of the arguments are all the usual arguments to loop.create_server() except protocol_factory; most common are positional host and port, with various optional keyword arguments following. The return value is the same as loop.create_server(). Additional optional keyword arguments are loop (to set the event loop instance to use) and limit (to set the buffer limit passed to the StreamReader). The return value is the same as loop.create_server(), i.e. a Server object which can be used to stop the service. """ if loop is None: loop = events.get_event_loop() else: warnings.warn("The loop argument is deprecated since Python 3.8, " "and scheduled for removal in Python 3.10.", DeprecationWarning, stacklevel=2) def factory(): reader = StreamReader(limit=limit, loop=loop) protocol = StreamReaderProtocol(reader, client_connected_cb, loop=loop) return protocol return await loop.create_server(factory, host, port, **kwds) if hasattr(socket, 'AF_UNIX'): # UNIX Domain Sockets are supported on this platform async def open_unix_connection(path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `open_connection` but works with UNIX Domain Sockets.""" if loop is None: loop = events.get_event_loop() else: warnings.warn("The loop argument is deprecated since Python 3.8, " "and scheduled for removal in Python 3.10.", DeprecationWarning, stacklevel=2) reader = StreamReader(limit=limit, loop=loop) protocol = StreamReaderProtocol(reader, loop=loop) transport, _ = await loop.create_unix_connection( lambda: protocol, path, **kwds) writer = StreamWriter(transport, protocol, reader, loop) return reader, writer async def start_unix_server(client_connected_cb, path=None, *, loop=None, limit=_DEFAULT_LIMIT, **kwds): """Similar to `start_server` but works with UNIX Domain Sockets.""" if loop is None: loop = events.get_event_loop() else: warnings.warn("The loop argument is deprecated since Python 3.8, " "and scheduled for removal in Python 3.10.", DeprecationWarning, stacklevel=2) def factory(): reader = StreamReader(limit=limit, loop=loop) protocol = StreamReaderProtocol(reader, client_connected_cb, loop=loop) return protocol return await loop.create_unix_server(factory, path, **kwds) class FlowControlMixin(protocols.Protocol): """Reusable flow control logic for StreamWriter.drain(). This implements the protocol methods pause_writing(), resume_writing() and connection_lost(). If the subclass overrides these it must call the super methods. StreamWriter.drain() must wait for _drain_helper() coroutine. """ def __init__(self, loop=None): if loop is None: self._loop = events.get_event_loop() else: self._loop = loop self._paused = False self._drain_waiter = None self._connection_lost = False def pause_writing(self): assert not self._paused self._paused = True if self._loop.get_debug(): logger.debug("%r pauses writing", self) def resume_writing(self): assert self._paused self._paused = False if self._loop.get_debug(): logger.debug("%r resumes writing", self) waiter = self._drain_waiter if waiter is not None: self._drain_waiter = None if not waiter.done(): waiter.set_result(None) def connection_lost(self, exc): self._connection_lost = True # Wake up the writer if currently paused. if not self._paused: return waiter = self._drain_waiter if waiter is None: return self._drain_waiter = None if waiter.done(): return if exc is None: waiter.set_result(None) else: waiter.set_exception(exc) async def _drain_helper(self): if self._connection_lost: raise ConnectionResetError('Connection lost') if not self._paused: return waiter = self._drain_waiter assert waiter is None or waiter.cancelled() waiter = self._loop.create_future() self._drain_waiter = waiter await waiter def _get_close_waiter(self, stream): raise NotImplementedError class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): """Helper class to adapt between Protocol and StreamReader. (This is a helper class instead of making StreamReader itself a Protocol subclass, because the StreamReader has other potential uses, and to prevent the user of the StreamReader to accidentally call inappropriate methods of the protocol.) """ _source_traceback = None def __init__(self, stream_reader, client_connected_cb=None, loop=None): super().__init__(loop=loop) if stream_reader is not None: self._stream_reader_wr = weakref.ref(stream_reader) self._source_traceback = stream_reader._source_traceback else: self._stream_reader_wr = None if client_connected_cb is not None: # This is a stream created by the `create_server()` function. # Keep a strong reference to the reader until a connection # is established. self._strong_reader = stream_reader self._reject_connection = False self._stream_writer = None self._transport = None self._client_connected_cb = client_connected_cb self._over_ssl = False self._closed = self._loop.create_future() @property def _stream_reader(self): if self._stream_reader_wr is None: return None return self._stream_reader_wr() def connection_made(self, transport): if self._reject_connection: context = { 'message': ('An open stream was garbage collected prior to ' 'establishing network connection; ' 'call "stream.close()" explicitly.') } if self._source_traceback: context['source_traceback'] = self._source_traceback self._loop.call_exception_handler(context) transport.abort() return self._transport = transport reader = self._stream_reader if reader is not None: reader.set_transport(transport) self._over_ssl = transport.get_extra_info('sslcontext') is not None if self._client_connected_cb is not None: self._stream_writer = StreamWriter(transport, self, reader, self._loop) res = self._client_connected_cb(reader, self._stream_writer) if coroutines.iscoroutine(res): self._loop.create_task(res) self._strong_reader = None def connection_lost(self, exc): reader = self._stream_reader if reader is not None: if exc is None: reader.feed_eof() else: reader.set_exception(exc) if not self._closed.done(): if exc is None: self._closed.set_result(None) else: self._closed.set_exception(exc) super().connection_lost(exc) self._stream_reader_wr = None self._stream_writer = None self._transport = None def data_received(self, data): reader = self._stream_reader if reader is not None: reader.feed_data(data) def eof_received(self): reader = self._stream_reader if reader is not None: reader.feed_eof() if self._over_ssl: # Prevent a warning in SSLProtocol.eof_received: # "returning true from eof_received() # has no effect when using ssl" return False return True def _get_close_waiter(self, stream): return self._closed def __del__(self): # Prevent reports about unhandled exceptions. # Better than self._closed._log_traceback = False hack closed = self._closed if closed.done() and not closed.cancelled(): closed.exception() class StreamWriter: """Wraps a Transport. This exposes write(), writelines(), [can_]write_eof(), get_extra_info() and close(). It adds drain() which returns an optional Future on which you can wait for flow control. It also adds a transport property which references the Transport directly. """ def __init__(self, transport, protocol, reader, loop): self._transport = transport self._protocol = protocol # drain() expects that the reader has an exception() method assert reader is None or isinstance(reader, StreamReader) self._reader = reader self._loop = loop self._complete_fut = self._loop.create_future() self._complete_fut.set_result(None) def __repr__(self): info = [self.__class__.__name__, f'transport={self._transport!r}'] if self._reader is not None: info.append(f'reader={self._reader!r}') return '<{}>'.format(' '.join(info)) @property def transport(self): return self._transport def write(self, data): self._transport.write(data) def writelines(self, data): self._transport.writelines(data) def write_eof(self): return self._transport.write_eof() def can_write_eof(self): return self._transport.can_write_eof() def close(self): return self._transport.close() def is_closing(self): return self._transport.is_closing() async def wait_closed(self): await self._protocol._get_close_waiter(self) def get_extra_info(self, name, default=None): return self._transport.get_extra_info(name, default) async def drain(self): """Flush the write buffer. The intended use is to write w.write(data) await w.drain() """ if self._reader is not None: exc = self._reader.exception() if exc is not None: raise exc if self._transport.is_closing(): # Wait for protocol.connection_lost() call # Raise connection closing error if any, # ConnectionResetError otherwise # Yield to the event loop so connection_lost() may be # called. Without this, _drain_helper() would return # immediately, and code that calls # write(...); await drain() # in a loop would never call connection_lost(), so it # would not see an error when the socket is closed. await sleep(0) await self._protocol._drain_helper() class StreamReader: _source_traceback = None def __init__(self, limit=_DEFAULT_LIMIT, loop=None): # The line length limit is a security feature; # it also doubles as half the buffer limit. if limit <= 0: raise ValueError('Limit cannot be <= 0') self._limit = limit if loop is None: self._loop = events.get_event_loop() else: self._loop = loop self._buffer = bytearray() self._eof = False # Whether we're done. self._waiter = None # A future used by _wait_for_data() self._exception = None self._transport = None self._paused = False if self._loop.get_debug(): self._source_traceback = format_helpers.extract_stack( sys._getframe(1)) def __repr__(self): info = ['StreamReader'] if self._buffer: info.append(f'{len(self._buffer)} bytes') if self._eof: info.append('eof') if self._limit != _DEFAULT_LIMIT: info.append(f'limit={self._limit}') if self._waiter: info.append(f'waiter={self._waiter!r}') if self._exception: info.append(f'exception={self._exception!r}') if self._transport: info.append(f'transport={self._transport!r}') if self._paused: info.append('paused') return '<{}>'.format(' '.join(info)) def exception(self): return self._exception def set_exception(self, exc): self._exception = exc waiter = self._waiter if waiter is not None: self._waiter = None if not waiter.cancelled(): waiter.set_exception(exc) def _wakeup_waiter(self): """Wakeup read*() functions waiting for data or EOF.""" waiter = self._waiter if waiter is not None: self._waiter = None if not waiter.cancelled(): waiter.set_result(None) def set_transport(self, transport): assert self._transport is None, 'Transport already set' self._transport = transport def _maybe_resume_transport(self): if self._paused and len(self._buffer) <= self._limit: self._paused = False self._transport.resume_reading() def feed_eof(self): self._eof = True self._wakeup_waiter() def at_eof(self): """Return True if the buffer is empty and 'feed_eof' was called.""" return self._eof and not self._buffer def feed_data(self, data): assert not self._eof, 'feed_data after feed_eof' if not data: return self._buffer.extend(data) self._wakeup_waiter() if (self._transport is not None and not self._paused and len(self._buffer) > 2 * self._limit): try: self._transport.pause_reading() except NotImplementedError: # The transport can't be paused. # We'll just have to buffer all data. # Forget the transport so we don't keep trying. self._transport = None else: self._paused = True async def _wait_for_data(self, func_name): """Wait until feed_data() or feed_eof() is called. If stream was paused, automatically resume it. """ # StreamReader uses a future to link the protocol feed_data() method # to a read coroutine. Running two read coroutines at the same time # would have an unexpected behaviour. It would not possible to know # which coroutine would get the next data. if self._waiter is not None: raise RuntimeError( f'{func_name}() called while another coroutine is ' f'already waiting for incoming data') assert not self._eof, '_wait_for_data after EOF' # Waiting for data while paused will make deadlock, so prevent it. # This is essential for readexactly(n) for case when n > self._limit. if self._paused: self._paused = False self._transport.resume_reading() self._waiter = self._loop.create_future() try: await self._waiter finally: self._waiter = None async def readline(self): """Read chunk of data from the stream until newline (b'\n') is found. On success, return chunk that ends with newline. If only partial line can be read due to EOF, return incomplete line without terminating newline. When EOF was reached while no bytes read, empty bytes object is returned. If limit is reached, ValueError will be raised. In that case, if newline was found, complete line including newline will be removed from internal buffer. Else, internal buffer will be cleared. Limit is compared against part of the line without newline. If stream was paused, this function will automatically resume it if needed. """ sep = b'\n' seplen = len(sep) try: line = await self.readuntil(sep) except exceptions.IncompleteReadError as e: return e.partial except exceptions.LimitOverrunError as e: if self._buffer.startswith(sep, e.consumed): del self._buffer[:e.consumed + seplen] else: self._buffer.clear() self._maybe_resume_transport() raise ValueError(e.args[0]) return line async def readuntil(self, separator=b'\n'): """Read data from the stream until ``separator`` is found. On success, the data and separator will be removed from the internal buffer (consumed). Returned data will include the separator at the end. Configured stream limit is used to check result. Limit sets the maximal length of data that can be returned, not counting the separator. If an EOF occurs and the complete separator is still not found, an IncompleteReadError exception will be raised, and the internal buffer will be reset. The IncompleteReadError.partial attribute may contain the separator partially. If the data cannot be read because of over limit, a LimitOverrunError exception will be raised, and the data will be left in the internal buffer, so it can be read again. """ seplen = len(separator) if seplen == 0: raise ValueError('Separator should be at least one-byte string') if self._exception is not None: raise self._exception # Consume whole buffer except last bytes, which length is # one less than seplen. Let's check corner cases with # separator='SEPARATOR': # * we have received almost complete separator (without last # byte). i.e buffer='some textSEPARATO'. In this case we # can safely consume len(separator) - 1 bytes. # * last byte of buffer is first byte of separator, i.e. # buffer='abcdefghijklmnopqrS'. We may safely consume # everything except that last byte, but this require to # analyze bytes of buffer that match partial separator. # This is slow and/or require FSM. For this case our # implementation is not optimal, since require rescanning # of data that is known to not belong to separator. In # real world, separator will not be so long to notice # performance problems. Even when reading MIME-encoded # messages :) # `offset` is the number of bytes from the beginning of the buffer # where there is no occurrence of `separator`. offset = 0 # Loop until we find `separator` in the buffer, exceed the buffer size, # or an EOF has happened. while True: buflen = len(self._buffer) # Check if we now have enough data in the buffer for `separator` to # fit. if buflen - offset >= seplen: isep = self._buffer.find(separator, offset) if isep != -1: # `separator` is in the buffer. `isep` will be used later # to retrieve the data. break # see upper comment for explanation. offset = buflen + 1 - seplen if offset > self._limit: raise exceptions.LimitOverrunError( 'Separator is not found, and chunk exceed the limit', offset) # Complete message (with full separator) may be present in buffer # even when EOF flag is set. This may happen when the last chunk # adds data which makes separator be found. That's why we check for # EOF *ater* inspecting the buffer. if self._eof: chunk = bytes(self._buffer) self._buffer.clear() raise exceptions.IncompleteReadError(chunk, None) # _wait_for_data() will resume reading if stream was paused. await self._wait_for_data('readuntil') if isep > self._limit: raise exceptions.LimitOverrunError( 'Separator is found, but chunk is longer than limit', isep) chunk = self._buffer[:isep + seplen] del self._buffer[:isep + seplen] self._maybe_resume_transport() return bytes(chunk) async def read(self, n=-1): """Read up to `n` bytes from the stream. If n is not provided, or set to -1, read until EOF and return all read bytes. If the EOF was received and the internal buffer is empty, return an empty bytes object. If n is zero, return empty bytes object immediately. If n is positive, this function try to read `n` bytes, and may return less or equal bytes than requested, but at least one byte. If EOF was received before any byte is read, this function returns empty byte object. Returned value is not limited with limit, configured at stream creation. If stream was paused, this function will automatically resume it if needed. """ if self._exception is not None: raise self._exception if n == 0: return b'' if n < 0: # This used to just loop creating a new waiter hoping to # collect everything in self._buffer, but that would # deadlock if the subprocess sends more than self.limit # bytes. So just call self.read(self._limit) until EOF. blocks = [] while True: block = await self.read(self._limit) if not block: break blocks.append(block) return b''.join(blocks) if not self._buffer and not self._eof: await self._wait_for_data('read') # This will work right even if buffer is less than n bytes data = bytes(self._buffer[:n]) del self._buffer[:n] self._maybe_resume_transport() return data async def readexactly(self, n): """Read exactly `n` bytes. Raise an IncompleteReadError if EOF is reached before `n` bytes can be read. The IncompleteReadError.partial attribute of the exception will contain the partial read bytes. if n is zero, return empty bytes object. Returned value is not limited with limit, configured at stream creation. If stream was paused, this function will automatically resume it if needed. """ if n < 0: raise ValueError('readexactly size can not be less than zero') if self._exception is not None: raise self._exception if n == 0: return b'' while len(self._buffer) < n: if self._eof: incomplete = bytes(self._buffer) self._buffer.clear() raise exceptions.IncompleteReadError(incomplete, n) await self._wait_for_data('readexactly') if len(self._buffer) == n: data = bytes(self._buffer) self._buffer.clear() else: data = bytes(self._buffer[:n]) del self._buffer[:n] self._maybe_resume_transport() return data def __aiter__(self): return self async def __anext__(self): val = await self.readline() if val == b'': raise StopAsyncIteration return val PK [�xD�Jj Jj sslproto.pynu �[��� import collections import warnings try: import ssl except ImportError: # pragma: no cover ssl = None from . import base_events from . import constants from . import protocols from . import transports from .log import logger def _create_transport_context(server_side, server_hostname): if server_side: raise ValueError('Server side SSL needs a valid SSLContext') # Client side may pass ssl=True to use a default # context; in that case the sslcontext passed is None. # The default is secure for client connections. # Python 3.4+: use up-to-date strong settings. sslcontext = ssl.create_default_context() if not server_hostname: sslcontext.check_hostname = False return sslcontext # States of an _SSLPipe. _UNWRAPPED = "UNWRAPPED" _DO_HANDSHAKE = "DO_HANDSHAKE" _WRAPPED = "WRAPPED" _SHUTDOWN = "SHUTDOWN" class _SSLPipe(object): """An SSL "Pipe". An SSL pipe allows you to communicate with an SSL/TLS protocol instance through memory buffers. It can be used to implement a security layer for an existing connection where you don't have access to the connection's file descriptor, or for some reason you don't want to use it. An SSL pipe can be in "wrapped" and "unwrapped" mode. In unwrapped mode, data is passed through untransformed. In wrapped mode, application level data is encrypted to SSL record level data and vice versa. The SSL record level is the lowest level in the SSL protocol suite and is what travels as-is over the wire. An SslPipe initially is in "unwrapped" mode. To start SSL, call do_handshake(). To shutdown SSL again, call unwrap(). """ max_size = 256 * 1024 # Buffer size passed to read() def __init__(self, context, server_side, server_hostname=None): """ The *context* argument specifies the ssl.SSLContext to use. The *server_side* argument indicates whether this is a server side or client side transport. The optional *server_hostname* argument can be used to specify the hostname you are connecting to. You may only specify this parameter if the _ssl module supports Server Name Indication (SNI). """ self._context = context self._server_side = server_side self._server_hostname = server_hostname self._state = _UNWRAPPED self._incoming = ssl.MemoryBIO() self._outgoing = ssl.MemoryBIO() self._sslobj = None self._need_ssldata = False self._handshake_cb = None self._shutdown_cb = None @property def context(self): """The SSL context passed to the constructor.""" return self._context @property def ssl_object(self): """The internal ssl.SSLObject instance. Return None if the pipe is not wrapped. """ return self._sslobj @property def need_ssldata(self): """Whether more record level data is needed to complete a handshake that is currently in progress.""" return self._need_ssldata @property def wrapped(self): """ Whether a security layer is currently in effect. Return False during handshake. """ return self._state == _WRAPPED def do_handshake(self, callback=None): """Start the SSL handshake. Return a list of ssldata. A ssldata element is a list of buffers The optional *callback* argument can be used to install a callback that will be called when the handshake is complete. The callback will be called with None if successful, else an exception instance. """ if self._state != _UNWRAPPED: raise RuntimeError('handshake in progress or completed') self._sslobj = self._context.wrap_bio( self._incoming, self._outgoing, server_side=self._server_side, server_hostname=self._server_hostname) self._state = _DO_HANDSHAKE self._handshake_cb = callback ssldata, appdata = self.feed_ssldata(b'', only_handshake=True) assert len(appdata) == 0 return ssldata def shutdown(self, callback=None): """Start the SSL shutdown sequence. Return a list of ssldata. A ssldata element is a list of buffers The optional *callback* argument can be used to install a callback that will be called when the shutdown is complete. The callback will be called without arguments. """ if self._state == _UNWRAPPED: raise RuntimeError('no security layer present') if self._state == _SHUTDOWN: raise RuntimeError('shutdown in progress') assert self._state in (_WRAPPED, _DO_HANDSHAKE) self._state = _SHUTDOWN self._shutdown_cb = callback ssldata, appdata = self.feed_ssldata(b'') assert appdata == [] or appdata == [b''] return ssldata def feed_eof(self): """Send a potentially "ragged" EOF. This method will raise an SSL_ERROR_EOF exception if the EOF is unexpected. """ self._incoming.write_eof() ssldata, appdata = self.feed_ssldata(b'') assert appdata == [] or appdata == [b''] def feed_ssldata(self, data, only_handshake=False): """Feed SSL record level data into the pipe. The data must be a bytes instance. It is OK to send an empty bytes instance. This can be used to get ssldata for a handshake initiated by this endpoint. Return a (ssldata, appdata) tuple. The ssldata element is a list of buffers containing SSL data that needs to be sent to the remote SSL. The appdata element is a list of buffers containing plaintext data that needs to be forwarded to the application. The appdata list may contain an empty buffer indicating an SSL "close_notify" alert. This alert must be acknowledged by calling shutdown(). """ if self._state == _UNWRAPPED: # If unwrapped, pass plaintext data straight through. if data: appdata = [data] else: appdata = [] return ([], appdata) self._need_ssldata = False if data: self._incoming.write(data) ssldata = [] appdata = [] try: if self._state == _DO_HANDSHAKE: # Call do_handshake() until it doesn't raise anymore. self._sslobj.do_handshake() self._state = _WRAPPED if self._handshake_cb: self._handshake_cb(None) if only_handshake: return (ssldata, appdata) # Handshake done: execute the wrapped block if self._state == _WRAPPED: # Main state: read data from SSL until close_notify while True: chunk = self._sslobj.read(self.max_size) appdata.append(chunk) if not chunk: # close_notify break elif self._state == _SHUTDOWN: # Call shutdown() until it doesn't raise anymore. self._sslobj.unwrap() self._sslobj = None self._state = _UNWRAPPED if self._shutdown_cb: self._shutdown_cb() elif self._state == _UNWRAPPED: # Drain possible plaintext data after close_notify. appdata.append(self._incoming.read()) except (ssl.SSLError, ssl.CertificateError) as exc: exc_errno = getattr(exc, 'errno', None) if exc_errno not in ( ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE, ssl.SSL_ERROR_SYSCALL): if self._state == _DO_HANDSHAKE and self._handshake_cb: self._handshake_cb(exc) raise self._need_ssldata = (exc_errno == ssl.SSL_ERROR_WANT_READ) # Check for record level data that needs to be sent back. # Happens for the initial handshake and renegotiations. if self._outgoing.pending: ssldata.append(self._outgoing.read()) return (ssldata, appdata) def feed_appdata(self, data, offset=0): """Feed plaintext data into the pipe. Return an (ssldata, offset) tuple. The ssldata element is a list of buffers containing record level data that needs to be sent to the remote SSL instance. The offset is the number of plaintext bytes that were processed, which may be less than the length of data. NOTE: In case of short writes, this call MUST be retried with the SAME buffer passed into the *data* argument (i.e. the id() must be the same). This is an OpenSSL requirement. A further particularity is that a short write will always have offset == 0, because the _ssl module does not enable partial writes. And even though the offset is zero, there will still be encrypted data in ssldata. """ assert 0 <= offset <= len(data) if self._state == _UNWRAPPED: # pass through data in unwrapped mode if offset < len(data): ssldata = [data[offset:]] else: ssldata = [] return (ssldata, len(data)) ssldata = [] view = memoryview(data) while True: self._need_ssldata = False try: if offset < len(view): offset += self._sslobj.write(view[offset:]) except ssl.SSLError as exc: # It is not allowed to call write() after unwrap() until the # close_notify is acknowledged. We return the condition to the # caller as a short write. exc_errno = getattr(exc, 'errno', None) if exc.reason == 'PROTOCOL_IS_SHUTDOWN': exc_errno = exc.errno = ssl.SSL_ERROR_WANT_READ if exc_errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE, ssl.SSL_ERROR_SYSCALL): raise self._need_ssldata = (exc_errno == ssl.SSL_ERROR_WANT_READ) # See if there's any record level data back for us. if self._outgoing.pending: ssldata.append(self._outgoing.read()) if offset == len(view) or self._need_ssldata: break return (ssldata, offset) class _SSLProtocolTransport(transports._FlowControlMixin, transports.Transport): _sendfile_compatible = constants._SendfileMode.FALLBACK def __init__(self, loop, ssl_protocol): self._loop = loop # SSLProtocol instance self._ssl_protocol = ssl_protocol self._closed = False def get_extra_info(self, name, default=None): """Get optional transport information.""" return self._ssl_protocol._get_extra_info(name, default) def set_protocol(self, protocol): self._ssl_protocol._set_app_protocol(protocol) def get_protocol(self): return self._ssl_protocol._app_protocol def is_closing(self): return self._closed def close(self): """Close the transport. Buffered data will be flushed asynchronously. No more data will be received. After all buffered data is flushed, the protocol's connection_lost() method will (eventually) called with None as its argument. """ self._closed = True self._ssl_protocol._start_shutdown() def __del__(self, _warn=warnings.warn): if not self._closed: _warn(f"unclosed transport {self!r}", ResourceWarning, source=self) self.close() def is_reading(self): tr = self._ssl_protocol._transport if tr is None: raise RuntimeError('SSL transport has not been initialized yet') return tr.is_reading() def pause_reading(self): """Pause the receiving end. No data will be passed to the protocol's data_received() method until resume_reading() is called. """ self._ssl_protocol._transport.pause_reading() def resume_reading(self): """Resume the receiving end. Data received will once again be passed to the protocol's data_received() method. """ self._ssl_protocol._transport.resume_reading() def set_write_buffer_limits(self, high=None, low=None): """Set the high- and low-water limits for write flow control. These two values control when to call the protocol's pause_writing() and resume_writing() methods. If specified, the low-water limit must be less than or equal to the high-water limit. Neither value can be negative. The defaults are implementation-specific. If only the high-water limit is given, the low-water limit defaults to an implementation-specific value less than or equal to the high-water limit. Setting high to zero forces low to zero as well, and causes pause_writing() to be called whenever the buffer becomes non-empty. Setting low to zero causes resume_writing() to be called only once the buffer is empty. Use of zero for either limit is generally sub-optimal as it reduces opportunities for doing I/O and computation concurrently. """ self._ssl_protocol._transport.set_write_buffer_limits(high, low) def get_write_buffer_size(self): """Return the current size of the write buffer.""" return self._ssl_protocol._transport.get_write_buffer_size() @property def _protocol_paused(self): # Required for sendfile fallback pause_writing/resume_writing logic return self._ssl_protocol._transport._protocol_paused def write(self, data): """Write some data bytes to the transport. This does not block; it buffers the data and arranges for it to be sent out asynchronously. """ if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError(f"data: expecting a bytes-like instance, " f"got {type(data).__name__}") if not data: return self._ssl_protocol._write_appdata(data) def can_write_eof(self): """Return True if this transport supports write_eof(), False if not.""" return False def abort(self): """Close the transport immediately. Buffered data will be lost. No more data will be received. The protocol's connection_lost() method will (eventually) be called with None as its argument. """ self._ssl_protocol._abort() self._closed = True class SSLProtocol(protocols.Protocol): """SSL protocol. Implementation of SSL on top of a socket using incoming and outgoing buffers which are ssl.MemoryBIO objects. """ def __init__(self, loop, app_protocol, sslcontext, waiter, server_side=False, server_hostname=None, call_connection_made=True, ssl_handshake_timeout=None): if ssl is None: raise RuntimeError('stdlib ssl module not available') if ssl_handshake_timeout is None: ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT elif ssl_handshake_timeout <= 0: raise ValueError( f"ssl_handshake_timeout should be a positive number, " f"got {ssl_handshake_timeout}") if not sslcontext: sslcontext = _create_transport_context( server_side, server_hostname) self._server_side = server_side if server_hostname and not server_side: self._server_hostname = server_hostname else: self._server_hostname = None self._sslcontext = sslcontext # SSL-specific extra info. More info are set when the handshake # completes. self._extra = dict(sslcontext=sslcontext) # App data write buffering self._write_backlog = collections.deque() self._write_buffer_size = 0 self._waiter = waiter self._loop = loop self._set_app_protocol(app_protocol) self._app_transport = _SSLProtocolTransport(self._loop, self) # _SSLPipe instance (None until the connection is made) self._sslpipe = None self._session_established = False self._in_handshake = False self._in_shutdown = False # transport, ex: SelectorSocketTransport self._transport = None self._call_connection_made = call_connection_made self._ssl_handshake_timeout = ssl_handshake_timeout def _set_app_protocol(self, app_protocol): self._app_protocol = app_protocol self._app_protocol_is_buffer = \ isinstance(app_protocol, protocols.BufferedProtocol) def _wakeup_waiter(self, exc=None): if self._waiter is None: return if not self._waiter.cancelled(): if exc is not None: self._waiter.set_exception(exc) else: self._waiter.set_result(None) self._waiter = None def connection_made(self, transport): """Called when the low-level connection is made. Start the SSL handshake. """ self._transport = transport self._sslpipe = _SSLPipe(self._sslcontext, self._server_side, self._server_hostname) self._start_handshake() def connection_lost(self, exc): """Called when the low-level connection is lost or closed. The argument is an exception object or None (the latter meaning a regular EOF is received or the connection was aborted or closed). """ if self._session_established: self._session_established = False self._loop.call_soon(self._app_protocol.connection_lost, exc) else: # Most likely an exception occurred while in SSL handshake. # Just mark the app transport as closed so that its __del__ # doesn't complain. if self._app_transport is not None: self._app_transport._closed = True self._transport = None self._app_transport = None if getattr(self, '_handshake_timeout_handle', None): self._handshake_timeout_handle.cancel() self._wakeup_waiter(exc) self._app_protocol = None self._sslpipe = None def pause_writing(self): """Called when the low-level transport's buffer goes over the high-water mark. """ self._app_protocol.pause_writing() def resume_writing(self): """Called when the low-level transport's buffer drains below the low-water mark. """ self._app_protocol.resume_writing() def data_received(self, data): """Called when some SSL data is received. The argument is a bytes object. """ if self._sslpipe is None: # transport closing, sslpipe is destroyed return try: ssldata, appdata = self._sslpipe.feed_ssldata(data) except (SystemExit, KeyboardInterrupt): raise except BaseException as e: self._fatal_error(e, 'SSL error in data received') return for chunk in ssldata: self._transport.write(chunk) for chunk in appdata: if chunk: try: if self._app_protocol_is_buffer: protocols._feed_data_to_buffered_proto( self._app_protocol, chunk) else: self._app_protocol.data_received(chunk) except (SystemExit, KeyboardInterrupt): raise except BaseException as ex: self._fatal_error( ex, 'application protocol failed to receive SSL data') return else: self._start_shutdown() break def eof_received(self): """Called when the other end of the low-level stream is half-closed. If this returns a false value (including None), the transport will close itself. If it returns a true value, closing the transport is up to the protocol. """ try: if self._loop.get_debug(): logger.debug("%r received EOF", self) self._wakeup_waiter(ConnectionResetError) if not self._in_handshake: keep_open = self._app_protocol.eof_received() if keep_open: logger.warning('returning true from eof_received() ' 'has no effect when using ssl') finally: self._transport.close() def _get_extra_info(self, name, default=None): if name in self._extra: return self._extra[name] elif self._transport is not None: return self._transport.get_extra_info(name, default) else: return default def _start_shutdown(self): if self._in_shutdown: return if self._in_handshake: self._abort() else: self._in_shutdown = True self._write_appdata(b'') def _write_appdata(self, data): self._write_backlog.append((data, 0)) self._write_buffer_size += len(data) self._process_write_backlog() def _start_handshake(self): if self._loop.get_debug(): logger.debug("%r starts SSL handshake", self) self._handshake_start_time = self._loop.time() else: self._handshake_start_time = None self._in_handshake = True # (b'', 1) is a special value in _process_write_backlog() to do # the SSL handshake self._write_backlog.append((b'', 1)) self._handshake_timeout_handle = \ self._loop.call_later(self._ssl_handshake_timeout, self._check_handshake_timeout) self._process_write_backlog() def _check_handshake_timeout(self): if self._in_handshake is True: msg = ( f"SSL handshake is taking longer than " f"{self._ssl_handshake_timeout} seconds: " f"aborting the connection" ) self._fatal_error(ConnectionAbortedError(msg)) def _on_handshake_complete(self, handshake_exc): self._in_handshake = False self._handshake_timeout_handle.cancel() sslobj = self._sslpipe.ssl_object try: if handshake_exc is not None: raise handshake_exc peercert = sslobj.getpeercert() except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: if isinstance(exc, ssl.CertificateError): msg = 'SSL handshake failed on verifying the certificate' else: msg = 'SSL handshake failed' self._fatal_error(exc, msg) return if self._loop.get_debug(): dt = self._loop.time() - self._handshake_start_time logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3) # Add extra info that becomes available after handshake. self._extra.update(peercert=peercert, cipher=sslobj.cipher(), compression=sslobj.compression(), ssl_object=sslobj, ) if self._call_connection_made: self._app_protocol.connection_made(self._app_transport) self._wakeup_waiter() self._session_established = True # In case transport.write() was already called. Don't call # immediately _process_write_backlog(), but schedule it: # _on_handshake_complete() can be called indirectly from # _process_write_backlog(), and _process_write_backlog() is not # reentrant. self._loop.call_soon(self._process_write_backlog) def _process_write_backlog(self): # Try to make progress on the write backlog. if self._transport is None or self._sslpipe is None: return try: for i in range(len(self._write_backlog)): data, offset = self._write_backlog[0] if data: ssldata, offset = self._sslpipe.feed_appdata(data, offset) elif offset: ssldata = self._sslpipe.do_handshake( self._on_handshake_complete) offset = 1 else: ssldata = self._sslpipe.shutdown(self._finalize) offset = 1 for chunk in ssldata: self._transport.write(chunk) if offset < len(data): self._write_backlog[0] = (data, offset) # A short write means that a write is blocked on a read # We need to enable reading if it is paused! assert self._sslpipe.need_ssldata if self._transport._paused: self._transport.resume_reading() break # An entire chunk from the backlog was processed. We can # delete it and reduce the outstanding buffer size. del self._write_backlog[0] self._write_buffer_size -= len(data) except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: if self._in_handshake: # Exceptions will be re-raised in _on_handshake_complete. self._on_handshake_complete(exc) else: self._fatal_error(exc, 'Fatal error on SSL transport') def _fatal_error(self, exc, message='Fatal error on transport'): if isinstance(exc, OSError): if self._loop.get_debug(): logger.debug("%r: %s", self, message, exc_info=True) else: self._loop.call_exception_handler({ 'message': message, 'exception': exc, 'transport': self._transport, 'protocol': self, }) if self._transport: self._transport._force_close(exc) def _finalize(self): self._sslpipe = None if self._transport is not None: self._transport.close() def _abort(self): try: if self._transport is not None: self._transport.abort() finally: self._finalize() PK [�4/�"