Skip to content

Instantly share code, notes, and snippets.

@frankgould
Last active November 29, 2019 14:19
Show Gist options
  • Save frankgould/88a85d34c61ad961fdd836195ae04448 to your computer and use it in GitHub Desktop.
Save frankgould/88a85d34c61ad961fdd836195ae04448 to your computer and use it in GitHub Desktop.
This is the local rover client's library.
#!/usr/bin/env python3
# rover_libclient.py is the REMOTE socket library and Message class
import sys, selectors, json, struct
class Message:
def __init__(self, selector, sock, addr, name, request):
self.selector = selector
self.sock = sock
self.addr = addr
self.name = name
self.request = request
self._recv_buffer = b""
self._send_buffer = b""
self._jsonheader_len = None
self.jsonheader = {}
self.recv_string = None
self.json_content = {}
self._request_queued = False
def read(self):
try:
data = self.sock.recv(1024) # Should be ready to read
except BlockingIOError: # Resource temporarily unavailable (errno EWOULDBLOCK)
print("libclient BlockingIOError Resource temporarily unavailable")
except Exception as e:
print("libclient error: self.sock.recv() exception for " + str(e))
pass
else:
if data:
# process protoheader
self._recv_buffer += data
hdrlen = 2
if len(self._recv_buffer) >= hdrlen:
self._jsonheader_len = struct.unpack(">H", self._recv_buffer[:hdrlen])[0]
self.recv_string = str(self._recv_buffer[hdrlen:])
self.recv_string = self.recv_string.replace("b","")
self.recv_string = self.recv_string.replace("'","")
# process jsonheader
hdrlen = self._jsonheader_len
if len(self._recv_buffer) >= hdrlen:
recv_data = self.recv_string[:hdrlen]
self.jsonheader = eval(recv_data)
for reqhdr in ("content-length","content-encoding",):
if reqhdr not in self.jsonheader:
raise ValueError(f'Missing required header "{reqhdr}".')
# process response
content_len = self.jsonheader["content-length"]
if len(self.recv_string) >= content_len:
recv_data = self.recv_string[-content_len:]
self.json_content = eval(recv_data)
self._recv_buffer = b""
else:
print('Rover Server connection closed.\n')
sys.exit(0)
def write(self):
if not self._request_queued:
self.queue_request()
events = selectors.EVENT_WRITE
self.selector.modify(self.sock, events, data=self)
if self._send_buffer:
try:
# Should be ready to write
sent = self.sock.send(self._send_buffer)
except BlockingIOError: # Resource temporarily unavailable (errno EWOULDBLOCK)
print("BlockingIOError")
pass
except Exception as e:
print(f"error: sending",f"{self.addr}: {repr(e)}",)
sys.exit(0)
self._send_buffer = self._send_buffer[sent:]
events = selectors.EVENT_READ
self.selector.modify(self.sock, events, data=self)
self._jsonheader_len = None # clear json values for last read
self.jsonheader = None
self.json_content = {}
def broadcast(self):
if self._send_buffer:
try:
# Should be ready to write
sent = self.client.send(self._send_buffer)
except BlockingIOError: # Resource temporarily unavailable (errno EWOULDBLOCK)
print('BlockingIOError occurred and message not sent.')
pass
self._send_buffer = self._send_buffer[sent:]
events = selectors.EVENT_READ
self.selector.modify(self.client, events, data=self)
def close(self):
print("libclient closing connection to", self.addr)
try:
self.selector.unregister(self.sock)
except Exception as e:
print(f"error: selector.unregister() exception for",f"{self.addr}: {repr(e)}",)
try:
self.sock.close()
except OSError as e:
print(f"error: socket.close() exception for",f"{self.addr}: {repr(e)}",)
finally:
# Delete reference to socket object for garbage collection
self.sock = None
def queue_request(self):
encoding_type = "utf-8"
message_content = self.request
json_message = json.dumps(message_content, ensure_ascii=False).encode(encoding_type)
json_header = {"content-encoding": encoding_type,"content-length": len(json_message)}
json_header_bytes = json.dumps(json_header, ensure_ascii=False).encode(encoding_type)
message_header = struct.pack(">H", len(json_header_bytes))
message = message_header + json_header_bytes + json_message
self._send_buffer += message
self._request_queued = True
events = selectors.EVENT_WRITE
self.selector.modify(self.sock, events, data=self)
def process_events(self, mask):
if mask & selectors.EVENT_READ:
self.read()
if mask & selectors.EVENT_WRITE:
self.write()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment