@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
256 lines (255 loc) • 9.25 kB
JavaScript
/**
* Shared NO_PROXY utility functions
* Extracted from awsProxyIntegration.ts and proxyFetch.ts to eliminate duplication
* Supports comprehensive NO_PROXY pattern matching including wildcards, domains, ports, and CIDR
*/
import { logger } from "../../utils/logger.js";
/**
* Check if an IP address is within a CIDR range
*/
function isIpInCIDR(ip, cidr) {
try {
const [cidrIp, prefixLength] = cidr.split("/");
const prefix = parseInt(prefixLength, 10);
if (isNaN(prefix) || prefix < 0 || prefix > 32) {
return false;
}
const ipToNumber = (ipStr) => {
const parts = ipStr.split(".").map(Number);
return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
};
const ipNum = ipToNumber(ip);
const cidrIpNum = ipToNumber(cidrIp);
const mask = (-1 << (32 - prefix)) >>> 0;
return (ipNum & mask) === (cidrIpNum & mask);
}
catch {
return false;
}
}
const IPV4_LITERAL = /^\d{1,3}(\.\d{1,3}){3}$/;
/**
* The form a URL gives an IPv6 literal (`[::1]`, lower-case, compressed), so
* `[0:0:0:0:0:0:0:1]` and `[::1]` compare equal. `null` when it is not one.
*/
function canonicalIpv6Host(address) {
try {
return new URL(`http://[${address}]`).hostname;
}
catch {
return null;
}
}
/** A fully qualified name's trailing dot, which a URL keeps and curl ignores. */
function withoutTrailingDot(name) {
return name.endsWith(".") ? name.slice(0, -1) : name;
}
/** An entry made only of dots (`.`, `..`) names no host. */
function isDotsOnly(entry) {
return /^\.+$/.test(entry);
}
/**
* curl's rules for one list entry: an optional leading `.` or `*.` is ignored;
* the entry names that host and every subdomain of it, an IP literal only
* itself; and a `:port` restricts it to that port. IPv6 is written bare (`::1`)
* or bracketed (`[::1]:8080`), and only the bracketed form can carry a port.
* One trailing dot is ignored on the entry's domain and on the URL's host name,
* so `example.com.` and `example.com` name the same host; an entry made only of
* dots names none.
*/
function matchesNoProxyEntry(entry, hostname, port) {
let host = entry;
let entryPort;
if (entry.startsWith("[")) {
const close = entry.indexOf("]");
if (close === -1) {
return false;
}
host = entry.slice(0, close + 1);
const rest = entry.slice(close + 1);
if (rest !== "") {
if (!/^:\d+$/.test(rest)) {
return false;
}
entryPort = rest.slice(1);
}
}
else {
const colons = entry.split(":").length - 1;
if (colons === 1) {
const separator = entry.indexOf(":");
host = entry.slice(0, separator);
entryPort = entry.slice(separator + 1);
if (!/^\d+$/.test(entryPort)) {
return false;
}
}
else if (colons > 1) {
host = `[${entry}]`;
}
}
if (entryPort !== undefined && entryPort !== port) {
return false;
}
if (host.startsWith("[")) {
return canonicalIpv6Host(host.slice(1, -1)) === hostname;
}
host = host.replace(/^(\*\.|\.)/, "");
if (/^\.*$/.test(host) || /[\s*/]/.test(host)) {
return false;
}
if (IPV4_LITERAL.test(host)) {
return hostname === host;
}
const domain = withoutTrailingDot(host);
// curl does not take "127.0.0.1." for an address, so it names nothing.
if (IPV4_LITERAL.test(domain)) {
return false;
}
const name = withoutTrailingDot(hostname);
return name === domain || name.endsWith(`.${domain}`);
}
/**
* The rules this function applied before it followed curl, still consulted so
* that a configuration that bypassed the proxy through them keeps doing so.
* That includes where they are looser than curl (`.example.com` also matches
* `notexample.com`) and an IPv4 CIDR range such as `192.168.1.0/24`, which
* curl-style entries do not express. They are unchanged except that an entry
* made only of dots names no host, as in the curl-style rules.
*/
function matchesLegacyNoProxyEntry(lowerPattern, hostname, port) {
// Taken as a suffix, "." leaves the empty string, which every host name ends
// with, and ".." leaves ".", which every host written with a trailing dot
// ends with.
if (isDotsOnly(lowerPattern)) {
return false;
}
// Domain suffix match (.example.com)
if (lowerPattern.startsWith(".")) {
const suffix = lowerPattern.slice(1);
return hostname.endsWith(suffix) || hostname === suffix;
}
// Port-specific match (hostname:port)
if (lowerPattern.includes(":")) {
const [patternHost, patternPort] = lowerPattern.split(":");
return hostname === patternHost && port === patternPort;
}
// CIDR notation (192.168.1.0/24) - only for IP addresses
if (lowerPattern.includes("/")) {
// Only apply CIDR when target is an IP literal
return IPV4_LITERAL.test(hostname) && isIpInCIDR(hostname, lowerPattern);
}
// Exact hostname match
return hostname === lowerPattern;
}
/**
* NO_PROXY bypass check. The value is a comma- or space-separated list; each
* entry is matched case-insensitively.
*
* Supported entries:
* - "*" - bypass all requests
* - "example.com", ".example.com", "*.example.com" - the host and any subdomain
* - "example.com:8443" - the same, on that port only; a URL without a port is
* on the scheme's default (80 for http, 443 for https)
* - "example.com." - the same as "example.com": one trailing dot is ignored on
* an entry and on the URL's host name, so "https://api.example.com./" matches
* - "192.168.1.10", "::1", "[::1]:8080" - an IP literal, exactly
* - "192.168.1.0/24" - an IPv4 CIDR range, for IPv4 literal targets (IPv6 CIDR
* ranges are not supported)
* - "." and ".." - an entry made only of dots names no host, so it matches
* nothing, as in curl
*
* @param targetUrl - The URL to check for proxy bypass
* @param noProxyEnv - Optional NO_PROXY environment variable value (if not provided, reads from process.env)
* @returns true if the URL should bypass proxy, false otherwise
*/
export function shouldBypassProxy(targetUrl, noProxyEnv) {
const noProxy = noProxyEnv || process.env.NO_PROXY || process.env.no_proxy;
if (!noProxy) {
return false;
}
try {
const url = new URL(targetUrl);
const hostname = url.hostname.toLowerCase();
const port = url.port || (url.protocol === "https:" ? "443" : "80");
const patterns = noProxy
.split(/[,\s]+/)
.map((p) => p.trim())
.filter(Boolean);
for (const pattern of patterns) {
const lowerPattern = pattern.toLowerCase();
// Wildcard match - bypass all
if (lowerPattern === "*") {
return true;
}
if (matchesNoProxyEntry(lowerPattern, hostname, port) ||
matchesLegacyNoProxyEntry(lowerPattern, hostname, port)) {
return true;
}
}
return false;
}
catch (error) {
logger.warn("[Proxy] Error in NO_PROXY bypass logic", { targetUrl, error });
return false;
}
}
/**
* Get the current NO_PROXY environment variable value
* Checks both uppercase and lowercase variants
*/
export function getNoProxyEnv() {
return process.env.NO_PROXY || process.env.no_proxy;
}
/**
* Simple NO_PROXY bypass check with basic pattern support
* Legacy function for backward compatibility with simpler use cases
*
* Supports:
* - "*" - bypass all
* - "example.com" - exact match
* - ".example.com" - domain suffix match
*
* An entry made only of dots names no host and matches nothing.
*
* @param targetUrl - The URL to check
* @param noProxyValue - The NO_PROXY value to check against
* @returns true if should bypass proxy
*/
export function shouldBypassProxySimple(targetUrl, noProxyValue) {
try {
const url = new URL(targetUrl);
const hostname = url.hostname.toLowerCase();
// Split NO_PROXY by comma and check each pattern
const patterns = noProxyValue.split(",").map((p) => p.trim().toLowerCase());
for (const pattern of patterns) {
if (!pattern) {
continue;
}
// As a suffix, "." would match every host written with a trailing dot.
if (isDotsOnly(pattern)) {
continue;
}
// Exact match
if (hostname === pattern) {
return true;
}
// Wildcard match (starts with .)
if (pattern.startsWith(".") && hostname.endsWith(pattern)) {
return true;
}
// Simple wildcard
if (pattern === "*") {
return true;
}
}
return false;
}
catch (error) {
logger.warn("[Proxy] Error in simple NO_PROXY bypass logic", {
targetUrl,
error,
});
return false;
}
}