Spaces:
Paused
Paused
| import { Agent } from 'http' | |
| import { RequestOptions, Agent as HttpsAgent } from 'https' | |
| import { Logger } from '@open-draft/logger' | |
| const logger = new Logger('utils getUrlByRequestOptions') | |
| // Request instance constructed by the "request" library | |
| // has a "self" property that has a "uri" field. This is | |
| // reproducible by performing a "XMLHttpRequest" request in JSDOM. | |
| export interface RequestSelf { | |
| uri?: URL | |
| } | |
| export type ResolvedRequestOptions = RequestOptions & RequestSelf | |
| export const DEFAULT_PATH = '/' | |
| const DEFAULT_PROTOCOL = 'http:' | |
| const DEFAULT_HOSTNAME = 'localhost' | |
| const SSL_PORT = 443 | |
| function getAgent( | |
| options: ResolvedRequestOptions | |
| ): Agent | HttpsAgent | undefined { | |
| return options.agent instanceof Agent ? options.agent : undefined | |
| } | |
| function getProtocolByRequestOptions(options: ResolvedRequestOptions): string { | |
| if (options.protocol) { | |
| return options.protocol | |
| } | |
| const agent = getAgent(options) | |
| const agentProtocol = (agent as RequestOptions)?.protocol | |
| if (agentProtocol) { | |
| return agentProtocol | |
| } | |
| const port = getPortByRequestOptions(options) | |
| const isSecureRequest = options.cert || port === SSL_PORT | |
| return isSecureRequest ? 'https:' : options.uri?.protocol || DEFAULT_PROTOCOL | |
| } | |
| function getPortByRequestOptions( | |
| options: ResolvedRequestOptions | |
| ): number | undefined { | |
| // Use the explicitly provided port. | |
| if (options.port) { | |
| return Number(options.port) | |
| } | |
| // Otherwise, try to resolve port from the agent. | |
| const agent = getAgent(options) | |
| if ((agent as HttpsAgent)?.options.port) { | |
| return Number((agent as HttpsAgent).options.port) | |
| } | |
| if ((agent as RequestOptions)?.defaultPort) { | |
| return Number((agent as RequestOptions).defaultPort) | |
| } | |
| // Lastly, return undefined indicating that the port | |
| // must inferred from the protocol. Do not infer it here. | |
| return undefined | |
| } | |
| interface RequestAuth { | |
| username: string | |
| password: string | |
| } | |
| function getAuthByRequestOptions( | |
| options: ResolvedRequestOptions | |
| ): RequestAuth | undefined { | |
| if (options.auth) { | |
| const [username, password] = options.auth.split(':') | |
| return { username, password } | |
| } | |
| } | |
| /** | |
| * Returns true if host looks like an IPv6 address without surrounding brackets | |
| * It assumes any host containing `:` is definitely not IPv4 and probably IPv6, | |
| * but note that this could include invalid IPv6 addresses as well. | |
| */ | |
| function isRawIPv6Address(host: string): boolean { | |
| return host.includes(':') && !host.startsWith('[') && !host.endsWith(']') | |
| } | |
| function getHostname(options: ResolvedRequestOptions): string | undefined { | |
| let host = options.hostname || options.host | |
| if (host) { | |
| if (isRawIPv6Address(host)) { | |
| host = `[${host}]` | |
| } | |
| // Check the presence of the port, and if it's present, | |
| // remove it from the host, returning a hostname. | |
| return new URL(`http://${host}`).hostname | |
| } | |
| return DEFAULT_HOSTNAME | |
| } | |
| /** | |
| * Creates a `URL` instance from a given `RequestOptions` object. | |
| */ | |
| export function getUrlByRequestOptions(options: ResolvedRequestOptions): URL { | |
| logger.info('request options', options) | |
| if (options.uri) { | |
| logger.info( | |
| 'constructing url from explicitly provided "options.uri": %s', | |
| options.uri | |
| ) | |
| return new URL(options.uri.href) | |
| } | |
| logger.info('figuring out url from request options...') | |
| const protocol = getProtocolByRequestOptions(options) | |
| logger.info('protocol', protocol) | |
| const port = getPortByRequestOptions(options) | |
| logger.info('port', port) | |
| const hostname = getHostname(options) | |
| logger.info('hostname', hostname) | |
| const path = options.path || DEFAULT_PATH | |
| logger.info('path', path) | |
| const credentials = getAuthByRequestOptions(options) | |
| logger.info('credentials', credentials) | |
| const authString = credentials | |
| ? `${credentials.username}:${credentials.password}@` | |
| : '' | |
| logger.info('auth string:', authString) | |
| const portString = typeof port !== 'undefined' ? `:${port}` : '' | |
| const url = new URL(`${protocol}//${hostname}${portString}${path}`) | |
| url.username = credentials?.username || '' | |
| url.password = credentials?.password || '' | |
| logger.info('created url:', url) | |
| return url | |
| } | |