-
Notifications
You must be signed in to change notification settings - Fork 160
Improve web socket implementation #194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,8 @@ class Drained(WebSocketException): | |
| class WebSocketClosed(WebSocketException): | ||
| pass | ||
|
|
||
| class InterruptOrTimeout(WebSocketException): | ||
| pass | ||
|
|
||
| class WebSocketConnection(object): | ||
| connections = [] | ||
|
|
@@ -175,60 +177,67 @@ def read_loop(self): | |
| def protected_read(num): | ||
| data = self.handler.rfile.read(num) | ||
| if data is None: | ||
| raise Drained() | ||
| if len(data) != num: | ||
| data = bytes() | ||
| while self.open and len(data) < num: | ||
| (read, _, _) = select.select([self.interruptPipeRecv, self.handler.rfile], [], [], 15) | ||
| if self.handler.rfile in read: | ||
| data += self.handler.rfile.read(num - len(data)) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. data length should be checked after every read. if the |
||
| else: | ||
| if len(data) == 0: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The else branch comes into effect if the interrupt pipe has been triggered, so typically there's no need to check the data length or have different types of exceptions here. |
||
| raise InterruptOrTimeout() | ||
| else: | ||
| raise IncompleteRead() | ||
| if len(data) < num: | ||
| raise IncompleteRead() | ||
| return data | ||
|
|
||
| self.open = True | ||
| while self.open: | ||
| (read, _, _) = select.select([self.interruptPipeRecv, self.handler.rfile], [], [], 15) | ||
| if self.handler.rfile in read: | ||
| available = True | ||
| try: | ||
| header = protected_read(2) | ||
| self.resetPing() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd suggest moving this to |
||
| while self.open and available: | ||
| opcode = header[0] & 0x0F | ||
| length = header[1] & 0x7F | ||
| mask = (header[1] & 0x80) >> 7 | ||
| if length == 126: | ||
| header = protected_read(2) | ||
| length = (header[0] << 8) + header[1] | ||
| if mask: | ||
| masking_key = protected_read(4) | ||
| data = None | ||
| if length > 0: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of implementing the check here, just pass |
||
| data = protected_read(length) | ||
| if mask: | ||
| data = bytes([b ^ masking_key[index % 4] for (index, b) in enumerate(data)]) | ||
| if opcode == OPCODE_TEXT_MESSAGE: | ||
| message = data.decode("utf-8") | ||
| try: | ||
| header = protected_read(2) | ||
| opcode = header[0] & 0x0F | ||
| length = header[1] & 0x7F | ||
| mask = (header[1] & 0x80) >> 7 | ||
| if length == 126: | ||
| header = protected_read(2) | ||
| length = (header[0] << 8) + header[1] | ||
| if mask: | ||
| masking_key = protected_read(4) | ||
| data = protected_read(length) | ||
| if mask: | ||
| data = bytes([b ^ masking_key[index % 4] for (index, b) in enumerate(data)]) | ||
| if opcode == OPCODE_TEXT_MESSAGE: | ||
| message = data.decode("utf-8") | ||
| try: | ||
| self.messageHandler.handleTextMessage(self, message) | ||
| except Exception: | ||
| logger.exception("Exception in websocket handler handleTextMessage()") | ||
| elif opcode == OPCODE_BINARY_MESSAGE: | ||
| try: | ||
| self.messageHandler.handleBinaryMessage(self, data) | ||
| except Exception: | ||
| logger.exception("Exception in websocket handler handleBinaryMessage()") | ||
| elif opcode == OPCODE_PING: | ||
| self.sendPong() | ||
| elif opcode == OPCODE_PONG: | ||
| # since every read resets the ping timer, there's nothing to do here. | ||
| pass | ||
| elif opcode == OPCODE_CLOSE: | ||
| logger.debug("websocket close frame received; closing connection") | ||
| self.open = False | ||
| else: | ||
| logger.warning("unsupported opcode: {0}".format(opcode)) | ||
| except Drained: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| available = False | ||
| except IncompleteRead: | ||
| logger.warning("incomplete read on websocket; closing connection") | ||
| self.open = False | ||
| except OSError: | ||
| logger.exception("OSError while reading data; closing connection") | ||
| self.open = False | ||
| self.messageHandler.handleTextMessage(self, message) | ||
| except Exception: | ||
| logger.exception("Exception in websocket handler handleTextMessage()") | ||
| elif opcode == OPCODE_BINARY_MESSAGE: | ||
| try: | ||
| self.messageHandler.handleBinaryMessage(self, data) | ||
| except Exception: | ||
| logger.exception("Exception in websocket handler handleBinaryMessage()") | ||
| elif opcode == OPCODE_PING: | ||
| self.sendPong() | ||
| elif opcode == OPCODE_PONG: | ||
| # since every read resets the ping timer, there's nothing to do here. | ||
| pass | ||
| elif opcode == OPCODE_CLOSE: | ||
| logger.debug("websocket close frame received; closing connection") | ||
| self.open = False | ||
| else: | ||
| logger.warning("unsupported opcode: {0}".format(opcode)) | ||
| except InterruptOrTimeout: | ||
| pass | ||
| except IncompleteRead: | ||
| logger.warning("incomplete read on websocket; closing connection") | ||
| self.open = False | ||
| except OSError: | ||
| logger.exception("OSError while reading data; closing connection") | ||
| self.open = False | ||
|
|
||
| self.interruptPipeSend.close() | ||
| self.interruptPipeSend = None | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all reading should be moved into the loop; just initialize
datato an empty array and start with a select. that way the code should be easier to read.