(url: string)
| 293 | } |
| 294 | |
| 295 | async function resolveAndValidate(url: string): Promise<ResolvedTarget> { |
| 296 | const denyList = getHttpDenyList() |
| 297 | |
| 298 | const u = new URL(url) |
| 299 | let hostname = u.hostname |
| 300 | // Strip IPv6 brackets if present |
| 301 | if (hostname.startsWith('[') && hostname.endsWith(']')) { |
| 302 | hostname = hostname.slice(1, -1) |
| 303 | } |
| 304 | const protocol: 'http' | 'https' = u.protocol === 'https:' ? 'https' : 'http' |
| 305 | |
| 306 | if (ipaddr.isValid(hostname)) { |
| 307 | isDeniedIP(hostname, denyList) |
| 308 | return { |
| 309 | hostname, |
| 310 | ip: hostname, |
| 311 | family: hostname.includes(':') ? 6 : 4, |
| 312 | protocol |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | const records = await dns.lookup(hostname, { all: true }) |
| 317 | if (records.length === 0) { |
| 318 | throw new Error(`DNS resolution failed for ${hostname}`) |
| 319 | } |
| 320 | |
| 321 | for (const r of records) { |
| 322 | isDeniedIP(r.address, denyList) |
| 323 | } |
| 324 | |
| 325 | const chosen = records.find((r) => r.family === 4) ?? records[0] |
| 326 | |
| 327 | return { |
| 328 | hostname, |
| 329 | ip: chosen.address, |
| 330 | family: chosen.family as 4 | 6, |
| 331 | protocol |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | function createPinnedAgent(target: ResolvedTarget, options?: { ca?: string | string[] | Buffer }): http.Agent | https.Agent { |
| 336 | const Agent = target.protocol === 'https' ? https.Agent : http.Agent |
no test coverage detected