add record/replay network layer
This commit is contained in:
parent
aa86748cdd
commit
1d9a5c490a
1 changed files with 108 additions and 0 deletions
108
src/matrix/net/replay.js
Normal file
108
src/matrix/net/replay.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue