add record/replay network layer

This commit is contained in:
Bruno Windels 2019-12-23 14:28:42 +01:00
parent aa86748cdd
commit 1d9a5c490a
1 changed files with 108 additions and 0 deletions

108
src/matrix/net/replay.js Normal file
View File

@ -0,0 +1,108 @@
import {
RequestAbortError,
NetworkError
} from "../error.js";
class RequestLogItem {
constructor(url, options) {
this.url = url;
this.options = options;
this.error = null;
this.body = null;
this.status = status;
this.start = performance.now();
this.end = 0;
}
async handleResponse(response) {
this.end = performance.now();
this.status = response.status;
this.body = response.body;
}
handleError(err) {
this.end = performance.now();
this.error = {
aborted: err instanceof RequestAbortError,
network: err instanceof NetworkError,
message: err.message,
};
}
}
export class RecordRequester {
constructor(request) {
this._origRequest = request;
this._requestLog = [];
this.request = this.request.bind(this);
}
request(url, options) {
const requestItem = new RequestLogItem(url, options);
this._requestLog.push(requestItem);
try {
const requestResult = this._origRequest(url, options);
requestResult.response().then(response => {
requestItem.handleResponse(response);
});
return requestResult;
} catch (err) {
requestItem.handleError(err);
throw err;
}
}
log() {
return this._requestLog;
}
}
export class ReplayRequester {
constructor(log, options) {
this._log = log.slice();
this._options = options;
this.request = this.request.bind(this);
}
request(url, options) {
const idx = this._log.findIndex(item => {
return item.url === url && options.method === item.options.method;
});
if (idx === -1) {
return new ReplayRequestResult({status: 404}, options);
} else {
const [item] = this._log.splice(idx, 1);
return new ReplayRequestResult(item, options);
}
}
}
class ReplayRequestResult {
constructor(item, options) {
this._item = item;
this._options = options;
this._aborted = false;
}
abort() {
this._aborted = true;
}
async response() {
if (this._options.delay) {
const delay = this._item.end - this._item.start;
await new Promise(resolve => setTimeout(resolve, delay));
}
if (this._item.error || this._aborted) {
const error = this._item.error;
if (error.aborted || this._aborted) {
throw new RequestAbortError(error.message);
} else if (error.network) {
throw new NetworkError(error.message);
} else {
throw new Error(error.message);
}
}
return this._item;
}
}