@upstash/context7-mcp
Version:
MCP server for Context7
61 lines (60 loc) • 2.06 kB
JavaScript
function stripIpv4MappedPrefix(ip) {
return ip.replace(/^::ffff:/i, "");
}
/**
* Returns true for RFC1918, CGNAT, loopback, link-local, and IPv6 private ranges.
*/
export function isPrivateOrLocalIp(ip) {
const plainIp = stripIpv4MappedPrefix(ip).toLowerCase();
if (plainIp.includes(".")) {
return (plainIp.startsWith("10.") ||
plainIp.startsWith("192.168.") ||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp) ||
/^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(plainIp) ||
plainIp.startsWith("127.") ||
plainIp.startsWith("169.254."));
}
// ::1 loopback in any textual form (e.g. "0::1", "0:0:0:0:0:0:0:1")
if (/^[0:]+1$/.test(plainIp)) {
return true;
}
// First hextets in fe80::/10 and fc00::/7 start with a non-zero digit, so a
// valid textual form always spells out all 4 digits.
// fe80::/10 link-local
if (/^fe[89ab][0-9a-f]:/.test(plainIp)) {
return true;
}
// fc00::/7 unique local
if (/^f[cd][0-9a-f]{2}:/.test(plainIp)) {
return true;
}
return false;
}
function pickClientIpFromForwardedList(ipList) {
for (const ip of ipList) {
const plainIp = stripIpv4MappedPrefix(ip);
if (!isPrivateOrLocalIp(plainIp)) {
return plainIp;
}
}
if (ipList.length === 0) {
return undefined;
}
return stripIpv4MappedPrefix(ipList[0]);
}
/**
* Extract client IP address from request headers.
* Handles X-Forwarded-For header for proxied requests.
*/
export function getClientIp(req) {
const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"];
if (forwardedFor) {
const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
const ipList = ips.split(",").map((ip) => ip.trim());
return pickClientIpFromForwardedList(ipList);
}
if (req.socket?.remoteAddress) {
return stripIpv4MappedPrefix(req.socket.remoteAddress);
}
return undefined;
}