{"version":3,"file":"http-proxy.mjs","names":[],"sources":["../src/http-proxy.ts"],"sourcesContent":["/* -------------------------------------------------------------------\n\n                       ⚡ Storm Software - Stryke\n\n This code was released as part of the Stryke project. Stryke\n is maintained by Storm Software under the Apache-2.0 license, and is\n free for commercial and private use. For more information, please visit\n our licensing page at https://stormsoftware.com/licenses/projects/stryke.\n\n Website:                  https://stormsoftware.com\n Repository:               https://github.com/storm-software/stryke\n Documentation:            https://docs.stormsoftware.com/projects/stryke\n Contact:                  https://stormsoftware.com/contact\n\n SPDX-License-Identifier:  Apache-2.0\n\n ------------------------------------------------------------------- */\n\nimport type { AgentConnectOpts } from \"agent-base\";\nimport { Buffer } from \"node:buffer\";\nimport { once } from \"node:events\";\nimport type {\n  AgentOptions,\n  ClientRequest,\n  OutgoingHttpHeaders\n} from \"node:http\";\nimport * as net from \"node:net\";\nimport * as tls from \"node:tls\";\nimport { URL } from \"node:url\";\nimport { Agent } from \"./agent\";\n\ntype Protocol<T> = T extends `${infer Protocol}:${infer _}` ? Protocol : never;\n\ninterface ConnectOptsMap {\n  http: Omit<net.TcpNetConnectOpts, \"host\" | \"port\">;\n  https: Omit<tls.ConnectionOptions, \"host\" | \"port\">;\n}\n\ntype ConnectOpts<T> = {\n  [P in keyof ConnectOptsMap]: Protocol<T> extends P\n    ? ConnectOptsMap[P]\n    : never;\n}[keyof ConnectOptsMap];\n\nexport type HttpProxyAgentOptions<T> = ConnectOpts<T> &\n  AgentOptions & {\n    headers?: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n  };\n\ninterface HttpProxyAgentClientRequest extends ClientRequest {\n  outputData?: {\n    data: string;\n  }[];\n  _header?: string | null;\n  _implicitHeader: () => void;\n}\n\n/**\n * The `HttpProxyAgent` implements an HTTP Agent subclass that connects to the specified \"HTTP proxy server\" in order to proxy HTTP requests.\n */\nexport class HttpProxyAgent<Uri extends string> extends Agent {\n  static protocols = [\"http\", \"https\"] as const;\n\n  readonly proxy: URL;\n\n  proxyHeaders: OutgoingHttpHeaders | (() => OutgoingHttpHeaders);\n\n  connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions;\n\n  constructor(proxy: Uri | URL, opts?: HttpProxyAgentOptions<Uri>) {\n    super(opts);\n    this.proxy = typeof proxy === \"string\" ? new URL(proxy) : proxy;\n    this.proxyHeaders = opts?.headers ?? {};\n\n    // Trim off the brackets from IPv6 addresses\n    const host = (this.proxy.hostname || this.proxy.host).replace(\n      /^\\[|\\]$/g,\n      \"\"\n    );\n    const port = this.proxy.port\n      ? Number.parseInt(this.proxy.port, 10)\n      : this.proxy.protocol === \"https:\"\n        ? 443\n        : 80;\n    this.connectOpts = {\n      ...(opts ? omit(opts, \"headers\") : null),\n      host,\n      port\n    };\n  }\n\n  addRequest(req: HttpProxyAgentClientRequest, opts: AgentConnectOpts): void {\n    req._header = null;\n    this.setRequestProps(req, opts);\n\n    // @ts-expect-error `addRequest()` isn't defined in `@types/node`\n    // eslint-disable-next-line ts/no-unsafe-call\n    super.addRequest(req, opts);\n  }\n\n  setRequestProps(\n    req: HttpProxyAgentClientRequest,\n    opts: AgentConnectOpts\n  ): void {\n    const { proxy } = this;\n    const protocol = opts.secureEndpoint ? \"https:\" : \"http:\";\n    const hostname = req.getHeader(\"host\") || \"localhost\";\n    const base = `${protocol}//${Array.isArray(hostname) ? hostname.join(\"\") : hostname}`;\n    const url = new URL(req.path, base);\n    if (opts.port !== 80) {\n      url.port = String(opts.port);\n    }\n\n    // Change the `http.ClientRequest` instance's \"path\" field\n    // to the absolute path of the URL that will be requested.\n    req.path = String(url);\n\n    // Inject the `Proxy-Authorization` header if necessary.\n\n    const headers: OutgoingHttpHeaders =\n      typeof this.proxyHeaders === \"function\"\n        ? this.proxyHeaders()\n        : { ...this.proxyHeaders };\n    if (proxy.username || proxy.password) {\n      const auth = `${decodeURIComponent(\n        proxy.username\n      )}:${decodeURIComponent(proxy.password)}`;\n      headers[\"Proxy-Authorization\"] = `Basic ${Buffer.from(auth).toString(\n        \"base64\"\n      )}`;\n    }\n\n    if (!headers[\"Proxy-Connection\"]) {\n      headers[\"Proxy-Connection\"] = this.keepAlive ? \"Keep-Alive\" : \"close\";\n    }\n    for (const name of Object.keys(headers)) {\n      const value = headers[name];\n      if (value) {\n        req.setHeader(name, value);\n      }\n    }\n  }\n\n  async connect(\n    req: HttpProxyAgentClientRequest,\n    opts: AgentConnectOpts\n  ): Promise<net.Socket> {\n    req._header = null;\n\n    if (!req.path.includes(\"://\")) {\n      this.setRequestProps(req, opts);\n    }\n\n    // At this point, the http ClientRequest's internal `_header` field\n    // might have already been set. If this is the case then we'll need\n    // to re-generate the string since we just changed the `req.path`.\n    let first: string;\n    let endOfHeaders: number;\n\n    req._implicitHeader();\n    if (req.outputData && req.outputData.length > 0) {\n      first = req.outputData[0]!.data;\n      endOfHeaders = first.indexOf(\"\\r\\n\\r\\n\") + 4;\n      req.outputData[0]!.data = req._header + first.substring(endOfHeaders);\n    }\n\n    // Create a socket connection to the proxy server.\n    let socket: net.Socket;\n    if (this.proxy.protocol === \"https:\") {\n      socket = tls.connect(this.connectOpts);\n    } else {\n      socket = net.connect(this.connectOpts);\n    }\n\n    // Wait for the socket's `connect` event, so that this `callback()`\n    // function throws instead of the `http` request machinery. This is\n    // important for i.e. `PacProxyAgent` which determines a failed proxy\n    // connection via the `callback()` function throwing.\n    await once(socket, \"connect\");\n\n    return socket;\n  }\n}\n\nfunction omit<T extends object, K extends [...(keyof T)[]]>(\n  obj: T,\n  ...keys: K\n): {\n  [K2 in Exclude<keyof T, K[number]>]: T[K2];\n} {\n  const ret = {} as {\n    [K in keyof typeof obj]: (typeof obj)[K];\n  };\n  let key: keyof typeof obj;\n  for (key in obj) {\n    if (!keys.includes(key)) {\n      ret[key] = obj[key];\n    }\n  }\n  return ret;\n}\n"],"mappings":";;;;;;;;;;;AA4DA,IAAa,iBAAb,cAAwD,MAAM;CAC5D,OAAO,YAAY,CAAC,QAAQ,QAAQ;CAEpC,AAAS;CAET;CAEA;CAEA,YAAY,OAAkB,MAAmC;AAC/D,QAAM,KAAK;AACX,OAAK,QAAQ,OAAO,UAAU,WAAW,IAAI,IAAI,MAAM,GAAG;AAC1D,OAAK,eAAe,MAAM,WAAW,EAAE;EAGvC,MAAM,QAAQ,KAAK,MAAM,YAAY,KAAK,MAAM,MAAM,QACpD,YACA,GACD;EACD,MAAM,OAAO,KAAK,MAAM,OACpB,OAAO,SAAS,KAAK,MAAM,MAAM,GAAG,GACpC,KAAK,MAAM,aAAa,WACtB,MACA;AACN,OAAK,cAAc;GACjB,GAAI,OAAO,KAAK,MAAM,UAAU,GAAG;GACnC;GACA;GACD;;CAGH,WAAW,KAAkC,MAA8B;AACzE,MAAI,UAAU;AACd,OAAK,gBAAgB,KAAK,KAAK;AAI/B,QAAM,WAAW,KAAK,KAAK;;CAG7B,gBACE,KACA,MACM;EACN,MAAM,EAAE,UAAU;EAClB,MAAM,WAAW,KAAK,iBAAiB,WAAW;EAClD,MAAM,WAAW,IAAI,UAAU,OAAO,IAAI;EAC1C,MAAM,OAAO,GAAG,SAAS,IAAI,MAAM,QAAQ,SAAS,GAAG,SAAS,KAAK,GAAG,GAAG;EAC3E,MAAM,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK;AACnC,MAAI,KAAK,SAAS,GAChB,KAAI,OAAO,OAAO,KAAK,KAAK;AAK9B,MAAI,OAAO,OAAO,IAAI;EAItB,MAAM,UACJ,OAAO,KAAK,iBAAiB,aACzB,KAAK,cAAc,GACnB,EAAE,GAAG,KAAK,cAAc;AAC9B,MAAI,MAAM,YAAY,MAAM,UAAU;GACpC,MAAM,OAAO,GAAG,mBACd,MAAM,SACP,CAAC,GAAG,mBAAmB,MAAM,SAAS;AACvC,WAAQ,yBAAyB,SAAS,OAAO,KAAK,KAAK,CAAC,SAC1D,SACD;;AAGH,MAAI,CAAC,QAAQ,oBACX,SAAQ,sBAAsB,KAAK,YAAY,eAAe;AAEhE,OAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,EAAE;GACvC,MAAM,QAAQ,QAAQ;AACtB,OAAI,MACF,KAAI,UAAU,MAAM,MAAM;;;CAKhC,MAAM,QACJ,KACA,MACqB;AACrB,MAAI,UAAU;AAEd,MAAI,CAAC,IAAI,KAAK,SAAS,MAAM,CAC3B,MAAK,gBAAgB,KAAK,KAAK;EAMjC,IAAI;EACJ,IAAI;AAEJ,MAAI,iBAAiB;AACrB,MAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,WAAQ,IAAI,WAAW,GAAI;AAC3B,kBAAe,MAAM,QAAQ,WAAW,GAAG;AAC3C,OAAI,WAAW,GAAI,OAAO,IAAI,UAAU,MAAM,UAAU,aAAa;;EAIvE,IAAI;AACJ,MAAI,KAAK,MAAM,aAAa,SAC1B,UAAS,IAAI,QAAQ,KAAK,YAAY;MAEtC,UAAS,IAAI,QAAQ,KAAK,YAAY;AAOxC,QAAM,KAAK,QAAQ,UAAU;AAE7B,SAAO;;;AAIX,SAAS,KACP,KACA,GAAG,MAGH;CACA,MAAM,MAAM,EAAE;CAGd,IAAI;AACJ,MAAK,OAAO,IACV,KAAI,CAAC,KAAK,SAAS,IAAI,CACrB,KAAI,OAAO,IAAI;AAGnB,QAAO"}