Server: If-Modified-Since, block bis Änderung; Client: sofortiger Re-Poll

This commit is contained in:
2026-05-24 15:34:27 +02:00
parent 7efa38c91a
commit ab4486cc98
2 changed files with 27 additions and 29 deletions

11
app.js
View File

@@ -440,13 +440,12 @@ function startAutoRefresh() {
document.getElementById('refresh-timer').textContent = '● Live'; document.getElementById('refresh-timer').textContent = '● Live';
document.getElementById('refresh-timer').style.color = '#4ade80'; document.getElementById('refresh-timer').style.color = '#4ade80';
async function poll() { const myId = ++pollId;
while (true) { (async function poll() {
while (pollId === myId) {
await loadPGN(false); await loadPGN(false);
await new Promise(r => setTimeout(r, 30000));
} }
} })();
poll();
} }
/** /**
@@ -470,7 +469,7 @@ function hideError() {
*/ */
document.getElementById('refresh-btn').addEventListener('click', () => { document.getElementById('refresh-btn').addEventListener('click', () => {
pollId++; pollId++;
loadPGN(true); loadPGN(true).then(() => startAutoRefresh());
}); });
/** /**

View File

@@ -8,6 +8,7 @@ import http.server
import socketserver import socketserver
import urllib.request import urllib.request
import urllib.parse import urllib.parse
import urllib.error
import sys import sys
import os import os
import threading import threading
@@ -31,28 +32,33 @@ last_standings_fetch_time = None
def fetch_pgn(): def fetch_pgn():
"""Lädt die PGN-Datei von der URL als Bytes.""" """Lädt die PGN-Datei von der URL als Bytes.
Nutzt If-Modified-Since gibt (data, changed) zurück.
changed=False bedeutet 304 Not Modified (keine neuen Daten)."""
global last_fetch_time global last_fetch_time
try: try:
headers = {"User-Agent": "Mozilla/5.0"} headers = {"User-Agent": "Mozilla/5.0"}
if last_fetch_time: if last_fetch_time:
headers["modified_since"] = str(last_fetch_time) headers["If-Modified-Since"] = datetime.utcfromtimestamp(last_fetch_time).strftime("%a, %d %b %Y %H:%M:%S GMT")
req = urllib.request.Request(PGN_URL, headers=headers) req = urllib.request.Request(PGN_URL, headers=headers)
with urllib.request.urlopen(req, timeout=31) as response: with urllib.request.urlopen(req, timeout=31) as response:
data = response.read() data = response.read()
last_fetch_time = time.time() last_fetch_time = time.time()
return data return data, True
except urllib.error.HTTPError as e:
if e.code == 304:
return None, False
print(f"[{datetime.now().strftime('%H:%M:%S')}] HTTP-Fehler: {e}")
return None, False
except Exception as e: except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Fehler beim Laden: {e}") print(f"[{datetime.now().strftime('%H:%M:%S')}] Fehler beim Laden: {e}")
return None return None, False
def get_pgn_content_longpoll(since=0): def get_pgn_content_longpoll(since=0):
"""Long-Poll: Gibt PGN-Inhalt + mtime zurück. """Long-Poll: Blockiert, bis sich die PGN-Datei tatsächlich ändert.
Wenn Cache seit `since` unverändert ist, wird bis zu 31s gewartet.""" Antwortet nur bei neuen Daten, nie mit unverändertem Stand."""
deadline = time.time() + 31 while True:
while time.time() < deadline:
if os.path.exists(CACHE_FILE): if os.path.exists(CACHE_FILE):
mtime = os.path.getmtime(CACHE_FILE) mtime = os.path.getmtime(CACHE_FILE)
if mtime > since: if mtime > since:
@@ -60,28 +66,21 @@ def get_pgn_content_longpoll(since=0):
return f.read(), mtime return f.read(), mtime
age = time.time() - mtime age = time.time() - mtime
if age >= CACHE_TTL: if age >= CACHE_TTL:
content = fetch_pgn() content, changed = fetch_pgn()
if content: if changed and content is not None:
with open(CACHE_FILE, "wb") as f: with open(CACHE_FILE, "wb") as f:
f.write(content) f.write(content)
new_mtime = os.path.getmtime(CACHE_FILE) new_mtime = os.path.getmtime(CACHE_FILE)
if new_mtime > since: print(f"[{datetime.now().strftime('%H:%M:%S')}] PGN via Long-Poll aktualisiert ({len(content)} Bytes)")
print(f"[{datetime.now().strftime('%H:%M:%S')}] PGN via Long-Poll aktualisiert ({len(content)} Bytes)") return content, new_mtime
return content, new_mtime
else: else:
content = fetch_pgn() content, changed = fetch_pgn()
if content: if changed and content is not None:
with open(CACHE_FILE, "wb") as f: with open(CACHE_FILE, "wb") as f:
f.write(content) f.write(content)
return content, os.path.getmtime(CACHE_FILE) return content, os.path.getmtime(CACHE_FILE)
time.sleep(2) time.sleep(5)
# Timeout aktuellen Stand zurückgeben
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "rb") as f:
return f.read(), os.path.getmtime(CACHE_FILE)
return None, 0
def fetch_standings(): def fetch_standings():