Add basic client-server implementation
This commit is contained in:
parent
54f8efa443
commit
498a871c70
5 changed files with 115 additions and 0 deletions
10
ipc_unix/client.py
Normal file
10
ipc_unix/client.py
Normal 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
40
ipc_unix/server.py
Normal 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
11
ipc_unix/utils.py
Normal 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
14
tests/__init__.py
Normal 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
40
tests/test_server.py
Normal 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)
|
Reference in a new issue