Thu, 25 Jan 2024 14:13:36 +0100
JSON server and client
- Changed the code code to secure the data with an Adler32 checksum.
src/eric7/EricNetwork/EricJsonClient.py | file | annotate | diff | comparison | revisions | |
src/eric7/EricNetwork/EricJsonServer.py | file | annotate | diff | comparison | revisions |
--- a/src/eric7/EricNetwork/EricJsonClient.py Wed Jan 24 18:52:50 2024 +0100 +++ b/src/eric7/EricNetwork/EricJsonClient.py Thu Jan 25 14:13:36 2024 +0100 @@ -12,8 +12,10 @@ import json import select import socket +import struct import sys import traceback +import zlib class EricJsonClient: @@ -52,8 +54,28 @@ "method": command, "params": params, } - cmd = json.dumps(commandDict) + "\n" - self.__connection.sendall(cmd.encode("utf8", "backslashreplace")) + data = json.dumps(commandDict).encode("utf8", "backslashreplace") + header = struct.pack(b"!II", len(data), zlib.adler32(data) & 0xFFFFFFFF) + self.__connection.sendall(header) + self.__connection.sendall(data) + + def __receiveBytes(self, length): + """ + Private method to receive the given length of bytes. + + @param length bytes to receive + @type int + @return received bytes or None if connection closed + @rtype bytes + """ + data = bytearray() + while len(data) < length: + newData = self.__connection.recv(length - len(data)) + if not newData: + return None + + data += newData + return data def __receiveJson(self): """ @@ -65,32 +87,36 @@ @rtype tuple of (str, dict) """ # step 1: receive the data - # The JSON RPC string is prefixed by a 9 character long length field. - length = self.__connection.recv(9) - if len(length) < 9: - # invalid length string received + header = self.__receiveBytes(struct.calcsize(b"!II")) + if not header: return None, None + length, datahash = struct.unpack(b"!II", header) + length = int(length) - data = bytearray() - while len(data) < length: - newData = self.__connection.recv(length - len(data)) - if not newData: - return None, None - - data += newData + data = self.__receiveBytes(length) + if not data or zlib.adler32(data) & 0xFFFFFFFF != datahash: + self.sendJson( + "ClientException", + { + "ExceptionType": "ProtocolError", + "ExceptionValue": "The checksum of the data does not match.", + "ProtocolData": data.decode("utf8", "backslashreplace"), + }, + ) + return None, None # step 2: decode and convert the data - line = data.decode("utf8", "backslashreplace") + jsonString = data.decode("utf8", "backslashreplace") try: - commandDict = json.loads(line.strip()) + commandDict = json.loads(jsonString.strip()) except (TypeError, ValueError) as err: self.sendJson( "ClientException", { "ExceptionType": "ProtocolError", "ExceptionValue": str(err), - "ProtocolData": line.strip(), + "ProtocolData": jsonString.strip(), }, ) return None, None
--- a/src/eric7/EricNetwork/EricJsonServer.py Wed Jan 24 18:52:50 2024 +0100 +++ b/src/eric7/EricNetwork/EricJsonServer.py Thu Jan 25 14:13:36 2024 +0100 @@ -9,6 +9,8 @@ import contextlib import json +import struct +import zlib from PyQt6.QtCore import ( QCoreApplication, @@ -155,15 +157,28 @@ else: connection = self.__connection - while connection and connection.canReadLine(): - data = connection.readLine() - jsonLine = bytes(data).decode("utf-8", "backslashreplace") + while connection and connection.bytesAvailable(): + header = connection.read(struct.calcsize(b"!II")) + length, datahash = struct.unpack(b"!II", header) - # - print("JSON Server ({0}): {1}".format(self.__name, jsonLine)) + data = bytearray() + while len(data) < length: + maxSize = length - len(data) + if connection.bytesAvailable() < maxSize: + connection.waitForReadyRead(50) + data += connection.read(maxSize) + + if zlib.adler32(data) & 0xFFFFFFFF != datahash: + # corrupted data -> discard and continue + continue + + jsonString = data.decode("utf-8", "backslashreplace") + + # - print("JSON Server ({0}): {1}".format(self.__name, jsonString)) # - this is for debugging only try: - clientDict = json.loads(jsonLine.strip()) + clientDict = json.loads(jsonString.strip()) except (TypeError, ValueError) as err: EricMessageBox.critical( None, @@ -175,7 +190,7 @@ """ eric bugs email address.</p>""" """<p>Error: {0}</p>""" """<p>Data:<br/>{1}</p>""" - ).format(str(err), Utilities.html_encode(jsonLine.strip())), + ).format(str(err), Utilities.html_encode(jsonString.strip())), EricMessageBox.Ok, ) return @@ -212,8 +227,9 @@ if connection is not None: data = cmd.encode("utf8", "backslashreplace") - length = "{0:09d}".format(len(data)) - connection.write(length.encode() + data) + header = struct.pack(b"!II", len(data), zlib.adler32(data) & 0xFFFFFFFF) + connection.write(header) + connection.write(data) if flush: connection.flush()