Enhance README and ok-server.py to include X-* header detection and display
This commit is contained in:
69
ok-server.py
69
ok-server.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user