Many windows compatibility tweaks, new source and updated logger
This commit is contained in:
parent
756d6d3dd4
commit
1cfc561301
@ -18,3 +18,7 @@
|
||||
|
||||
- [SqlAlchemy session generator](https://stackoverflow.com/a/71053353)
|
||||
- [Error handling decorator](https://chatgpt.com/share/67a46109-d38c-8005-ac36-677e6511ddcd)
|
||||
|
||||
### Platform specific problems
|
||||
|
||||
- [Windows sends partial data](https://stackoverflow.com/a/31754798)
|
@ -46,8 +46,6 @@ class BankNode():
|
||||
|
||||
# Handle windows related signals
|
||||
if sys.platform == "win32":
|
||||
signal.signal(signal.CTRL_C_EVENT, self.gracefully_exit)
|
||||
signal.signal(signal.CTRL_BREAK_EVENT, self.gracefully_exit)
|
||||
signal.signal(signal.SIGBREAK, self.gracefully_exit)
|
||||
|
||||
def start(self):
|
||||
@ -78,6 +76,7 @@ class BankNode():
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_server:
|
||||
socket_server.bind((self.config.ip, port))
|
||||
socket_server.listen()
|
||||
socket_server.setblocking(True)
|
||||
self.socket_server = socket_server
|
||||
self.logger.info("Listening on %s:%s", self.config.ip, port)
|
||||
|
||||
|
@ -8,21 +8,20 @@ import sys
|
||||
from bank_protocol.command_handler import CommandHandler
|
||||
from core import Request, Response, BankNodeConfig
|
||||
from core.exceptions import BankNodeError
|
||||
from utils.logger import setup_logger
|
||||
|
||||
|
||||
class BankWorker(multiprocessing.Process):
|
||||
def __init__(self, client_socket: socket.socket, client_address: Tuple, config: BankNodeConfig):
|
||||
super().__init__()
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
self.client_socket = client_socket
|
||||
self.client_socket.settimeout(config.client_idle_timeout)
|
||||
self.client_address = client_address
|
||||
|
||||
self.command_handler = CommandHandler(config)
|
||||
self.config = config
|
||||
|
||||
self.logger = None
|
||||
self.command_handler = None
|
||||
|
||||
def __setup_signals(self):
|
||||
self.logger.debug("Setting up exit signal hooks for worker")
|
||||
|
||||
@ -32,27 +31,60 @@ class BankWorker(multiprocessing.Process):
|
||||
|
||||
# Handle windows related signals
|
||||
if sys.platform == "win32":
|
||||
signal.signal(signal.CTRL_C_EVENT, self.gracefully_exit_worker)
|
||||
signal.signal(signal.CTRL_BREAK_EVENT, self.gracefully_exit_worker)
|
||||
signal.signal(signal.SIGBREAK, self.gracefully_exit_worker)
|
||||
|
||||
def run(self):
|
||||
# Logging behaves weirdly with processes on windows
|
||||
# and loses its configuration by default
|
||||
# -> Set it up again in the fresh process
|
||||
if sys.platform == "win32":
|
||||
setup_logger(self.config.verbosity)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.command_handler = CommandHandler(self.config)
|
||||
|
||||
self.client_socket.settimeout(self.config.client_idle_timeout)
|
||||
self.client_socket.setblocking(True)
|
||||
|
||||
self.__setup_signals()
|
||||
|
||||
with self.client_socket:
|
||||
self.serve_client()
|
||||
|
||||
self.logger.debug("Closing process for %s", self.client_address[0])
|
||||
|
||||
def serve_client(self):
|
||||
buffer = ""
|
||||
|
||||
while True:
|
||||
try:
|
||||
raw_request = self.client_socket.recv(1024).decode("utf-8")
|
||||
data = self.client_socket.recv(1024).decode("utf-8")
|
||||
|
||||
if not raw_request:
|
||||
if not data:
|
||||
self.logger.debug("%s disconnected", self.client_address[0])
|
||||
break
|
||||
|
||||
request = Request(raw_request)
|
||||
self.logger.debug("Received request from %s - %s", self.client_address[0], request)
|
||||
buffer += data
|
||||
self.logger.debug("Buffer updated: %r", buffer)
|
||||
|
||||
response: Response = self.command_handler.execute(request) + "\n\r"
|
||||
if "\r\n" in buffer:
|
||||
self.logger.debug("CRLF detected")
|
||||
request_data, buffer = buffer.split("\r\n", 1)
|
||||
elif "\n" in buffer:
|
||||
self.logger.debug("LF detected")
|
||||
request_data, buffer = buffer.split("\n", 1)
|
||||
elif "\r" in buffer:
|
||||
self.logger.debug("CR detected")
|
||||
request_data, buffer = buffer.split("\r", 1)
|
||||
else:
|
||||
continue
|
||||
|
||||
self.logger.debug("Processing request: %r", request_data)
|
||||
|
||||
request = Request(request_data)
|
||||
response: Response = self.command_handler.execute(request) + "\r\n"
|
||||
self.client_socket.sendall(response.encode("utf-8"))
|
||||
self.logger.debug("Response sent to %s", self.client_address[0])
|
||||
|
||||
except socket.timeout:
|
||||
self.logger.debug("Client was idle for too long. Ending connection")
|
||||
@ -74,8 +106,6 @@ class BankWorker(multiprocessing.Process):
|
||||
response = "ER Internal server error\n\r"
|
||||
break
|
||||
|
||||
self.logger.debug("Closing process for %s", self.client_address[0])
|
||||
|
||||
def gracefully_exit_worker(self, signum, _):
|
||||
"""Log the signal caught and exit with status 0"""
|
||||
|
||||
|
@ -3,18 +3,22 @@ import socket
|
||||
import threading
|
||||
import logging
|
||||
|
||||
class BankScanner():
|
||||
from core.peer import Peer
|
||||
|
||||
|
||||
class BankScanner(threading.Thread):
|
||||
def __init__(self, host: str, port_start: int, port_end: int):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.host = host
|
||||
self.port_start = port_start
|
||||
self.port_end = port_end
|
||||
self.peer = Peer()
|
||||
|
||||
def scan(self) -> Optional[socket]:
|
||||
threads = []
|
||||
|
||||
for port in range(self.port_start, self.port_end + 1):
|
||||
t = threading.Thread(target=self.__probe_for_open_ports, args=(self.host, port,), name=f"ScannerThread-{self.host}:{port}")
|
||||
t = threading.Thread(target=self.__probe_for_open_ports, args=(self.host, port,), name=f"BankScannerThread-{self.host}:{port}")
|
||||
threads.append(t)
|
||||
|
||||
def __probe_for_open_ports(self, host: str, port: int) -> Optional[socket]:
|
||||
|
@ -36,6 +36,8 @@ class CommandHandler:
|
||||
self.logger.warning("Unknown command %s", request.command_code)
|
||||
raise BankNodeError(f"Unknown command {request.command_code}")
|
||||
|
||||
self.logger.debug("Serving %s", request.command_code)
|
||||
|
||||
command = self.registered_commands[request.command_code]
|
||||
try:
|
||||
response = command(request, self.config)
|
||||
|
@ -0,0 +1,2 @@
|
||||
class Peer():
|
||||
pass
|
@ -1,15 +1,20 @@
|
||||
import re
|
||||
|
||||
from bank_protocol.exceptions import InvalidRequest
|
||||
import logging
|
||||
|
||||
|
||||
class Request():
|
||||
|
||||
def __init__(self, raw_request: str):
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if re.match(r"^[A-Z]{2}$", raw_request):
|
||||
logger.debug("Found 2 char command")
|
||||
self.command_code = raw_request[0:2] # Still take the first 2 characters, because of lingering crlf
|
||||
self.body = None
|
||||
elif re.match(r"^[A-Z]{2} .+", raw_request):
|
||||
logger.debug("Found command with arguments")
|
||||
command_code: str = raw_request[0:2]
|
||||
body: str = raw_request[3:-1] or ""
|
||||
|
||||
|
@ -4,7 +4,7 @@ import logging
|
||||
|
||||
def setup_logger(verbosity: str):
|
||||
if verbosity == "DEBUG":
|
||||
log_format = "[ %(levelname)s / %(processName)s ] - %(name)s:%(lineno)d - %(message)s"
|
||||
log_format = "[ %(levelname)s / %(processName)s ] - %(module)s/%(filename)s:%(lineno)d - %(message)s"
|
||||
else:
|
||||
log_format = "[ %(levelname)s ] - %(message)s"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user