Enhance README and ok-server.py to include X-* header detection and display
This commit is contained in:
32
README.md
32
README.md
@@ -1,3 +1,33 @@
|
|||||||
# ok-server
|
# Connectivity Test Server
|
||||||
|
|
||||||
A simple HTTP server designed for performing connectivity tests. May be run from command line or using a container.
|
A simple HTTP server designed for performing connectivity tests. May be run from command line or using a container.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Command Line
|
||||||
|
|
||||||
|
To run the server from the command line, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x ok-server.py
|
||||||
|
./ok-server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
Use the included `Dockerfile` to build and run the server in a Docker container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t ok-server .
|
||||||
|
docker run -p 8000:8000 ok-server
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the server and expose it on port 8000.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
The `ok-server.py` file was generated using AI. The owner of the repository has reviewed and approved the generated content, however, it is important to note that the AI-generated code may contain errors or may not be optimized for all use cases. Users are encouraged to review and test the code before using it in production environments. The owner of the repository is not responsible for any issues that may arise from using the AI-generated code.
|
||||||
|
|||||||
69
ok-server.py
69
ok-server.py
@@ -4,12 +4,13 @@
|
|||||||
Behavior:
|
Behavior:
|
||||||
- GET returns HTTP/1.1 200 with a message including the requester IP
|
- GET returns HTTP/1.1 200 with a message including the requester IP
|
||||||
- HEAD returns the same headers as GET but no body
|
- 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
|
- If User-Agent contains "curl" or "wget" (case-insensitive) the server
|
||||||
responds with Content-Type: text/plain; otherwise Content-Type: text/html.
|
responds with Content-Type: text/plain; otherwise Content-Type: text/html.
|
||||||
- --look controls which HTML variant is returned for non-CLI agents:
|
- --look controls which HTML variant is returned for non-CLI agents:
|
||||||
* basic - plain HTML with no external references
|
* basic - plain HTML with no external references
|
||||||
* nice - includes Google Font "Noto Sans" (default)
|
* nice - includes Google Font "Noto Sans" (default)
|
||||||
* bootstrap - Bootstrap 5 layout
|
* bootstrap - Bootstrap 5 layout
|
||||||
- The URL query parameter "look" (e.g. /?look=nice) overrides the command-line --look
|
- 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.
|
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
|
wget -S -O - http://localhost:8000/ # text/plain for wget
|
||||||
open http://localhost:8000/ # browser gets HTML variant per look
|
open http://localhost:8000/ # browser gets HTML variant per look
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
from html import escape
|
||||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
@@ -34,9 +37,11 @@ from urllib.parse import urlparse, parse_qs
|
|||||||
PLAIN_TEMPLATE = (
|
PLAIN_TEMPLATE = (
|
||||||
"Hello, This is a test HTTP server.\n\n"
|
"Hello, This is a test HTTP server.\n\n"
|
||||||
"Your request came from {ip}.\n\n"
|
"Your request came from {ip}.\n\n"
|
||||||
|
"{proxy_headers_block}"
|
||||||
"Have a nice day!\n"
|
"Have a nice day!\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
HTML_BASIC = """<!doctype html>
|
HTML_BASIC = """<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -47,11 +52,13 @@ HTML_BASIC = """<!doctype html>
|
|||||||
<h1>Hello,</h1>
|
<h1>Hello,</h1>
|
||||||
<p>This is a test HTTP server.</p>
|
<p>This is a test HTTP server.</p>
|
||||||
<p>Your request came from <strong>{ip}</strong>.</p>
|
<p>Your request came from <strong>{ip}</strong>.</p>
|
||||||
|
{proxy_headers_html}
|
||||||
<p>Have a nice day!</p>
|
<p>Have a nice day!</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
HTML_NICE = """<!doctype html>
|
HTML_NICE = """<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -71,12 +78,14 @@ HTML_NICE = """<!doctype html>
|
|||||||
<h1>Hello,</h1>
|
<h1>Hello,</h1>
|
||||||
<p>This is a test HTTP server.</p>
|
<p>This is a test HTTP server.</p>
|
||||||
<p>Your request came from <strong>{ip}</strong>.</p>
|
<p>Your request came from <strong>{ip}</strong>.</p>
|
||||||
|
{proxy_headers_html}
|
||||||
<p>Have a nice day!</p>
|
<p>Have a nice day!</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
HTML_BOOTSTRAP = """<!doctype html>
|
HTML_BOOTSTRAP = """<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -100,6 +109,7 @@ HTML_BOOTSTRAP = """<!doctype html>
|
|||||||
<h1 class="card-title">Hello,</h1>
|
<h1 class="card-title">Hello,</h1>
|
||||||
<p class="card-text">This is a test HTTP server.</p>
|
<p class="card-text">This is a test HTTP server.</p>
|
||||||
<p class="card-text">Your request came from <strong>{ip}</strong>.</p>
|
<p class="card-text">Your request came from <strong>{ip}</strong>.</p>
|
||||||
|
{proxy_headers_html}
|
||||||
<hr>
|
<hr>
|
||||||
<p class="mb-0">Have a nice day!</p>
|
<p class="mb-0">Have a nice day!</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,6 +132,32 @@ VALID_LOOKS = ("basic", "nice", "bootstrap")
|
|||||||
class OkHandler(BaseHTTPRequestHandler):
|
class OkHandler(BaseHTTPRequestHandler):
|
||||||
protocol_version = "HTTP/1.1"
|
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:
|
def _is_cli_agent(self, ua: str) -> bool:
|
||||||
if not ua:
|
if not ua:
|
||||||
return False
|
return False
|
||||||
@@ -142,17 +178,32 @@ class OkHandler(BaseHTTPRequestHandler):
|
|||||||
return look
|
return look
|
||||||
return None
|
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":
|
if look == "basic":
|
||||||
return HTML_BASIC.format(ip=ip)
|
return HTML_BASIC.format(
|
||||||
|
ip=ip,
|
||||||
|
proxy_headers_html=proxy_headers_html,
|
||||||
|
)
|
||||||
if look == "nice":
|
if look == "nice":
|
||||||
return HTML_NICE.format(ip=ip)
|
return HTML_NICE.format(
|
||||||
|
ip=ip,
|
||||||
|
proxy_headers_html=proxy_headers_html,
|
||||||
|
)
|
||||||
# fallback to bootstrap
|
# 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]:
|
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):
|
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"
|
ctype = "text/plain; charset=utf-8"
|
||||||
return body, ctype
|
return body, ctype
|
||||||
|
|
||||||
@@ -164,7 +215,7 @@ class OkHandler(BaseHTTPRequestHandler):
|
|||||||
# fallback to server-level default (now: nice)
|
# fallback to server-level default (now: nice)
|
||||||
look = getattr(self.server, "look", "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")
|
body = html.encode("utf-8")
|
||||||
ctype = "text/html; charset=utf-8"
|
ctype = "text/html; charset=utf-8"
|
||||||
return body, ctype
|
return body, ctype
|
||||||
|
|||||||
Reference in New Issue
Block a user