Organize scripts and add TWS plugins with HTTP timeouts

Vendor pinned T5 and T6 plugin sources under the new plugins layout. Add native HTTP deadlines, status and timing diagnostics, telemetry integration, and build/test tooling.
This commit is contained in:
2026-10-09 11:02:20 -04:00
parent b172086301
commit 1121b44857
253 changed files with 28028 additions and 59 deletions
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Compile both real HTTP utilities and exercise native deadlines on loopback."""
import http.server
import json
import os
from pathlib import Path
import socketserver
import subprocess
import tempfile
import threading
import time
ROOT = Path(__file__).resolve().parents[1]
class Handler(http.server.BaseHTTPRequestHandler):
def log_message(self, *_):
pass
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
assert body == b'{"event":"heartbeat"}'
assert self.headers.get("Content-Type") == "application/json"
assert self.headers.get("Authorization") == "Bearer test-only"
if self.path == "/stall":
time.sleep(2)
return
if self.path == "/partial":
self.send_response(200)
self.send_header("Content-Length", "100")
self.end_headers()
self.wfile.write(b"{")
self.wfile.flush()
time.sleep(2)
return
if self.path == "/slow":
time.sleep(0.1)
status = int(self.path[1:]) if self.path[1:].isdigit() else 200
payload = b'{"ok":true}' if status == 200 else b'{"ok":false}'
self.send_response(status)
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
class StallTLS(socketserver.BaseRequestHandler):
def handle(self):
# Accept TCP but never finish TLS: the connection deadline must fire.
time.sleep(6)
class Server(socketserver.ThreadingTCPServer):
daemon_threads = True
def main():
with Server(("127.0.0.1", 0), Handler) as http, Server(("127.0.0.1", 0), StallTLS) as tls, tempfile.TemporaryDirectory(prefix="tws-http-tests-") as temp:
for server in (http, tls):
threading.Thread(target=server.serve_forever, daemon=True).start()
try:
for game in ("t5", "t6"):
source = ROOT / game / "plugins" / f"tws-{game}-gsc-utils" / "src" / "utils"
executable = Path(temp) / game
subprocess.run([os.environ.get("CXX", "c++"), "-std=c++20", "-Wall", "-Wextra", "-Werror", "-I", str(source),
str(ROOT / "tools/tests/http_probe.cpp"), str(source / "http.cpp"), "-lcurl", "-o", str(executable)], check=True)
base = f"http://127.0.0.1:{http.server_address[1]}"
env = {**os.environ, "NO_PROXY": "*", "no_proxy": "*"}
def probe(path, connect="1000", total="1000"):
url = path if "://" in path else base + path
completed = subprocess.run([str(executable), url, connect, total], env=env, capture_output=True, text=True, check=True, timeout=8)
return json.loads(completed.stdout)
normal = probe("/200", "-", "-")
assert normal["curl_code"] == 0 and normal["http_status"] == 200 and normal["body_size"] == 11, normal
assert 0 <= normal["dns_ms"] <= normal["connect_ms"] <= normal["first_byte_ms"] <= normal["total_ms"], normal
for status in (401, 429, 500):
rejected = probe(f"/{status}")
assert rejected["curl_code"] == 0 and rejected["http_status"] == status and rejected["body_size"] == 12, rejected
for path, status in (("/stall", 0), ("/partial", 200)):
timed_out = probe(path, total="200")
assert timed_out["curl_code"] == 28 and timed_out["http_status"] == status, timed_out
assert 150 <= timed_out["total_ms"] < 1500 and timed_out["wall_ms"] < 1500, timed_out
assert timed_out["body_size"] == 0, timed_out
if status: assert timed_out["first_byte_ms"] > 0, timed_out
assert probe("/200")["curl_code"] == 0 # The next request completes after the stall.
slow = probe("/slow")
assert slow["curl_code"] == 0 and slow["first_byte_ms"] >= 90, slow
tls_url = f"https://127.0.0.1:{tls.server_address[1]}/"
stalled_connect = probe(tls_url, connect="150", total="1000")
assert stalled_connect["curl_code"] == 28 and 100 <= stalled_connect["total_ms"] < 800, stalled_connect
# Total timeout must also bound connection setup when it is shorter.
total_connect = probe(tls_url, connect="1000", total="150")
assert total_connect["curl_code"] == 28 and 100 <= total_connect["total_ms"] < 800, total_connect
for connect, total in (("0", "100"), ("100", "0"), ("-1", "100")):
invalid = probe("/200", connect, total)
assert invalid["curl_code"] == 43 and invalid["http_status"] == 0, invalid
print(f"PASS {game}: POST body/headers, HTTP errors, stalled/partial responses, recovery, TLS/connect and total deadlines, invalid limits", flush=True)
finally:
http.shutdown()
tls.shutdown()
if __name__ == "__main__":
main()