-
Notifications
You must be signed in to change notification settings - Fork 5.9k
feat(har): adjust capturing WebSocket data based on content config
#41124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,7 @@ export class HarTracer { | |
| private _pageEntrySymbol: symbol; | ||
| private _baseURL: string | undefined; | ||
| private _page: Page | null; | ||
| private _saveOpenWebSocketMessagesFunctions = new Set<() => void>(); | ||
|
|
||
| constructor(context: BrowserContext | APIRequestContext, page: Page | null, delegate: HarTracerDelegate, options: HarTracerOptions) { | ||
| this._context = context; | ||
|
|
@@ -438,7 +439,28 @@ export class HarTracer { | |
| const pageEntry = this._createPageEntryIfNeeded(page); | ||
| const harEntry = createHarEntry(pageEntry?.id, method, url, page.mainFrame().guid, this._options, webSocket.wallTimeMs()); | ||
| harEntry._resourceType = 'websocket'; | ||
| harEntry._webSocketMessages = []; | ||
|
|
||
| const messages: har.WebSocketMessage[] = []; | ||
| if (this._options.content === 'embed') | ||
| harEntry._webSocketMessages = messages; | ||
|
|
||
| let saveMessages: (() => void) | undefined; | ||
| if (this._options.content === 'attach') { | ||
| saveMessages = () => { | ||
| if (!messages.length) | ||
| return; | ||
|
|
||
| const buffer = Buffer.from(JSON.stringify(messages)); | ||
| const sha1 = calculateSha1(buffer) + '.json'; | ||
| if (this._options.includeTraceInfo) | ||
| harEntry.response.content._sha1 = sha1; | ||
| else | ||
| harEntry.response.content._file = sha1; | ||
| if (this._started) | ||
| this._delegate.onContentBlob(sha1, buffer); | ||
| }; | ||
| this._saveOpenWebSocketMessagesFunctions.add(saveMessages); | ||
| } | ||
|
|
||
| let oldestWallTimeMs = Infinity; | ||
| let newestWallTimeMs = -Infinity; | ||
|
|
@@ -475,12 +497,17 @@ export class HarTracer { | |
| } | ||
| }), | ||
| eventsHelper.addEventListener(webSocket, network.WebSocket.Events.FrameSent, ({ opcode, data, wallTimeMs }: { opcode: number, data: string, wallTimeMs: number }) => { | ||
| harEntry._webSocketMessages!.push({ type: 'send', time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data }); | ||
| if (this._options.content !== 'omit') | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be a separate option? I can imagine not everyone would like websocket traffic (that is sometimes very heavy) to be recorder. Perhaps overall, an option for (not) recording websockets?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we discussed offline and felt that it's probably not supper common for a developer to want the network metadata (e.g. headers, timing, etc.) without also wanting the content for a if needed we could add an environment variable, but that might be overeager |
||
| messages.push({ type: 'send', time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data }); | ||
|
|
||
| updateTime(wallTimeMs); | ||
| }), | ||
| eventsHelper.addEventListener(webSocket, network.WebSocket.Events.FrameReceived, ({ opcode, data, wallTimeMs }: { opcode: number, data: string, wallTimeMs: number }) => { | ||
| harEntry._webSocketMessages!.push({ type: 'receive', time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data }); | ||
| if (this._options.content !== 'omit') | ||
| messages.push({ type: 'receive', time: this._options.omitTiming ? -1 : wallTimeMs, opcode, data }); | ||
|
|
||
| updateTime(wallTimeMs); | ||
|
|
||
| if (!this._options.omitSizes) { | ||
| const length = (opcode === 1) ? Buffer.byteLength(data, 'utf8') : base64ByteLength(data); | ||
|
|
||
|
|
@@ -503,6 +530,11 @@ export class HarTracer { | |
| eventsHelper.addEventListener(webSocket, network.WebSocket.Events.Close, () => { | ||
| eventsHelper.removeEventListeners(eventListeners); | ||
|
|
||
| if (saveMessages) { | ||
| this._saveOpenWebSocketMessagesFunctions.delete(saveMessages); | ||
| saveMessages(); | ||
| } | ||
|
|
||
| if (this._started) | ||
| this._delegate.onEntryFinished(harEntry); | ||
| }), | ||
|
|
@@ -633,6 +665,12 @@ export class HarTracer { | |
| } | ||
|
|
||
| stop() { | ||
| // Unlike other requests that have a single response, a WebSocket can receive multiple frames. | ||
| // As such, we don't finish the entry until the WebSocket is closed, which delays when the captured frames are saved. | ||
| // Make sure to save at least what has been captured so far. | ||
| for (const saveOpenWebSocketMessages of this._saveOpenWebSocketMessagesFunctions) | ||
| saveOpenWebSocketMessages(); | ||
|
|
||
| this._started = false; | ||
| eventsHelper.removeEventListeners(this._eventListeners); | ||
| this._barrierPromises.clear(); | ||
|
|
@@ -665,6 +703,7 @@ export class HarTracer { | |
| } | ||
| } | ||
| this._pageEntries = []; | ||
| this._saveOpenWebSocketMessagesFunctions.clear(); | ||
| return log; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to support websockets in the trace, we'll have to do this separately in HarRecorder vs Tracing. Let's move this out to HarRecorder through HarTracerDelegate right away?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it's OK with you, id rather handle this when i actually work on supporting
WebSocketin tracing as right now i dont really know what the best approach would be (i.e. i'd hate to implement something for it now and then have to completely change it once i start working on tracing)i plan to do that next so this shouldn't be "stale" for too long