Repository: electron-userland/electron-builder
File: packages/builder-util-runtime/src/httpExecutor.ts (TypeScript)

Code before:
      try {
        return task()
      } catch (e: any) {
        if (attemptNumber < maxRetries && ((e instanceof HttpError && e.isServerError()) || e.code === "EPIPE")) {
          continue
        }
        throw e
      }
    }
  }
}

export interface DownloadCallOptions {
  responseHandler: ((response: IncomingMessage, callback: (error: Error | null) => void) => void) | null
  onCancel: (callback: () => void) => void
  callback: (error: Error | null) => void

  options: DownloadOptions

  destination: string | null
}

function parseUrl(url: string, options: RequestOptions): URL {
  if (url.startsWith("http://") || url.startsWith("https://")) {
    // Absolute URL
    return new URL(url)
  } else {
    // Relative URL - construct base URL from original options
    const hostname = options.hostname
    const protocol = options.protocol || "https:"
    const port = options.port ? `:${options.port}` : ""
    const baseUrl = `${protocol}//${hostname}${port}`
    return new URL(url, baseUrl)
  }
}

export function configureRequestOptionsFromUrl(url: string, options: RequestOptions) {
  const result = configureRequestOptions(options)
  const parsedUrl = parseUrl(url, options)
  configureRequestUrl(parsedUrl, result)
  return result
}

export function configureRequestUrl(url: URL, options: RequestOptions): void {
  options.protocol = url.protocol
  options.hostname = url.hostname
  if (url.port) {
    options.port = url.port
  } else if (options.port) {
    delete options.port
  }

Review comment (suggestion):
Just noticed that this logic might actually be fragile since it only accepts http subprefix.

Since a relative URL throws when constructing a URL, what if we just `try-catch` here?
```
try {
    // will throw exception when not absolute URL
    return new URL(url)
} catch {
    // Relative URL - construct base URL from original options
    const hostname = options.hostname
    const protocol = options.protocol || "https:"
    const port = options.port ? `:${options.port}` : ""
    const baseUrl = `${protocol}//${hostname}${port}`
    return new URL(url, baseUrl)
}
```

Not sure if that'll throw a typescript compiler error though. Alternative code
```
let urlObj: URL
try {
    // will throw exception when not absolute URL
    urlObj = new URL(url)
} catch {
    // Relative URL - construct base URL from original options
    const hostname = options.hostname
    const protocol = options.protocol || "https:"
    const port = options.port ? `:${options.port}` : ""
    const baseUrl = `${protocol}//${hostname}${port}`
    urlObj = new URL(url, baseUrl)
}
return urlObj!
```

Code after:
      try {
        return task()
      } catch (e: any) {
        if (attemptNumber < maxRetries && ((e instanceof HttpError && e.isServerError()) || e.code === "EPIPE")) {
          continue
        }
        throw e
      }
    }
  }
}

export interface DownloadCallOptions {
  responseHandler: ((response: IncomingMessage, callback: (error: Error | null) => void) => void) | null
  onCancel: (callback: () => void) => void
  callback: (error: Error | null) => void

  options: DownloadOptions

  destination: string | null
}

function parseUrl(url: string, options: RequestOptions): URL {
  try {
    // Would throw exception if url is not absolute
    return new URL(url)
  } catch {
    // Relative URL - construct base URL from original options
    const hostname = options.hostname
    const protocol = options.protocol || "https:"
    const port = options.port ? `:${options.port}` : ""
    const baseUrl = `${protocol}//${hostname}${port}`
    return new URL(url, baseUrl)
  }
}

export function configureRequestOptionsFromUrl(url: string, options: RequestOptions) {
  const result = configureRequestOptions(options)
  const parsedUrl = parseUrl(url, options)
  configureRequestUrl(parsedUrl, result)
  return result
}

export function configureRequestUrl(url: URL, options: RequestOptions): void {
  options.protocol = url.protocol
  options.hostname = url.hostname
  if (url.port) {
    options.port = url.port
  } else if (options.port) {
    delete options.port
  }