feat: add Go server implementation with HTTPS support and update README for usage instructions
This commit is contained in:
+49
-28
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user