2020-08-05 18:38:55 +02:00
|
|
|
/*
|
|
|
|
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2019-12-23 14:28:27 +01:00
|
|
|
import {
|
2020-04-04 17:34:46 +02:00
|
|
|
AbortError,
|
2020-04-19 19:05:12 +02:00
|
|
|
ConnectionError
|
2020-04-20 19:47:45 +02:00
|
|
|
} from "../../error.js";
|
2020-08-05 15:36:44 +00:00
|
|
|
import {abortOnTimeout} from "../timeout.js";
|
2019-12-23 14:28:27 +01:00
|
|
|
|
|
|
|
class RequestResult {
|
|
|
|
constructor(promise, controller) {
|
|
|
|
if (!controller) {
|
|
|
|
const abortPromise = new Promise((_, reject) => {
|
|
|
|
this._controller = {
|
|
|
|
abort() {
|
|
|
|
const err = new Error("fetch request aborted");
|
|
|
|
err.name = "AbortError";
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
2020-08-05 15:36:44 +00:00
|
|
|
this.promise = Promise.race([promise, abortPromise]);
|
2019-12-23 14:28:27 +01:00
|
|
|
} else {
|
2020-08-05 15:36:44 +00:00
|
|
|
this.promise = promise;
|
2019-12-23 14:28:27 +01:00
|
|
|
this._controller = controller;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abort() {
|
|
|
|
this._controller.abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
response() {
|
2020-08-05 15:36:44 +00:00
|
|
|
return this.promise;
|
2019-12-23 14:28:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 15:36:44 +00:00
|
|
|
export function createFetchRequest(createTimeout) {
|
|
|
|
return function fetchRequest(url, options) {
|
|
|
|
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
|
|
if (controller) {
|
|
|
|
options = Object.assign(options, {
|
|
|
|
signal: controller.signal
|
|
|
|
});
|
|
|
|
}
|
2019-12-23 14:28:27 +01:00
|
|
|
options = Object.assign(options, {
|
2020-08-05 15:36:44 +00:00
|
|
|
mode: "cors",
|
|
|
|
credentials: "omit",
|
|
|
|
referrer: "no-referrer",
|
|
|
|
cache: "no-cache",
|
2019-12-23 14:28:27 +01:00
|
|
|
});
|
2020-08-05 15:36:44 +00:00
|
|
|
if (options.headers) {
|
|
|
|
const headers = new Headers();
|
|
|
|
for(const [name, value] of options.headers.entries()) {
|
|
|
|
headers.append(name, value);
|
|
|
|
}
|
|
|
|
options.headers = headers;
|
2020-04-22 20:46:47 +02:00
|
|
|
}
|
2020-08-05 15:36:44 +00:00
|
|
|
const promise = fetch(url, options).then(async response => {
|
|
|
|
const {status} = response;
|
|
|
|
const body = await response.json();
|
|
|
|
return {status, body};
|
|
|
|
}, err => {
|
|
|
|
if (err.name === "AbortError") {
|
|
|
|
throw new AbortError();
|
|
|
|
} else if (err instanceof TypeError) {
|
|
|
|
// Network errors are reported as TypeErrors, see
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
|
|
|
|
// this can either mean user is offline, server is offline, or a CORS error (server misconfiguration).
|
|
|
|
//
|
|
|
|
// One could check navigator.onLine to rule out the first
|
|
|
|
// but the 2 latter ones are indistinguishable from javascript.
|
|
|
|
throw new ConnectionError(`${options.method} ${url}: ${err.message}`);
|
|
|
|
}
|
|
|
|
throw err;
|
|
|
|
});
|
|
|
|
const result = new RequestResult(promise, controller);
|
|
|
|
|
|
|
|
if (options.timeout) {
|
|
|
|
result.promise = abortOnTimeout(createTimeout, options.timeout, result, result.promise);
|
2019-12-23 14:28:27 +01:00
|
|
|
}
|
2020-08-05 15:36:44 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2019-12-23 14:28:27 +01:00
|
|
|
}
|