Compare commits
5 Commits
1.1.2
...
41d1984d5b
Author | SHA1 | Date | |
---|---|---|---|
41d1984d5b | |||
4ed6833a9e | |||
d68ca4e787 | |||
4d8ad30541 | |||
a638f39cc5 |
15
config.py
15
config.py
@@ -1,4 +1,4 @@
|
|||||||
name="debweb 1.1.2"
|
name="debweb 1.1.3"
|
||||||
proxied=False
|
proxied=False
|
||||||
|
|
||||||
addr="localhost"
|
addr="localhost"
|
||||||
@@ -15,5 +15,14 @@ start_msg="started at <ADDR>"
|
|||||||
conn_msg="conn from <ADDR>"
|
conn_msg="conn from <ADDR>"
|
||||||
get_msg="<ADDR> got <FILE>"
|
get_msg="<ADDR> got <FILE>"
|
||||||
|
|
||||||
e404_file="404.html"
|
err_files = {
|
||||||
e404_msg="<ADDR> err 404 <FILE>"
|
404: "html/404.html",
|
||||||
|
403: "html/403.html",
|
||||||
|
418: "html/418.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
err_msgs = {
|
||||||
|
404: "<ADDR> err 404 <FILE>",
|
||||||
|
403: "<ADDR> err 403 <FILE>",
|
||||||
|
418: "<ADDR> err 418 (teapot) <FILE>"
|
||||||
|
}
|
9
html/403.html
Normal file
9
html/403.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<head><title>404 Forbidden</title></head>
|
||||||
|
<body>
|
||||||
|
<center><h1>404 Forbidden</h1></center>
|
||||||
|
<hr><center>debweb</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<!-- точно не спиздил с nginx'a!!! -->
|
9
html/418.html
Normal file
9
html/418.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<head><title>418 teapot</title></head>
|
||||||
|
<body>
|
||||||
|
<center><h1>пошолнахуй</h1></center>
|
||||||
|
<hr><center>debweb</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<!-- точно не спиздил с nginx'a!!! -->
|
165
main.py
165
main.py
@@ -7,45 +7,64 @@ import asyncio
|
|||||||
import config
|
import config
|
||||||
import os
|
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:
|
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:
|
async def log(self, text: str, addr: tuple=None, file: str=None) -> None:
|
||||||
text = text.replace("<ADDR>", f"{addr[0]}:{addr[1]}" if addr else "")
|
text = text.replace("<ADDR>", f"{addr[0]}:{addr[1]}" if addr else "")
|
||||||
text = text.replace("<FILE>", file if file else "")
|
text = text.replace("<FILE>", file if file else "")
|
||||||
|
|
||||||
if self._log_file:
|
if config.log_file:
|
||||||
async with aiofiles.open(self._log_file, mode="a") as file:
|
async with aiofiles.open(config.log_file, mode="a") as file:
|
||||||
await file.write(text + "\n")
|
await file.write(text + "\n")
|
||||||
else:
|
else:
|
||||||
print(text)
|
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:
|
async def handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
||||||
addr = writer.get_extra_info("peername")
|
addr = writer.get_extra_info("peername")
|
||||||
await self.log(self.conn_msg, addr)
|
await self.log(config.conn_msg, addr)
|
||||||
rdata = await reader.read(self._read_buffer)
|
rdata = await reader.read(config.read_buffer)
|
||||||
data = rdata.decode()
|
data = rdata.decode()
|
||||||
|
|
||||||
if not data: return
|
if not data: return
|
||||||
|
|
||||||
real_addr = None
|
real_addr = None
|
||||||
@@ -54,7 +73,7 @@ class WebServer:
|
|||||||
real_addr = line[len("X-Real-IP: "):].strip()
|
real_addr = line[len("X-Real-IP: "):].strip()
|
||||||
break
|
break
|
||||||
|
|
||||||
if real_addr and self.proxied:
|
if real_addr and config.proxied:
|
||||||
addr = (real_addr, addr[1])
|
addr = (real_addr, addr[1])
|
||||||
|
|
||||||
request = data.split("\n")[0]
|
request = data.split("\n")[0]
|
||||||
@@ -63,7 +82,18 @@ class WebServer:
|
|||||||
|
|
||||||
path = unquote(parts[1])
|
path = unquote(parts[1])
|
||||||
file_name = path[1:] if path.startswith('/') else path
|
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):
|
if os.path.isfile(file_path):
|
||||||
mime, _ = mimetypes.guess_type(file_path)
|
mime, _ = mimetypes.guess_type(file_path)
|
||||||
@@ -71,36 +101,17 @@ class WebServer:
|
|||||||
if not mime: mime = "application/octet-stream"
|
if not mime: mime = "application/octet-stream"
|
||||||
if mime.startswith("text"): mime += "; charset=utf-8"
|
if mime.startswith("text"): mime += "; charset=utf-8"
|
||||||
|
|
||||||
headers = (
|
await self.send_headers(writer, 200, file_size, mime)
|
||||||
"HTTP/1.1 200 OK\r\n"
|
await self.send_file(writer, file_path, file_size)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
elif os.path.isdir(file_path):
|
elif os.path.isdir(file_path):
|
||||||
resp = ""
|
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()
|
resp = await f.read()
|
||||||
|
|
||||||
files = ""
|
files = ""
|
||||||
base_path = os.path.relpath(file_path, self.directory).replace('\\', '/')
|
base_path = os.path.relpath(file_path, config.directory).replace('\\', '/')
|
||||||
if base_path == '.':
|
if base_path == '.':
|
||||||
base_path = ''
|
base_path = ''
|
||||||
|
|
||||||
@@ -108,10 +119,8 @@ class WebServer:
|
|||||||
item_path = os.path.join(file_path, item)
|
item_path = os.path.join(file_path, item)
|
||||||
is_dir = os.path.isdir(item_path)
|
is_dir = os.path.isdir(item_path)
|
||||||
|
|
||||||
if base_path:
|
if base_path: rel_path = f"{base_path}/{item}"
|
||||||
rel_path = f"{base_path}/{item}"
|
else: rel_path = item
|
||||||
else:
|
|
||||||
rel_path = item
|
|
||||||
|
|
||||||
if is_dir:
|
if is_dir:
|
||||||
rel_path += "/"
|
rel_path += "/"
|
||||||
@@ -127,37 +136,25 @@ class WebServer:
|
|||||||
|
|
||||||
resp = resp.replace("<FILES>", files)
|
resp = resp.replace("<FILES>", files)
|
||||||
resp = resp.encode()
|
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))
|
||||||
else:
|
|
||||||
await self.log(self.e404_msg, addr, file_path)
|
writer.write(resp)
|
||||||
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()
|
await writer.drain()
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
await self.log(config.err_msgs[404], addr, config.err_files[404])
|
||||||
|
file_size = os.path.getsize(config.err_files[404])
|
||||||
|
await self.send_headers(writer, 404, file_size)
|
||||||
|
await self.send_file(writer, config.err_files[404], file_size)
|
||||||
|
|
||||||
writer.close()
|
writer.close()
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
await self.log(self.get_msg, addr, file_path)
|
await self.log(config.get_msg, addr, file_path)
|
||||||
await writer.drain()
|
|
||||||
|
|
||||||
writer.close()
|
writer.close()
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
|
|
||||||
@@ -165,10 +162,10 @@ class WebServer:
|
|||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
server = await asyncio.start_server(
|
server = await asyncio.start_server(
|
||||||
self.handle,
|
self.handle,
|
||||||
self._addr,
|
config.addr,
|
||||||
self._port
|
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:
|
async with server:
|
||||||
await server.serve_forever()
|
await server.serve_forever()
|
||||||
@@ -176,13 +173,13 @@ class WebServer:
|
|||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
s = WebServer()
|
s = WebServer()
|
||||||
if not s.directory:
|
if not config.directory:
|
||||||
print("directory not set!")
|
print("directory not set!")
|
||||||
exit(1)
|
exit(1)
|
||||||
if not s.preset_file:
|
if not config.preset_file:
|
||||||
print("preset file not set!")
|
print("preset file not set!")
|
||||||
exit(1)
|
exit(1)
|
||||||
if not os.path.isfile(s.preset_file):
|
if not os.path.isfile(config.preset_file):
|
||||||
print("invalid preset file")
|
print("invalid preset file")
|
||||||
exit(1)
|
exit(1)
|
||||||
await s.start()
|
await s.start()
|
||||||
|
Reference in New Issue
Block a user