feat(gateway): rewrite cross-name absolute links through /bns/
theseus.x had a hardcoded href="https://sirius.x/" that worked in Theseus (which resolves BCH names natively) but 404'd for ordinary browsers served through the relay. Fixing it in every source file each time a site adds a cross-name link is thankless; fix it once at the gateway. New transformHtml() = injectBase() + rewriteBnsLinks(). The rewrite matches href="https?://<label>.<tld>/..." and, when <tld> is a known BCDN-only TLD, replaces the URL with /bns/<host>/... so the click stays inside the relay origin. Allowlisted TLDs: bch, x, p2p, deviant, bit, nav, test, coin, cash. Intentionally EXCLUDED: st, com, gt, sc, de — those exist in ICANN and rewriting them would hijack real web references (dl.silentmode.st is the working case). The rewrite only fires on gateway-served HTML, so the source sites remain clean when consumed at their native origin by Theseus or Ariadne. Verified: https://sirius.x/ on theseus.x home + plug-ins pages now becomes /bns/sirius.x/; ordinary dl.silentmode.st links unchanged.
This commit is contained in:
parent
0c6753d6a3
commit
63aba73d98
1 changed files with 30 additions and 4 deletions
|
|
@ -267,6 +267,32 @@ function injectBase(html, href) {
|
|||
if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (m) => m + `<base href="${href}">`);
|
||||
return `<base href="${href}">` + html;
|
||||
}
|
||||
// TLDs the gateway will route through /bns/ when it sees them on a cross-name
|
||||
// absolute link inside served HTML. Restricted to TLDs that are BCDN-only or
|
||||
// deliberately BCDN-shadowed (never a live ICANN answer today) so we don't
|
||||
// mis-hijack an ordinary web reference. .st, .com, .gt, .sc are intentionally
|
||||
// EXCLUDED — they live in ICANN and rewriting them would break real links.
|
||||
const BNS_TLDS_REWRITABLE = new Set(["bch","x","p2p","deviant","bit","nav","test","coin","cash"]);
|
||||
// Rewrite <a href="https://<label>.<bcdn-tld>/..."> to <a href="/bns/<label>.<bcdn-tld>/...">
|
||||
// so an ordinary browser served through the relay follows the link back through
|
||||
// the relay instead of trying to resolve the BCDN name via DNS (which returns
|
||||
// NXDOMAIN). This is a NO-OP on the native origin (Theseus/Ariadne), because
|
||||
// the gateway never serves there — those clients see the original URLs.
|
||||
function rewriteBnsLinks(html) {
|
||||
return html.replace(
|
||||
/\bhref\s*=\s*"(https?:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)*\.([a-z0-9-]+))(\/[^"]*)?)"/gi,
|
||||
(m, full, host, tld, path) => {
|
||||
if (!BNS_TLDS_REWRITABLE.has(tld.toLowerCase())) return m;
|
||||
return `href="/bns/${host.toLowerCase()}${path || "/"}"`;
|
||||
}
|
||||
);
|
||||
}
|
||||
// Apply both transforms (base first, then link rewrite) — order doesn't
|
||||
// matter because they touch disjoint constructs, but grouping them here
|
||||
// means every text/html response goes through the same path.
|
||||
function transformHtml(html, baseHref) {
|
||||
return rewriteBnsLinks(injectBase(html, baseHref));
|
||||
}
|
||||
const nx = (h) => `<div style="font:16px system-ui;padding:2rem;color:#333"><h2>NXDOMAIN</h2><p><code>${h}</code> is not registered on the Bitcoin Cash chain.</p></div>`;
|
||||
|
||||
// BNS `ip`-record fetch. The default `fetch` fails here for two reasons:
|
||||
|
|
@ -404,7 +430,7 @@ async function serve(res, name, reqPath, baseHref) {
|
|||
case "h":
|
||||
if (reqPath !== "/") { res.writeHead(404); return res.end("not found"); }
|
||||
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
||||
return res.end(injectBase(r.h, baseHref));
|
||||
return res.end(transformHtml(r.h, baseHref));
|
||||
case "s3": {
|
||||
const sub = subFolder(name);
|
||||
let base = r.s3; if (base.endsWith("/") && sub) base += sub + "/";
|
||||
|
|
@ -442,7 +468,7 @@ async function serve(res, name, reqPath, baseHref) {
|
|||
}
|
||||
const buf = Buffer.from(await up.arrayBuffer());
|
||||
let ct = up.headers.get("content-type"); if (!ct || ct === "application/octet-stream") ct = guess(key);
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, { "content-type": ct }); return res.end(injectBase(buf.toString("utf8"), baseHref)); }
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, { "content-type": ct }); return res.end(transformHtml(buf.toString("utf8"), baseHref)); }
|
||||
res.writeHead(up.status, { "content-type": ct }); return res.end(buf);
|
||||
}
|
||||
case "ip": {
|
||||
|
|
@ -464,7 +490,7 @@ async function serve(res, name, reqPath, baseHref) {
|
|||
if (lk === "content-type") continue;
|
||||
outHeaders[k] = v;
|
||||
}
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, outHeaders); return res.end(injectBase(buf.toString("utf8"), baseHref)); }
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, outHeaders); return res.end(transformHtml(buf.toString("utf8"), baseHref)); }
|
||||
res.writeHead(up.status, outHeaders); return res.end(buf);
|
||||
}
|
||||
case "p": {
|
||||
|
|
@ -483,7 +509,7 @@ async function serve(res, name, reqPath, baseHref) {
|
|||
if (lk === "content-type") continue;
|
||||
outHeaders[k] = v;
|
||||
}
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, outHeaders); return res.end(injectBase(buf.toString("utf8"), baseHref)); }
|
||||
if (ct.includes("text/html")) { res.writeHead(up.status, outHeaders); return res.end(transformHtml(buf.toString("utf8"), baseHref)); }
|
||||
res.writeHead(up.status, outHeaders); return res.end(buf);
|
||||
}
|
||||
case "u":
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue