hy2-panel/lib/request-origin.ts

103 lines
2.5 KiB
TypeScript

import { headers } from "next/headers";
import { getEnv } from "@/lib/env";
function getFirstHeaderValue(value: string | null) {
return value?.split(",")[0]?.trim() || null;
}
function sanitizeProto(value: string | null) {
const candidate = getFirstHeaderValue(value)?.toLowerCase();
if (candidate === "http" || candidate === "https") {
return candidate;
}
return null;
}
function sanitizeHost(value: string | null) {
const candidate = getFirstHeaderValue(value)?.replace(/^"|"$/g, "");
if (!candidate) {
return null;
}
if (candidate.includes("/") || /\s/.test(candidate)) {
return null;
}
return candidate;
}
function parseForwardedHeader(value: string | null) {
const firstEntry = getFirstHeaderValue(value);
if (!firstEntry) {
return {
host: null,
proto: null,
};
}
let host: string | null = null;
let proto: "http" | "https" | null = null;
for (const segment of firstEntry.split(";")) {
const [rawKey, ...rest] = segment.split("=");
const key = rawKey?.trim().toLowerCase();
const rawValue = rest.join("=").trim();
if (!key || !rawValue) {
continue;
}
if (key === "host") {
host = sanitizeHost(rawValue) || host;
} else if (key === "proto") {
proto = sanitizeProto(rawValue) || proto;
}
}
return { host, proto };
}
function buildOriginFromParts(
host: string | null,
proto: "http" | "https" | null,
fallback: string,
) {
if (!host) {
return fallback;
}
return `${proto || "http"}://${host}`;
}
export async function getRequestOrigin() {
const headerList = await headers();
const envOrigin = getEnv().APP_URL;
const forwarded = parseForwardedHeader(headerList.get("forwarded"));
const host =
sanitizeHost(headerList.get("x-forwarded-host")) ||
forwarded.host ||
sanitizeHost(headerList.get("host"));
const proto =
sanitizeProto(headerList.get("x-forwarded-proto")) || forwarded.proto;
return buildOriginFromParts(host, proto, envOrigin);
}
export function getOriginFromRequest(request: Request) {
const url = new URL(request.url);
const forwarded = parseForwardedHeader(request.headers.get("forwarded"));
const host =
sanitizeHost(request.headers.get("x-forwarded-host")) ||
forwarded.host ||
sanitizeHost(request.headers.get("host")) ||
url.host;
const proto =
sanitizeProto(request.headers.get("x-forwarded-proto")) ||
forwarded.proto ||
sanitizeProto(url.protocol.replace(/:$/, ""));
return buildOriginFromParts(host, proto, getEnv().APP_URL);
}