diff --git a/lib/nntp/tiny/server.py b/lib/nntp/tiny/server.py index 63dec61..1270119 100644 --- a/lib/nntp/tiny/server.py +++ b/lib/nntp/tiny/server.py @@ -1,10 +1,12 @@ import enum -import socket -import ssl import threading +import socket +import selectors +import ssl from configparser import ConfigParser +from nntp.tiny.config import ConfigException from nntp.tiny.db import Database from nntp.tiny.newsgroup import Newsgroup from nntp.tiny.session import Session @@ -37,28 +39,52 @@ class Server(): for newsgroup in db.query(Newsgroup).each(): self.newsgroups[newsgroup.name.casefold()] = newsgroup - def run(self): - host = self.config['listen']['host'] - port = int(self.config['listen']['port']) - - listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + def listen(self, host: str, port: int, af: int): + listener = socket.socket(af, socket.SOCK_STREAM) listener.bind((host, port)) listener.listen() if self.sslctx: - listener = self.sslctx.wrap_socket(listener, server_side=True) + return self.sslctx.wrap_socket(listener, server_side=True) + + return listener + + def accept(self, listener): + try: + sock, addr = listener.accept() + + def spawn(): + session = Session(self, sock) + session.handle() + + thread = threading.Thread(target=spawn) + thread.start() + except ssl.SSLEOFError as e: + pass + + def run(self): + port = int(self.config['listen']['port']) + + listeners = list() + + if self.config.has_option('listen', 'host_inet'): + host = self.config.get('listen', 'host_inet') + listeners.append(self.listen(host, port, socket.AF_INET)) + + if self.config.has_option('listen', 'host_inet6'): + host = self.config.get('listen', 'host_inet6') + listeners.append(self.listen(host, port, socket.AF_INET6)) + + if len(listeners) == 0: + raise ConfigException("No listener hosts specified") + + sel = selectors.DefaultSelector() + + for listener in listeners: + sel.register(listener, selectors.EVENT_READ) while True: - try: - sock, addr = listener.accept() + events = sel.select() - def spawn(): - session = Session(self, sock) - session.handle() - - thread = threading.Thread(target=spawn) - thread.start() - except ssl.SSLEOFError as e: - pass - - listener.close() + for key, ev in events: + self.accept(key.fileobj)