You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
9.3 KiB
293 lines
9.3 KiB
cdef class UVBaseTransport(UVSocketHandle): |
|
|
|
def __cinit__(self): |
|
# Flow control |
|
self._high_water = FLOW_CONTROL_HIGH_WATER * 1024 |
|
self._low_water = FLOW_CONTROL_HIGH_WATER // 4 |
|
|
|
self._protocol = None |
|
self._protocol_connected = 0 |
|
self._protocol_paused = 0 |
|
self._protocol_data_received = None |
|
|
|
self._server = None |
|
self._waiter = None |
|
self._extra_info = None |
|
|
|
self._conn_lost = 0 |
|
|
|
self._closing = 0 |
|
|
|
cdef size_t _get_write_buffer_size(self): |
|
return 0 |
|
|
|
cdef inline _schedule_call_connection_made(self): |
|
self._loop._call_soon_handle( |
|
new_MethodHandle(self._loop, |
|
"UVTransport._call_connection_made", |
|
<method_t>self._call_connection_made, |
|
self.context, |
|
self)) |
|
|
|
cdef inline _schedule_call_connection_lost(self, exc): |
|
self._loop._call_soon_handle( |
|
new_MethodHandle1(self._loop, |
|
"UVTransport._call_connection_lost", |
|
<method1_t>self._call_connection_lost, |
|
self.context, |
|
self, exc)) |
|
|
|
cdef _fatal_error(self, exc, throw, reason=None): |
|
# Overload UVHandle._fatal_error |
|
|
|
self._force_close(exc) |
|
|
|
if not isinstance(exc, OSError): |
|
|
|
if throw or self._loop is None: |
|
raise exc |
|
|
|
msg = f'Fatal error on transport {self.__class__.__name__}' |
|
if reason is not None: |
|
msg = f'{msg} ({reason})' |
|
|
|
self._loop.call_exception_handler({ |
|
'message': msg, |
|
'exception': exc, |
|
'transport': self, |
|
'protocol': self._protocol, |
|
}) |
|
|
|
cdef inline _maybe_pause_protocol(self): |
|
cdef: |
|
size_t size = self._get_write_buffer_size() |
|
|
|
if size <= self._high_water: |
|
return |
|
|
|
if not self._protocol_paused: |
|
self._protocol_paused = 1 |
|
try: |
|
# _maybe_pause_protocol() is always triggered from user-calls, |
|
# so we must copy the context to avoid entering context twice |
|
run_in_context( |
|
self.context.copy(), self._protocol.pause_writing, |
|
) |
|
except (KeyboardInterrupt, SystemExit): |
|
raise |
|
except BaseException as exc: |
|
self._loop.call_exception_handler({ |
|
'message': 'protocol.pause_writing() failed', |
|
'exception': exc, |
|
'transport': self, |
|
'protocol': self._protocol, |
|
}) |
|
|
|
cdef inline _maybe_resume_protocol(self): |
|
cdef: |
|
size_t size = self._get_write_buffer_size() |
|
|
|
if self._protocol_paused and size <= self._low_water: |
|
self._protocol_paused = 0 |
|
try: |
|
# We're copying the context to avoid entering context twice, |
|
# even though it's not always necessary to copy - it's easier |
|
# to copy here than passing down a copied context. |
|
run_in_context( |
|
self.context.copy(), self._protocol.resume_writing, |
|
) |
|
except (KeyboardInterrupt, SystemExit): |
|
raise |
|
except BaseException as exc: |
|
self._loop.call_exception_handler({ |
|
'message': 'protocol.resume_writing() failed', |
|
'exception': exc, |
|
'transport': self, |
|
'protocol': self._protocol, |
|
}) |
|
|
|
cdef _wakeup_waiter(self): |
|
if self._waiter is not None: |
|
if not self._waiter.cancelled(): |
|
if not self._is_alive(): |
|
self._waiter.set_exception( |
|
RuntimeError( |
|
'closed Transport handle and unset waiter')) |
|
else: |
|
self._waiter.set_result(True) |
|
self._waiter = None |
|
|
|
cdef _call_connection_made(self): |
|
if self._protocol is None: |
|
raise RuntimeError( |
|
'protocol is not set, cannot call connection_made()') |
|
|
|
# We use `_is_alive()` and not `_closing`, because we call |
|
# `transport._close()` in `loop.create_connection()` if an |
|
# exception happens during `await waiter`. |
|
if not self._is_alive(): |
|
# A connection waiter can be cancelled between |
|
# 'await loop.create_connection()' and |
|
# `_schedule_call_connection_made` and |
|
# the actual `_call_connection_made`. |
|
self._wakeup_waiter() |
|
return |
|
|
|
# Set _protocol_connected to 1 before calling "connection_made": |
|
# if transport is aborted or closed, "connection_lost" will |
|
# still be scheduled. |
|
self._protocol_connected = 1 |
|
|
|
try: |
|
self._protocol.connection_made(self) |
|
except BaseException: |
|
self._wakeup_waiter() |
|
raise |
|
|
|
if not self._is_alive(): |
|
# This might happen when "transport.abort()" is called |
|
# from "Protocol.connection_made". |
|
self._wakeup_waiter() |
|
return |
|
|
|
self._start_reading() |
|
self._wakeup_waiter() |
|
|
|
cdef _call_connection_lost(self, exc): |
|
if self._waiter is not None: |
|
if not self._waiter.done(): |
|
self._waiter.set_exception(exc) |
|
self._waiter = None |
|
|
|
if self._closed: |
|
# The handle is closed -- likely, _call_connection_lost |
|
# was already called before. |
|
return |
|
|
|
try: |
|
if self._protocol_connected: |
|
self._protocol.connection_lost(exc) |
|
finally: |
|
self._clear_protocol() |
|
|
|
self._close() |
|
|
|
server = self._server |
|
if server is not None: |
|
(<Server>server)._detach() |
|
self._server = None |
|
|
|
cdef inline _set_server(self, Server server): |
|
self._server = server |
|
(<Server>server)._attach() |
|
|
|
cdef inline _set_waiter(self, object waiter): |
|
if waiter is not None and not isfuture(waiter): |
|
raise TypeError( |
|
f'invalid waiter object {waiter!r}, expected asyncio.Future') |
|
|
|
self._waiter = waiter |
|
|
|
cdef _set_protocol(self, object protocol): |
|
self._protocol = protocol |
|
# Store a reference to the bound method directly |
|
try: |
|
self._protocol_data_received = protocol.data_received |
|
except AttributeError: |
|
pass |
|
|
|
cdef _clear_protocol(self): |
|
self._protocol = None |
|
self._protocol_data_received = None |
|
|
|
cdef inline _init_protocol(self): |
|
self._loop._track_transport(self) |
|
if self._protocol is None: |
|
raise RuntimeError('invalid _init_protocol call') |
|
self._schedule_call_connection_made() |
|
|
|
cdef inline _add_extra_info(self, str name, object obj): |
|
if self._extra_info is None: |
|
self._extra_info = {} |
|
self._extra_info[name] = obj |
|
|
|
cdef bint _is_reading(self): |
|
raise NotImplementedError |
|
|
|
cdef _start_reading(self): |
|
raise NotImplementedError |
|
|
|
cdef _stop_reading(self): |
|
raise NotImplementedError |
|
|
|
# === Public API === |
|
|
|
property _paused: |
|
# Used by SSLProto. Might be removed in the future. |
|
def __get__(self): |
|
return bool(not self._is_reading()) |
|
|
|
def get_protocol(self): |
|
return self._protocol |
|
|
|
def set_protocol(self, protocol): |
|
self._set_protocol(protocol) |
|
if self._is_reading(): |
|
self._stop_reading() |
|
self._start_reading() |
|
|
|
def _force_close(self, exc): |
|
# Used by SSLProto. Might be removed in the future. |
|
if self._conn_lost or self._closed: |
|
return |
|
if not self._closing: |
|
self._closing = 1 |
|
self._stop_reading() |
|
self._conn_lost += 1 |
|
self._schedule_call_connection_lost(exc) |
|
|
|
def abort(self): |
|
self._force_close(None) |
|
|
|
def close(self): |
|
if self._closing or self._closed: |
|
return |
|
|
|
self._closing = 1 |
|
self._stop_reading() |
|
|
|
if not self._get_write_buffer_size(): |
|
# The write buffer is empty |
|
self._conn_lost += 1 |
|
self._schedule_call_connection_lost(None) |
|
|
|
def is_closing(self): |
|
return self._closing |
|
|
|
def get_write_buffer_size(self): |
|
return self._get_write_buffer_size() |
|
|
|
def set_write_buffer_limits(self, high=None, low=None): |
|
self._ensure_alive() |
|
|
|
self._high_water, self._low_water = add_flowcontrol_defaults( |
|
high, low, FLOW_CONTROL_HIGH_WATER) |
|
|
|
self._maybe_pause_protocol() |
|
|
|
def get_write_buffer_limits(self): |
|
return (self._low_water, self._high_water) |
|
|
|
def get_extra_info(self, name, default=None): |
|
if self._extra_info is not None and name in self._extra_info: |
|
return self._extra_info[name] |
|
if name == 'socket': |
|
return self._get_socket() |
|
if name == 'sockname': |
|
return self._get_socket().getsockname() |
|
if name == 'peername': |
|
try: |
|
return self._get_socket().getpeername() |
|
except socket_error: |
|
return default |
|
return default
|
|
|