Compare commits
1 Commits
1.3.0
...
4d4a44fd06
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d4a44fd06 |
39
README.md
39
README.md
@@ -5,6 +5,43 @@ The upstream of this project is on my own [Gitea instance](https://git.novacow.c
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
## Installing
|
## 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.
|
Installing and running PyWebServer is very simple.
|
||||||
Assuming you're running Linux:
|
Assuming you're running Linux:
|
||||||
```bash
|
```bash
|
||||||
@@ -48,4 +85,4 @@ 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.
|
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
|
### 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.
|
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, the last version of 1.x will be supported for a while longer, but no new features will be added.
|
When 2.0 will come out, 1.x will be developed further, but 2.0 will be the main focus.
|
||||||
|
|||||||
92
pywebsrv.py
92
pywebsrv.py
@@ -12,6 +12,8 @@ License:
|
|||||||
Contact:
|
Contact:
|
||||||
E-mail: nova@novacow.ch
|
E-mail: nova@novacow.ch
|
||||||
|
|
||||||
|
NOTE: Once 2.0 is released, PyWebServer will become the Amethyst Web Server
|
||||||
|
|
||||||
This is PyWebServer, an ultra minimalist webserver, meant to still have
|
This is PyWebServer, an ultra minimalist webserver, meant to still have
|
||||||
a lot standard webserver features. A comprehensive list is below:
|
a lot standard webserver features. A comprehensive list is below:
|
||||||
Features:
|
Features:
|
||||||
@@ -40,39 +42,35 @@ import mimetypes
|
|||||||
import threading
|
import threading
|
||||||
import ssl
|
import ssl
|
||||||
import socket
|
import socket
|
||||||
|
import re
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from certgen import AutoCertGen
|
from certgen import AutoCertGen
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(
|
# just do nothing, it's not working anyway.
|
||||||
"WARN: You need the AutoCertGen plugin! Please install it from\n"
|
# print(
|
||||||
"https://git.novacow.ch/Nova/AutoCertGen/"
|
# "WARN: You need the AutoCertGen plugin! Please install it from\n"
|
||||||
)
|
# "https://git.novacow.ch/Nova/AutoCertGen/"
|
||||||
|
# )
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FileHandler:
|
class FileHandler:
|
||||||
CONFIG_FILE = "pywebsrv.conf"
|
CONFIG_FILE = "pywebsrv.conf"
|
||||||
DEFAULT_CONFIG = (
|
|
||||||
"port:8080\nport-https:8443\nhttp:1"
|
|
||||||
"\nhttps:0\ndirectory:{cwd}\nhost:localhost"
|
|
||||||
"\nallow-localhost:1"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, base_dir=None):
|
def __init__(self, base_dir=None):
|
||||||
self.config_path = os.path.join(os.getcwd(), self.CONFIG_FILE)
|
self.config_path = os.path.join(os.getcwd(), self.CONFIG_FILE)
|
||||||
self.base_dir = self.read_config("directory")
|
self.base_dir = self.read_config("directory")
|
||||||
|
self.cached_conf = None
|
||||||
def check_first_run(self):
|
if not os.path.exists(self.config_path):
|
||||||
if not os.path.isfile(self.config_path):
|
print(
|
||||||
self.on_first_run()
|
"The pywebsrv.conf file needs to be in the same directory "
|
||||||
return True
|
"as pywebsrv.py! Get the default config file from:\n"
|
||||||
return False
|
"https://git.novacow.ch/Nova/PyWebServer/raw/branch/main/pywebsrv.conf"
|
||||||
|
)
|
||||||
def on_first_run(self):
|
exit(1)
|
||||||
with open(self.config_path, "w") as f:
|
|
||||||
f.write(self.DEFAULT_CONFIG.format(cwd=os.getcwd()))
|
|
||||||
|
|
||||||
def read_file(self, file_path):
|
def read_file(self, file_path):
|
||||||
if "../" in file_path:
|
if "../" in file_path:
|
||||||
@@ -163,9 +161,55 @@ class FileHandler:
|
|||||||
return value
|
return value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def read_new_config(self, option, host=None):
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
text = fh.read()
|
||||||
|
|
||||||
|
blocks = re.findall(
|
||||||
|
r'^(host\s+(\S+)|globals)\s*\{([^}]*)\}', text, re.MULTILINE
|
||||||
|
)
|
||||||
|
parsed = {}
|
||||||
|
host_list = []
|
||||||
|
for tag, hostname, body in blocks:
|
||||||
|
section = hostname if hostname else "globals"
|
||||||
|
if hostname:
|
||||||
|
host_list.append(hostname)
|
||||||
|
kv = {}
|
||||||
|
for line in body.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or ":" not in line or line.starswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
key, rest = line.split(":", 1)
|
||||||
|
key = key.strip()
|
||||||
|
rest = rest.strip()
|
||||||
|
|
||||||
|
# Split comma-separated values (e.g. GET,PUT)
|
||||||
|
if "," in rest:
|
||||||
|
kv[key] = [item.strip() for item in rest.split(",")]
|
||||||
|
else:
|
||||||
|
kv[key] = rest
|
||||||
|
parsed[section] = kv
|
||||||
|
parsed["globals"]["hosts"] = host_list
|
||||||
|
self.cached_conf = parsed
|
||||||
|
else:
|
||||||
|
parsed = self.cached_conf
|
||||||
|
if option == "host":
|
||||||
|
try:
|
||||||
|
return host_list
|
||||||
|
except Exception:
|
||||||
|
return parsed["globals"]["hosts"]
|
||||||
|
section = parsed.get(host or "globals", {})
|
||||||
|
return section.get(option)
|
||||||
|
|
||||||
def autocert(self):
|
def autocert(self):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
"""
|
"""
|
||||||
autocert = AutoCertGen()
|
autocert = AutoCertGen()
|
||||||
autocert.gen_cert()
|
autocert.gen_cert()
|
||||||
@@ -229,6 +273,12 @@ class RequestParser:
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
#
|
||||||
|
# class ProxyServer:
|
||||||
|
# def __init__(
|
||||||
|
# self,
|
||||||
|
# ):
|
||||||
|
|
||||||
|
|
||||||
class WebServer:
|
class WebServer:
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -455,7 +505,7 @@ class WebServer:
|
|||||||
status_message = messages.get(status_code)
|
status_message = messages.get(status_code)
|
||||||
headers = (
|
headers = (
|
||||||
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
||||||
f"Server: PyWebServer/1.2.1\r\n"
|
f"Server: PyWebServer/1.4\r\n"
|
||||||
f"Content-Type: {content_type}\r\n"
|
f"Content-Type: {content_type}\r\n"
|
||||||
f"Content-Length: {len(binary_data)}\r\n"
|
f"Content-Length: {len(binary_data)}\r\n"
|
||||||
f"Connection: close\r\n\r\n"
|
f"Connection: close\r\n\r\n"
|
||||||
@@ -492,7 +542,7 @@ class WebServer:
|
|||||||
# Don't encode yet, if 302 status code we have to include location.
|
# Don't encode yet, if 302 status code we have to include location.
|
||||||
headers = (
|
headers = (
|
||||||
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
f"HTTP/1.1 {status_code} {status_message}\r\n"
|
||||||
f"Server: PyWebServer/1.2.1\r\n"
|
f"Server: PyWebServer/1.4\r\n"
|
||||||
f"Content-Length: {len(body)}\r\n"
|
f"Content-Length: {len(body)}\r\n"
|
||||||
f"Connection: close\r\n\r\n"
|
f"Connection: close\r\n\r\n"
|
||||||
).encode()
|
).encode()
|
||||||
|
|||||||
Reference in New Issue
Block a user