feat: add support for request redirection and abort signal handling
This commit is contained in:
		
							
								
								
									
										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); | ||||
| }); | ||||
| export default async (urlString, options = {}) => { | ||||
| const fetch = async (urlString, options = {}, redirectCount = 0) => { | ||||
|     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 | ||||
|         ? options.headers?.["Content-Type"] === "application/json" | ||||
|             ? JSON.stringify(options.body) | ||||
| @@ -48,6 +50,11 @@ export default async (urlString, options = {}) => { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         const requester = protocol === "https:" ? https : http; | ||||
|         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)) | ||||
|                 return reject(new Error(`Request failed with status code ${res.statusCode}`)); | ||||
|             resolve({ | ||||
| @@ -64,8 +71,15 @@ export default async (urlString, options = {}) => { | ||||
|             reject(new Error("Request timed out")); | ||||
|         }); | ||||
|         req.on("error", reject); | ||||
|         if (options.signal) { | ||||
|             options.signal.addEventListener("abort", () => { | ||||
|                 req.destroy(new Error("Request aborted")); | ||||
|                 reject(new Error("Request aborted")); | ||||
|             }); | ||||
|         } | ||||
|         if (body) | ||||
|             req.write(body); | ||||
|         req.end(); | ||||
|     }); | ||||
| }; | ||||
| export default fetch; | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -16,6 +16,9 @@ type ResponseData = { | ||||
| interface ExtendedRequestOptions extends http.RequestOptions { | ||||
|   body?: any; | ||||
|   timeout?: number; | ||||
|   followRedirects?: boolean; | ||||
|   maxRedirects?: number; | ||||
|   signal?: AbortSignal; | ||||
| } | ||||
|  | ||||
| const decompress = ( | ||||
| @@ -53,12 +56,16 @@ const readData = <T>( | ||||
|       .on("error", reject); | ||||
|   }); | ||||
|  | ||||
| export default async ( | ||||
| const fetch = async ( | ||||
|   urlString: string, | ||||
|   options: ExtendedRequestOptions = {} | ||||
|   options: ExtendedRequestOptions = {}, | ||||
|   redirectCount: number = 0 | ||||
| ): Promise<ResponseData> => { | ||||
|   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 | ||||
|       ? options.headers?.["Content-Type"] === "application/json" | ||||
| @@ -81,6 +88,12 @@ export default async ( | ||||
|   return new Promise((resolve, reject) => { | ||||
|     const requester = protocol === "https:" ? https : http; | ||||
|     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)) | ||||
|         return reject(new Error(`Request failed with status code ${res.statusCode}`)); | ||||
|       resolve({ | ||||
| @@ -99,8 +112,18 @@ export default async ( | ||||
|     }); | ||||
|  | ||||
|     req.on("error", reject); | ||||
|  | ||||
|     if(options.signal) { | ||||
|       options.signal.addEventListener("abort", () => { | ||||
|         req.destroy(new Error("Request aborted")); | ||||
|         reject(new Error("Request aborted")); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     if(body) | ||||
|       req.write(body); | ||||
|     req.end(); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| export default fetch; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user