Add basic client-server implementation

This commit is contained in:
Jake Howard 2018-12-07 20:35:34 +00:00
parent 54f8efa443
commit 498a871c70
Signed by: jake
GPG key ID: 57AFB45680EDD477
5 changed files with 115 additions and 0 deletions

10
ipc_unix/client.py Normal file
View file

@ -0,0 +1,10 @@
import socket
from ipc_unix.utils import read_payload
import ujson
def send_to(socket_path, data):
with socket.socket(socket.AF_UNIX, type=socket.SOCK_STREAM) as sock:
sock.connect(socket_path)
sock.sendall(ujson.dumps(data).encode() + b"\n")
return read_payload(sock)

40
ipc_unix/server.py Normal file
View file

@ -0,0 +1,40 @@
import socketserver
import threading
import ujson
from ipc_unix.utils import read_payload
class RequestHandler(socketserver.BaseRequestHandler):
def handle_request(self, request):
raise NotImplementedError("Failed to override `handle_request`")
def handle(self):
data = read_payload(self.request)
response = self.handle_request(data)
self.request.sendall(ujson.dumps(response).encode())
class Server:
def __init__(self, socket_path):
class InstanceRequestHandler(RequestHandler):
handle_request = self.handle_request
self.server = socketserver.UnixStreamServer(socket_path, InstanceRequestHandler)
def serve_forever(self):
self.server.serve_forever()
def serve_in_thread(self):
thread = threading.Thread(target=self.serve_forever)
thread.start()
return thread
def shutdown(self):
self.server.shutdown()
def close(self):
self.shutdown()
self.server.server_close()
def handle_request(self, request):
raise NotImplementedError("Must override `handle_request`")

11
ipc_unix/utils.py Normal file
View file

@ -0,0 +1,11 @@
import ujson
def read_payload(payload):
data = b""
while b"\n" not in data:
message = payload.recv(4096)
if message == b"":
break
data += message
return ujson.loads(data)

14
tests/__init__.py Normal file
View file

@ -0,0 +1,14 @@
from ipc_unix import server
import tempfile
import os
class EchoServer(server.Server):
def handle_request(self, request):
return request
def get_random_path():
_, temp_file_path = tempfile.mkstemp()
os.remove(temp_file_path)
return temp_file_path

40
tests/test_server.py Normal file
View file

@ -0,0 +1,40 @@
from unittest import TestCase
from tests import EchoServer, get_random_path
from ipc_unix import client
from functools import partial
class BasicServerTestCase(TestCase):
def setUp(self):
self.socket_path = get_random_path()
self.server = EchoServer(self.socket_path)
self.server.serve_in_thread()
self.send_to_client = partial(client.send_to, self.socket_path)
def tearDown(self):
self.server.shutdown()
def test_sending_dict(self):
data = {"foo": "bar"}
response = self.send_to_client(data)
self.assertEqual(response, data)
def test_sending_array(self):
data = ["foo", "bar"]
response = self.send_to_client(data)
self.assertEqual(response, data)
def test_sending_full_buffer(self):
data = ["foo"] * 4096 # Pad out the buffer
response = self.send_to_client(data)
self.assertEqual(response, data)
def test_sending_empty_payload(self):
response = self.send_to_client("")
self.assertEqual(response, "")
def test_multiple_send_to_same_server(self):
data = {"foo": "bar"}
for _ in range(10):
response = self.send_to_client(data)
self.assertEqual(response, data)