Compare commits
2
Commits
3276224943
...
f3034575ee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3034575ee | ||
|
|
4a93ffa20c |
@@ -1,7 +1,11 @@
|
|||||||
# PES
|
# PES
|
||||||
|
|
||||||
This is a quick reference document on how to implement PES.
|
This is a quick reference document on how to implement PES.
|
||||||
|
|
||||||
## Example script:
|
## Example script:
|
||||||
|
|
||||||
The default script is the following:
|
The default script is the following:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -12,26 +16,28 @@ import amethyst
|
|||||||
|
|
||||||
|
|
||||||
class PES:
|
class PES:
|
||||||
"""
|
|
||||||
class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# DO NOT USE THIS CLASS FOR PROGRAM, ONLY ON_REQUEST PLEASE!!
|
# DO NOT USE THIS FUNCTION FOR PROGRAM, ONLY ON_REQUEST PLEASE!!
|
||||||
# Below go definitions to get things working.
|
# 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.build_response = amethyst.WebServer.build_binary_response
|
||||||
self.fh = amethyst.FileHandler("..")
|
self.fh = amethyst.FileHandler("..")
|
||||||
self.rq = amethyst.RequestParser()
|
self.rq = amethyst.RequestParser()
|
||||||
# NOTE: THREAD_SAFETY is a required setting, as it defines
|
self.THREAD_SAFETY: bool = True
|
||||||
# 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
|
|
||||||
|
|
||||||
def on_request(self, req):
|
def on_request(self, req):
|
||||||
return self.build_response(200, "This is a test", "text/html")
|
return self.build_response(200, b"Heyhey! This is the default PES script!", "text/html")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Code to run if it is not thread-safe.
|
|
||||||
PES.on_request(PES, "request")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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
@@ -38,16 +38,18 @@ TODO: actually put normal comments in
|
|||||||
|
|
||||||
TODO: INPROG: add typing to all code, new code will feature it by default.
|
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 os
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import threading
|
import threading
|
||||||
import ssl
|
import ssl
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
# import re
|
|
||||||
import signal
|
import signal
|
||||||
import sys
|
# Experimental imports go here
|
||||||
|
import select
|
||||||
|
import subprocess
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not os.getcwd() in sys.path:
|
if not os.getcwd() in sys.path:
|
||||||
@@ -61,7 +63,7 @@ except ImportError:
|
|||||||
)
|
)
|
||||||
# pass
|
# 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/"
|
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.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.max_length = int(self.file_handler.read_config("max-length")) or 8192
|
||||||
self.skip_ssl = False
|
self.skip_ssl = False
|
||||||
|
self.threading = bool(self.file_handler.read_config("threading"))
|
||||||
|
self.tlock = threading.Lock()
|
||||||
|
|
||||||
# me when no certificate and key file
|
# me when no certificate and key file
|
||||||
if not os.path.exists(self.cert_file) or not os.path.exists(self.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 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||||
self.http_socket.bind(("::", self.http_port))
|
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 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||||
self.https_socket_raw.bind(("::", self.https_port))
|
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)
|
self.proxy_handler = ProxyServer(self.file_handler)
|
||||||
|
|
||||||
@@ -487,6 +491,8 @@ class WebServer:
|
|||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
def start(self, http, https):
|
def start(self, http, https):
|
||||||
|
signal.signal(signal.SIGINT, self.shutdown)
|
||||||
|
signal.signal(signal.SIGTERM, self.shutdown)
|
||||||
if https is True:
|
if https is True:
|
||||||
if self.skip_ssl is True:
|
if self.skip_ssl is True:
|
||||||
print("WARN: You have enabled HTTPS without SSL!!")
|
print("WARN: You have enabled HTTPS without SSL!!")
|
||||||
@@ -506,8 +512,17 @@ class WebServer:
|
|||||||
print(f"HTTP server listening on port {self.http_port}...")
|
print(f"HTTP server listening on port {self.http_port}...")
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
|
ready, _, _ = select.select(
|
||||||
|
[self.http_socket],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
1.0
|
||||||
|
)
|
||||||
|
if not ready:
|
||||||
|
continue
|
||||||
conn, addr = self.http_socket.accept()
|
conn, addr = self.http_socket.accept()
|
||||||
if self.file_handler.read_config("threading") is True:
|
conn.settimeout(2)
|
||||||
|
if self.threading:
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=self.handle_connection,
|
target=self.handle_connection,
|
||||||
args=(conn, addr),
|
args=(conn, addr),
|
||||||
@@ -515,10 +530,14 @@ class WebServer:
|
|||||||
).start()
|
).start()
|
||||||
else:
|
else:
|
||||||
self.handle_connection(conn, addr)
|
self.handle_connection(conn, addr)
|
||||||
except OSError:
|
except socket.timeout:
|
||||||
|
continue
|
||||||
|
except OSError as e:
|
||||||
|
if not self.running:
|
||||||
break
|
break
|
||||||
|
print(f"OSError! {e}")
|
||||||
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not "timeout" in f"{e}":
|
|
||||||
print(f"HTTP error: {e}")
|
print(f"HTTP error: {e}")
|
||||||
|
|
||||||
def start_https(self):
|
def start_https(self):
|
||||||
@@ -526,8 +545,17 @@ class WebServer:
|
|||||||
print(f"HTTPS server listening on port {self.https_port}...")
|
print(f"HTTPS server listening on port {self.https_port}...")
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
|
ready, _, _ = select.select(
|
||||||
|
[self.https_socket],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
1.0
|
||||||
|
)
|
||||||
|
if not ready:
|
||||||
|
continue
|
||||||
conn, addr = self.https_socket.accept()
|
conn, addr = self.https_socket.accept()
|
||||||
if self.file_handler.read_config("threading") is True:
|
conn.settimeout(2)
|
||||||
|
if self.threading:
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=self.handle_connection,
|
target=self.handle_connection,
|
||||||
args=(conn, addr),
|
args=(conn, addr),
|
||||||
@@ -535,10 +563,14 @@ class WebServer:
|
|||||||
).start()
|
).start()
|
||||||
else:
|
else:
|
||||||
self.handle_connection(conn, addr)
|
self.handle_connection(conn, addr)
|
||||||
except OSError:
|
except socket.timeout:
|
||||||
|
continue
|
||||||
|
except OSError as e:
|
||||||
|
if not self.running:
|
||||||
break
|
break
|
||||||
|
print(f"OSError! {e}")
|
||||||
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not "timeout" in f"{e}":
|
|
||||||
print(f"HTTPS error: {e}")
|
print(f"HTTPS error: {e}")
|
||||||
|
|
||||||
def handle_connection(self, conn, addr):
|
def handle_connection(self, conn, addr):
|
||||||
@@ -680,7 +712,14 @@ class WebServer:
|
|||||||
try:
|
try:
|
||||||
pesclass = pes.PES()
|
pesclass = pes.PES()
|
||||||
threadcompat = pesclass.THREAD_SAFETY
|
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)
|
return pesclass.on_request(data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.build_response(
|
return self.build_response(
|
||||||
@@ -806,7 +845,6 @@ class WebServer:
|
|||||||
self.running = False
|
self.running = False
|
||||||
self.http_socket.close()
|
self.http_socket.close()
|
||||||
self.https_socket.close()
|
self.https_socket.close()
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user