From 5adb53d990f29fefae7093f3b3d967dfd9624a39 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Tue, 11 Jan 2022 19:57:52 +0100 Subject: [PATCH] distinguish between error condition and normal socket close --- owrx/connection.py | 21 +++++++++++---------- owrx/websocket.py | 45 +++++++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/owrx/connection.py b/owrx/connection.py index c0b7861..699da31 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -56,9 +56,10 @@ class Client(Handler, metaclass=ABCMeta): try: self.conn.send(data) except IOError: - self.close() + logger.exception("error in Client::send()") + self.close(error=True) - def close(self): + def close(self, error: bool = False): if self.multithreadingQueue is not None: while True: try: @@ -70,7 +71,7 @@ class Client(Handler, metaclass=ABCMeta): except Full: # this shouldn't happen, we just emptied the queue, but it's not worth risking the exception logger.exception("impossible queue state: Full after Empty") - self.conn.close() + self.conn.close(socketError=error) def mp_send(self, data): if self.multithreadingQueue is None: @@ -78,7 +79,7 @@ class Client(Handler, metaclass=ABCMeta): try: self.multithreadingQueue.put(data, block=False) except Full: - self.close() + self.close(error=True) @abstractmethod def handleTextMessage(self, conn, message): @@ -107,9 +108,9 @@ class OpenWebRxClient(Client, metaclass=ABCMeta): def write_receiver_details(self, details): self.send({"type": "receiver_details", "value": details}) - def close(self): + def close(self, error: bool = False): self._detailsSubscription.cancel() - super().close() + super().close(error) class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): @@ -339,7 +340,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): def handleNoSdrsAvailable(self): self.write_sdr_error("No SDR Devices available") - def close(self): + def close(self, error: bool = False): if self.sdr is not None: self.sdr.removeClient(self) self.stopDsp() @@ -350,7 +351,7 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient): if self.bookmarkSub is not None: self.bookmarkSub.cancel() self.bookmarkSub = None - super().close() + super().close(error) def stopDsp(self): with self.dspLock: @@ -466,9 +467,9 @@ class MapConnection(OpenWebRxClient): def handleTextMessage(self, conn, message): pass - def close(self): + def close(self, error: bool = False): Map.getSharedInstance().removeClient(self) - super().close() + super().close(error) def write_config(self, cfg): self.send({"type": "config", "value": cfg}) diff --git a/owrx/websocket.py b/owrx/websocket.py index 0733892..55badf6 100644 --- a/owrx/websocket.py +++ b/owrx/websocket.py @@ -62,6 +62,7 @@ class WebSocketConnection(object): self.setMessageHandler(messageHandler) (self.interruptPipeRecv, self.interruptPipeSend) = Pipe(duplex=False) self.open = True + self.socketError = False self.sendLock = threading.Lock() headers = {key.lower(): value for key, value in self.handler.headers.items()} @@ -136,30 +137,30 @@ class WebSocketConnection(object): for i in range(0, len(input), n): yield input[i: i + n] - try: - with self.sendLock: - if not self.open: - logger.warning("_sendBytes() after connection was closed, ignoring") - else: + with self.sendLock: + if self.socketError: + logger.warning("_sendBytes() after socket error, ignoring") + else: + try: for chunk in chunks(data_to_send, 1024): (_, write, _) = select.select([], [self.handler.wfile], [], 10) if self.handler.wfile in write: written = self.handler.wfile.write(chunk) if written != len(chunk): logger.error("incomplete write! closing socket!") - self.close() + self.close(socketError=True) break else: logger.debug("socket not returned from select; closing") - self.close() + self.close(socketError=True) break - # these exception happen when the socket is closed - except OSError: - logger.exception("OSError while writing data") - self.close() - except ValueError: - logger.exception("ValueError while writing data") - self.close() + # these exception happen when the socket is closed + except OSError: + logger.exception("OSError while writing data") + self.close(socketError=True) + except ValueError: + logger.exception("ValueError while writing data") + self.close(socketError=True) def interrupt(self): if self.interruptPipeSend is None: @@ -177,10 +178,13 @@ class WebSocketConnection(object): self.messageHandler.handleClose() self.cancelPing() - logger.debug("websocket loop ended; sending close frame") + if self.socketError: + logger.debug("websocket closed in error, skipping close frame") + else: + logger.debug("websocket loop ended; sending close frame") - header = self.get_header(0, OPCODE_CLOSE) - self._sendBytes(header) + header = self.get_header(0, OPCODE_CLOSE) + self._sendBytes(header) try: WebSocketConnection.connections.remove(self) @@ -242,9 +246,11 @@ class WebSocketConnection(object): available = False except IncompleteRead: logger.warning("incomplete read on websocket; closing connection") + self.socketError = True self.open = False except OSError: logger.exception("OSError while reading data; closing connection") + self.socketError = True self.open = False self.interruptPipeSend.close() @@ -259,7 +265,10 @@ class WebSocketConnection(object): self.interruptPipeRecv.close() self.interruptPipeRecv = None - def close(self): + def close(self, socketError: bool = False): + # only set flag if it is True + if socketError: + self.socketError = True if not self.open: return self.open = False