Author SHA1 Message Date
Nova f3034575ee Version 0.3.1
Actually working properly!
2026-08-10 15:56:44 +02:00
Bill Gates 4a93ffa20c fix for broken socket timeout 2026-08-10 13:33:19 +02:00
2 changed files with 79 additions and 35 deletions
+22 -16
View File
@@ -1,7 +1,11 @@
# PES
This is a quick reference document on how to implement PES.
## Example script:
The default script is the following:
```python
import sys
import os
@@ -12,26 +16,28 @@ import amethyst
class PES:
"""
class
"""
def __init__(self):
# DO NOT USE THIS CLASS FOR PROGRAM, ONLY ON_REQUEST PLEASE!!
# Below go definitions to get things working.
# DO NOT USE THIS FUNCTION FOR PROGRAM, ONLY ON_REQUEST PLEASE!!
# WARNING: ONLY CHANGE THE THREAD_SAFETY VARIABLE! DO NOT ADD OR REMOVE ANYTHING!
# THEY WILL COMPROMISE ANY THREAD-SAFETY SECURITY MECHANISMS AMETHYST HAS IN PLACE!
# YOU HAVE BEEN WARNED!
self.build_response = amethyst.WebServer.build_binary_response
self.fh = amethyst.FileHandler("..")
self.rq = amethyst.RequestParser()
# NOTE: THREAD_SAFETY is a required setting, as it defines
# if it can run within the Amethyst thread pool or needs to
# run independently
# False = run independent. True = run in Amethyst thread pool.
self.THREAD_SAFETY: bool = False
self.THREAD_SAFETY: bool = True
def on_request(self, req):
return self.build_response(200, "This is a test", "text/html")
if __name__ == "__main__":
# Code to run if it is not thread-safe.
PES.on_request(PES, "request")
return self.build_response(200, b"Heyhey! This is the default PES script!", "text/html")
```
## Threading and Thread-safety.
When you execute PES scripts, they have a high chance of not being thread-safe because they edit the page you visit. It means you cannot guarantee the data you want is actually the data getting sent over the wire. This issue can be fixed in multiple ways:
1. Disabling threading, then only one script can run at once, but this costs a lot of performance
2. Enforcing thread-safety on all scripts, this will mean every script can be trusted, but makes development difficult (especially for people with no coding experience)
3. Implementing mechanisms that will try to patch up holes if threading is enabled and a thread-unsafe script is loaded.
Amethyst uses fix 3. It only allows one script to run at a time (if both threading is enabled and a thread-unsafe script is loaded), but if you specify only one host to use PES, all other hosts still enjoy the benefits of a multithreaded server! This makes sure that if user 1 and user 2 both request something and PES is involved, user 1 receives part of the data they want and part of the data user 2 wants and vice versa. This security mechanism only works if you respect them. Amethyst will throw a warning if you have a situation you have an unsafe script and run threaded, this warning isn't critical, as the script will still execute with security mechanisms, but the warning in the script is clear. Amethyst can't help if you make it explicitly unsafe yourself.
+53 -15
View File
@@ -38,16 +38,18 @@ TODO: actually put normal comments in
TODO: INPROG: add typing to all code, new code will feature it by default.
"""
# Stable imports go here
import sys
import threading
import os
import mimetypes
import threading
import ssl
import socket
# import re
import signal
import sys
# Experimental imports go here
import select
import subprocess
try:
if not os.getcwd() in sys.path:
@@ -61,7 +63,7 @@ except ImportError:
)
# pass
AMETHYST_BUILD_NUMBER = "0.3.0-0114-mt-tryout1"
AMETHYST_BUILD_NUMBER = "0.3.1-0130-mt-tryout2"
AMETHYST_REPO = "https://git.novacow.ch/Nova/PyWebServer/"
@@ -423,6 +425,8 @@ class WebServer:
self.key_file = self.file_handler.read_config("key") or key_file
self.max_length = int(self.file_handler.read_config("max-length")) or 8192
self.skip_ssl = False
self.threading = bool(self.file_handler.read_config("threading"))
self.tlock = threading.Lock()
# me when no certificate and key file
if not os.path.exists(self.cert_file) or not os.path.exists(self.key_file):
@@ -450,11 +454,11 @@ class WebServer:
self.http_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
self.http_socket.bind(("::", self.http_port))
self.http_socket.settimeout(25)
# self.http_socket.settimeout(1)
self.https_socket_raw = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
self.https_socket_raw.bind(("::", self.https_port))
self.https_socket_raw.settimeout(25)
# self.https_socket_raw.settimeout(1)
self.proxy_handler = ProxyServer(self.file_handler)
@@ -487,6 +491,8 @@ class WebServer:
self.running = True
def start(self, http, https):
signal.signal(signal.SIGINT, self.shutdown)
signal.signal(signal.SIGTERM, self.shutdown)
if https is True:
if self.skip_ssl is True:
print("WARN: You have enabled HTTPS without SSL!!")
@@ -506,8 +512,17 @@ class WebServer:
print(f"HTTP server listening on port {self.http_port}...")
while self.running:
try:
ready, _, _ = select.select(
[self.http_socket],
[],
[],
1.0
)
if not ready:
continue
conn, addr = self.http_socket.accept()
if self.file_handler.read_config("threading") is True:
conn.settimeout(2)
if self.threading:
threading.Thread(
target=self.handle_connection,
args=(conn, addr),
@@ -515,10 +530,14 @@ class WebServer:
).start()
else:
self.handle_connection(conn, addr)
except OSError:
except socket.timeout:
continue
except OSError as e:
if not self.running:
break
print(f"OSError! {e}")
continue
except Exception as e:
if not "timeout" in f"{e}":
print(f"HTTP error: {e}")
def start_https(self):
@@ -526,8 +545,17 @@ class WebServer:
print(f"HTTPS server listening on port {self.https_port}...")
while self.running:
try:
ready, _, _ = select.select(
[self.https_socket],
[],
[],
1.0
)
if not ready:
continue
conn, addr = self.https_socket.accept()
if self.file_handler.read_config("threading") is True:
conn.settimeout(2)
if self.threading:
threading.Thread(
target=self.handle_connection,
args=(conn, addr),
@@ -535,10 +563,14 @@ class WebServer:
).start()
else:
self.handle_connection(conn, addr)
except OSError:
except socket.timeout:
continue
except OSError as e:
if not self.running:
break
print(f"OSError! {e}")
continue
except Exception as e:
if not "timeout" in f"{e}":
print(f"HTTPS error: {e}")
def handle_connection(self, conn, addr):
@@ -680,7 +712,14 @@ class WebServer:
try:
pesclass = pes.PES()
threadcompat = pesclass.THREAD_SAFETY
# if not threadcompat:
if not threadcompat and self.threading is True:
print(
"PES is not thread-safe yet threading is enabled!\n"
"Amethyst CANNOT guarantee data intergity!\n"
"It is HIGHLY recommended you make your script thread-safe!\n"
)
with self.tlock:
return pesclass.on_request(data)
return pesclass.on_request(data)
except Exception as e:
return self.build_response(
@@ -806,7 +845,6 @@ class WebServer:
self.running = False
self.http_socket.close()
self.https_socket.close()
sys.exit(0)
def main():