diff --git a/lesson04/client/__main__.py b/lesson04/client/__main__.py new file mode 100644 index 0000000..713d571 --- /dev/null +++ b/lesson04/client/__main__.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 +__author__ = "Андрей Петров" + +""" +клиент отправляет запрос серверу; +сервер отвечает соответствующим кодом результата. Клиент и сервер должны быть реализованы в виде отдельных скриптов, +содержащих соответствующие функции. + +Функции клиента: +сформировать presence-сообщение; +отправить сообщение серверу; +получить ответ сервера; +разобрать сообщение сервера; +параметры командной строки скрипта client.py []: addr — ip-адрес сервера; +port — tcp-порт на сервере, +по умолчанию 7777. + +""" +import re +import sys +import argparse +import socket +import json +from datetime import datetime + + +def createParser(): + parser = argparse.ArgumentParser() + parser.add_argument('host', nargs='?', default='localhost') + parser.add_argument('port', nargs='?', default='7777') + return parser + + +parser = createParser() +args = parser.parse_args(sys.argv[1:]) + +port = int(re.search('[0-9]{2,}', args.port).group(0)) + +sock = socket.socket() +sock.connect((args.host, port)) + +# сформировать presence-сообщение; +# В формате JIM +msg_presence = json.dumps( + { + "action": "presence", + "time": datetime.now().timestamp(), + "type": "status", + "user": { + "account_name": "User Name", + "status": "Yep, I am here!" + } + } +) + +msg_lower = json.dumps( + { + "action": "lower_text", + "data": 'GGGGG' + } +) + +# отправить сообщение серверу; +sock.send(msg_presence.encode()) +#sock.send(msg_lower.encode()) + +# получить ответ сервера; +data = sock.recv(1024) +response = json.loads( + data.decode('utf-8') +) +# разобрать сообщение сервера; +if response.get('response') == 200: + print( + f"Response Message: {response.get('msg')}" + ) +else: + print( + f"Error: {response.get('error')}" + ) +sock.close() diff --git a/lesson04/server/__main__.py b/lesson04/server/__main__.py new file mode 100644 index 0000000..c72ea45 --- /dev/null +++ b/lesson04/server/__main__.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 +__author__ = "Андрей Петров" + +""" +клиент отправляет запрос серверу; +сервер отвечает соответствующим кодом результата. Клиент и сервер должны быть реализованы в виде отдельных скриптов, +содержащих соответствующие функции. + +Функции сервера: +принимает сообщение клиента; +формирует ответ клиенту; +отправляет ответ клиенту; +имеет параметры командной строки: -p — TCP-порт для работы (по умолчанию использует 7777); +-a — IP-адрес для прослушивания (по умолчанию слушает все доступные адреса). +""" + +import sys +import os +import json +import socket +import argparse +from routes import get_server_routes + + +def createParser(): + parser = argparse.ArgumentParser() + parser.add_argument('-a', '--addr', nargs='?', default='') + parser.add_argument('-p', '--port', nargs='?', default='7777') + return parser + + +parser = createParser() +args = parser.parse_args(sys.argv[1:]) + + +sock = socket.socket() +sock.bind((args.addr, int(args.port))) +sock.listen(5) + + +## Без этого не работал код из урока +os.chdir('server') +sys.path.append(os.getcwd()) +## + +while True: + # принимает сообщение клиента; + client, address = sock.accept() + + data = client.recv(1024) + request = json.loads( + data.decode('utf-8') + ) + + # формирует ответ клиенту; + + client_action = request.get('action') + + resolved_routes = list( + filter( + lambda itm: itm.get('action') == client_action, + get_server_routes() + ) + ) + + route = resolved_routes[0] if resolved_routes else None + if route: + controller = route.get('controller') + response = controller(request) + + else: + response = { + "response": 400, + "error": "Wrong action, try again" + } + + # отправляет ответ клиенту; + client.send(json.dumps(response).encode('utf-8')) + client.close() diff --git a/lesson04/server/routes.py b/lesson04/server/routes.py new file mode 100644 index 0000000..3451d82 --- /dev/null +++ b/lesson04/server/routes.py @@ -0,0 +1,20 @@ +import os +from importlib import import_module +from functools import reduce + + +def get_server_routes(): + return reduce( + lambda routes, module: routes + getattr(module, 'routes', []), + reduce( + lambda modules, dir: modules + [import_module(f'{dir}.routes')], + filter( + lambda itm: os.path.isdir(itm) and itm != '__pycache__' and itm != '.pytest_cache', + os.listdir() + ), + [] + ), + [] + ) + + diff --git a/lesson04/server/test_module/controllers.py b/lesson04/server/test_module/controllers.py new file mode 100644 index 0000000..4b1996d --- /dev/null +++ b/lesson04/server/test_module/controllers.py @@ -0,0 +1,5 @@ +def send_presence(request): + return { + "response": 200, + "msg": f"Hi {request.get('user')['account_name']}" + } diff --git a/lesson04/server/test_module/routes.py b/lesson04/server/test_module/routes.py new file mode 100644 index 0000000..1eb5c4c --- /dev/null +++ b/lesson04/server/test_module/routes.py @@ -0,0 +1,6 @@ +from .controllers import send_presence + + +routes = [ + {'action': 'presence', 'controller': send_presence} +] \ No newline at end of file diff --git a/lesson04/tests/test_server.py b/lesson04/tests/test_server.py new file mode 100644 index 0000000..46ed84b --- /dev/null +++ b/lesson04/tests/test_server.py @@ -0,0 +1,22 @@ +import sys +sys.path.append('../') + +from server.test_module.controllers import send_presence + +REQUEST = { + "action": "presence", + "type": "status", + "user": { + "account_name": "User Name", + "status": "Yep, I am here!" + } + } + +RESPONSE = { + "response": 200, + "msg": "Hi User Name" + } + + +def presence_msg_request_to_response(): + assert send_presence(REQUEST) == RESPONSE