Merge branch 'development' into imports
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import * as https from "https";
|
||||
import * as http from "http";
|
||||
|
||||
const MAX_DEFAULT_REDIRECTS = 20;
|
||||
const redirectCodes = new Set([301, 302, 307, 308]);
|
||||
|
||||
/**
|
||||
* @typedef {Object} FetchOptions
|
||||
* @property {"GET" | "PUT" | "POST" | "DELETE"} [method] - Request method.
|
||||
* @property {Record<string, string>} [headers] - Request headers.
|
||||
* @property {"manual" | "follow"} [redirect] - Whether to follow redirects.
|
||||
* @property {number} [maxRedirects] - Maximum amount of redirects to be followed.
|
||||
* @property {AbortSignal} [signal] - Signal to abruptly cancel the request
|
||||
* @property {Uint8Array | string} [body] - Defines a request body. Data must be serializable.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {FetchOptions} options
|
||||
*/
|
||||
export function nativeFetch(url, options) {
|
||||
let state = "PENDING";
|
||||
const data = {content: [], headers: null, statusCode: null, url: url, statusText: "", redirected: false};
|
||||
const listeners = new Set();
|
||||
const errors = new Set();
|
||||
|
||||
/** * @param {URL} url */
|
||||
const execute = (url, options, redirectCount = 0) => {
|
||||
const Module = url.protocol === "http" ? http : https;
|
||||
|
||||
const req = Module.request(url.href, {
|
||||
headers: options.headers ?? {},
|
||||
method: options.method ?? "GET"
|
||||
}, res => {
|
||||
if (redirectCodes.has(res.statusCode) && res.headers.location && options.redirect !== "manual") {
|
||||
redirectCount++;
|
||||
|
||||
if (redirectCount >= (options.maxRedirects ?? MAX_DEFAULT_REDIRECTS)) {
|
||||
state = "ABORTED";
|
||||
const error = new Error(`Maximum amount of redirects reached (${options.maxRedirects ?? MAX_DEFAULT_REDIRECTS})`);
|
||||
errors.forEach(e => e(error));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let final;
|
||||
try {
|
||||
final = new URL(res.headers.location);
|
||||
}
|
||||
catch (error) {
|
||||
state = "ABORTED";
|
||||
errors.forEach(e => e(error));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, value] of new URL(url).searchParams.entries()) {
|
||||
final.searchParams.set(key, value);
|
||||
}
|
||||
|
||||
return execute(final, options, redirectCount);
|
||||
}
|
||||
|
||||
res.on("data", chunk => data.content.push(chunk));
|
||||
res.on("end", () => {
|
||||
data.content = Buffer.concat(data.content);
|
||||
data.headers = res.headers;
|
||||
data.statusCode = res.statusCode;
|
||||
data.url = url.toString();
|
||||
data.statusText = res.statusMessage;
|
||||
data.redirected = redirectCount > 0;
|
||||
state = "DONE";
|
||||
|
||||
listeners.forEach(listener => listener());
|
||||
});
|
||||
res.on("error", error => {
|
||||
state = "ABORTED";
|
||||
errors.forEach(e => e(error));
|
||||
});
|
||||
});
|
||||
|
||||
if (options.body) {
|
||||
try {req.write(options.body)}
|
||||
catch (error) {
|
||||
state = "ABORTED";
|
||||
errors.forEach(e => e(error));
|
||||
}
|
||||
finally {
|
||||
req.end();
|
||||
}
|
||||
}
|
||||
else {
|
||||
req.end();
|
||||
}
|
||||
|
||||
if (options.signal) {
|
||||
options.signal.addEventListener("abort", () => {
|
||||
req.end();
|
||||
state = "ABORTED";
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
execute(parsed, options);
|
||||
}
|
||||
catch (error) {
|
||||
state = "ABORTED";
|
||||
errors.forEach(e => e(error));
|
||||
}
|
||||
|
||||
return {
|
||||
onComplete(listener) {
|
||||
listeners.add(listener);
|
||||
},
|
||||
onError(listener) {
|
||||
errors.add(listener);
|
||||
},
|
||||
readData() {
|
||||
switch (state) {
|
||||
case "PENDING":
|
||||
throw new Error("Cannot read data before request is done!");
|
||||
case "ABORTED":
|
||||
throw new Error("Request was aborted.");
|
||||
case "DONE":
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,15 @@ const makeRequest = (url, options, callback, setReq) => {
|
||||
req.end();
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
|
||||
if (options.formData) {
|
||||
// Make sure to close the socket.
|
||||
try {req.write(options.formData);}
|
||||
finally {req.end();}
|
||||
} else {
|
||||
req.end();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const request = function (url, options, callback) {
|
||||
|
||||
@@ -3,6 +3,7 @@ export {default as https} from "./https";
|
||||
export * as electron from "./electron";
|
||||
export * as crypto from "./crypto";
|
||||
export * as vm from "./vm";
|
||||
export * from "./fetch";
|
||||
|
||||
// We can expose that without any issues.
|
||||
export * as path from "path";
|
||||
|
||||
Reference in New Issue
Block a user