This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
/
server.py
115 lines (94 loc) · 4.37 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import sys
from datetime import datetime
from json import JSONDecodeError
from loguru import logger
from starlette.applications import Starlette
from starlette.websockets import WebSocket
from starlette.websockets import WebSocketDisconnect
from mod.config.handler import read_config
from mod.config.models import ConfigModel
from mod.db.dbhandler import DBHandler
from mod.log_handler import add_logging
from mod.protocol.worker import MTProtocol
class MoreliaServer:
_starlette_app: Starlette
_config_options: ConfigModel
_database: DBHandler
def __init__(self):
self._config_options = read_config()
add_logging(self._config_options)
self._database = DBHandler(uri=self._config_options.database.url)
self._database.create_table()
self._starlette_app = Starlette()
self._starlette_app.add_websocket_route("/ws", self._ws_endpoint)
self._starlette_app.add_event_handler("startup", self._on_start)
def get_starlette_app(self):
return self._starlette_app
def _on_start(self):
logger.info("Server started")
logger.info(f"Started time {datetime.now()}")
async def _ws_endpoint(self, websocket: WebSocket):
"""
Responsible for establishing a websocket connection.
Notes:
Waiting for client to connect via websockets, after which receive a
request from client as a JSON-object create request object and pass
to class MainHandler.
After MTProtocol or MatrixProtocol processing request, "get_response"
method generates response in JSON-object format.
After disconnecting the client (by decision of client or error)
must interrupt cycle otherwise the next clients will not be able
to connect.
`code = 1000` - normal session termination
Args:
websocket(WebSocket):
Returns:
websocket(WebSocket):
"""
# Waiting for the client to connect via websockets
await websocket.accept()
if websocket.client is not None:
logger.info("".join(("Clients information: ",
"host: ", str(websocket.client.host),
" port: ", str(websocket.client.port))))
logger.debug(f"Websocket scope: {str(websocket.scope)}")
while True:
try:
# Receive a request from the client as a JSON object
data = await websocket.receive_json()
logger.success("Receive a request from client")
logger.debug(f"Request: {str(data)}")
# create a "client" object and pass the request body to
# it as a parameter. The "get_response" method generates
# a response in JSON-object format.
request = MTProtocol(request=data,
database=self._database,
config_option=self._config_options)
await websocket.send_text(request.get_response())
logger.info("Response sent to client")
# After disconnecting the client (by the decision of the client,
# the error) must interrupt the cycle otherwise the next clients
# will not be able to connect.
except WebSocketDisconnect as STATUS:
logger.debug(f"Disconnection status: {str(STATUS)}")
break
except (RuntimeError, JSONDecodeError) as ERROR:
CODE = 1002
logger.exception(f"Runtime or Decode error: {str(ERROR)}")
await websocket.close(CODE)
logger.info(f"Close with code: {CODE}")
break
else:
if websocket.client_state.value == 0:
CODE = 1000 # normal session termination
await websocket.close(CODE)
logger.info(f"Close with code: {CODE}")
if __name__ == "__main__":
print("to start the server, write the following command in the console:")
print("uvicorn server:app --host 0.0.0.0 --port 8000 --reload "
"--use-colors --http h11 --ws websockets &")
else:
module_that_imported_use_uvicorn = bool(sys.modules.get("uvicorn"))
if module_that_imported_use_uvicorn:
_server = MoreliaServer()
app = MoreliaServer().get_starlette_app()