index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { createRequester } from "./_chunks/createRequester-VFTkDs_7.js";
  2. import decompressResponse from "decompress-response";
  3. import follow from "follow-redirects";
  4. import http from "http";
  5. import https from "https";
  6. import toStream from "into-stream";
  7. import isStream from "is-stream";
  8. import progressStream from "progress-stream";
  9. import qs from "querystring";
  10. import url from "url";
  11. import * as tunnel from "tunnel-agent";
  12. function lowerCaseHeaders(headers) {
  13. return Object.keys(headers || {}).reduce((acc, header) => (acc[header.toLowerCase()] = headers[header], acc), {});
  14. }
  15. function formatHostname(hostname) {
  16. return hostname.replace(/^\.*/, ".").toLowerCase();
  17. }
  18. function parseNoProxyZone(zoneStr) {
  19. const zone = zoneStr.trim().toLowerCase(), zoneParts = zone.split(":", 2), zoneHost = formatHostname(zoneParts[0]), zonePort = zoneParts[1], hasPort = zone.indexOf(":") > -1;
  20. return { hostname: zoneHost, port: zonePort, hasPort };
  21. }
  22. function uriInNoProxy(uri, noProxy) {
  23. const port = uri.port || (uri.protocol === "https:" ? "443" : "80"), hostname = formatHostname(uri.hostname);
  24. return noProxy.split(",").map(parseNoProxyZone).some((noProxyZone) => {
  25. const isMatchedAt = hostname.indexOf(noProxyZone.hostname), hostnameMatched = isMatchedAt > -1 && isMatchedAt === hostname.length - noProxyZone.hostname.length;
  26. return noProxyZone.hasPort ? port === noProxyZone.port && hostnameMatched : hostnameMatched;
  27. });
  28. }
  29. function getProxyFromUri(uri) {
  30. const noProxy = process.env.NO_PROXY || process.env.no_proxy || "";
  31. return noProxy === "*" || noProxy !== "" && uriInNoProxy(uri, noProxy) ? null : uri.protocol === "http:" ? process.env.HTTP_PROXY || process.env.http_proxy || null : uri.protocol === "https:" && (process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy) || null;
  32. }
  33. function getHostFromUri(uri) {
  34. let host = uri.host;
  35. return uri.port && (uri.port === "80" && uri.protocol === "http:" || uri.port === "443" && uri.protocol === "https:") && (host = uri.hostname), host;
  36. }
  37. function getHostHeaderWithPort(uri) {
  38. const port = uri.port || (uri.protocol === "https:" ? "443" : "80");
  39. return `${uri.hostname}:${port}`;
  40. }
  41. function rewriteUriForProxy(reqOpts, uri, proxy) {
  42. const headers = reqOpts.headers || {}, options = Object.assign({}, reqOpts, { headers });
  43. return headers.host = headers.host || getHostHeaderWithPort(uri), options.protocol = proxy.protocol || options.protocol, options.hostname = proxy.host.replace(/:\d+/, ""), options.port = proxy.port, options.host = getHostFromUri(Object.assign({}, uri, proxy)), options.href = `${options.protocol}//${options.host}${options.path}`, options.path = url.format(uri), options;
  44. }
  45. function getProxyOptions(options) {
  46. let proxy;
  47. if (options.hasOwnProperty("proxy"))
  48. proxy = options.proxy;
  49. else {
  50. const uri = url.parse(options.url);
  51. proxy = getProxyFromUri(uri);
  52. }
  53. return typeof proxy == "string" ? url.parse(proxy) : proxy;
  54. }
  55. /*! simple-concat. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
  56. function concat(stream, cb) {
  57. const chunks = [];
  58. stream.on("data", function(chunk) {
  59. chunks.push(chunk);
  60. }), stream.once("end", function() {
  61. cb && cb(null, Buffer.concat(chunks)), cb = null;
  62. }), stream.once("error", function(err) {
  63. cb && cb(err), cb = null;
  64. });
  65. }
  66. function timedOut(req, time) {
  67. if (req.timeoutTimer)
  68. return req;
  69. const delays = isNaN(time) ? time : { socket: time, connect: time }, hostHeader = req.getHeader("host"), host = hostHeader ? " to " + hostHeader : "";
  70. delays.connect !== void 0 && (req.timeoutTimer = setTimeout(function() {
  71. req.abort();
  72. const e = new Error("Connection timed out on request" + host);
  73. e.code = "ETIMEDOUT", req.emit("error", e);
  74. }, delays.connect)), req.on("socket", function(socket) {
  75. if (!(socket.connecting || socket._connecting)) {
  76. connect();
  77. return;
  78. }
  79. socket.once("connect", connect);
  80. });
  81. function clear() {
  82. req.timeoutTimer && (clearTimeout(req.timeoutTimer), req.timeoutTimer = null);
  83. }
  84. function connect() {
  85. clear(), delays.socket !== void 0 && req.setTimeout(delays.socket, function() {
  86. req.abort();
  87. const e = new Error("Socket timed out on request" + host);
  88. e.code = "ESOCKETTIMEDOUT", req.emit("error", e);
  89. });
  90. }
  91. return req.on("error", clear);
  92. }
  93. const uriParts = [
  94. "protocol",
  95. "slashes",
  96. "auth",
  97. "host",
  98. "port",
  99. "hostname",
  100. "hash",
  101. "search",
  102. "query",
  103. "pathname",
  104. "path",
  105. "href"
  106. ], defaultProxyHeaderWhiteList = [
  107. "accept",
  108. "accept-charset",
  109. "accept-encoding",
  110. "accept-language",
  111. "accept-ranges",
  112. "cache-control",
  113. "content-encoding",
  114. "content-language",
  115. "content-location",
  116. "content-md5",
  117. "content-range",
  118. "content-type",
  119. "connection",
  120. "date",
  121. "expect",
  122. "max-forwards",
  123. "pragma",
  124. "referer",
  125. "te",
  126. "user-agent",
  127. "via"
  128. ], defaultProxyHeaderExclusiveList = ["proxy-authorization"];
  129. function shouldEnable(options) {
  130. return typeof options.tunnel < "u" ? !!options.tunnel : url.parse(options.url).protocol === "https:";
  131. }
  132. function applyAgent(opts = {}, proxy) {
  133. const options = Object.assign({}, opts), proxyHeaderWhiteList = defaultProxyHeaderWhiteList.concat(options.proxyHeaderWhiteList || []).map((header) => header.toLowerCase()), proxyHeaderExclusiveList = defaultProxyHeaderExclusiveList.concat(options.proxyHeaderExclusiveList || []).map((header) => header.toLowerCase()), proxyHeaders = getAllowedProxyHeaders(options.headers, proxyHeaderWhiteList);
  134. proxyHeaders.host = constructProxyHost(options), options.headers = Object.keys(options.headers || {}).reduce((headers, header) => (proxyHeaderExclusiveList.indexOf(header.toLowerCase()) === -1 && (headers[header] = options.headers[header]), headers), {});
  135. const tunnelFn = getTunnelFn(options, proxy), tunnelOptions = constructTunnelOptions(options, proxy, proxyHeaders);
  136. return options.agent = tunnelFn(tunnelOptions), options;
  137. }
  138. function getTunnelFn(options, proxy) {
  139. const uri = getUriParts(options), tunnelFnName = constructTunnelFnName(uri, proxy);
  140. return tunnel[tunnelFnName];
  141. }
  142. function getUriParts(options) {
  143. return uriParts.reduce((uri, part) => (uri[part] = options[part], uri), {});
  144. }
  145. function constructTunnelFnName(uri, proxy) {
  146. const uriProtocol = uri.protocol === "https:" ? "https" : "http", proxyProtocol = proxy.protocol === "https:" ? "Https" : "Http";
  147. return `${uriProtocol}Over${proxyProtocol}`;
  148. }
  149. function constructProxyHost(uri) {
  150. const port = uri.port, protocol = uri.protocol;
  151. let proxyHost = `${uri.hostname}:`;
  152. return port ? proxyHost += port : protocol === "https:" ? proxyHost += "443" : proxyHost += "80", proxyHost;
  153. }
  154. function getAllowedProxyHeaders(headers, whiteList) {
  155. return Object.keys(headers).filter((header) => whiteList.indexOf(header.toLowerCase()) !== -1).reduce((set, header) => (set[header] = headers[header], set), {});
  156. }
  157. function constructTunnelOptions(options, proxy, proxyHeaders) {
  158. return {
  159. proxy: {
  160. host: proxy.hostname,
  161. port: +proxy.port,
  162. proxyAuth: proxy.auth,
  163. headers: proxyHeaders
  164. },
  165. headers: options.headers,
  166. ca: options.ca,
  167. cert: options.cert,
  168. key: options.key,
  169. passphrase: options.passphrase,
  170. pfx: options.pfx,
  171. ciphers: options.ciphers,
  172. rejectUnauthorized: options.rejectUnauthorized,
  173. secureOptions: options.secureOptions,
  174. secureProtocol: options.secureProtocol
  175. };
  176. }
  177. const adapter = "node", reduceResponse = (res, reqUrl, method, body) => ({
  178. body,
  179. url: reqUrl,
  180. method,
  181. headers: res.headers,
  182. statusCode: res.statusCode,
  183. statusMessage: res.statusMessage
  184. }), httpRequester = (context, cb) => {
  185. const { options } = context, uri = Object.assign({}, url.parse(options.url));
  186. if (typeof fetch == "function" && options.fetch) {
  187. const controller = new AbortController(), reqOpts2 = context.applyMiddleware("finalizeOptions", {
  188. ...uri,
  189. method: options.method,
  190. headers: {
  191. ...typeof options.fetch == "object" && options.fetch.headers ? lowerCaseHeaders(options.fetch.headers) : {},
  192. ...lowerCaseHeaders(options.headers)
  193. },
  194. maxRedirects: options.maxRedirects
  195. }), fetchOpts = {
  196. credentials: options.withCredentials ? "include" : "omit",
  197. ...typeof options.fetch == "object" ? options.fetch : {},
  198. method: reqOpts2.method,
  199. headers: reqOpts2.headers,
  200. body: options.body,
  201. signal: controller.signal
  202. }, injectedResponse2 = context.applyMiddleware("interceptRequest", void 0, {
  203. adapter,
  204. context
  205. });
  206. if (injectedResponse2) {
  207. const cbTimer = setTimeout(cb, 0, null, injectedResponse2);
  208. return { abort: () => clearTimeout(cbTimer) };
  209. }
  210. const request2 = fetch(options.url, fetchOpts);
  211. return context.applyMiddleware("onRequest", { options, adapter, request: request2, context }), request2.then(async (res) => {
  212. const body = options.rawBody ? res.body : await res.text(), headers = {};
  213. res.headers.forEach((value, key) => {
  214. headers[key] = value;
  215. }), cb(null, {
  216. body,
  217. url: res.url,
  218. method: options.method,
  219. headers,
  220. statusCode: res.status,
  221. statusMessage: res.statusText
  222. });
  223. }).catch((err) => {
  224. err.name != "AbortError" && cb(err);
  225. }), { abort: () => controller.abort() };
  226. }
  227. const bodyType = isStream(options.body) ? "stream" : typeof options.body;
  228. if (bodyType !== "undefined" && bodyType !== "stream" && bodyType !== "string" && !Buffer.isBuffer(options.body))
  229. throw new Error(`Request body must be a string, buffer or stream, got ${bodyType}`);
  230. const lengthHeader = {};
  231. options.bodySize ? lengthHeader["content-length"] = options.bodySize : options.body && bodyType !== "stream" && (lengthHeader["content-length"] = Buffer.byteLength(options.body));
  232. let aborted = !1;
  233. const callback = (err, res) => !aborted && cb(err, res);
  234. context.channels.abort.subscribe(() => {
  235. aborted = !0;
  236. });
  237. let reqOpts = Object.assign({}, uri, {
  238. method: options.method,
  239. headers: Object.assign({}, lowerCaseHeaders(options.headers), lengthHeader),
  240. maxRedirects: options.maxRedirects
  241. });
  242. const proxy = getProxyOptions(options), tunnel2 = proxy && shouldEnable(options), injectedResponse = context.applyMiddleware("interceptRequest", void 0, {
  243. adapter,
  244. context
  245. });
  246. if (injectedResponse) {
  247. const cbTimer = setImmediate(callback, null, injectedResponse);
  248. return { abort: () => clearImmediate(cbTimer) };
  249. }
  250. if (options.maxRedirects !== 0 && (reqOpts.maxRedirects = options.maxRedirects || 5), proxy && tunnel2 ? reqOpts = applyAgent(reqOpts, proxy) : proxy && !tunnel2 && (reqOpts = rewriteUriForProxy(reqOpts, uri, proxy)), !tunnel2 && proxy && proxy.auth && !reqOpts.headers["proxy-authorization"]) {
  251. const [username, password] = proxy.auth.username ? [proxy.auth.username, proxy.auth.password] : proxy.auth.split(":").map((item) => qs.unescape(item)), authBase64 = Buffer.from(`${username}:${password}`, "utf8").toString("base64");
  252. reqOpts.headers["proxy-authorization"] = `Basic ${authBase64}`;
  253. }
  254. const transport = getRequestTransport(reqOpts, proxy, tunnel2);
  255. typeof options.debug == "function" && proxy && options.debug(
  256. "Proxying using %s",
  257. reqOpts.agent ? "tunnel agent" : `${reqOpts.host}:${reqOpts.port}`
  258. );
  259. const tryCompressed = reqOpts.method !== "HEAD";
  260. tryCompressed && !reqOpts.headers["accept-encoding"] && options.compress !== !1 && (reqOpts.headers["accept-encoding"] = "br, gzip, deflate");
  261. const finalOptions = context.applyMiddleware(
  262. "finalizeOptions",
  263. reqOpts
  264. ), request = transport.request(finalOptions, (response) => {
  265. const res = tryCompressed ? decompressResponse(response) : response, resStream = context.applyMiddleware("onHeaders", res, {
  266. headers: response.headers,
  267. adapter,
  268. context
  269. }), reqUrl = "responseUrl" in response ? response.responseUrl : options.url;
  270. if (options.stream) {
  271. callback(null, reduceResponse(res, reqUrl, reqOpts.method, resStream));
  272. return;
  273. }
  274. concat(resStream, (err, data) => {
  275. if (err)
  276. return callback(err);
  277. const body = options.rawBody ? data : data.toString(), reduced = reduceResponse(res, reqUrl, reqOpts.method, body);
  278. return callback(null, reduced);
  279. });
  280. });
  281. options.timeout && timedOut(request, options.timeout), request.once("error", callback);
  282. const { bodyStream, progress } = getProgressStream(options);
  283. return context.applyMiddleware("onRequest", { options, adapter, request, context, progress }), bodyStream ? bodyStream.pipe(request) : request.end(options.body), { abort: () => request.abort() };
  284. };
  285. function getProgressStream(options) {
  286. if (!options.body)
  287. return {};
  288. const bodyIsStream = isStream(options.body), length = options.bodySize || (bodyIsStream ? null : Buffer.byteLength(options.body));
  289. if (!length)
  290. return bodyIsStream ? { bodyStream: options.body } : {};
  291. const progress = progressStream({ time: 16, length });
  292. return { bodyStream: (bodyIsStream ? options.body : toStream(options.body)).pipe(progress), progress };
  293. }
  294. function getRequestTransport(reqOpts, proxy, tunnel2) {
  295. const isHttpsRequest = reqOpts.protocol === "https:", transports = reqOpts.maxRedirects === 0 ? { http, https } : { http: follow.http, https: follow.https };
  296. if (!proxy || tunnel2)
  297. return isHttpsRequest ? transports.https : transports.http;
  298. let isHttpsProxy = proxy.port === 443;
  299. return proxy.protocol && (isHttpsProxy = /^https:?/.test(proxy.protocol)), isHttpsProxy ? transports.https : transports.http;
  300. }
  301. const getIt = (initMiddleware = [], httpRequest = httpRequester) => createRequester(initMiddleware, httpRequest), environment = "node";
  302. export {
  303. adapter,
  304. environment,
  305. getIt
  306. };
  307. //# sourceMappingURL=index.js.map