Fix the two reliability problems in the in-process Xray emulator by matching XTLS/Xray-core's transport semantics: - XHTTP upload queue: rewrite as a faithful port of xray-core's uploadQueue (bounded channel + sequence reorder heap). Packet-up POSTs are now acked immediately on buffering instead of blocking until the tunnel reader consumes them. The old block-until-consumed behavior throttled the uplink to the reassembly rate and deadlocked against the client's concurrent-POST limit, which showed up as "download a burst, stall, repeat" on video/large downloads. - Mux: dial the backend and pump uplink on a per-session goroutine fed by a bounded channel (mirrors xray-core's per-session buffered pipe). Previously the dial and backend writes ran inline in the shared read loop, so one slow target or backpressured session stalled every other muxed session. - XHTTP download writer: flush every write (matches httpServerConn.Write) instead of batching behind a 2ms/32KB window. - XHTTP: enforce a single download (stream-down) per session to stop two GETs from splitting the decoded stream and corrupting the tunnel. - Fix a close-of-closed-channel race in the mux session teardown (sync.Once). Remove the now-inert XHTTP tuning knobs (xhttp_queue_timeout_ms, xhttp_flush_ms, xhttp_flush_bytes) from the backend struct and the admin panel UI. Split admin/assets/app.js into ordered classic-script modules under admin/assets/js/ for maintainability. The concatenation is byte-identical to the old file and load order is preserved via defer, so behavior is unchanged. Add regression tests for the mux head-of-line stall and the out-of-order packet-up burst-stall; add golang.org/x/text to go.mod so tests build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
28 lines
1.2 KiB
JavaScript
28 lines
1.2 KiB
JavaScript
// ─── Auth error ───────────────────────────────────────────────────────────────
|
|
function doAuthError() {
|
|
sessionToken = "";
|
|
localStorage.removeItem("SESSION_TOKEN");
|
|
clearTimers();
|
|
mainApp.classList.add("hidden");
|
|
loginOverlay.classList.remove("hidden");
|
|
loginErr.textContent = t("Session expired — please sign in again.");
|
|
}
|
|
|
|
// ─── Boot ─────────────────────────────────────────────────────────────────────
|
|
window.addEventListener("load", () => {
|
|
if (sessionToken) {
|
|
// Try to validate the stored token
|
|
api("/api/auth/me").then(async res => {
|
|
if (!res.ok) { doAuthError(); return; }
|
|
const d = await res.json();
|
|
currentRole = d.role;
|
|
currentUser = d.username;
|
|
loginOverlay.classList.add("hidden");
|
|
mainApp.classList.remove("hidden");
|
|
initAfterLogin();
|
|
}).catch(() => doAuthError());
|
|
} else {
|
|
loginOverlay.classList.remove("hidden");
|
|
}
|
|
});
|