Enhance README and ok-server.py to include X-* header detection and display

This commit is contained in:
2026-03-27 09:27:32 +01:00
parent 5ad1e04d0a
commit 5c323d07b5
2 changed files with 92 additions and 11 deletions

View File

@@ -4,12 +4,13 @@
Behavior:
- GET returns HTTP/1.1 200 with a message including the requester IP
- HEAD returns the same headers as GET but no body
- 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.
- --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)
* bootstrap - Bootstrap 5 layout
* basic - plain HTML with no external references
* nice - includes Google Font "Noto Sans" (default)
* bootstrap - Bootstrap 5 layout
- The URL query parameter "look" (e.g. /?look=nice) overrides the command-line --look
for that request only. Values are case-insensitive and must be one of basic,nice,bootstrap.
@@ -25,8 +26,10 @@ Test:
wget -S -O - http://localhost:8000/ # text/plain for wget
open http://localhost:8000/ # browser gets HTML variant per look
"""
import argparse
import sys
from html import escape
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Tuple
from urllib.parse import urlparse, parse_qs
@@ -34,9 +37,11 @@ from urllib.parse import urlparse, parse_qs
PLAIN_TEMPLATE = (
"Hello, This is a test HTTP server.\n\n"
"Your request came from {ip}.\n\n"
"{proxy_headers_block}"
"Have a nice day!\n"
)
HTML_BASIC = """<!doctype html>
<html lang="en">
<head>
@@ -47,11 +52,13 @@ HTML_BASIC = """<!doctype html>
<h1>Hello,</h1>
<p>This is a test HTTP server.</p>
<p>Your request came from <strong>{ip}</strong>.</p>
{proxy_headers_html}
<p>Have a nice day!</p>
</body>
</html>
"""
HTML_NICE = """<!doctype html>
<html lang="en">
<head>
@@ -71,12 +78,14 @@ HTML_NICE = """<!doctype html>
<h1>Hello,</h1>
<p>This is a test HTTP server.</p>
<p>Your request came from <strong>{ip}</strong>.</p>
{proxy_headers_html}
<p>Have a nice day!</p>
</div>
</body>
</html>
"""
HTML_BOOTSTRAP = """<!doctype html>
<html lang="en">
<head>
@@ -100,6 +109,7 @@ HTML_BOOTSTRAP = """<!doctype html>
<h1 class="card-title">Hello,</h1>
<p class="card-text">This is a test HTTP server.</p>
<p class="card-text">Your request came from <strong>{ip}</strong>.</p>
{proxy_headers_html}
<hr>
<p class="mb-0">Have a nice day!</p>
</div>
@@ -122,6 +132,32 @@ VALID_LOOKS = ("basic", "nice", "bootstrap")
class OkHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def _collect_x_headers(self):
x_headers = []
for name, value in self.headers.items():
if name.lower().startswith("x-"):
x_headers.append((name, value))
return sorted(x_headers, key=lambda item: item[0].lower())
def _proxy_markup(self, x_headers):
if not x_headers:
return "", ""
plain_lines = [f" - {name}: {value}" for name, value in x_headers]
proxy_headers_block = (
"Reverse proxy condition: detected via X-* headers.\n"
"X-* headers:\n" + "\n".join(plain_lines) + "\n\n"
)
html_items = "".join(
[f"<li><code>{escape(name)}</code>: {escape(value)}</li>" for name, value in x_headers]
)
proxy_headers_html = (
"<p>Reverse proxy condition: <strong>detected via X-* headers</strong>.</p>"
"<p>X-* headers:</p><ul>" + html_items + "</ul>"
)
return proxy_headers_block, proxy_headers_html
def _is_cli_agent(self, ua: str) -> bool:
if not ua:
return False
@@ -142,17 +178,32 @@ class OkHandler(BaseHTTPRequestHandler):
return look
return None
def _html_for_look(self, look: str, ip: str) -> str:
def _html_for_look(self, look: str, ip: str, proxy_headers_html: str) -> str:
if look == "basic":
return HTML_BASIC.format(ip=ip)
return HTML_BASIC.format(
ip=ip,
proxy_headers_html=proxy_headers_html,
)
if look == "nice":
return HTML_NICE.format(ip=ip)
return HTML_NICE.format(
ip=ip,
proxy_headers_html=proxy_headers_html,
)
# fallback to bootstrap
return HTML_BOOTSTRAP.format(ip=ip)
return HTML_BOOTSTRAP.format(
ip=ip,
proxy_headers_html=proxy_headers_html,
)
def _make_body_and_type(self, client_ip: str, user_agent: str) -> Tuple[bytes, str]:
x_headers = self._collect_x_headers()
proxy_headers_block, proxy_headers_html = self._proxy_markup(x_headers)
if self._is_cli_agent(user_agent):
body = PLAIN_TEMPLATE.format(ip=client_ip).encode("utf-8")
body = PLAIN_TEMPLATE.format(
ip=client_ip,
proxy_headers_block=proxy_headers_block,
).encode("utf-8")
ctype = "text/plain; charset=utf-8"
return body, ctype
@@ -164,7 +215,7 @@ class OkHandler(BaseHTTPRequestHandler):
# fallback to server-level default (now: nice)
look = getattr(self.server, "look", "nice")
html = self._html_for_look(look, client_ip)
html = self._html_for_look(look, client_ip, proxy_headers_html)
body = html.encode("utf-8")
ctype = "text/html; charset=utf-8"
return body, ctype