close websocket connections in an improved way

This commit is contained in:
Jakob Ketterl 2019-09-21 13:49:37 +02:00
parent 428a9ca509
commit 2edeffb761
3 changed files with 56 additions and 37 deletions

View File

@ -20,13 +20,14 @@ class Client(object):
self.conn.send(data) self.conn.send(data)
# these exception happen when the socket is closed # these exception happen when the socket is closed
except OSError: except OSError:
logger.exception("OSError while sending data")
self.close() self.close()
except ValueError: except ValueError:
logger.exception("ValueError while sending data")
self.close() self.close()
def close(self): def close(self):
self.conn.close() self.conn.close()
logger.debug("connection closed")
class OpenWebRxReceiverClient(Client): class OpenWebRxReceiverClient(Client):
@ -288,7 +289,3 @@ class WebSocketMessageHandler(object):
def handleBinaryMessage(self, conn, data): def handleBinaryMessage(self, conn, data):
logger.error("unsupported binary message, discarding") logger.error("unsupported binary message, discarding")
def handleClose(self, conn):
if self.client:
self.client.close()

View File

@ -10,8 +10,8 @@ from owrx.controllers import (
AprsSymbolsController, AprsSymbolsController,
) )
from http.server import BaseHTTPRequestHandler from http.server import BaseHTTPRequestHandler
import re
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
import re
import logging import logging

View File

@ -1,16 +1,24 @@
import base64 import base64
import hashlib import hashlib
import json import json
import os
import select
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class IncompleteRead(Exception):
pass
class WebSocketConnection(object): class WebSocketConnection(object):
def __init__(self, handler, messageHandler): def __init__(self, handler, messageHandler):
self.handler = handler self.handler = handler
self.messageHandler = messageHandler self.messageHandler = messageHandler
(self.interruptPipeRecv, self.interruptPipeSend) = os.pipe()
self.open = True
my_headers = self.handler.headers.items() my_headers = self.handler.headers.items()
my_header_keys = list(map(lambda x: x[0], my_headers)) my_header_keys = list(map(lambda x: x[0], my_headers))
h_key_exists = lambda x: my_header_keys.count(x) h_key_exists = lambda x: my_header_keys.count(x)
@ -78,33 +86,49 @@ class WebSocketConnection(object):
else: else:
self.handler.wfile.flush() self.handler.wfile.flush()
def read_loop(self): def protected_read(self, num):
open = True data = self.handler.rfile.read(num)
while open: if len(data) != num:
header = self.handler.rfile.read(2) raise IncompleteRead()
opcode = header[0] & 0x0F return data
length = header[1] & 0x7F
mask = (header[1] & 0x80) >> 7 def interrupt(self):
if length == 126: os.write(self.interruptPipeSend, bytes(0x00))
header = self.handler.rfile.read(2)
length = (header[0] << 8) + header[1] def read_loop(self):
if mask: self.open = True
masking_key = self.handler.rfile.read(4) while self.open:
data = self.handler.rfile.read(length) try:
if mask: (read, _, _) = select.select([self.interruptPipeRecv, self.handler.rfile], [], [])
data = bytes([b ^ masking_key[index % 4] for (index, b) in enumerate(data)]) if read[0] == self.handler.rfile:
if opcode == 1: header = self.protected_read(2)
message = data.decode("utf-8") opcode = header[0] & 0x0F
self.messageHandler.handleTextMessage(self, message) length = header[1] & 0x7F
elif opcode == 2: mask = (header[1] & 0x80) >> 7
self.messageHandler.handleBinaryMessage(self, data) if length == 126:
elif opcode == 8: header = self.protected_read(2)
open = False length = (header[0] << 8) + header[1]
self.messageHandler.handleClose(self) if mask:
else: masking_key = self.protected_read(4)
logger.warning("unsupported opcode: {0}".format(opcode)) data = self.protected_read(length)
if mask:
data = bytes([b ^ masking_key[index % 4] for (index, b) in enumerate(data)])
if opcode == 1:
message = data.decode("utf-8")
self.messageHandler.handleTextMessage(self, message)
elif opcode == 2:
self.messageHandler.handleBinaryMessage(self, data)
elif opcode == 8:
logger.debug("websocket close frame received; closing connection")
self.open = False
else:
logger.warning("unsupported opcode: {0}".format(opcode))
except IncompleteRead:
logger.warning("incomplete websocket read; closing socket")
self.open = False
logger.debug("websocket loop ended; sending close frame")
def close(self):
try: try:
header = self.get_header(0, 8) header = self.get_header(0, 8)
self.handler.wfile.write(header) self.handler.wfile.write(header)
@ -114,11 +138,9 @@ class WebSocketConnection(object):
except OSError: except OSError:
logger.exception("OSError while writing close frame:") logger.exception("OSError while writing close frame:")
try: def close(self):
self.handler.finish() self.open = False
self.handler.connection.close() self.interrupt()
except Exception:
logger.exception("while closing connection:")
class WebSocketException(Exception): class WebSocketException(Exception):