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