From 5ddc02ebc854c9dca3a92f0c5940e69c75faf1e5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 5 Aug 2020 15:37:37 +0000 Subject: [PATCH] XHR request support for legacy browsers --- src/matrix/net/request/xhr.js | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/matrix/net/request/xhr.js diff --git a/src/matrix/net/request/xhr.js b/src/matrix/net/request/xhr.js new file mode 100644 index 00000000..ee35311b --- /dev/null +++ b/src/matrix/net/request/xhr.js @@ -0,0 +1,81 @@ +import { + AbortError, + ConnectionError +} from "../../error.js"; + +class RequestResult { + constructor(promise, xhr) { + this._promise = promise; + this._xhr = xhr; + } + + abort() { + this._xhr.abort(); + } + + response() { + return this._promise; + } +} + +function send(url, options) { + const xhr = new XMLHttpRequest(); + xhr.open(options.method, url); + if (options.headers) { + for(const [name, value] of options.headers.entries()) { + xhr.setRequestHeader(name, value); + } + } + if (options.timeout) { + xhr.timeout = options.timeout; + } + + xhr.send(options.body || null); + + return xhr; +} + +function xhrAsPromise(xhr, method, url) { + return new Promise((resolve, reject) => { + xhr.addEventListener("load", () => resolve(xhr)); + xhr.addEventListener("abort", () => reject(new AbortError())); + xhr.addEventListener("error", () => reject(new ConnectionError(`Error ${method} ${url}`))); + xhr.addEventListener("timeout", () => reject(new ConnectionError(`Timeout ${method} ${url}`, true))); + }); +} + +function addCacheBuster(urlStr, random = Math.random) { + // XHR doesn't have a good way to disable cache, + // so add a random query param + // see https://davidtranscend.com/blog/prevent-ie11-cache-ajax-requests/ + if (urlStr.includes("?")) { + urlStr = urlStr + "&"; + } else { + urlStr = urlStr + "?"; + } + return urlStr + `_cacheBuster=${Math.ceil(random() * Number.MAX_SAFE_INTEGER)}`; +} + +export function xhrRequest(url, options) { + url = addCacheBuster(url); + const xhr = send(url, options); + const promise = xhrAsPromise(xhr, options.method, url).then(xhr => { + const {status} = xhr; + let body = xhr.responseText; + if (xhr.getResponseHeader("Content-Type") === "application/json") { + body = JSON.parse(body); + } + return {status, body}; + }); + return new RequestResult(promise, xhr); +} + +export function tests() { + return { + "add cache buster": assert => { + const random = () => 0.5; + assert.equals(addCacheBuster("http://foo", random), "http://foo?_cacheBuster=5"); + assert.equals(addCacheBuster("http://foo?bar=baz", random), "http://foo?bar=baz&_cacheBuster=5"); + } + } +} \ No newline at end of file