From a638f39cc5336dcc6ef6fc3b5e7e6af142dda1ca Mon Sep 17 00:00:00 2001 From: pablusha Date: Sat, 9 Aug 2025 19:28:04 +0300 Subject: [PATCH] many things --- config.py | 13 +++- html/403.html | 9 +++ 404.html => html/404.html | 0 html/418.html | 9 +++ main.py | 157 +++++++++++++++++++------------------- 5 files changed, 106 insertions(+), 82 deletions(-) create mode 100644 html/403.html rename 404.html => html/404.html (100%) create mode 100644 html/418.html diff --git a/config.py b/config.py index 444d332..9bc9a1b 100644 --- a/config.py +++ b/config.py @@ -15,5 +15,14 @@ start_msg="started at " conn_msg="conn from " get_msg=" got " -e404_file="404.html" -e404_msg=" err 404 " \ No newline at end of file +err_files = { + 404: "html/404.html", + 403: "html/403.html", + 418: "html/418.html" +} + +err_msgs = { + 404: " err 404 ", + 403: " err 403 ", + 418: " err 418 (teapot) " +} \ No newline at end of file diff --git a/html/403.html b/html/403.html new file mode 100644 index 0000000..cee9707 --- /dev/null +++ b/html/403.html @@ -0,0 +1,9 @@ + +404 Forbidden + +

404 Forbidden

+
debweb
+ + + + \ No newline at end of file diff --git a/404.html b/html/404.html similarity index 100% rename from 404.html rename to html/404.html diff --git a/html/418.html b/html/418.html new file mode 100644 index 0000000..8853fa8 --- /dev/null +++ b/html/418.html @@ -0,0 +1,9 @@ + +418 teapot + +

пошолнахуй

