diff --git a/jerry-debugger/jerry_client.py b/jerry-debugger/jerry_client.py index e56ca96c67..c2ca5083ed 100755 --- a/jerry-debugger/jerry_client.py +++ b/jerry-debugger/jerry_client.py @@ -22,7 +22,7 @@ import sys import logging import time -import jerry_client_ws +import jerry_client_main def write(string): print(string, end='') @@ -248,9 +248,9 @@ def src_check_args(args): # pylint: disable=too-many-branches,too-many-locals,too-many-statements def main(): - args = jerry_client_ws.arguments_parse() + args = jerry_client_main.arguments_parse() - debugger = jerry_client_ws.JerryDebugger(args.address) + debugger = jerry_client_main.JerryDebugger(args.address) debugger.non_interactive = args.non_interactive logging.debug("Connected to JerryScript on %d port", debugger.port) diff --git a/jerry-debugger/jerry_client_ws.py b/jerry-debugger/jerry_client_main.py similarity index 85% rename from jerry-debugger/jerry_client_ws.py rename to jerry-debugger/jerry_client_main.py index defee5b7d8..d4f1ddb2bc 100644 --- a/jerry-debugger/jerry_client_ws.py +++ b/jerry-debugger/jerry_client_main.py @@ -19,9 +19,9 @@ import logging import re import select -import socket import struct import sys +from jerry_client_websocket import WebSocket # Expected debugger protocol version. JERRY_DEBUGGER_VERSION = 8 @@ -101,10 +101,6 @@ JERRY_DEBUGGER_GET_SCOPE_CHAIN = 19 JERRY_DEBUGGER_GET_SCOPE_VARIABLES = 20 -MAX_BUFFER_SIZE = 128 -WEBSOCKET_BINARY_FRAME = 2 -WEBSOCKET_FIN_BIT = 0x80 - JERRY_DEBUGGER_SCOPE_WITH = 1 JERRY_DEBUGGER_SCOPE_LOCAL = 2 JERRY_DEBUGGER_SCOPE_CLOSURE = 3 @@ -278,7 +274,6 @@ def __init__(self, address): print("Connecting to: %s:%s" % (self.host, self.port)) - self.message_data = b"" self.prompt = False self.function_list = {} self.source = '' @@ -303,62 +298,27 @@ def __init__(self, address): self.nocolor = '' self.src_offset = 0 self.src_offset_diff = 0 - self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.client_socket.connect((self.host, self.port)) self.non_interactive = False self.current_out = b"" self.current_log = b"" - self.send_message(b"GET /jerry-debugger HTTP/1.1\r\n" + - b"Upgrade: websocket\r\n" + - b"Connection: Upgrade\r\n" + - b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n") - result = b"" - expected = (b"HTTP/1.1 101 Switching Protocols\r\n" + - b"Upgrade: websocket\r\n" + - b"Connection: Upgrade\r\n" + - b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") - - len_expected = len(expected) - - while len(result) < len_expected: - result += self.client_socket.recv(1024) - - len_result = len(result) - - if result[0:len_expected] != expected: - raise Exception("Unexpected handshake") - - if len_result > len_expected: - result = result[len_expected:] - else: - result = b"" + self.channel = WebSocket(address=(self.host, self.port)) - len_expected = 10 - # Network configurations, which has the following struct: - # header [2] - opcode[1], size[1] + config_size = 8 + # The server will send the configuration message after connection established # type [1] # configuration [1] # version [4] # max_message_size [1] # cpointer_size [1] + result = self.channel.connect(config_size) - while len(result) < len_expected: - result += self.client_socket.recv(1024) - - len_result = len(result) - - expected = struct.pack("BBB", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - 8, - JERRY_DEBUGGER_CONFIGURATION) - - if result[0:3] != expected: + if len(result) != config_size or ord(result[0]) != JERRY_DEBUGGER_CONFIGURATION: raise Exception("Unexpected configuration") - self.little_endian = ord(result[3]) & JERRY_DEBUGGER_LITTLE_ENDIAN - self.max_message_size = ord(result[8]) - self.cp_size = ord(result[9]) + self.little_endian = ord(result[1]) & JERRY_DEBUGGER_LITTLE_ENDIAN + self.max_message_size = ord(result[6]) + self.cp_size = ord(result[7]) if self.little_endian: self.byte_order = "<" @@ -374,18 +334,15 @@ def __init__(self, address): self.idx_format = "I" - self.version = struct.unpack(self.byte_order + self.idx_format, result[4:8])[0] + self.version = struct.unpack(self.byte_order + self.idx_format, result[2:6])[0] if self.version != JERRY_DEBUGGER_VERSION: raise Exception("Incorrect debugger version from target: %d expected: %d" % (self.version, JERRY_DEBUGGER_VERSION)) logging.debug("Compressed pointer size: %d", self.cp_size) - if len_result > len_expected: - self.message_data = result[len_expected:] - def __del__(self): - self.client_socket.close() + self.channel.close() def _exec_command(self, command_id): self.send_command(command_id) @@ -515,15 +472,16 @@ def backtrace(self, args): return "Error: Positive integer number expected, %s\n" % (val_errno) self.frame_index = min_depth - message = struct.pack(self.byte_order + "BBIB" + self.idx_format + self.idx_format + "B", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + 4 + 4 + 1, - 0, + + message = struct.pack(self.byte_order + "BB" + self.idx_format + self.idx_format + "B", + 1 + 4 + 4 + 1, JERRY_DEBUGGER_GET_BACKTRACE, min_depth, max_depth, get_total) - self.send_message(message) + + self.channel.send_message(self.byte_order, message) + self.prompt = False return "" @@ -539,13 +497,13 @@ def scope_variables(self, args): except ValueError as val_errno: return "Error: Non negative integer number expected, %s\n" % (val_errno) - message = struct.pack(self.byte_order + "BBIB" + self.idx_format, - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + 4, - 0, + message = struct.pack(self.byte_order + "BB" + self.idx_format, + 1 + 4, JERRY_DEBUGGER_GET_SCOPE_VARIABLES, index) - self.send_message(message) + + self.channel.send_message(self.byte_order, message) + self.prompt = False return "" @@ -605,18 +563,16 @@ def _send_string(self, args, message_type, index=0): max_fragment = min(self.max_message_size - message_header, size) - message = struct.pack(self.byte_order + "BBIBI", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + max_fragment + message_header, - 0, + message = struct.pack(self.byte_order + "BBI", + max_fragment + message_header, message_type, size) if size == max_fragment: - self.send_message(message + args) + self.channel.send_message(self.byte_order, message + args) return - self.send_message(message + args[0:max_fragment]) + self.channel.send_message(self.byte_order, message + args[0:max_fragment]) offset = max_fragment if message_type == JERRY_DEBUGGER_EVAL: @@ -631,15 +587,14 @@ def _send_string(self, args, message_type, index=0): while offset < size: next_fragment = min(max_fragment, size - offset) - message = struct.pack(self.byte_order + "BBIB", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + next_fragment + message_header, - 0, + message = struct.pack(self.byte_order + "BB", + next_fragment + message_header, message_type) prev_offset = offset offset += next_fragment - self.send_message(message + args[prev_offset:offset]) + + self.channel.send_message(self.byte_order, message + args[prev_offset:offset]) def delete_active(self): for i in self.active_breakpoint_list.values(): @@ -663,50 +618,40 @@ def breakpoint_pending_exists(self, breakpoint): return False def send_breakpoint(self, breakpoint): - message = struct.pack(self.byte_order + "BBIBB" + self.cp_format + self.idx_format, - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + 1 + self.cp_size + 4, - 0, + message = struct.pack(self.byte_order + "BBB" + self.cp_format + self.idx_format, + 1 + 1 + self.cp_size + 4, JERRY_DEBUGGER_UPDATE_BREAKPOINT, int(breakpoint.active_index >= 0), breakpoint.function.byte_code_cp, breakpoint.offset) - self.send_message(message) + self.channel.send_message(self.byte_order, message) def send_bytecode_cp(self, byte_code_cp): - message = struct.pack(self.byte_order + "BBIB" + self.cp_format, - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + self.cp_size, - 0, + message = struct.pack(self.byte_order + "BB" + self.cp_format, + 1 + self.cp_size, JERRY_DEBUGGER_FREE_BYTE_CODE_CP, byte_code_cp) - self.send_message(message) + self.channel.send_message(self.byte_order, message) def send_command(self, command): - message = struct.pack(self.byte_order + "BBIB", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1, - 0, + message = struct.pack(self.byte_order + "BB", + 1, command) - self.send_message(message) + self.channel.send_message(self.byte_order, message) def send_exception_config(self, enable): - message = struct.pack(self.byte_order + "BBIBB", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + 1, - 0, + message = struct.pack(self.byte_order + "BBB", + 1 + 1, JERRY_DEBUGGER_EXCEPTION_CONFIG, enable) - self.send_message(message) + self.channel.send_message(self.byte_order, message) def send_parser_config(self, enable): - message = struct.pack(self.byte_order + "BBIBB", - WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, - WEBSOCKET_FIN_BIT + 1 + 1, - 0, + message = struct.pack(self.byte_order + "BBB", + 1 + 1, JERRY_DEBUGGER_PARSER_CONFIG, enable) - self.send_message(message) + self.channel.send_message(self.byte_order, message) def set_colors(self): self.nocolor = '\033[0m' @@ -717,46 +662,6 @@ def set_colors(self): self.yellow_bg = '\033[43m\033[30m' self.blue = '\033[94m' - def send_message(self, message): - size = len(message) - while size > 0: - bytes_send = self.client_socket.send(message) - if bytes_send < size: - message = message[bytes_send:] - size -= bytes_send - - - def get_message(self, blocking): - # Connection was closed - if self.message_data is None: - return None - - while True: - if len(self.message_data) >= 2: - if ord(self.message_data[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT: - raise Exception("Unexpected data frame") - - size = ord(self.message_data[1]) - if size == 0 or size >= 126: - raise Exception("Unexpected data frame") - - if len(self.message_data) >= size + 2: - result = self.message_data[0:size + 2] - self.message_data = self.message_data[size + 2:] - return result - - if not blocking: - select_result = select.select([self.client_socket], [], [], 0)[0] - if self.client_socket not in select_result: - return b'' - - data = self.client_socket.recv(MAX_BUFFER_SIZE) - - if not data: - self.message_data = None - return None - self.message_data += data - def store_client_sources(self, args): self.client_sources = args @@ -783,7 +688,7 @@ def process_messages(self): result = "" while True: - data = self.get_message(False) + data = self.channel.get_message(False) if not self.non_interactive: if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: sys.stdin.readline() @@ -796,8 +701,8 @@ def process_messages(self): if not data: # Break the while loop if there is no more data. return DebuggerAction(DebuggerAction.END, "") - buffer_type = ord(data[2]) - buffer_size = ord(data[1]) - 1 + buffer_type = ord(data[0]) + buffer_size = len(data) -1 logging.debug("Main buffer type: %d, message size: %d", buffer_type, buffer_size) @@ -822,7 +727,7 @@ def process_messages(self): self._release_function(data) elif buffer_type in [JERRY_DEBUGGER_BREAKPOINT_HIT, JERRY_DEBUGGER_EXCEPTION_HIT]: - breakpoint_data = struct.unpack(self.byte_order + self.cp_format + self.idx_format, data[3:]) + breakpoint_data = struct.unpack(self.byte_order + self.cp_format + self.idx_format, data[1:]) breakpoint = self._get_breakpoint(breakpoint_data) self.last_breakpoint_hit = breakpoint[0] @@ -850,20 +755,20 @@ def process_messages(self): return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type == JERRY_DEBUGGER_EXCEPTION_STR: - self.exception_string += data[3:] + self.exception_string += data[1:] elif buffer_type == JERRY_DEBUGGER_EXCEPTION_STR_END: - self.exception_string += data[3:] + self.exception_string += data[1:] elif buffer_type == JERRY_DEBUGGER_BACKTRACE_TOTAL: - total = struct.unpack(self.byte_order + self.idx_format, data[3:])[0] + total = struct.unpack(self.byte_order + self.idx_format, data[1:])[0] result += "Total number of frames: %d\n" % (total) return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type in [JERRY_DEBUGGER_BACKTRACE, JERRY_DEBUGGER_BACKTRACE_END]: frame_index = self.frame_index - buffer_pos = 3 + buffer_pos = 1 while buffer_size > 0: breakpoint_data = struct.unpack(self.byte_order + self.cp_format + self.idx_format, data[buffer_pos: buffer_pos + self.cp_size + 4]) @@ -894,7 +799,7 @@ def process_messages(self): elif buffer_type == JERRY_DEBUGGER_MEMSTATS_RECEIVE: memory_stats = struct.unpack(self.byte_order + self.idx_format * 5, - data[3: 3 + 4 * 5]) + data[1: 1 + 4 * 5]) result += "Allocated bytes: %s\n" % memory_stats[0] result += "Byte code bytes: %s\n" % memory_stats[1] @@ -909,7 +814,7 @@ def process_messages(self): self.send_client_source() elif buffer_type in [JERRY_DEBUGGER_SCOPE_CHAIN, JERRY_DEBUGGER_SCOPE_CHAIN_END]: - self.scopes = data[3:] + self.scopes = data[1:] if buffer_type == JERRY_DEBUGGER_SCOPE_CHAIN_END: result = self.process_scopes() @@ -920,7 +825,7 @@ def process_messages(self): return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type in [JERRY_DEBUGGER_SCOPE_VARIABLES, JERRY_DEBUGGER_SCOPE_VARIABLES_END]: - self.scope_vars += "".join(data[3:]) + self.scope_vars += "".join(data[1:]) if buffer_type == JERRY_DEBUGGER_SCOPE_VARIABLES_END: result = self.process_scope_variables() @@ -988,8 +893,8 @@ def _parse_source(self, data): if data is None: return "Error: connection lost during source code receiving" - buffer_type = ord(data[2]) - buffer_size = ord(data[1]) - 1 + buffer_type = ord(data[0]) + buffer_size = len(data) - 1 logging.debug("Parser buffer type: %d, message size: %d", buffer_type, buffer_size) @@ -998,19 +903,19 @@ def _parse_source(self, data): return "" elif buffer_type in [JERRY_DEBUGGER_SOURCE_CODE, JERRY_DEBUGGER_SOURCE_CODE_END]: - source_code += data[3:] + source_code += data[1:] elif buffer_type in [JERRY_DEBUGGER_SOURCE_CODE_NAME, JERRY_DEBUGGER_SOURCE_CODE_NAME_END]: - source_code_name += data[3:] + source_code_name += data[1:] elif buffer_type in [JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_FUNCTION_NAME_END]: - function_name += data[3:] + function_name += data[1:] elif buffer_type == JERRY_DEBUGGER_PARSE_FUNCTION: logging.debug("Source name: %s, function name: %s", source_code_name, function_name) position = struct.unpack(self.byte_order + self.idx_format + self.idx_format, - data[3: 3 + 4 + 4]) + data[1: 1 + 4 + 4]) stack.append({"source": source_code, "source_name": source_code_name, @@ -1028,7 +933,7 @@ def _parse_source(self, data): logging.debug("Breakpoint %s received", name) - buffer_pos = 3 + buffer_pos = 1 while buffer_size > 0: line = struct.unpack(self.byte_order + self.idx_format, data[buffer_pos: buffer_pos + 4]) @@ -1038,7 +943,7 @@ def _parse_source(self, data): elif buffer_type == JERRY_DEBUGGER_BYTE_CODE_CP: byte_code_cp = struct.unpack(self.byte_order + self.cp_format, - data[3: 3 + self.cp_size])[0] + data[1: 1 + self.cp_size])[0] logging.debug("Byte code cptr received: {0x%x}", byte_code_cp) @@ -1068,7 +973,7 @@ def _parse_source(self, data): elif buffer_type == JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP: # Redefined functions are dropped during parsing. byte_code_cp = struct.unpack(self.byte_order + self.cp_format, - data[3: 3 + self.cp_size])[0] + data[1: 1 + self.cp_size])[0] if byte_code_cp in new_function_list: del new_function_list[byte_code_cp] @@ -1084,7 +989,7 @@ def _parse_source(self, data): logging.error("Parser error!") raise Exception("Unexpected message") - data = self.get_message(True) + data = self.channel.get_message(True) # Copy the ready list to the global storage. self.function_list.update(new_function_list) @@ -1131,7 +1036,7 @@ def _parse_source(self, data): def _release_function(self, data): byte_code_cp = struct.unpack(self.byte_order + self.cp_format, - data[3: 3 + self.cp_size])[0] + data[1: 1 + self.cp_size])[0] function = self.function_list[byte_code_cp] @@ -1234,13 +1139,13 @@ def _process_incoming_text(self, buffer_type, data): if buffer_type in [JERRY_DEBUGGER_EVAL_RESULT_END, JERRY_DEBUGGER_OUTPUT_RESULT_END]: subtype = ord(data[-1]) - message += data[3:-1] + message += data[1:-1] break else: - message += data[3:] + message += data[1:] - data = self.get_message(True) - buffer_type = ord(data[2]) + data = self.channel.get_message(True) + buffer_type = ord(data[0]) # Checks if the next frame would be an invalid data frame. # If it is not the message type, or the end type of it, an exception is thrown. if buffer_type not in [msg_type, msg_type + 1]: diff --git a/jerry-debugger/jerry_client_tcp.py b/jerry-debugger/jerry_client_tcp.py new file mode 100644 index 0000000000..11d8a61979 --- /dev/null +++ b/jerry-debugger/jerry_client_tcp.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# Copyright JS Foundation and other contributors, http://js.foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import socket +import select + +class Socket(object): + """ Create a new socket using the given address family, socket type and protocol number. """ + def __init__(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM, proto=0, fileno=None): + self.socket = socket.socket(socket_family, socket_type, proto, fileno) + + def connect(self, address): + """ + Connect to a remote socket at address (host, port). + The format of address depends on the address family. + """ + self.socket.connect(address) + + def close(self): + """" Mark the socket closed. """ + self.socket.close() + + def receive_data(self, max_size=1024): + """ The maximum amount of data to be received at once is specified by max_size. """ + return self.socket.recv(max_size) + + def send_data(self, data): + """ Send data to the socket. The socket must be connected to a remote socket. """ + return self.socket.send(data) + + def ready(self): + """ Monitor the file descriptor. """ + result = select.select([self.socket], [], [], 0)[0] + + return self.socket in result diff --git a/jerry-debugger/jerry_client_websocket.py b/jerry-debugger/jerry_client_websocket.py new file mode 100644 index 0000000000..4218540342 --- /dev/null +++ b/jerry-debugger/jerry_client_websocket.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +# Copyright JS Foundation and other contributors, http://js.foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import struct +from jerry_client_tcp import Socket + +MAX_BUFFER_SIZE = 128 +WEBSOCKET_BINARY_FRAME = 2 +WEBSOCKET_FIN_BIT = 0x80 + +class WebSocket(object): + def __init__(self, address, protocol=Socket()): + + self.data_buffer = b"" + self.protocol = protocol + self.address = address + + def __handshake(self): + """ Client Handshake Request. """ + self.__send_data(b"GET /jerry-debugger HTTP/1.1\r\n" + + b"Upgrade: websocket\r\n" + + b"Connection: Upgrade\r\n" + + b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n") + + # Expected answer from the handshake. + expected = (b"HTTP/1.1 101 Switching Protocols\r\n" + + b"Upgrade: websocket\r\n" + + b"Connection: Upgrade\r\n" + + b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") + + len_expected = len(expected) + + while len(self.data_buffer) < len_expected: + self.data_buffer += self.protocol.receive_data() + + if self.data_buffer[0:len_expected] != expected: + raise Exception("Unexpected handshake") + + if len(self.data_buffer) > len_expected: + self.data_buffer = self.data_buffer[len_expected:] + else: + self.data_buffer = b"" + + def connect(self, config_size): + """ WebSockets connection. """ + self.protocol.connect(self.address) + self.data_buffer = b"" + self.__handshake() + + # It will return with the Network configurations, which has the following struct: + # header [2] - opcode[1], size[1] + # configuration [config_size] + len_expected = config_size + 2 + + while len(self.data_buffer) < len_expected: + self.data_buffer += self.protocol.receive_data() + + expected = struct.pack("BB", + WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, + config_size) + + if self.data_buffer[0:2] != expected: + raise Exception("Unexpected configuration") + + result = self.data_buffer[2:len_expected] + self.data_buffer = self.data_buffer[len_expected:] + + return result + + def __send_data(self, data): + """ Private function to send data using the given protocol. """ + size = len(data) + + while size > 0: + bytes_send = self.protocol.send_data(data) + if bytes_send < size: + data = data[bytes_send:] + size -= bytes_send + + def send_message(self, byte_order, packed_data): + """ Send message. """ + message = struct.pack(byte_order + "BBI", + WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT, + WEBSOCKET_FIN_BIT + struct.unpack(byte_order + "B", packed_data[0])[0], + 0) + packed_data[1:] + + self.__send_data(message) + + def close(self): + """ Close the WebSockets connection. """ + self.protocol.close() + + def get_message(self, blocking): + """ Receive message. """ + + # Connection was closed + if self.data_buffer is None: + return None + + while True: + if len(self.data_buffer) >= 2: + if ord(self.data_buffer[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT: + raise Exception("Unexpected data frame") + + size = ord(self.data_buffer[1]) + if size == 0 or size >= 126: + raise Exception("Unexpected data frame") + + if len(self.data_buffer) >= size + 2: + result = self.data_buffer[2:size + 2] + self.data_buffer = self.data_buffer[size + 2:] + return result + + if not blocking and not self.protocol.ready(): + return b'' + + data = self.protocol.receive_data(MAX_BUFFER_SIZE) + + if not data: + self.data_buffer = None + return None + self.data_buffer += data