This repository has been archived by the owner on Jun 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
137 lines (100 loc) · 3.66 KB
/
main.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from abc import ABC, abstractmethod
from datetime import datetime
from http.server import BaseHTTPRequestHandler, HTTPServer
from json import dump, load
from json.decoder import JSONDecodeError
from logging import INFO, basicConfig, info
from mimetypes import guess_type
from pathlib import Path
from socket import AF_INET, SOCK_DGRAM, socket
from threading import Thread
from urllib.parse import unquote_plus, urlparse
ROOT = Path('www')
ADDRESS = '127.0.0.1', 5000
class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
code = 200
path = urlparse(self.path).path
path = ROOT.joinpath('index.html' if path == '/' else path[1:])
if not path.exists():
code = 404
path = ROOT.joinpath('error.html')
self.send_response(code)
self.send_header('Content-Type', guess_type(path)[0] or 'text/plain')
self.end_headers()
with open(path, 'rb') as file:
self.wfile.write(file.read())
def do_POST(self) -> None:
if size := self.headers.get('Content-Length'):
client = SocketServer.init()
client.sendto(self.rfile.read(int(size)), ADDRESS)
client.close()
self.send_response(302)
self.send_header('Location', '/message.html')
self.end_headers()
class Server(ABC, Thread):
def __init__(self, name: str, host: str, port: int) -> None:
super().__init__(name=f'{name} server')
self._address = host, port
self.start()
def run(self) -> None:
self._init()
try:
self._up()
except KeyboardInterrupt:
info('Stopped.')
finally:
self._down()
@abstractmethod
def _init(self) -> None:
...
@abstractmethod
def _up(self) -> None:
...
@abstractmethod
def _down(self) -> None:
...
class WebServer(Server):
def _init(self) -> None:
self.__server = HTTPServer(self._address, Handler)
info(f'Navigate to http://{self._address[0]}:{self._address[1]} to '
'visit your website.')
def _up(self) -> None:
self.__server.serve_forever()
def _down(self) -> None:
self.__server.server_close()
class SocketServer(Server):
@staticmethod
def init() -> socket:
return socket(AF_INET, SOCK_DGRAM)
def _init(self) -> None:
self.__server = self.init()
self.__server.bind(self._address)
def _up(self) -> None:
while True:
content, _ = self.__server.recvfrom(1_024)
items = content.decode().split('&')
items = [map(unquote_plus, item.split('=')) for item in items]
items = {key: value for key, value in items if value}
if items:
if (path := ROOT.joinpath('storage/data.json')).exists():
with open(path, encoding='utf-8') as file:
try:
data = load(file)
except JSONDecodeError:
...
if 'data' not in locals() or type(data) is not dict:
data = {}
# E.g.: 2022-10-29 20:20:58.020261
key = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
data[key] = items
with open(path, 'w', encoding='utf-8') as file:
dump(data, file, ensure_ascii=False, indent=2)
def _down(self) -> None:
self.__server.close()
def main() -> None:
basicConfig(level=INFO, format='%(threadName)s: %(message)s')
WebServer('HTTP', '0.0.0.0', 3000)
SocketServer('Socket', *ADDRESS)
if __name__ == '__main__':
main()