-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnntp_server.py
86 lines (73 loc) · 3 KB
/
nntp_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
import asyncio
from asyncio import StreamReader, StreamWriter, Task
from logging import Logger
from typing import List, Optional, Union
from backend.base import Backend
from backend.dtn7sqlite.models import Article, Newsgroup
from client_connection import ClientConnection
from logger import global_logger
class AsyncNNTPServer:
def __init__(self, hostname: str, port: int) -> None:
self.hostname: str = hostname
self.port: int = port
self.clients: dict = {}
self.logger: Logger = global_logger()
self._terminated: bool = False
self._empty_token_counter: int = 0
self._cmd_args: Optional[List[str]] = None
self._selected_group: Optional[Newsgroup] = None # field to save a selected group
self._selected_article: Optional[Article] = None # field to save a selected group
self._post_mode: bool = False
self._article_buffer: List[str] = []
self._command: Optional[str]
self._backend: Optional[Backend] = None
self._sockserver = None
def send(self, writer: StreamWriter, send_obj: Union[List[str], str]) -> None:
if type(send_obj) is str:
self.logger.debug(f"server > {send_obj}")
writer.write(f"{send_obj}\r\n".encode(encoding="utf-8"))
else:
send_obj.append(".")
for line in send_obj:
self.logger.debug(f"server > {line}")
writer.write(f"{line}\r\n".encode(encoding="utf-8"))
async def _accept_client(self, reader: StreamReader, writer: StreamWriter) -> None:
"""
Accepts a new client and transfers control of the reader and writer to it
"""
client_conn: ClientConnection = ClientConnection(server=self, reader=reader, writer=writer)
task: Task = asyncio.create_task(client_conn.handle_client())
self.clients[task] = (reader, writer)
def client_done(tsk: asyncio.Task):
self.logger.info("Discarding connection")
self.clients[tsk].stop()
del self.clients[tsk]
task.add_done_callback(client_done)
addr: str
port: int
addr, port = writer.get_extra_info(name="peername")
self.logger.info(f"Connected to client at {addr}:{port}")
async def start_serving(self):
self._sockserver = await asyncio.start_server(
client_connected_cb=self._accept_client,
host=self.hostname,
port=self.port,
reuse_address=True,
)
if self.backend is not None:
await self.backend.start()
def stop_serving(self) -> None:
self._terminated = True
if self.backend is not None:
self.backend.stop()
if self._sockserver is not None:
self._sockserver.close()
@property
def backend(self):
return self._backend
@backend.setter
def backend(self, new_backend: Backend):
self._backend = new_backend
@property
def terminated(self) -> bool:
return self._terminated