From c9154cdb08798a2abe089b24f2924065957e2312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD=20=D0=A1=D0=BE=D0=BB=D0=BD=D1=86?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Thu, 3 Oct 2024 14:26:15 +0300 Subject: [PATCH] Code review Change name from start_http_server() to init_server_socket(). Improvements: - Change child threads to daemon type; - Send client HTTP error codes & add new HTTP codes description; - Change log output format; - Add some debug log strings; Remove: - Startup ASCII logo; --- config.py | 4 +- main.py | 22 +++------ server/__init__.py | 0 server/main.py | 120 ++++++++++++++++++++++++++------------------- server/response.py | 6 +++ 5 files changed, 84 insertions(+), 68 deletions(-) create mode 100644 server/__init__.py diff --git a/config.py b/config.py index db0fc83..dbde6b0 100644 --- a/config.py +++ b/config.py @@ -20,10 +20,10 @@ SETUP = { if SETUP["setup"]["log_to_file"]: logging.basicConfig(filename=os.path.join(os.getcwd(), "log", "main.log"), filemode="a", encoding="UTF-8", level=SETUP["setup"]["log_level"], - format="[%(name)s][%(asctime)s][%(levelname)s] %(message)s") + format="[%(asctime)s][%(levelname)s][%(name)s] %(message)s") else: logging.basicConfig(encoding="UTF-8", level=SETUP["setup"]["log_level"], - format="[%(name)s][%(asctime)s][%(levelname)s] %(message)s") + format="[%(asctime)s][%(levelname)s][%(name)s] %(message)s") urls = [ diff --git a/main.py b/main.py index 5f01f68..ef2a453 100755 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ import os import logging -from server.main import start_http_server +from server.main import init_server_socket import config @@ -12,20 +12,10 @@ if __name__ == "__main__": try: os.mkdir(os.path.join(os.getcwd(), "log")) except OSError: - pass #print("dir is exists") + log.debug("Directory \"log\" is exists") + else: + log.debug("Directory \"log\" created") - project_logotype = """ - ________ _ _ _______ _______ ______ _ _ - | ______ | | |_ | | | _____ | | _____| | ___ | | | | | - | | |_| | | | | | | |_| | | | | | | |_| |_| - | | | ||_| | | |_|___ | |___ | |___| | | | | | - | | | | |_| | | |___|_ | ___| | ____| |_| |_| - | | _ | | |_|| | _ | | | | | |_|_ | | | | - | |______| | | | |_ | | |_____| | | |_____ | | |_|_ |_|_|_| - |________| |_| |_| |_______| |_______| | | |_| |_| + log.error("Attempt to start server...") -============================== Start HTTP server ===============================""" - - log.info(project_logotype) - - start_http_server() + init_server_socket() diff --git a/server/__init__.py b/server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/main.py b/server/main.py index ef45812..7ffbeb5 100644 --- a/server/main.py +++ b/server/main.py @@ -20,8 +20,8 @@ class HTTPHandler: self.conn = conn self.addr = addr - self.read_fb = conn.makefile("rb") - self.write_fb = conn.makefile("wb") + self.read_fd = conn.makefile("rb") + self.write_fd = conn.makefile("wb") self.http_method = "" self.http_address = "" @@ -30,22 +30,38 @@ class HTTPHandler: self.data = b"" - self.pre_handle_connection() + self.__handle_connection() - def pre_handle_connection(self): + def __write_data(self, data_iter): + for chunk in data_iter: + self.write_fd.write(chunk) + self.write_fd.flush() + + def __read_data(self, size=MAX_REQLINE+1): + return self.read_fd.readline(size) + + def __close(self): + self.write_fd.close() + self.read_fd.close() + self.conn.close() + + def __handle_connection(self): """ Chain of handle first line of HTTP request """ - raw = self.read_fb.readline(MAX_REQLINE + 1) + raw = self.__read_data() if len(raw) > MAX_REQLINE: log.debug("Request line too long") - self.conn.send(b"") - self.conn.close() + + r = Response(status_code=413, data=b"Request line too long") + self.__write_data(r) + + self.__close() return + # FIXME data maybe isn't UTF-8 or just binary req_line = raw.decode().rstrip("\r\n") - # method, address, protocol = req_line_splited req_line_splited = req_line.split(" ") if len(req_line_splited) != 3: @@ -54,18 +70,29 @@ class HTTPHandler: else: log.debug("Request head too small") - self.conn.send(b"") - self.conn.close() + r = Response(status_code=400, data=b"Bad request") + self.__write_data(r) + self.__close() return - if (req_line_splited[2] == "HTTP/1.0" or - req_line_splited[2] == "HTTP/1.1"): - http_method = req_line_splited[0] + if req_line_splited[2].startswith("HTTP/"): + if (req_line_splited[2] == "HTTP/1.0" or + req_line_splited[2] == "HTTP/1.1"): + http_method = req_line_splited[0] + else: + log.debug("Unsupported HTTP version") + r = Response(status_code=505, data=b"HTTP Version Not Supported") + self.__write_data(r) + self.__close() + return else: - self.conn.send(b"") - self.conn.close() + log.debug("Protocol isn't HTTP version 1.0 or 1.1") + + r = Response(status_code=400, data=b"Bad request") + self.__write_data(r) + self.__close() return if http_method == "GET": @@ -85,8 +112,11 @@ class HTTPHandler: elif http_method == "PATCH": self.http_method = HTTPMethod.PATCH else: - self.conn.send(b"") - self.conn.close() + log.debug("Method not allowed") + + r = Response(status_code=405, data=b"Method not allowed") + self.__write_data(r) + self.__close() return if (self.http_method == HTTPMethod.GET and @@ -110,14 +140,14 @@ class HTTPHandler: self.http_address = req_line_splited[1] self.http_get_request = {} - self.http_header_handle() + self.__http_header_handle() - def http_header_handle(self): + def __http_header_handle(self): """ Chain of handle headers in HTTP request """ while True: - raw = self.read_fb.readline(MAX_REQLINE + 1) + raw = self.__read_data() if len(raw) > MAX_REQLINE: log.debug("Request header line too long") @@ -126,12 +156,13 @@ class HTTPHandler: return if raw == b"": + log.debug("Client not send data, close connection") self.conn.send(b"") self.conn.close() return - if raw == b"\r\n": - self.http_data_handle() + if raw == b"\r\n" or raw == b"\n": + self.__http_data_handle() break else: decoded_data = raw.decode("UTF-8").rstrip("\r\n") @@ -141,7 +172,7 @@ class HTTPHandler: decoded_data_split[0]: decoded_data_split[1].strip(" ") }) - def http_data_handle(self): + def __http_data_handle(self): """ Chain of receive data partitions HTTP request """ @@ -161,7 +192,7 @@ class HTTPHandler: return log.debug("Want to receive {} bytes from client".format(bytes_to_receive)) - self.data = self.read_fb.read(bytes_to_receive) + self.data = self.read_fd.read(bytes_to_receive) log.debug("Request method: %s" % self.http_method) log.debug("Requested address: %s" % self.http_address) @@ -169,9 +200,9 @@ class HTTPHandler: log.debug("Request in GET after ?: %s" % self.http_get_request) log.debug("Data: %s" % self.data) - self.send_form_data() + self.__send_form_data() - def send_form_data(self): + def __send_form_data(self): """ Chain of handle, pack & send data to client """ @@ -185,10 +216,7 @@ class HTTPHandler: if path.handler_type == HandlerType.STATIC_FILE: log.debug("Address associated with static file on path {}".format(path.link)) r = Response(data=fileiter(path.link)) - - for i in r: - self.write_fb.write(i) - self.write_fb.flush() + self.__write_data(r) elif path.handler_type == HandlerType.FUNCTION: log.debug("Address associated with function") @@ -206,44 +234,36 @@ class HTTPHandler: elif func.__code__.co_argcount == 3: func_return = func(self.http_headers, self.http_get_request, self.data) - for i in func_return: - self.write_fb.write(i) - self.write_fb.flush() + self.__write_data(func_return) else: log.warning("Address configured on server, but type not allowed URL type in URLs list") - r = Response(status_code=404, data=b"Address configured on server, but type not allowed URL type in URLs list") - for i in r: - self.write_fb.write(i) - self.write_fb.flush() + r = Response(status_code=404, + data=b"Address configured on server, but type not allowed URL type in URLs list") + self.__write_data(r) if not found: r = Response(status_code=404, data=b"Not found!") + self.__write_func(r) - for i in r: - self.write_fb.write(i) - self.write_fb.flush() + self.__close() - self.write_fb.close() - self.read_fb.close() - self.conn.close() - - -def start_http_server(): +def init_server_socket(): try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as main_server_socket: main_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + log.debug("Attempt to bind socket to 0.0.0.0:{}".format(config.SETUP["server"]["port"])) main_server_socket.bind(("0.0.0.0", config.SETUP["server"]["port"])) + log.debug("Make socket is listen") main_server_socket.listen() while True: conn, addr = main_server_socket.accept() - log.info("Accept connection from %s", addr[0]) + log.info("Accepted connection from %s", addr[0]) - thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,)) + thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,), daemon=True) thr_serv_conn.run() except KeyboardInterrupt: - log.info("Server get keyboard interrupt. Initialize server to stop") - log.info("==================== End of log ====================") + log.info("Server get keyboard interrupt, bye!") diff --git a/server/response.py b/server/response.py index d04ba84..b3f469f 100644 --- a/server/response.py +++ b/server/response.py @@ -4,8 +4,14 @@ log = logging.getLogger(__name__) STATUS_BY_CODE = { 200: "OK", + + 400: "Bad Request", 404: "Not Found", + 405: "Method Not Allowed", + 413: "Payload Too Large", + 500: "Internal Server Error", + 505: "HTTP Version Not Supported", }