<NME> electronHttpExecutor.ts
<BEF> import { DownloadOptions, HttpExecutor, configureRequestOptions, configureRequestUrl } from "builder-util-runtime"
import { AuthInfo } from "electron"
import { RequestOptions } from "http"
import Session = Electron.Session
import ClientRequest = Electron.ClientRequest

export type LoginCallback = (username: string, password: string) => void
export const NET_SESSION_NAME = "electron-updater"

export function getNetSession(): Session {
  return require("electron").session.fromPartition(NET_SESSION_NAME, {
    cache: false,
  })
}

export class ElectronHttpExecutor extends HttpExecutor<Electron.ClientRequest> {
  private cachedSession: Session | null = null

  constructor(private readonly proxyLoginCallback?: (authInfo: AuthInfo, callback: LoginCallback) => void) {
    super()
  }

  async download(url: URL, destination: string, options: DownloadOptions): Promise<string> {
    return await options.cancellationToken.createPromise<string>((resolve, reject, onCancel) => {
      const requestOptions = {
        headers: options.headers || undefined,
        redirect: "manual",
      }
      configureRequestUrl(url, requestOptions)
      configureRequestOptions(requestOptions)
      this.doDownload(
        requestOptions,
        {
          destination,
          options,
          onCancel,
          callback: error => {
            if (error == null) {
              resolve(destination)
            } else {
              reject(error)
            }
          },
          responseHandler: null,
        },
  }

  createRequest(options: any, callback: (response: any) => void): any {
    // differential downloader can call this method very often, so, better to cache session
    if (this.cachedSession == null) {
      this.cachedSession = getNetSession()
    if (this.cachedSession == null) {
      this.cachedSession = getNetSession()
    }

    const request = require("electron").net.request({
      ...options,
      session: this.cachedSession,
    }) as Electron.ClientRequest
    request.on("response", callback)
    if (this.proxyLoginCallback != null) {
      request.on("login", this.proxyLoginCallback)
    }
    return request
  }

  protected addRedirectHandlers(
    request: ClientRequest,
    options: RequestOptions,
    reject: (error: Error) => void,
    redirectCount: number,
    handler: (options: RequestOptions) => void
  ): void {
    request.on("redirect", (statusCode: number, method: string, redirectUrl: string) => {
      // no way to modify request options, abort old and make a new one
      // https://github.com/electron/electron/issues/11505
      request.abort()

      if (redirectCount > this.maxRedirects) {
        reject(this.createMaxRedirectError())
      } else {
        handler(HttpExecutor.prepareRedirectUrlOptions(redirectUrl, options))
      }
    })
  }
}

<MSG> fix: remove host property from headers when signed by aws4.sign (#4848)

Close #4758
<DFF> @@ -46,6 +46,15 @@ export class ElectronHttpExecutor extends HttpExecutor<Electron.ClientRequest> {
   }
 
   createRequest(options: any, callback: (response: any) => void): any {
+
+    // fix (node 7+) for making electron updater work when using AWS private buckets, check if headers contain Host property
+    if (options.headers && options.headers.Host){
+      // set host value from headers.Host
+      options.host = options.headers.Host
+      // remove header property 'Host', if not removed causes net::ERR_INVALID_ARGUMENT exception
+      delete options.headers.Host;
+    }
+
     // differential downloader can call this method very often, so, better to cache session
     if (this.cachedSession == null) {
       this.cachedSession = getNetSession()

{'commit': "diff --git a/packages/electron-updater/src/electronHttpExecutor.ts b/packages/electron-updater/src/electronHttpExecutor.ts\nindex 32cadb23..5e8bb758 100644\n--- a/packages/electron-updater/src/electronHttpExecutor.ts\n+++ b/packages/electron-updater/src/electronHttpExecutor.ts\n@@ -46,6 +46,15 @@ export class ElectronHttpExecutor extends HttpExecutor<Electron.ClientRequest> {\n   }\n \n   createRequest(options: any, callback: (response: any) => void): any {\n+\n+    // fix (node 7+) for making electron updater work when using AWS private buckets, check if headers contain Host property\n+    if (options.headers && options.headers.Host){\n+      // set host value from headers.Host\n+      options.host = options.headers.Host\n+      // remove header property 'Host', if not removed causes net::ERR_INVALID_ARGUMENT exception\n+      delete options.headers.Host;\n+    }\n+\n     // differential downloader can call this method very often, so, better to cache session\n     if (this.cachedSession == null) {\n       this.cachedSession = getNetSession()\n", 'commit_subject': 'fix: remove host property from headers when signed by aws4.sign (#4848)', 'repo_name': 'electron-userland/electron-builder', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1586068377', 'tz_offset': '120', 'date': {'seconds': '1586068377'}}, 'commit_tree': 'd62cee88cba2d22c7096ed41c002939e7e65260d', 'commit_message': 'fix: remove host property from headers when signed by aws4.sign (#4848)\n\nClose #4758', 'language_name': 'JavaScript', 'license': 'mit', 'hunks': "@@ -46,6 +46,15 @@ export class ElectronHttpExecutor extends HttpExecutor<Electron.ClientRequest> {\n   }\n \n   createRequest(options: any, callback: (response: any) => void): any {\n+\n+    // fix (node 7+) for making electron updater work when using AWS private buckets, check if headers contain Host property\n+    if (options.headers && options.headers.Host){\n+      // set host value from headers.Host\n+      options.host = options.headers.Host\n+      // remove header property 'Host', if not removed causes net::ERR_INVALID_ARGUMENT exception\n+      delete options.headers.Host;\n+    }\n+\n     // differential downloader can call this method very often, so, better to cache session\n     if (this.cachedSession == null) {\n       this.cachedSession = getNetSession()\n", 'addition_count': 9, 'deletion_count': 0, 'src_file': 'a/packages/electron-updater/src/electronHttpExecutor.ts', 'tgt_file': 'b/packages/electron-updater/src/electronHttpExecutor.ts', 'file_extension': '.ts', 'before_file': 'import { DownloadOptions, HttpExecutor, configureRequestOptions, configureRequestUrl } from "builder-util-runtime"\nimport { AuthInfo } from "electron"\nimport { RequestOptions } from "http"\nimport Session = Electron.Session\nimport ClientRequest = Electron.ClientRequest\n\nexport type LoginCallback = (username: string, password: string) => void\nexport const NET_SESSION_NAME = "electron-updater"\n\nexport function getNetSession(): Session {\n  return require("electron").session.fromPartition(NET_SESSION_NAME, {\n    cache: false,\n  })\n}\n\nexport class ElectronHttpExecutor extends HttpExecutor<Electron.ClientRequest> {\n  private cachedSession: Session | null = null\n\n  constructor(private readonly proxyLoginCallback?: (authInfo: AuthInfo, callback: LoginCallback) => void) {\n    super()\n  }\n\n  async download(url: URL, destination: string, options: DownloadOptions): Promise<string> {\n    return await options.cancellationToken.createPromise<string>((resolve, reject, onCancel) => {\n      const requestOptions = {\n        headers: options.headers || undefined,\n        redirect: "manual",\n      }\n      configureRequestUrl(url, requestOptions)\n      configureRequestOptions(requestOptions)\n      this.doDownload(\n        requestOptions,\n        {\n          destination,\n          options,\n          onCancel,\n          callback: error => {\n            if (error == null) {\n              resolve(destination)\n            } else {\n              reject(error)\n            }\n          },\n          responseHandler: null,\n        },\n  }\n\n  createRequest(options: any, callback: (response: any) => void): any {\n    // differential downloader can call this method very often, so, better to cache session\n    if (this.cachedSession == null) {\n      this.cachedSession = getNetSession()\n    if (this.cachedSession == null) {\n      this.cachedSession = getNetSession()\n    }\n\n    const request = require("electron").net.request({\n      ...options,\n      session: this.cachedSession,\n    }) as Electron.ClientRequest\n    request.on("response", callback)\n    if (this.proxyLoginCallback != null) {\n      request.on("login", this.proxyLoginCallback)\n    }\n    return request\n  }\n\n  protected addRedirectHandlers(\n    request: ClientRequest,\n    options: RequestOptions,\n    reject: (error: Error) => void,\n    redirectCount: number,\n    handler: (options: RequestOptions) => void\n  ): void {\n    request.on("redirect", (statusCode: number, method: string, redirectUrl: string) => {\n      // no way to modify request options, abort old and make a new one\n      // https://github.com/electron/electron/issues/11505\n      request.abort()\n\n      if (redirectCount > this.maxRedirects) {\n        reject(this.createMaxRedirectError())\n      } else {\n        handler(HttpExecutor.prepareRedirectUrlOptions(redirectUrl, options))\n      }\n    })\n  }\n}\n', 'message': 'fix: remove host property from headers when signed by aws4.sign (#4848)\n\nClose #4758', 'author_name': 'GitHub', 'lang': 'ts'}