src/vs/base/common/sseParser.ts
245 LOC · 241 covered · 4 uncovered · 56 ranges · 54 concepts · 17 introducers · 46 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
/*---------------------------------------------------------------------------------------------
sseParser.ts ×9
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Parser for Server-Sent Events (SSE) streams according to the HTML specification.
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
*/
/**
* Represents an event dispatched from an SSE stream.
*/
export interface ISSEEvent {
/**
* The event type. If not specified, the type is "message".
*/
type: string;
/**
* The event data.
*/
data: string;
/**
* The last event ID, used for reconnection.
*/
id?: string;
/**
* Reconnection time in milliseconds.
*/
retry?: number;
}
/**
* Callback function type for event dispatch.
*/
export type SSEEventHandler = (event: ISSEEvent) => void;
const enum Chr {
CR = 13, // '\r'
LF = 10, // '\n'
COLON = 58, // ':'
SPACE = 32, // ' '
}
/**
* Parser for Server-Sent Events (SSE) streams.
*/
export class SSEParser {
private dataBuffer = '';
private eventTypeBuffer = '';
private currentEventId?: string;
private lastEventIdBuffer?: string;
private reconnectionTime?: number;
private buffer: Uint8Array[] = [];
private endedOnCR = false;
private readonly onEventHandler: SSEEventHandler;
private readonly decoder: TextDecoder;
/**
* Creates a new SSE parser.
* @param onEvent The callback to invoke when an event is dispatched.
*/
constructor(onEvent: SSEEventHandler) {
this.decoder = new TextDecoder('utf-8');
}
/**
* Gets the last event ID received by this parser.
*/
public getLastEventId(): string | undefined {
}
* Gets the reconnection time in milliseconds, if one was specified by the server.
*/
public getReconnectionTime(): number | undefined {
}
/**
* Feeds a chunk of the SSE stream to the parser.
* @param chunk The chunk to parse as a Uint8Array of UTF-8 encoded data.
*/
public feed(chunk: Uint8Array): void {
return;
}
let offset = 0;
// If the data stream was bifurcated between a CR and LF, avoid processing the CR as an extra newline
if (this.endedOnCR && chunk[0] === Chr.LF) {
}
// Process complete lines from the buffer
while (offset < chunk.length) {
const indexCR = chunk.indexOf(Chr.CR, offset);
const indexLF = chunk.indexOf(Chr.LF, offset);
const index = indexCR === -1 ? indexLF : (indexLF === -1 ? indexCR : Math.min(indexCR, indexLF));
if (index === -1) {
}
let str = '';
for (const buf of this.buffer) {
}
this.processLine(str);
this.buffer.length = 0;
offset = index + (chunk[index] === Chr.CR && chunk[index + 1] === Chr.LF ? 2 : 1);
}
if (offset < chunk.length) {
this.endedOnCR = chunk[chunk.length - 1] === Chr.CR;
}
}
* Processes a single line from the SSE stream.
*/
private processLine(line: string): void {
this.dispatchEvent();
return;
}
if (line.startsWith(':')) {
}
// Parse the field name and value
let field: string;
let value: string;
const colonIndex = line.indexOf(':');
if (colonIndex === -1) {
field = line;
value = '';
field = line.substring(0, colonIndex);
value = line.substring(colonIndex + 1);
// If value starts with a space, remove it
if (value.startsWith(' ')) {
}
this.processField(field, value);
}
* Processes a field with the given name and value.
*/
private processField(field: string, value: string): void {
case 'event':
break;
case 'data':
this.dataBuffer += value;
this.dataBuffer += '\n';
break;
case 'id':
if (!value.includes('\0')) {
}
case 'retry':
// If the field value consists only of ASCII digits, set the reconnection time
sseParser.ts ×3
if (/^\d+$/.test(value)) {
}
// Ignore any other fields
}
}
* Dispatches the event based on the current buffer states.
*/
private dispatchEvent(): void {
if (this.dataBuffer === '') {
this.eventTypeBuffer = '';
return;
}
// If the data buffer's last character is a newline, remove it
if (this.dataBuffer.endsWith('\n')) {
this.dataBuffer = this.dataBuffer.substring(0, this.dataBuffer.length - 1);
}
// Create and dispatch the event
const event: ISSEEvent = {
type: this.eventTypeBuffer || 'message',
};
// Add optional fields if they exist
if (this.currentEventId !== undefined) {
}
if (this.reconnectionTime !== undefined) {
}
// Dispatch the event
this.onEventHandler(event);
// Reset the data and event type buffers
this.reset();
/**
* Resets the parser state.
*/
public reset(): void {
this.eventTypeBuffer = '';
this.currentEventId = undefined;
// Note: lastEventIdBuffer is not reset as it's used for reconnection
}