blob: 42a33312cdb4fdae63ec833fcc3db2b76d7c0ac3 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as TextUtils from '../../models/text_utils/text_utils.js';
import {Events, type EventSourceMessage, type NetworkRequest} from './NetworkRequest.js';
import {ServerSentEventsParser} from './ServerSentEventsProtocol.js';
/**
* Server sent events only arrive via CDP (Explicit Network.eventSourceMessageReceived) when
* the page uses "EventSource" in the code.
*
* If the page manually uses 'fetch' or XHR we have to do the protocol parsing
* ourselves.
*
* `ServerSentEvents` is a small helper class that manages this distinction for a specific
* request, stores the event data and sends out "EventSourceMessageAdded" events for a request.
*/
export class ServerSentEvents {
readonly #request: NetworkRequest;
readonly #parser?: ServerSentEventsParser;
// In the case where we parse the events ourselves we use the time of the last 'dataReceived'
// event for all the events that come out of the corresponding chunk of data.
#lastDataReceivedTime = 0;
readonly #eventSourceMessages: EventSourceMessage[] = [];
constructor(request: NetworkRequest, parseFromStreamedData: boolean) {
this.#request = request;
// Only setup parsing if we don't get the events over CDP directly.
if (parseFromStreamedData) {
this.#lastDataReceivedTime = request.pseudoWallTime(request.startTime);
this.#parser = new ServerSentEventsParser(this.#onParserEvent.bind(this), request.charset() ?? undefined);
// Get the streaming content and add the already received bytes if someone else started
// the streaming earlier.
void this.#request.requestStreamingContent().then(streamingContentData => {
if (!TextUtils.StreamingContentData.isError(streamingContentData)) {
void this.#parser?.addBase64Chunk(streamingContentData.content().base64);
streamingContentData.addEventListener(
TextUtils.StreamingContentData.Events.CHUNK_ADDED, ({data: {chunk}}) => {
this.#lastDataReceivedTime = request.pseudoWallTime(request.endTime);
void this.#parser?.addBase64Chunk(chunk);
});
}
});
}
}
get eventSourceMessages(): readonly EventSourceMessage[] {
return this.#eventSourceMessages;
}
/** Forwarded Network.eventSourceMessage received */
onProtocolEventSourceMessageReceived(eventName: string, data: string, eventId: string, time: number): void {
this.#recordMessageAndDispatchEvent({
eventName,
eventId,
data,
time,
});
}
#onParserEvent(eventName: string, data: string, eventId: string): void {
this.#recordMessageAndDispatchEvent({
eventName,
eventId,
data,
time: this.#lastDataReceivedTime,
});
}
#recordMessageAndDispatchEvent(message: EventSourceMessage): void {
this.#eventSourceMessages.push(message);
this.#request.dispatchEventToListeners(Events.EVENT_SOURCE_MESSAGE_ADDED, message);
}
}