feat: add Go server implementation with HTTPS support and update README for usage instructions

This commit is contained in:
2026-05-22 08:29:38 +02:00
parent fca0b84216
commit 6c8d014615
6 changed files with 524 additions and 63 deletions
+49 -28
View File
@@ -7,8 +7,9 @@ Behavior:
- Displays whether any incoming X-* headers were detected and lists them
- If User-Agent contains "curl" or "wget" (case-insensitive) the server
responds with Content-Type: text/plain; otherwise Content-Type: text/html.
- --pem path to a PEM bundle (cert chain + private key) to enable HTTPS;
without --pem the server uses plain HTTP.
- --pem path to a PEM bundle (cert chain + private key). When provided,
the server listens on both HTTP (--port) and HTTPS (--tls-port).
Without --pem the server listens on plain HTTP only.
- --look controls which HTML variant is returned for non-CLI agents:
* basic - plain HTML with no external references
* nice - includes Google Font "Noto Sans" (default)
@@ -18,9 +19,11 @@ Behavior:
for that request only. Values are case-insensitive and must be one of basic,nice,bootstrap,tailwind.
Usage:
python3 ok_server.py # binds 0.0.0.0:8080, nice look
python3 ok_server.py # binds 0.0.0.0:8080, nice look
python3 ok_server.py --look basic
python3 ok_server.py -b 127.0.0.1 -p 8080 --look tailwind
python3 ok_server.py --pem /path/to/bundle.pem # HTTP :8080 + HTTPS :8443
python3 ok_server.py --pem /path/to/bundle.pem --tls-port 9443
Test:
curl -i http://localhost:8080/ # text/plain for curl
@@ -34,6 +37,7 @@ import argparse
import signal
import ssl
import sys
import threading
from html import escape
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Tuple
@@ -306,35 +310,50 @@ class OkHandler(BaseHTTPRequestHandler):
(self.client_address[0], self.log_date_time_string(), format % args))
def run(bind: str, port: int, look: str, pem: str | None = None):
addr = (bind, port)
def run(bind: str, port: int, tls_port: int, look: str, pem: str | None = None):
ssl_ctx = None
if pem:
try:
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_ctx.load_cert_chain(pem)
except (ssl.SSLError, OSError) as e:
print(f"Cannot load PEM file '{pem}': {e}", file=sys.stderr)
sys.exit(1)
http_server = ThreadingHTTPServer((bind, port), OkHandler)
http_server.look = look
https_server = None
if ssl_ctx:
https_server = ThreadingHTTPServer((bind, tls_port), OkHandler)
https_server.look = look
https_server.socket = ssl_ctx.wrap_socket(https_server.socket, server_side=True)
def _handle_termination(signum, _frame):
_ = signum
if https_server:
https_server.shutdown()
raise KeyboardInterrupt
signal.signal(signal.SIGINT, _handle_termination)
signal.signal(signal.SIGTERM, _handle_termination)
try:
with ThreadingHTTPServer(addr, OkHandler) as httpd:
def _handle_termination(signum, _frame):
_ = signum
raise KeyboardInterrupt
signal.signal(signal.SIGINT, _handle_termination)
signal.signal(signal.SIGTERM, _handle_termination)
# attach chosen look to server so handlers can use it as default
httpd.look = look
if pem:
try:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain(pem)
except (ssl.SSLError, OSError) as e:
print(f"Cannot load PEM file '{pem}': {e}", file=sys.stderr)
sys.exit(1)
httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
scheme = "https" if pem else "http"
print(f"Serving on {scheme}://{bind}:{port} (default look={look}) (Ctrl-C to stop)")
httpd.serve_forever()
print(f"Serving on http://{bind}:{port} (default look={look})")
if https_server:
print(f"Serving on https://{bind}:{tls_port} (default look={look})")
t = threading.Thread(target=https_server.serve_forever, daemon=True)
t.start()
http_server.serve_forever()
except KeyboardInterrupt:
print("\nShutting down server")
except Exception as e:
print("Server error:", e, file=sys.stderr)
raise
finally:
http_server.server_close()
if https_server:
https_server.server_close()
if __name__ == "__main__":
@@ -345,7 +364,9 @@ if __name__ == "__main__":
help="Port to listen on (default: 8080)")
parser.add_argument("--look", choices=VALID_LOOKS, default=VALID_LOOKS[1],
help="Default HTML look for non-cli agents (basic, nice, bootstrap, tailwind). Default: nice")
parser.add_argument("--tls-port", type=int, default=8443, dest="tls_port",
help="TLS port to listen on when --pem is provided (default: 8443)")
parser.add_argument("--pem", default=None, metavar="PEMFILE",
help="PEM bundle file (cert chain + private key) to enable HTTPS; plain HTTP if omitted")
help="PEM bundle file (cert chain + private key); enables HTTPS on --tls-port alongside HTTP on --port")
args = parser.parse_args()
run(args.bind, args.port, args.look, args.pem)
run(args.bind, args.port, args.tls_port, args.look, args.pem)