T-HUB/packages/egress/src/transport.ts
glass beadian aa5e262f3a Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
2026-10-10 11:56:42 +00:00

77 lines
4.1 KiB
TypeScript

import http, { type IncomingMessage } from "node:http";
import https from "node:https";
import { EgressError } from "./errors.js";
import type { ValidatedEndpoint } from "./policy.js";
export interface WireResponse { status: number; statusText: string; headers: Headers; body: Buffer }
/** A fresh socket for every hop: no proxy, second DNS lookup, or unvalidated pooled connection. */
export function requestPinned(endpoint: ValidatedEndpoint, method: string, headers: Headers, body: Buffer | undefined,
signal: AbortSignal, maxResponseBytes: number,
onHeaders: (status: number, headers: Headers) => Promise<void>): Promise<WireResponse> {
return new Promise((resolve, reject) => {
const requestHeaders = Object.fromEntries(headers.entries());
requestHeaders.host = endpoint.url.host;
requestHeaders["accept-encoding"] = "identity";
delete requestHeaders["content-length"];
delete requestHeaders["transfer-encoding"];
delete requestHeaders.connection;
if (body) requestHeaders["content-length"] = String(body.length);
let response: IncomingMessage | undefined;
let settled = false;
const fail = (error: EgressError) => {
if (settled) return;
settled = true;
reject(error);
response?.destroy();
request.destroy();
};
const request = (endpoint.url.protocol === "https:" ? https : http).request(endpoint.url, {
method, headers: requestHeaders, agent: false, family: endpoint.family,
// Keep URL hostname for TLS verification/SNI and Host, but return only the vetted IP.
lookup: (_hostname, options, callback) => {
if (options.all) callback(null, [{ address: endpoint.pinnedAddress, family: endpoint.family }]);
else callback(null, endpoint.pinnedAddress, endpoint.family);
},
}, (incoming) => {
response = incoming;
const responseHeaders = new Headers();
for (let i = 0; i < incoming.rawHeaders.length; i += 2) responseHeaders.append(incoming.rawHeaders[i]!, incoming.rawHeaders[i + 1]!);
incoming.on("error", () => fail(new EgressError("NETWORK_ERROR", "Response stream failed")));
incoming.on("aborted", () => fail(new EgressError("NETWORK_ERROR", "Response stream aborted")));
void (async () => {
const status = incoming.statusCode ?? 502;
await onHeaders(status, responseHeaders);
if (settled) return;
const hasBody = method !== "HEAD" && ![204, 205, 304].includes(status);
const length = Number(responseHeaders.get("content-length"));
if (hasBody && Number.isFinite(length) && length > maxResponseBytes) {
fail(new EgressError("RESPONSE_TOO_LARGE", "Response exceeds size limit")); return;
}
// Identity encoding makes both the size cap and returned byte representation explicit.
const encoding = responseHeaders.get("content-encoding");
if (hasBody && encoding && encoding.toLowerCase() !== "identity") {
fail(new EgressError("NETWORK_ERROR", "Upstream ignored identity encoding")); return;
}
const chunks: Buffer[] = [];
let size = 0;
incoming.on("data", (chunk: Buffer) => {
size += chunk.length;
if (size > maxResponseBytes) fail(new EgressError("RESPONSE_TOO_LARGE", "Response exceeds size limit"));
else chunks.push(chunk);
});
incoming.on("end", () => {
if (settled) return;
settled = true;
resolve({ status, statusText: incoming.statusMessage ?? "", headers: responseHeaders, body: Buffer.concat(chunks) });
});
})().catch((error: unknown) => fail(error instanceof EgressError ? error : new EgressError("NETWORK_ERROR", "Response processing failed")));
});
const aborted = () => fail(signal.reason instanceof EgressError ? signal.reason : new EgressError("ABORTED", "Request aborted"));
signal.addEventListener("abort", aborted, { once: true });
request.on("close", () => signal.removeEventListener("abort", aborted));
request.on("error", () => fail(new EgressError("NETWORK_ERROR", "Connection failed")));
if (signal.aborted) { aborted(); return; }
request.end(body);
});
}