idk anymore man

This commit is contained in:
2026-03-13 22:13:59 +01:00
parent 127612d408
commit 97896c87a7
4 changed files with 88 additions and 63 deletions
+26
View File
@@ -0,0 +1,26 @@
"""
This is the Amethyst API mode Python interface whatevers.
Docs will follow.
"""
# Below go imports.
import sys
import os
if not os.getcwd() in sys.path:
sys.path.append(os.getcwd())
import pywebsrv
class API:
"""
class
"""
def __init__(self):
# DO NOT USE THIS CLASS FOR PROGRAM, ONLY ON_REQUEST PLEASE!!
# Below go definitions to get things working.
self.build_response = pywebsrv.WebServer.build_binary_response
def on_request(self, req):
return self.build_response(200, "This is a test", "text/html")
+1 -1
View File
@@ -8,7 +8,7 @@
<h1>Hello from Amethyst!</h1> <h1>Hello from Amethyst!</h1>
<h2>This page confirms Amethyst can read files from your PC and serve them to your browser!</h2> <h2>This page confirms Amethyst can read files from your PC and serve them to your browser!</h2>
<p>If you see this page and you're not the server owner, tell them they misconfigured something!</p> <p>If you see this page and you're not the server owner, tell them they misconfigured something!</p>
<p>This server runs Amethyst Alpha Build 0039</p> <p>This server runs Amethyst Alpha Build 0048</p>
</center> </center>
</body> </body>
</html> </html>
+1
View File
@@ -10,6 +10,7 @@ host localhost {
directory:/home/nova/PyWebServer/html2 directory:/home/nova/PyWebServer/html2
allowed-methods:GET,PUT allowed-methods:GET,PUT
block-ip:10.1.100.2 block-ip:10.1.100.2
apimode:0
block-ua:match("Discordbot") block-ua:match("Discordbot")
} }
+60 -62
View File
@@ -50,33 +50,35 @@ import signal
import sys import sys
try: try:
if not os.getcwd() in sys.path:
sys.path.append(f"{os.getcwd()}")
from certgen import AutoCertGen from certgen import AutoCertGen
except ImportError: except ImportError:
# just do nothing, it's not working anyway. # just do nothing, it's not working anyway.
# print( print(
# "WARN: You need the AutoCertGen plugin! Please install it from\n" "WARN: You need the AutoCertGen plugin! Please install it from\n"
# "https://git.novacow.ch/Nova/AutoCertGen/" "https://git.novacow.ch/Nova/AutoCertGen/"
# ) )
pass # pass
AMETHYST_BUILD_NUMBER = "0046" AMETHYST_BUILD_NUMBER = "0052"
AMETHYST_REPO = "https://git.novacow.ch/Nova/PyWebServer/" AMETHYST_REPO = "https://git.novacow.ch/Nova/PyWebServer/"
class ConfigParser: class ConfigParser:
def __init__(self, text): def __init__(self, text):
self.data = {"hosts": {}, "globals": {}} self.data: dict = {"hosts": {}, "globals": {}}
self._parse(text) self._parse(text)
def _parse(self, text): def _parse(self, text):
lines = [ lines: list = [
line.strip() line.strip()
for line in text.splitlines() for line in text.splitlines()
if line.strip() and not line.strip().startswith("#") if line.strip() and not line.strip().startswith("#")
] ]
current_block = None current_block: tuple | None = None
current_name = None current_name: str | None = None
for line in lines: for line in lines:
if line.startswith("host ") and line.endswith("{"): if line.startswith("host ") and line.endswith("{"):
@@ -96,8 +98,8 @@ class ConfigParser:
if ":" in line and current_block: if ":" in line and current_block:
key, value = line.split(":", 1) key, value = line.split(":", 1)
key = key.strip() key: str = key.strip()
value = value.strip() value: str = value.strip()
if "," in value: if "," in value:
value = [v.strip() for v in value.split(",")] value = [v.strip() for v in value.split(",")]
@@ -139,6 +141,8 @@ class FileHandler:
def read_file(self, file_path, directory=None): def read_file(self, file_path, directory=None):
if "../" in file_path or "%" in file_path: if "../" in file_path or "%" in file_path:
return 403, None return 403, None
if file_path == "api.py":
return 404, None
if directory is not None: if directory is not None:
full_path = os.path.join(directory, file_path.lstrip("/")) full_path = os.path.join(directory, file_path.lstrip("/"))
@@ -239,8 +243,6 @@ class FileHandler:
Generate some self-signed certificates using AutoCertGen Generate some self-signed certificates using AutoCertGen
TODO: doesn't work, need to fix. probably add `./` to $PATH TODO: doesn't work, need to fix. probably add `./` to $PATH
""" """
if not os.getcwd() in sys.path:
sys.path.append(f"{os.getcwd()}")
autocert = AutoCertGen() autocert = AutoCertGen()
autocert.gen_cert() autocert.gen_cert()
@@ -251,14 +253,17 @@ class RequestParser:
self.hosts = self.file_handler.read_new_config("hosts") self.hosts = self.file_handler.read_new_config("hosts")
print(f"Hosts: {self.hosts}") print(f"Hosts: {self.hosts}")
def parse_request_line(self, line): def parse_request_line(self, line, host):
"""Parses the HTTP request line.""" """Parses the HTTP request line."""
try: try:
method, path, version = line.split(" ") method, path, version = line.split(" ")
except ValueError: except ValueError:
return "DELETE", "/this/is/a/bogus/request", "HTTP/1.0" return "DELETE", "/this/is/a/bogus/request", "HTTP/1.0"
if path.endswith("/") and "." not in path: if path.endswith("/") or ("." not in path):
path += "index.html" if not path.endswith("/"):
path += "/"
index = self.file_handler.read_new_config("index", host) or "index.html"
path += f"{index}"
return method, path, version return method, path, version
def ua_is_allowed(self, ua, host=None): def ua_is_allowed(self, ua, host=None):
@@ -276,18 +281,20 @@ class RequestParser:
# return False # return False
# return True # return True
def is_method_allowed(self, method): def is_method_allowed(self, method, host=None):
""" """
Checks if the HTTP method is allowed. Checks if the HTTP method is allowed.
Reads allowed methods from a configuration file. Reads allowed methods from a configuration file.
Falls back to allowing only 'GET' if the file does not exist. Falls back to allowing only 'GET' if the file does not exist.
Should (for now) only be GET as I haven't implemented the logic for PUT Should (for now) only be GET as I haven't implemented the logic for PUT
""" """
allowed_methods = ["GET"] # allowed_methods = ["GET"]
# While the logic for PUT, DELETE, etc. is not added, we shouldn't # While the logic for PUT, DELETE, etc. is not added, we shouldn't
# allow for it to attempt it. # allow for it to attempt it.
# Prepatched for new update. # Prepatched for new update.
# allowed_methods = self.file_handler.read_config("allowed-methods") allowed_methods = self.file_handler.read_new_config("allowed-methods", host)
if allowed_methods is None:
allowed_methods = ["GET"]
return method in allowed_methods return method in allowed_methods
def host_parser(self, host): def host_parser(self, host):
@@ -313,13 +320,6 @@ class RequestParser:
return True return True
#
# class ProxyServer:
# def __init__(
# self,
# ):
class WebServer: class WebServer:
def __init__( def __init__(
self, http_port=8080, https_port=8443, cert_file="cert.pem", key_file="key.pem" self, http_port=8080, https_port=8443, cert_file="cert.pem", key_file="key.pem"
@@ -356,17 +356,6 @@ class WebServer:
"This host cannot be reached without sending a `Host` header." "This host cannot be reached without sending a `Host` header."
) )
# TODO: enable experimental ipv6 support in config
# ipv6 when????/??//?????//?
# self.http_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.http_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# self.http_socket.bind(("0.0.0.0", self.http_port))
#
# self.https_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.https_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# self.https_socket.bind(("0.0.0.0", self.https_port))
self.http_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) self.http_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
self.http_socket.bind(("::", self.http_port)) self.http_socket.bind(("::", self.http_port))
@@ -505,7 +494,12 @@ class WebServer:
else: else:
return self.build_response(400, "You cannot connect without a User-Agent.") return self.build_response(400, "You cannot connect without a User-Agent.")
method, path, version = self.parser.parse_request_line(request_line) if ":" in host:
host2 = host.rsplit(":", 1)[0]
else:
host2 = host
method, path, version = self.parser.parse_request_line(request_line, host2)
if not all([method, path, version]): if not all([method, path, version]):
return self.build_response(400, "Bad Request") return self.build_response(400, "Bad Request")
@@ -515,21 +509,24 @@ class WebServer:
print("Got reload command! Reloading configuration...") print("Got reload command! Reloading configuration...")
self.file_handler = FileHandler() self.file_handler = FileHandler()
self.parser = RequestParser() self.parser = RequestParser()
return self.build_response(302, "") return self.build_response(302, "", host=host2)
if not self.parser.is_method_allowed(method): if not self.parser.is_method_allowed(method):
return self.build_response(405, self.http_405_html) return self.build_response(405, self.http_405_html)
if ":" in host:
host2 = host.rsplit(":", 1)[0]
else:
host2 = host
directory = ( directory = (
self.file_handler.read_new_config("directory", host2) self.file_handler.read_new_config("directory", host2)
or self.file_handler.base_dir or self.file_handler.base_dir
) )
if self.file_handler.read_new_config("apimode", host2) is True:
if not os.path.join(os.getcwd(), directory) in sys.path:
sys.path.append(f"{os.path.join(os.getcwd(), directory)}")
import api
apiclass = api.API()
return apiclass.on_request(data)
file_content, mimetype = self.file_handler.read_file(path, directory) file_content, mimetype = self.file_handler.read_file(path, directory)
if file_content == 403: if file_content == 403:
@@ -577,7 +574,8 @@ class WebServer:
) )
return headers.encode() + binary_data return headers.encode() + binary_data
def build_response(self, status_code, body): @staticmethod
def build_response(status_code, body, host=None):
""" """
For textfiles we'll not have to guess MIME-types, though the other function For textfiles we'll not have to guess MIME-types, though the other function
build_binary_response will be merged in here anyway. build_binary_response will be merged in here anyway.
@@ -593,7 +591,7 @@ class WebServer:
405: "Method Not Allowed", 405: "Method Not Allowed",
413: "Payload Too Large", 413: "Payload Too Large",
500: "Internal Server Error", 500: "Internal Server Error",
635: "Go Away", 621: "fuck off! :3",
} }
status_message = messages.get(status_code) status_message = messages.get(status_code)
@@ -613,20 +611,10 @@ class WebServer:
# 302 currently only happens when the reload is triggered. # 302 currently only happens when the reload is triggered.
# Why not 307, Moved Permanently? Because browsers will cache the # Why not 307, Moved Permanently? Because browsers will cache the
# response and not send the reload command. # response and not send the reload command.
host = self.file_handler.read_config("host")[0] # if port == 443:
port = self.file_handler.read_config( # host = f"https://{host}/"
"port-https" # else:
) or self.file_handler.read_config("port") # host = f"http://{host}/"
if port != 80 and port != 443:
if port == 8443:
host = f"https://{host}:{port}/"
else:
host = f"http://{host}:{port}/"
else:
if port == 443:
host = f"https://{host}/"
else:
host = f"http://{host}/"
headers = ( headers = (
f"HTTP/1.1 {status_code} {status_message}\r\n" f"HTTP/1.1 {status_code} {status_message}\r\n"
f"Location: {host}\r\n" f"Location: {host}\r\n"
@@ -635,6 +623,16 @@ class WebServer:
f"Connection: close\r\n\r\n" f"Connection: close\r\n\r\n"
).encode() ).encode()
if status_code == 621:
headers = (
f"HTTP/1.1 {status_code} {status_message}\r\n"
"Server: PyWebServer/amethyst-build-0621\r\n"
"Content-Length: 30\r\n"
f"Connection: close\r\n\r\n"
)
body = "https://e621.net/posts/6155664"
print(f"{headers + body}")
return headers + body return headers + body
def shutdown(self, signum, frame): def shutdown(self, signum, frame):
@@ -660,7 +658,7 @@ def main():
file_handler = FileHandler() file_handler = FileHandler()
file_handler.base_dir = file_handler.read_config("directory") file_handler.base_dir = file_handler.read_config("directory")
http_port = file_handler.read_new_config("port") or 8080 http_port = file_handler.read_new_config("port") or 8080
https_port = file_handler.read_new_config("port-https") or 8443 https_port = file_handler.read_new_config("https-port") or 8443
http_enabled = bool(file_handler.read_new_config("http")) or True http_enabled = bool(file_handler.read_new_config("http")) or True
print(http_enabled) print(http_enabled)
https_enabled = bool(file_handler.read_new_config("https")) or False https_enabled = bool(file_handler.read_new_config("https")) or False