Compare commits
2 Commits
4d4a44fd06
...
f5dafb689e
| Author | SHA1 | Date | |
|---|---|---|---|
| f5dafb689e | |||
| 4eada65040 |
93
README.md
93
README.md
@@ -1,88 +1,9 @@
|
||||
# PyWebServer
|
||||
# Amethyst Web Server
|
||||
|
||||
## GitHub
|
||||
The upstream of this project is on my own [Gitea instance](https://git.novacow.ch/Nova/PyWebServer/).
|
||||
Because of that I'll mostly reply to issues and PRs there, you can submit issues and PRs on GitHub, but it might take longer before I read it.
|
||||
## A word of warning!
|
||||
Currently Amethyst is in very early alpha stage, a lot of things will be broken, names won't be correct,
|
||||
promised features missing, but I'm very much working on it live!
|
||||
Every save I do increments the build number by 1, I won't publish all of them, but most of them will be published.
|
||||
Once a milestone is hit (e.g. a new feature fully implemented), I'll publish a release!
|
||||
|
||||
## Installing
|
||||
### The little tiny easier route
|
||||
Installing and running PyWebServer is very simple.
|
||||
First off, download the latest release from the 'Releases' tab, choose the Zip variant if unsure.
|
||||
When it's done downloading, unpack the files in a directory of choice, for the purpose of this README,
|
||||
I've chosen `./pywebserver/` (for Windows: `.\pywebserver\`).
|
||||
From there, open up your favorite text editor and open the file `pywebsrv.conf` in the directory you unpacked PyWebServer.
|
||||
In there, you should see this somewhere:
|
||||
```
|
||||
# Here you choose what directory PyWebServer looks in for files.
|
||||
directory:<Enter directory here>
|
||||
```
|
||||
After the colon, enter your directory where you have your website stored.
|
||||
After that, make sure you have installed Python. Here's how you can install Python:
|
||||
Linux:
|
||||
```bash
|
||||
sudo apt install python3 # Debian / Ubuntu
|
||||
sudo dnf install python3 # Fedora / Nobara
|
||||
sudo pacman -S python3 # Arch and derivatives.
|
||||
```
|
||||
macOS:
|
||||
```bash
|
||||
brew install python3
|
||||
```
|
||||
Windows:
|
||||
```powershell
|
||||
# You can change the `3.12` with whatever version you want/need.
|
||||
winget install -e --id Python.Python.3.12 --scope machine
|
||||
```
|
||||
Then, in the terminal window you have open, go to the directory you unpacked PyWebServer and type this:
|
||||
```
|
||||
python3 ./pywebsrv.py
|
||||
# For Windows users, if the above command doesn't work, try this:
|
||||
py ./pywebsrv.py
|
||||
```
|
||||
And there you go! You've now set up PyWebServer!
|
||||
|
||||
### The little tiny harder route
|
||||
Installing and running PyWebServer is very simple.
|
||||
Assuming you're running Linux:
|
||||
```bash
|
||||
git clone https://git.novacow.ch/Nova/PyWebServer.git
|
||||
cd ./PyWebServer/
|
||||
```
|
||||
Windows users, make sure you have installed Git, from there:
|
||||
```powershell
|
||||
git clone https://git.novacow.ch/Nova/PyWebServer.git
|
||||
Set-Location .\PyWebServer\
|
||||
```
|
||||
Then, open `pywebsrv.conf` in your favorite text editor and change the `directory` key to the full path where your files are stored.
|
||||
After that, put your files in and run this:
|
||||
Linux:
|
||||
```bash
|
||||
python3 /path/to/pywebsrv.py
|
||||
```
|
||||
Windows:
|
||||
```powershell
|
||||
# If you have installed Python via the Microsoft Store:
|
||||
python3 \path\to\pywebsrv.py
|
||||
# Via the python.org website:
|
||||
py \path\to\pywebsrv.py
|
||||
```
|
||||
|
||||
## SSL Support
|
||||
PyWebServer supports SSL/TLS for authentication via HTTPS. In the config file, you should enable the HTTPS port. After that you need to create the certificate.
|
||||
Currently PyWebServer looks for the `cert.pem` and the `key.pem` files in the root directory of the installation.
|
||||
|
||||
## HTTP support
|
||||
Currently PyWebServer only supports HTTP/1.1, this is very unlikely to change, as most of the modern web today still uses HTTP/1.1.
|
||||
For methods PyWebServer only supports `GET`, this is being reworked though, check issue [#3](https://git.novacow.ch/Nova/PyWebServer/issues/3) for progress.
|
||||
|
||||
## Files support
|
||||
Unlike other small web servers, PyWebServer has full support for binary files being sent and received (once that logic is put in) over HTTP(S).
|
||||
|
||||
## Support
|
||||
PyWebServer will follow a standard support scheme.
|
||||
### 1.x
|
||||
For every 1.x version there will be support until 2 newer versions come out.
|
||||
So that means that 1.0 will still be supported when 1.1 comes out, but no longer be supported when 1.2 comes out.
|
||||
### 2.x
|
||||
I am planning on releasing a 2.x version with will have a lot more advanced features, like nginx's server block emulation amongst other things.
|
||||
When 2.0 will come out, 1.x will be developed further, but 2.0 will be the main focus.
|
||||
## Currently W.I.P. Check back later!
|
||||
|
||||
30
new_conf.conf
Normal file
30
new_conf.conf
Normal file
@@ -0,0 +1,30 @@
|
||||
# WARNING: This is an alpha spec of NSCL 2.0!!
|
||||
|
||||
host example.com {
|
||||
directory:/home/nova/Downloads/test/html
|
||||
allowed-methods:GET
|
||||
block-ip:match-ip("192.168",2)
|
||||
block-ua:match("Discordbot",0),match("Google",0)
|
||||
}
|
||||
|
||||
host cdn.example.com {
|
||||
directory:/home/nova/Downloads/test/cdn
|
||||
allowed-methods:GET,PUT
|
||||
block-ip:10.1.100.2
|
||||
block-ua:match("Discordbot",0)
|
||||
}
|
||||
|
||||
host modem.example.com {
|
||||
proxy:192.168.2.254
|
||||
key-file:/home/nova/Downloads/test/proxykey.pem
|
||||
cert-file:/home/nova/Downloads/test/proxycert.pem
|
||||
}
|
||||
|
||||
globals {
|
||||
http:1
|
||||
https:1
|
||||
port:8080
|
||||
https-port:8443
|
||||
global-key:/home/nova/Downloads/test/key.pem
|
||||
global-cert:/home/nova/Downloads/test/cert.pem
|
||||
}
|
||||
54
pywebsrv.py
54
pywebsrv.py
@@ -56,12 +56,16 @@ except ImportError:
|
||||
# )
|
||||
pass
|
||||
|
||||
AMETHYST_BUILD_NUMBER = "0001"
|
||||
AMETHYST_REPO = "https://git.novacow.ch/Nova/PyWebServer/"
|
||||
|
||||
class FileHandler:
|
||||
CONFIG_FILE = "pywebsrv.conf"
|
||||
new_conf = "new_conf.conf"
|
||||
|
||||
def __init__(self, base_dir=None):
|
||||
self.config_path = os.path.join(os.getcwd(), self.CONFIG_FILE)
|
||||
self.new_conf = os.path.join(os.getcwd(), self.new_conf)
|
||||
self.base_dir = self.read_config("directory")
|
||||
self.cached_conf = None
|
||||
if not os.path.exists(self.config_path):
|
||||
@@ -166,7 +170,7 @@ class FileHandler:
|
||||
Reads the configuration file and returns a dict
|
||||
"""
|
||||
if self.cached_conf is None:
|
||||
with open(self.config_path, "r", encoding="utf-8") as fh:
|
||||
with open(self.new_conf, "r", encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
|
||||
blocks = re.findall(
|
||||
@@ -174,6 +178,7 @@ class FileHandler:
|
||||
)
|
||||
parsed = {}
|
||||
host_list = []
|
||||
print(f"Blocks: {blocks}")
|
||||
for tag, hostname, body in blocks:
|
||||
section = hostname if hostname else "globals"
|
||||
if hostname:
|
||||
@@ -181,7 +186,7 @@ class FileHandler:
|
||||
kv = {}
|
||||
for line in body.splitlines():
|
||||
line = line.strip()
|
||||
if not line or ":" not in line or line.starswith("#"):
|
||||
if not line or ":" not in line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
key, rest = line.split(":", 1)
|
||||
@@ -218,7 +223,8 @@ class FileHandler:
|
||||
class RequestParser:
|
||||
def __init__(self):
|
||||
self.file_handler = FileHandler()
|
||||
self.hosts = self.file_handler.read_config("host")
|
||||
self.hosts = self.file_handler.read_new_config("host")
|
||||
print(f"Hosts: {self.hosts}")
|
||||
|
||||
def parse_request_line(self, line):
|
||||
"""Parses the HTTP request line."""
|
||||
@@ -230,8 +236,9 @@ class RequestParser:
|
||||
path += "index.html"
|
||||
return method, path, version
|
||||
|
||||
def ua_blocker(self, ua):
|
||||
def ua_blocker(self, ua, host=None):
|
||||
"""Parses and matches UA to block"""
|
||||
del host
|
||||
match, literal = self.file_handler.read_config("block-ua")
|
||||
if ua in literal:
|
||||
return False
|
||||
@@ -260,6 +267,7 @@ class RequestParser:
|
||||
Mfw im in an ugly code writing contest and my opponent is nova while writing a side project
|
||||
"""
|
||||
host = f"{host}"
|
||||
print(f"hosts: {self.hosts}, host: {host}")
|
||||
if ":" in host:
|
||||
host = host.split(":", 1)[0]
|
||||
host = host.lstrip()
|
||||
@@ -284,8 +292,8 @@ class WebServer:
|
||||
def __init__(
|
||||
self, http_port=8080, https_port=8443, cert_file="cert.pem", key_file="key.pem"
|
||||
):
|
||||
self.http_port = http_port
|
||||
self.https_port = https_port
|
||||
self.http_port = int(http_port)
|
||||
self.https_port = int(https_port)
|
||||
self.cert_file = cert_file
|
||||
self.key_file = key_file
|
||||
self.file_handler = FileHandler()
|
||||
@@ -337,17 +345,17 @@ class WebServer:
|
||||
|
||||
self.http_404_html = (
|
||||
"<html><head><title>HTTP 404 - PyWebServer</title></head>"
|
||||
"<body><center><h1>HTTP 404 - Not Found!</h1><p>Running PyWebServer/1.2.1</p>"
|
||||
f"<body><center><h1>HTTP 404 - Not Found!</h1><p>Running PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}</p>"
|
||||
"</center></body></html>"
|
||||
)
|
||||
self.http_403_html = (
|
||||
"<html><head><title>HTTP 403 - PyWebServer</title></head>"
|
||||
"<body><center><h1>HTTP 403 - Forbidden</h1><p>Running PyWebServer/1.2.1</p>"
|
||||
f"<body><center><h1>HTTP 403 - Forbidden</h1><p>Running PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}</p>"
|
||||
"</center></body></html>"
|
||||
)
|
||||
self.http_405_html = (
|
||||
"<html><head><title>HTTP 405 - PyWebServer</title></head>"
|
||||
"<body><center><h1>HTTP 405 - Method not allowed</h1><p>Running PyWebServer/1.2.1</p>"
|
||||
f"<body><center><h1>HTTP 405 - Method not allowed</h1><p>Running PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}</p>"
|
||||
"</center></body></html>"
|
||||
)
|
||||
|
||||
@@ -505,7 +513,7 @@ class WebServer:
|
||||
status_message = messages.get(status_code)
|
||||
headers = (
|
||||
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
||||
f"Server: PyWebServer/1.4\r\n"
|
||||
f"Server: PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}\r\n"
|
||||
f"Content-Type: {content_type}\r\n"
|
||||
f"Content-Length: {len(binary_data)}\r\n"
|
||||
f"Connection: close\r\n\r\n"
|
||||
@@ -542,7 +550,7 @@ class WebServer:
|
||||
# Don't encode yet, if 302 status code we have to include location.
|
||||
headers = (
|
||||
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
||||
f"Server: PyWebServer/1.4\r\n"
|
||||
f"Server: PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}\r\n"
|
||||
f"Content-Length: {len(body)}\r\n"
|
||||
f"Connection: close\r\n\r\n"
|
||||
).encode()
|
||||
@@ -566,7 +574,7 @@ class WebServer:
|
||||
headers = (
|
||||
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
||||
f"Location: {host}\r\n"
|
||||
f"Server: PyWebServer/1.2.1\r\n"
|
||||
f"Server: PyWebServer/amethyst-build-{AMETHYST_BUILD_NUMBER}\r\n"
|
||||
f"Content-Length: {len(body)}\r\n"
|
||||
f"Connection: close\r\n\r\n"
|
||||
).encode()
|
||||
@@ -582,13 +590,25 @@ class WebServer:
|
||||
|
||||
|
||||
def main():
|
||||
print(
|
||||
"WARNING!!\n"
|
||||
f"This is Amethyst alpha build {AMETHYST_BUILD_NUMBER}\n"
|
||||
"Since this is an alpha version of Amethyst, most features aren't working!\n"
|
||||
"These builds are also very verbose and will spit out a lot on the terminal. "
|
||||
"As you can imagine, this is for debugging purposes.\n"
|
||||
"THERE IS ABSOLUTELY NO SUPPORT FOR THESE VERSIONS!\n"
|
||||
"DO NOT USE THEM IN PRODUCTION SETTINGS!\n"
|
||||
f"Please report any bugs on {AMETHYST_REPO}\n"
|
||||
)
|
||||
input("Press <Enter> to continue. ")
|
||||
file_handler = FileHandler()
|
||||
file_handler.check_first_run()
|
||||
file_handler.base_dir = file_handler.read_config("directory")
|
||||
http_port = file_handler.read_config("port") or 8080
|
||||
https_port = file_handler.read_config("port-https") or 8443
|
||||
http_enabled = file_handler.read_config("http") or True
|
||||
https_enabled = file_handler.read_config("https") or False
|
||||
http_port = file_handler.read_new_config("port") or 8080
|
||||
https_port = file_handler.read_new_config("port-https") or 8443
|
||||
http_enabled = bool(file_handler.read_new_config("http")) or True
|
||||
print(http_enabled)
|
||||
https_enabled = bool(file_handler.read_new_config("https")) or False
|
||||
print(https_enabled)
|
||||
server = WebServer(http_port=http_port, https_port=https_port)
|
||||
server.start(http_enabled, https_enabled)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user