#!/usr/bin/python3.12 import threading import socket import logging import config MAX_REQLINE = 64 * 1024 STATUS_CODE = { 200: "OK", 404: "Not Found", 500: "Internal Server Error", } class Response: def __init__(self, status_code=200, additional_headers={}, data=None): self.status_code = status_code self.additional_headers = additional_headers self.data = data def get(self): resp = f"HTTP/1.1 {self.status_code} {STATUS_CODE[self.status_code]}\r\n" resp += "Server: cnserv\r\n" resp += "Connection: close\r\n" resp += "\r\n" resp = resp.encode("UTF-8") if self.data: resp += self.data return resp class HTTPHandler: http_type = "" http_address = "" headers = {} data = b"" def __init__(self, conn, addr): self.conn = conn self.addr = addr self.read_fb = conn.makefile("rb") self.write_fb = conn.makefile("wb") self.pre_handle_connection() def pre_handle_connection(self): raw = self.read_fb.readline(MAX_REQLINE + 1) if len(raw) > MAX_REQLINE: logging.debug("Request line too long") self.conn.send(b"") self.conn.close() return req_line = raw.decode().rstrip("\r\n") req_line_splited = req_line.split(" ") if len(req_line_splited) != 3: logging.debug("Request head too long") self.conn.send(b"") self.conn.close() return if req_line_splited[2] == "HTTP/1.0" or req_line_splited[2] == "HTTP/1.1": self.http_type = req_line_splited[0] self.http_address = req_line_splited[1] self.http_header_handle() else: self.conn.send(b"") self.conn.close() def http_header_handle(self): while True: raw = self.read_fb.readline(MAX_REQLINE + 1) if len(raw) > MAX_REQLINE: logging.debug("Request header line too long") self.conn.send(b"") self.conn.close() return if raw == b"": self.conn.send(b"") self.conn.close() return if raw == b"\r\n": self.http_data_handle() break else: decoded_data = raw.decode("UTF-8").rstrip("\r\n") decoded_data_split = decoded_data.split(":", 1) self.headers.update({decoded_data_split[0]: decoded_data_split[1].strip(" ")}) def http_data_handle(self): # TODO: Get from headers Content-Length and get body from request """ raw = self.read_fb.readline(1024) if len(raw) == 1024: while True: self.data += raw if len(raw) < 1024: break else: raw = self.read_fb.readline(1024) """ logging.debug(self.http_type) logging.debug(self.http_address) logging.debug(self.headers) logging.debug(self.data) self.send_form_data() def send_form_data(self): if self.http_type == "GET": if self.http_address == "/": r = Response(data=b"Tested!") self.write_fb.write(r.get()) elif self.http_address == "/server-status": form_data = "{\"server-name\": \"cnserv\", \"state\": \"running\"}" r = Response(data=form_data.encode("UTF-8")) self.write_fb.write(r.get()) else: form_data = "Not found!" r = Response(status_code=404, data=form_data.encode("UTF-8")) self.write_fb.write(r.get()) self.write_fb.flush() self.write_fb.close() self.read_fb.close() self.conn.close() if __name__ == "__main__": logging.basicConfig(filename="main.log", filemode="a", encoding="UTF-8", level=logging.DEBUG, format="[%(asctime)s][%(levelname)s] %(message)s") logging.info("=== Separator ===") 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) main_server_socket.bind(("0.0.0.0", config.SETUP["server"]["port"])) main_server_socket.listen() while True: conn, addr = main_server_socket.accept() logging.info("Accept connection from %s", addr[0]) thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,)) thr_serv_conn.run() except KeyboardInterrupt: logging.info("Server get keyboard interrupt. Initialize server to stop")