feat: add support for request redirection and abort signal handling
This commit is contained in:
parent
3f1effbb4a
commit
65f1707358
16
dist/index.js
vendored
16
dist/index.js
vendored
@ -27,8 +27,10 @@ const readData = (res, mode) => new Promise((resolve, reject) => {
|
|||||||
})
|
})
|
||||||
.on("error", reject);
|
.on("error", reject);
|
||||||
});
|
});
|
||||||
export default async (urlString, options = {}) => {
|
const fetch = async (urlString, options = {}, redirectCount = 0) => {
|
||||||
const { protocol, hostname, pathname, search, port } = new URL(urlString);
|
const { protocol, hostname, pathname, search, port } = new URL(urlString);
|
||||||
|
if (options.signal?.aborted)
|
||||||
|
throw new Error("Request aborted");
|
||||||
const body = options.method === "POST" && options.body
|
const body = options.method === "POST" && options.body
|
||||||
? options.headers?.["Content-Type"] === "application/json"
|
? options.headers?.["Content-Type"] === "application/json"
|
||||||
? JSON.stringify(options.body)
|
? JSON.stringify(options.body)
|
||||||
@ -48,6 +50,11 @@ export default async (urlString, options = {}) => {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const requester = protocol === "https:" ? https : http;
|
const requester = protocol === "https:" ? https : http;
|
||||||
const req = requester.request(requestOptions, res => {
|
const req = requester.request(requestOptions, res => {
|
||||||
|
if (options.followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||||
|
if (redirectCount >= (options.maxRedirects || 5))
|
||||||
|
return reject(new Error("Max redirects exceeded"));
|
||||||
|
return resolve(fetch(new URL(res.headers.location, urlString).toString(), options, redirectCount + 1));
|
||||||
|
}
|
||||||
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300))
|
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300))
|
||||||
return reject(new Error(`Request failed with status code ${res.statusCode}`));
|
return reject(new Error(`Request failed with status code ${res.statusCode}`));
|
||||||
resolve({
|
resolve({
|
||||||
@ -64,8 +71,15 @@ export default async (urlString, options = {}) => {
|
|||||||
reject(new Error("Request timed out"));
|
reject(new Error("Request timed out"));
|
||||||
});
|
});
|
||||||
req.on("error", reject);
|
req.on("error", reject);
|
||||||
|
if (options.signal) {
|
||||||
|
options.signal.addEventListener("abort", () => {
|
||||||
|
req.destroy(new Error("Request aborted"));
|
||||||
|
reject(new Error("Request aborted"));
|
||||||
|
});
|
||||||
|
}
|
||||||
if (body)
|
if (body)
|
||||||
req.write(body);
|
req.write(body);
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export default fetch;
|
||||||
|
27
src/index.ts
27
src/index.ts
@ -16,6 +16,9 @@ type ResponseData = {
|
|||||||
interface ExtendedRequestOptions extends http.RequestOptions {
|
interface ExtendedRequestOptions extends http.RequestOptions {
|
||||||
body?: any;
|
body?: any;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
followRedirects?: boolean;
|
||||||
|
maxRedirects?: number;
|
||||||
|
signal?: AbortSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decompress = (
|
const decompress = (
|
||||||
@ -53,12 +56,16 @@ const readData = <T>(
|
|||||||
.on("error", reject);
|
.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async (
|
const fetch = async (
|
||||||
urlString: string,
|
urlString: string,
|
||||||
options: ExtendedRequestOptions = {}
|
options: ExtendedRequestOptions = {},
|
||||||
|
redirectCount: number = 0
|
||||||
): Promise<ResponseData> => {
|
): Promise<ResponseData> => {
|
||||||
const { protocol, hostname, pathname, search, port } = new URL(urlString);
|
const { protocol, hostname, pathname, search, port } = new URL(urlString);
|
||||||
|
|
||||||
|
if(options.signal?.aborted)
|
||||||
|
throw new Error("Request aborted");
|
||||||
|
|
||||||
const body =
|
const body =
|
||||||
options.method === "POST" && options.body
|
options.method === "POST" && options.body
|
||||||
? options.headers?.["Content-Type"] === "application/json"
|
? options.headers?.["Content-Type"] === "application/json"
|
||||||
@ -81,6 +88,12 @@ export default async (
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const requester = protocol === "https:" ? https : http;
|
const requester = protocol === "https:" ? https : http;
|
||||||
const req = requester.request(requestOptions, res => {
|
const req = requester.request(requestOptions, res => {
|
||||||
|
if(options.followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||||
|
if(redirectCount >= (options.maxRedirects || 5))
|
||||||
|
return reject(new Error("Max redirects exceeded"));
|
||||||
|
return resolve(fetch(new URL(res.headers.location, urlString).toString(), options, redirectCount + 1));
|
||||||
|
}
|
||||||
|
|
||||||
if(res.statusCode && (res.statusCode < 200 || res.statusCode >= 300))
|
if(res.statusCode && (res.statusCode < 200 || res.statusCode >= 300))
|
||||||
return reject(new Error(`Request failed with status code ${res.statusCode}`));
|
return reject(new Error(`Request failed with status code ${res.statusCode}`));
|
||||||
resolve({
|
resolve({
|
||||||
@ -99,8 +112,18 @@ export default async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
req.on("error", reject);
|
req.on("error", reject);
|
||||||
|
|
||||||
|
if(options.signal) {
|
||||||
|
options.signal.addEventListener("abort", () => {
|
||||||
|
req.destroy(new Error("Request aborted"));
|
||||||
|
reject(new Error("Request aborted"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(body)
|
if(body)
|
||||||
req.write(body);
|
req.write(body);
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default fetch;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user