UNPKG

@rynn-k/proxy-agent

Version:

Efficient proxy rotation agent for Node.js with seamless axios integration

235 lines (195 loc) 5.91 kB
const net = require('net'); /** * Validate IP address * @param {string} ip - IP address to validate * @returns {boolean} True if valid IP */ function isValidIP(ip) { return net.isIP(ip) !== 0; } /** * Validate port number * @param {string|number} port - Port to validate * @returns {boolean} True if valid port */ function isValidPort(port) { const portNum = parseInt(port, 10); return !isNaN(portNum) && portNum > 0 && portNum <= 65535; } /** * Validate proxy object * @param {Object} proxy - Proxy object to validate * @returns {boolean} True if valid proxy */ function validateProxy(proxy) { if (!proxy || typeof proxy !== 'object') { return false; } if (!proxy.ip || !proxy.port) { return false; } if (!isValidIP(proxy.ip)) { return false; } if (!isValidPort(proxy.port)) { return false; } if (proxy.protocol) { const validProtocols = ['http', 'https', 'socks4', 'socks5']; if (!validProtocols.includes(proxy.protocol.toLowerCase())) { return false; } } if (proxy.username || proxy.password) { if (!proxy.username || !proxy.password) { return false; } if (typeof proxy.username !== 'string' || typeof proxy.password !== 'string') { return false; } if (proxy.username.length === 0 || proxy.password.length === 0) { return false; } } return true; } /** * Parse proxy line from file * @param {string} line - Proxy line from file * @param {number} lineNumber - Line number for error reporting * @param {string} defaultProtocol - Default protocol if not specified * @returns {Object} Parsed proxy object */ function parseProxyLine(line, lineNumber = 0, defaultProtocol = 'http') { line = line.trim(); const protocolMatch = line.match(/^(https?|socks4|socks5):\/\//i); if (protocolMatch) { try { const url = new URL(line); const protocol = url.protocol.replace(':', '').toLowerCase(); const proxy = { ip: url.hostname, port: parseInt(url.port, 10), protocol: protocol, url: line }; if (url.username && url.password) { proxy.username = decodeURIComponent(url.username); proxy.password = decodeURIComponent(url.password); } return proxy; } catch (error) { throw new Error(`Invalid proxy URL format at line ${lineNumber}: ${error.message}`); } } const parts = line.split(':'); if (parts.length < 2) { throw new Error(`Invalid proxy format at line ${lineNumber}: expected at least ip:port`); } const firstPart = parts[0].toLowerCase(); if (['http', 'https', 'socks4', 'socks5'].includes(firstPart)) { if (parts.length === 3) { const protocol = parts[0].toLowerCase(); const ip = parts[1].trim(); const port = parts[2].trim(); if (!ip || !port) { throw new Error(`Empty IP or port at line ${lineNumber}`); } return { ip, port: parseInt(port, 10), protocol, url: `${protocol}://${ip}:${port}` }; } else if (parts.length === 5) { const protocol = parts[0].toLowerCase(); const ip = parts[1].trim(); const port = parts[2].trim(); const username = parts[3].trim(); const password = parts[4].trim(); if (!ip || !port) { throw new Error(`Empty IP or port at line ${lineNumber}`); } if (!username || !password) { throw new Error(`Empty username or password at line ${lineNumber}`); } const encodedUsername = encodeURIComponent(username); const encodedPassword = encodeURIComponent(password); return { ip, port: parseInt(port, 10), username, password, protocol, url: `${protocol}://${encodedUsername}:${encodedPassword}@${ip}:${port}` }; } else { throw new Error(`Invalid proxy format at line ${lineNumber}: unsupported format with ${parts.length} parts`); } } const ip = parts[0].trim(); const port = parts[1].trim(); if (!ip || !port) { throw new Error(`Empty IP or port at line ${lineNumber}`); } if (parts.length === 2) { return { ip, port: parseInt(port, 10), protocol: defaultProtocol, url: `${defaultProtocol}://${ip}:${port}` }; } if (parts.length === 4) { const username = parts[2].trim(); const password = parts[3].trim(); if (!username || !password) { throw new Error(`Empty username or password at line ${lineNumber}`); } const encodedUsername = encodeURIComponent(username); const encodedPassword = encodeURIComponent(password); return { ip, port: parseInt(port, 10), username, password, protocol: defaultProtocol, url: `${defaultProtocol}://${encodedUsername}:${encodedPassword}@${ip}:${port}` }; } throw new Error(`Invalid proxy format at line ${lineNumber}: unsupported format with ${parts.length} parts`); } /** * Parse proxy URL * @param {string} proxyUrl - Proxy URL to parse * @returns {Object} Parsed proxy object */ function parseProxyUrl(proxyUrl) { try { const url = new URL(proxyUrl); const protocol = url.protocol.replace(':', '').toLowerCase(); if (!['http', 'https', 'socks4', 'socks5'].includes(protocol)) { throw new Error(`Unsupported protocol: ${protocol}`); } const proxy = { ip: url.hostname, port: parseInt(url.port, 10), protocol: protocol, url: proxyUrl }; if (url.username && url.password) { proxy.username = decodeURIComponent(url.username); proxy.password = decodeURIComponent(url.password); } return proxy; } catch (error) { throw new Error(`Invalid proxy URL: ${error.message}`); } } module.exports = { isValidIP, isValidPort, validateProxy, parseProxyLine, parseProxyUrl };