From fb947f9525efd9444e74d7270b83a10942e3e2b7 Mon Sep 17 00:00:00 2001 From: Tom Wang Date: Tue, 24 Mar 2020 00:50:07 -0700 Subject: [PATCH] Update app_client_blocking.py --- app_client_blocking.py | 160 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 app_client_blocking.py diff --git a/app_client_blocking.py b/app_client_blocking.py new file mode 100644 index 0000000..d9b9675 --- /dev/null +++ b/app_client_blocking.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +""" +Created on Sun Mar 22 10:46:20 2020 + +@author: cpan + +Simple client implementation with timeout feature to prevent hanging + +""" + +import sys +import socket +import json +import io +import struct + + +def create_request(action, value): + if action == "search": + return dict( + type="text/json", + encoding="utf-8", + content=dict(action=action, value=value), + ) + else: + return dict( + type="binary/custom-client-binary-type", + encoding="binary", + content=bytes(action + value, encoding="utf-8"), + ) + + +def _json_encode(obj, encoding): + return json.dumps(obj, ensure_ascii=False).encode(encoding) + + +def _json_decode(json_bytes, encoding): + tiow = io.TextIOWrapper( + io.BytesIO(json_bytes), encoding=encoding, newline="" + ) + obj = json.load(tiow) + tiow.close() + return obj + + +def _create_message(*, content_bytes, content_type, content_encoding): + jsonheader = { + "byteorder": sys.byteorder, + "content-type": content_type, + "content-encoding": content_encoding, + "content-length": len(content_bytes), + } + jsonheader_bytes = _json_encode(jsonheader, "utf-8") + message_hdr = struct.pack(">H", len(jsonheader_bytes)) + message = message_hdr + jsonheader_bytes + content_bytes + return message + + +def create_message(request): + content = request["content"] + content_type = request["type"] + content_encoding = request["encoding"] + if content_type == "text/json": + req = { + "content_bytes": _json_encode(content, content_encoding), + "content_type": content_type, + "content_encoding": content_encoding, + } + else: + req = { + "content_bytes": content, + "content_type": content_type, + "content_encoding": content_encoding, + } + message = _create_message(**req) + return message + + +def main(): + if len(sys.argv) != 5: + print("usage:", sys.argv[0], " ") + sys.exit(1) + + host, port = sys.argv[1], int(sys.argv[2]) + action, value = sys.argv[3], sys.argv[4] + request = create_request(action, value) + message = create_message(request) + + addr = (host, port) + print("starting connection to", addr) + sock = socket.create_connection(addr) + sock.settimeout(5) + + print(f"sending request {repr(message)} to {addr}") + sock.sendall(message) + + _recv_buffer = b"" + + # read and process protoheader (fixed length of 2 byte in network order) + _jsonheader_len = None + hdrlen = 2 + while len(_recv_buffer) < hdrlen: + data = sock.recv(4096) + _recv_buffer += data + + # process_protoheader() + _jsonheader_len = struct.unpack(">H", _recv_buffer[:hdrlen])[0] + _recv_buffer = _recv_buffer[hdrlen:] + + # read and process jsonheader + jsonheader = None + while len(_recv_buffer) < _jsonheader_len: + data = sock.recv(4096) + _recv_buffer += data + + # process_jsonheader() + jsonheader = _json_decode(_recv_buffer[:_jsonheader_len], "utf-8") + _recv_buffer = _recv_buffer[_jsonheader_len:] + for reqhdr in ( + "byteorder", + "content-length", + "content-type", + "content-encoding", + ): + if reqhdr not in jsonheader: + raise ValueError(f'Missing required header "{reqhdr}".') + + # read and process response + response = None + content_len = jsonheader["content-length"] + while len(_recv_buffer) < content_len: + data = sock.recv(4096) + _recv_buffer += data + + # process_response(): + data = _recv_buffer[:content_len] + _recv_buffer = _recv_buffer[content_len:] + if jsonheader["content-type"] == "text/json": + encoding = jsonheader["content-encoding"] + response = _json_decode(data, encoding) + print("received response", repr(response), "from", addr) + content = response + result = content.get("result") + print(f"got result: {result}") + else: + # Binary or unknown content-type + response = data + print( + f'received {jsonheader["content-type"]} response from', + addr, + ) + content = response + print(f"got response: {repr(content)}") + + sock.shutdown(socket.SHUT_RDWR) + sock.close() + + +if __name__ == '__main__': + main() \ No newline at end of file