UNPKG

@xan105/request

Version:

Simple HTTP request client with file download progress

216 lines (199 loc) 6.57 kB
/* Copyright (c) Anthony Beaumont This source code is licensed under the MIT License found in the LICENSE file in the root directory of this source tree. */ /* Like h1/request.js but with the fetch API. This is meant to be used in a Browser env. */ export default function request(href, option = {}) { let options = { method: option.method || "GET", timeout: option.timeout || 3000, maxRedirect: option.maxRedirect || option.maxRedirect == 0 ? option.maxRedirect : 3, maxRetry: option.maxRetry || option.maxRetry == 0 ? option.maxRetry : 0, retryDelay: option.retryDelay || 200, headers: new Headers(option.headers), mode: option.mode || "cors", cache: option.cache || "no-store", // default,no-store,reload,no-cache,force-cache,only-if-cached }; return new Promise((resolve, reject) => { if (typeof href !== "string" && !Array.isArray(href)) return reject({ code: "ERR_INVALID_ARG_TYPE", message: `URL is not a string. Received ${typeof href}`, }); if (Array.isArray(href) && !href.every((url) => typeof url === "string")) return reject({ code: "UNEXPECTED_URL_ARRAY", message: "URL as an array is only used internally and should be made only of strings !", }); if (!Array.isArray(href) && typeof href === "string") href = [href]; let url; try { url = new URL(href[href.length - 1]); } catch (err) { return reject({ code: "BAD_URL", message: `URL is malformed. Received: "${href}"`, }); } if ( typeof window !== "undefined" && typeof window.document !== "undefined" && window.fetch ) { //Browser ? and has fetch API ? const controller = new AbortController(); const signal = controller.signal; options.signal = signal; options.redirect = "manual"; const timeout = setTimeout(function () { option.maxRetry = options.maxRetry - 1; if (option.maxRetry < 0) { reject({ code: "ETIMEOUT", message: "connect ETIMEDOUT", url: url.href, trace: href, }); controller.abort(); } else { setTimeout(function () { return resolve(request(href, option)); }, options.retryDelay); } }, options.timeout); fetch(url, options) .then(function (res) { clearTimeout(timeout); if (options.method === "HEAD") { resolve({ code: res.status, message: res.statusText, url: url.href, headers: parseHeaders(res.headers), }); } else if (res.status >= 200 && res.status < 300) { if (res.ok) { res.text().then((data) => { resolve({ code: res.status, message: res.statusText, url: url.href, trace: href, headers: parseHeaders(res.headers), body: data, }); }); } else { option.maxRetry = options.maxRetry - 1; if (option.maxRetry < 0) { reject({ code: "EINTERRUPTED", message: "The connection was terminated while the message was still being sent", url: url.href, trace: href, headers: parseHeaders(res.headers), }); } else { setTimeout(function () { return resolve(request(href, option)); }, options.retryDelay); } } } else if (res.status >= 300 && res.status < 400 && res.redirected) { option.maxRedirect = options.maxRedirect - 1; if (options.maxRedirect < 0) { return reject({ code: "EREDIRECTMAX", message: "Maximum redirection reached", url: url.href, trace: href, headers: parseHeaders(res.headers), }); } else { if ( options.method === "POST" && [301, 302, 303].includes(res.status) ) { option.method = "GET"; if (option.headers) { delete option.headers["content-length"]; delete option.headers["content-type"]; } } href.push(res.url); return resolve(request(href, option)); } } else { option.maxRetry = options.maxRetry - 1; if (option.maxRetry < 0) { reject({ code: res.status, message: res.statusText, url: url.href, trace: href, headers: parseHeaders(res.headers), }); controller.abort(); } else { setTimeout(function () { return resolve(request(href, option)); }, options.retryDelay); } } }) .catch((err) => { clearTimeout(timeout); option.maxRetry = options.maxRetry - 1; if (option.maxRetry < 0) { reject({ code: err.code, message: err.message, url: url.href, trace: href, }); controller.abort(); } else { setTimeout(function () { return resolve(request(href, option)); }, options.retryDelay); } }); } else { return reject({ code: "ENOSUPPORT", message: "Web (Browser) Fetch API is not available !", }); } }); } export function get(url, option = {}) { option.method = "GET"; return request(url, option); } export function head(url, option = {}) { option.method = "HEAD"; return request(url, option); } export async function getJson(url, option = {}) { if (!option.headers) option.headers = {}; if (!option.headers["Accept"]) option.headers["Accept"] = "application/json, application/json;indent=2"; option.method = "GET"; const { body: data } = await request(url, option); const json = JSON.parse(data); return json; } function parseHeaders(headers) { let response = {}; headers.forEach((value, name) => { response[name] = value; }); return response; }