+
debweb
+ + + + \ No newline at end of file diff --git a/main.py b/main.py index f56c76e..e4d3fcf 100644 --- a/main.py +++ b/main.py @@ -7,45 +7,64 @@ import asyncio import config import os +STATUS = { + 200: "200 OK", + 403: "403 Forbidden", + 404: "404 Not Found", + 405: "405 Method Not Allowed", + 418: "418 I'm a teapot" +} + +# file_size = os.path.getsize(file_path) + class WebServer: - def __init__(self): - self.name = config.name - self.proxied = config.proxied - - self._addr = config.addr - self._port = config.port - - self._log_file = config.log_file - self.preset_file = config.preset_file - self.directory = config.directory - - self._read_buffer = config.read_buffer - self._write_size = config.write_buffer - - self.conn_msg = config.conn_msg - self.start_msg = config.start_msg - self.get_msg = config.get_msg - - self._e404_file = config.e404_file - self.e404_msg = config.e404_msg - - async def log(self, text: str, addr: tuple=None, file: str=None) -> None: text = text.replace("", f"{addr[0]}:{addr[1]}" if addr else "") text = text.replace("", file if file else "") - if self._log_file: - async with aiofiles.open(self._log_file, mode="a") as file: + if config.log_file: + async with aiofiles.open(config.log_file, mode="a") as file: await file.write(text + "\n") else: print(text) + async def send_headers(self, writer: asyncio.StreamWriter, status: int, file_size: int, mime: str="text/html; charset=utf-8") -> None: + headers = ( + f"HTTP/1.1 {STATUS[status]}\r\n" + f"Content-Type: {mime}\r\n" + f"Content-Length: {file_size}\r\n" + f"Server: {config.name}\r\n" + "\r\n" + ) + + writer.write(headers.encode()) + await writer.drain() + + + async def send_file(self, writer: asyncio.StreamWriter, file_path: str, file_size: int): + if not os.path.isfile(file_path): + writer.write("file error") + await writer.drain() + + sent = 0 + async with aiofiles.open(file_path, "rb") as f: + while sent < file_size: + chunk = await f.read(config.write_buffer) + if not chunk: break + + writer.write(chunk) + await writer.drain() + + sent += len(chunk) + + async def handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: addr = writer.get_extra_info("peername") - await self.log(self.conn_msg, addr) - rdata = await reader.read(self._read_buffer) + await self.log(config.conn_msg, addr) + rdata = await reader.read(config.read_buffer) data = rdata.decode() + if not data: return real_addr = None @@ -54,7 +73,7 @@ class WebServer: real_addr = line[len("X-Real-IP: "):].strip() break - if real_addr and self.proxied: + if real_addr and config.proxied: addr = (real_addr, addr[1]) request = data.split("\n")[0] @@ -63,7 +82,18 @@ class WebServer: path = unquote(parts[1]) file_name = path[1:] if path.startswith('/') else path - file_path = os.path.abspath(os.path.join(self.directory, file_name)) + file_path = os.path.abspath(os.path.join(config.directory, file_name)) + + if not file_path.startswith(os.path.abspath(config.directory)): + await self.log(config.err_msgs[418], addr, file_path) + file_size = os.path.getsize(config.err_files[418]) + await self.send_headers(writer, 418, file_size) + await self.send_file(writer, config.err_files[418], file_size) + + await writer.close() + await writer.wait_closed() + return + if os.path.isfile(file_path): mime, _ = mimetypes.guess_type(file_path) @@ -71,36 +101,17 @@ class WebServer: if not mime: mime = "application/octet-stream" if mime.startswith("text"): mime += "; charset=utf-8" - headers = ( - "HTTP/1.1 200 OK\r\n" - f"Content-Type: {mime}\r\n" - f"Content-Length: {file_size}\r\n" - f"Server: {self.name}\r\n" - "\r\n" - ) - - writer.write("".join(headers).encode()) - await writer.drain() - - sent = 0 - async with aiofiles.open(file_path, "rb") as f: - while sent < file_size: - chunk = await f.read(self._write_size) - if not chunk: break - - writer.write(chunk) - await writer.drain() - - sent += len(chunk) + await self.send_headers(writer, 200, file_size, mime) + await self.send_file(writer, file_path, file_size) elif os.path.isdir(file_path): resp = "" - async with aiofiles.open(self.preset_file, "r") as f: + async with aiofiles.open(config.preset_file, "r", encoding="utf-8") as f: resp = await f.read() files = "" - base_path = os.path.relpath(file_path, self.directory).replace('\\', '/') + base_path = os.path.relpath(file_path, config.directory).replace('\\', '/') if base_path == '.': base_path = '' @@ -108,10 +119,8 @@ class WebServer: item_path = os.path.join(file_path, item) is_dir = os.path.isdir(item_path) - if base_path: - rel_path = f"{base_path}/{item}" - else: - rel_path = item + if base_path: rel_path = f"{base_path}/{item}" + else: rel_path = item if is_dir: rel_path += "/" @@ -127,37 +136,25 @@ class WebServer: resp = resp.replace("", files) resp = resp.encode() - headers = ( - "HTTP/1.1 200 OK\r\n" - f"Content-Type: text/html; charset=utf-8\r\n" - f"Server: {self.name}\r\n" - f"Content-Length: {len(resp)}\r\n" - "\r\n" - ) - writer.write(headers.encode() + resp) + await self.send_headers(writer, 200, len(resp)) + + writer.write(resp) + await writer.drain() + + else: await self.log(self.e404_msg, addr, file_path) - async with aiofiles.open(self._e404_file, "r", encoding="utf-8") as f: - content = await f.read() - response = ( - "HTTP/1.1 404 Not Found\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - f"Server: {self.name}\r\n" - "\r\n" - f"{content}" - ) - writer.write(response.encode()) - await writer.drain() + file_size = os.path.getsize(file_path) + await self.send_headers(writer, 404, file_size) + await self.send_file(writer, self._e404_file, file_size) writer.close() await writer.wait_closed() return - await self.log(self.get_msg, addr, file_path) - await writer.drain() - + await self.log(config.get_msg, addr, file_path) writer.close() await writer.wait_closed() @@ -165,10 +162,10 @@ class WebServer: async def start(self) -> None: server = await asyncio.start_server( self.handle, - self._addr, - self._port + config.addr, + config.port ) - await self.log(f"{self.start_msg}", (self._addr, self._port)) + await self.log(f"{config.start_msg}", (config.addr, config.port)) async with server: await server.serve_forever()