@@ -407,6 +407,9 @@ cdef class WSListener:
407407 """
408408 Called after websocket handshake is complete and websocket is ready to send and receive frames.
409409 Initiate disconnect if exception is thrown by user handler.
410+
411+ * client side: the exception will be transferred to and reraised by :any:`wait_disconnected`.
412+ * server side: the exception will be 'swallowed' by the library and logged at the ERROR level.
410413
411414 :param transport: :any:`WSTransport` object
412415 """
@@ -418,8 +421,12 @@ cdef class WSListener:
418421
419422 Initiate disconnect if exception is thrown by user handler and
420423 `disconnect_on_exception` was set to True in :any:`ws_connect`
421- or :any:`ws_create_server`
422-
424+ or :any:`ws_create_server`.
425+ In such case:
426+
427+ * client side: the exception will be transferred to and reraised by :any:`wait_disconnected`.
428+ * server side: the exception will be 'swallowed' by the library and logged at the ERROR level.
429+
423430 .. DANGER::
424431 WSFrame is essentially just a pointer to a chunk of memory in the receiving buffer. It does not own
425432 the memory. Do NOT cache or store WSFrame object for later processing because the data may be invalidated
@@ -498,10 +505,10 @@ cdef class WSTransport:
498505 self .auto_ping_expect_pong = False
499506 self .pong_received_at_future = None
500507 self .listener_proxy = None
508+ self .disconnected_future = loop.create_future()
501509 self ._logger = logger
502510 self ._log_debug_enabled = self ._logger.isEnabledFor(PICOWS_DEBUG_LL)
503511 self ._close_frame_is_sent = False
504- self ._disconnected_future = loop.create_future()
505512 self ._write_buf = MemoryBuffer(1024 )
506513 self ._socket = underlying_transport.get_extra_info(' socket' ).fileno()
507514
@@ -722,7 +729,7 @@ cdef class WSTransport:
722729 (underlying transport is closed, on_ws_disconnected has been called)
723730
724731 """
725- await asyncio.shield(self ._disconnected_future )
732+ await asyncio.shield(self .disconnected_future )
726733
727734 async def measure_roundtrip_time(self , int rounds) - > List[float ]:
728735 """
@@ -828,10 +835,6 @@ cdef class WSTransport:
828835 self .response = response
829836 self .underlying_transport.write(response_bytes)
830837
831- cdef _mark_disconnected(self ):
832- if not self ._disconnected_future.done():
833- self ._disconnected_future.set_result(None )
834-
835838 cdef _try_native_write_then_transport_write(self , char * ptr, Py_ssize_t sz):
836839 if < size_t> self .underlying_transport.get_write_buffer_size() > 0 :
837840 self .underlying_transport.write(PyBytes_FromStringAndSize(ptr, sz))
@@ -876,6 +879,7 @@ cdef class WSProtocol:
876879 bint _log_debug_enabled
877880 bint is_client_side
878881 bint _disconnect_on_exception
882+ object _disconnect_exception # : Optional[Exception]
879883
880884 object _loop
881885
@@ -936,6 +940,7 @@ cdef class WSProtocol:
936940 self ._log_debug_enabled = self ._logger.isEnabledFor(PICOWS_DEBUG_LL)
937941 self .is_client_side = is_client_side
938942 self ._disconnect_on_exception = disconnect_on_exception
943+ self ._disconnect_exception = None
939944
940945 self ._loop = asyncio.get_running_loop()
941946
@@ -1035,7 +1040,13 @@ cdef class WSProtocol:
10351040 self .transport.pong_received_at_future.set_exception(ConnectionResetError())
10361041 self .transport.pong_received_at_future = None
10371042
1038- self .transport._mark_disconnected()
1043+ if not self .transport.disconnected_future.done():
1044+ # The server side does not allow to await on a particular client or retrieve its disconnect exception.
1045+ # Do not set exception on future to avoid warnings about unconsumed exception from asyncio.
1046+ if self ._disconnect_exception is None or not self .is_client_side:
1047+ self .transport.disconnected_future.set_result(None )
1048+ else :
1049+ self .transport.disconnected_future.set_exception(self ._disconnect_exception)
10391050
10401051 def eof_received (self ) -> bool:
10411052 if self._log_debug_enabled:
@@ -1531,8 +1542,12 @@ cdef class WSProtocol:
15311542 cdef inline _invoke_on_ws_connected(self ):
15321543 try :
15331544 self .listener.on_ws_connected(self .transport)
1534- except Exception as e:
1535- self ._logger.exception(" Unhandled exception in on_ws_connected, initiate disconnect" )
1545+ except Exception as exc:
1546+ if self .is_client_side:
1547+ self ._logger.info(" Exception from user's WSListener.on_ws_connected handler, initiate disconnect" )
1548+ self ._disconnect_exception = exc
1549+ else :
1550+ self ._logger.exception(" Exception from user's WSListener.on_ws_connected handler, initiate disconnect" )
15361551 self .transport.send_close(WSCloseCode.INTERNAL_ERROR)
15371552 self ._loop.call_later(DISCONNECT_AFTER_ERROR_DELAY, self .transport.disconnect)
15381553
@@ -1560,19 +1575,27 @@ cdef class WSProtocol:
15601575 return
15611576
15621577 self .listener.on_ws_frame(self .transport, frame)
1563- except Exception as e :
1578+ except Exception as exc :
15641579 if self ._disconnect_on_exception:
1565- self ._logger.exception(" Unhandled exception in on_ws_frame, initiate disconnect" )
1580+ if self .is_client_side:
1581+ if self ._disconnect_exception is None :
1582+ self ._disconnect_exception = exc
1583+ self ._logger.info(" Exception from user's WSListener.on_ws_frame, initiate disconnect" )
1584+ else :
1585+ self ._logger.exception(" Secondary exception from user's WSListener.on_ws_frame" )
1586+ else :
1587+ self ._logger.exception(" Exception from user's WSListener.on_ws_frame, initiate disconnect" )
1588+
15661589 self .transport.send_close(WSCloseCode.INTERNAL_ERROR)
15671590 self ._loop.call_later(DISCONNECT_AFTER_ERROR_DELAY, self .transport.disconnect)
15681591 else :
1569- self ._logger.exception(" Unhandled exception in on_ws_frame" )
1592+ self ._logger.exception(" Unhandled exception from user's WSListener. on_ws_frame" )
15701593
15711594 cdef inline _invoke_on_ws_disconnected(self ):
15721595 try :
15731596 self .listener.on_ws_disconnected(self .transport)
15741597 except :
1575- self ._logger.exception(" Unhandled exception in on_ws_disconnected" )
1598+ self ._logger.exception(" Unhandled exception from user's on_ws_disconnected" )
15761599
15771600 cdef inline _shrink_buffer(self ):
15781601 if self ._f_curr_frame_start_pos > 0 :
0 commit comments