create trace viewer server backend

This commit is contained in:
Simon Knott 2024-11-07 16:39:37 +01:00
parent 5a8b49910a
commit cdcf06e3a8
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
2 changed files with 34 additions and 15 deletions

View file

@ -18,7 +18,7 @@ import { splitProgress } from './progress';
import { unwrapPopoutUrl } from './snapshotRenderer'; import { unwrapPopoutUrl } from './snapshotRenderer';
import { SnapshotServer } from './snapshotServer'; import { SnapshotServer } from './snapshotServer';
import { TraceModel } from './traceModel'; import { TraceModel } from './traceModel';
import { FetchTraceModelBackend, ZipTraceModelBackend } from './traceModelBackends'; import { FetchTraceModelBackend, TraceViewerServerBackend, ZipTraceModelBackend } from './traceModelBackends';
import { TraceVersionError } from './traceModernizer'; import { TraceVersionError } from './traceModernizer';
// @ts-ignore // @ts-ignore
@ -38,7 +38,7 @@ const loadedTraces = new Map<string, { traceModel: TraceModel, snapshotServer: S
const clientIdToTraceUrls = new Map<string, { limit: number | undefined, traceUrls: Set<string> }>(); const clientIdToTraceUrls = new Map<string, { limit: number | undefined, traceUrls: Set<string> }>();
async function loadTrace(traceUrl: string, traceFileName: string | null, clientId: string, limit: number | undefined, progress: (done: number, total: number) => undefined): Promise<TraceModel> { async function loadTrace(traceUrl: string, traceFileName: string | null, clientId: string, traceViewerServer: TraceViewerServerBackend, limit: number | undefined, progress: (done: number, total: number) => undefined): Promise<TraceModel> {
await gc(); await gc();
let data = clientIdToTraceUrls.get(clientId); let data = clientIdToTraceUrls.get(clientId);
if (!data) { if (!data) {
@ -51,7 +51,7 @@ async function loadTrace(traceUrl: string, traceFileName: string | null, clientI
try { try {
// Allow 10% to hop from sw to page. // Allow 10% to hop from sw to page.
const [fetchProgress, unzipProgress] = splitProgress(progress, [0.5, 0.4, 0.1]); const [fetchProgress, unzipProgress] = splitProgress(progress, [0.5, 0.4, 0.1]);
const backend = traceUrl.endsWith('json') ? new FetchTraceModelBackend(traceUrl) : new ZipTraceModelBackend(traceUrl, fetchProgress); const backend = traceUrl.endsWith('json') ? new FetchTraceModelBackend(traceUrl, traceViewerServer) : new ZipTraceModelBackend(traceUrl, traceViewerServer, fetchProgress);
await traceModel.load(backend, unzipProgress); await traceModel.load(backend, unzipProgress);
} catch (error: any) { } catch (error: any) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -98,7 +98,7 @@ async function doFetch(event: FetchEvent): Promise<Response> {
if (relativePath === '/contexts') { if (relativePath === '/contexts') {
try { try {
const limit = url.searchParams.has('limit') ? +url.searchParams.get('limit')! : undefined; const limit = url.searchParams.has('limit') ? +url.searchParams.get('limit')! : undefined;
const traceModel = await loadTrace(traceUrl!, url.searchParams.get('traceFileName'), event.clientId, limit, (done: number, total: number) => { const traceModel = await loadTrace(traceUrl!, url.searchParams.get('traceFileName'), event.clientId, traceViewerServer, limit, (done: number, total: number) => {
client.postMessage({ method: 'progress', params: { done, total } }); client.postMessage({ method: 'progress', params: { done, total } });
}); });
return new Response(JSON.stringify(traceModel!.contextEntries), { return new Response(JSON.stringify(traceModel!.contextEntries), {

View file

@ -28,11 +28,11 @@ export class ZipTraceModelBackend implements TraceModelBackend {
private _entriesPromise: Promise<Map<string, zip.Entry>>; private _entriesPromise: Promise<Map<string, zip.Entry>>;
private _traceURL: string; private _traceURL: string;
constructor(traceURL: string, progress: Progress) { constructor(traceURL: string, server: TraceViewerServerBackend, progress: Progress) {
this._traceURL = traceURL; this._traceURL = traceURL;
zipjs.configure({ baseURL: self.location.href } as any); zipjs.configure({ baseURL: self.location.href } as any);
this._zipReader = new zipjs.ZipReader( this._zipReader = new zipjs.ZipReader(
new zipjs.HttpReader(formatUrl(traceURL), { mode: 'cors', preventHeadRequest: true } as any), new zipjs.HttpReader(formatUrl(traceURL, server), { mode: 'cors', preventHeadRequest: true } as any),
{ useWebWorkers: false }); { useWebWorkers: false });
this._entriesPromise = this._zipReader.getEntries({ onprogress: progress }).then(entries => { this._entriesPromise = this._zipReader.getEntries({ onprogress: progress }).then(entries => {
const map = new Map<string, zip.Entry>(); const map = new Map<string, zip.Entry>();
@ -83,12 +83,16 @@ export class ZipTraceModelBackend implements TraceModelBackend {
export class FetchTraceModelBackend implements TraceModelBackend { export class FetchTraceModelBackend implements TraceModelBackend {
private _entriesPromise: Promise<Map<string, string>>; private _entriesPromise: Promise<Map<string, string>>;
private _traceURL: string; private _path: string;
private _server: TraceViewerServerBackend;
constructor(traceURL: string) { constructor(path: string, server: TraceViewerServerBackend) {
this._traceURL = traceURL; this._path = path;
this._entriesPromise = fetch('/trace/file?path=' + encodeURIComponent(traceURL)).then(async response => { this._server = server;
const json = JSON.parse(await response.text()); this._entriesPromise = server.readFile(path).then(async response => {
if (!response)
throw new Error('File not found');
const json = await response.json();
const entries = new Map<string, string>(); const entries = new Map<string, string>();
for (const entry of json.entries) for (const entry of json.entries)
entries.set(entry.name, entry.path); entries.set(entry.name, entry.path);
@ -101,7 +105,7 @@ export class FetchTraceModelBackend implements TraceModelBackend {
} }
traceURL(): string { traceURL(): string {
return this._traceURL; return this._path;
} }
async entryNames(): Promise<string[]> { async entryNames(): Promise<string[]> {
@ -129,14 +133,29 @@ export class FetchTraceModelBackend implements TraceModelBackend {
const fileName = entries.get(entryName); const fileName = entries.get(entryName);
if (!fileName) if (!fileName)
return; return;
return fetch('/trace/file?path=' + encodeURIComponent(fileName)); return this._server.readFile(fileName);
} }
} }
function formatUrl(trace: string) { function formatUrl(trace: string, server: TraceViewerServerBackend) {
let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `file?path=${encodeURIComponent(trace)}`; let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : server.getFileURL(trace).toString();
// Dropbox does not support cors. // Dropbox does not support cors.
if (url.startsWith('https://www.dropbox.com/')) if (url.startsWith('https://www.dropbox.com/'))
url = 'https://dl.dropboxusercontent.com/' + url.substring('https://www.dropbox.com/'.length); url = 'https://dl.dropboxusercontent.com/' + url.substring('https://www.dropbox.com/'.length);
return url; return url;
} }
export class TraceViewerServerBackend {
constructor(private readonly baseUrl: string) {}
getFileURL(path: string): URL {
const url = new URL('trace/file', this.baseUrl);
url.searchParams.set('path', path);
return url;
}
async readFile(path: string): Promise<Response | undefined> {
const response = await fetch(this.getFileURL(path));
if (response.status === 404)
return;
return response;
}
}