Path: blob/main/src/vs/platform/agentHost/electron-browser/tunnelRelayTransport.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter } from '../../../base/common/event.js';6import { Disposable } from '../../../base/common/lifecycle.js';7import type { AhpServerNotification, JsonRpcResponse, ProtocolMessage } from '../common/state/sessionProtocol.js';8import type { IProtocolTransport } from '../common/state/sessionTransport.js';9import type { ITunnelAgentHostMainService, ITunnelRelayMessage } from '../common/tunnelAgentHost.js';10import { MALFORMED_FRAMES_FORCE_CLOSE_THRESHOLD, MALFORMED_FRAMES_LOG_CAP } from '../common/transportConstants.js';1112/**13* A protocol transport that relays messages through the shared process14* tunnel relay via IPC, instead of using a direct WebSocket connection.15*16* The shared process manages the actual dev tunnel relay connection17* and forwards messages bidirectionally through this IPC channel.18*/19export class TunnelRelayTransport extends Disposable implements IProtocolTransport {2021private readonly _onMessage = this._register(new Emitter<ProtocolMessage>());22readonly onMessage = this._onMessage.event;2324private readonly _onClose = this._register(new Emitter<void>());25readonly onClose = this._onClose.event;2627private _malformedFrames = 0;2829constructor(30private readonly _connectionId: string,31private readonly _tunnelService: ITunnelAgentHostMainService,32) {33super();3435// Listen for relay messages from the shared process36this._register(this._tunnelService.onDidRelayMessage((msg: ITunnelRelayMessage) => {37if (msg.connectionId !== this._connectionId) {38return;39}40let parsed: ProtocolMessage;41try {42parsed = JSON.parse(msg.data) as ProtocolMessage;43} catch (err) {44this._malformedFrames++;45if (this._malformedFrames <= MALFORMED_FRAMES_LOG_CAP) {46const preview = msg.data.length > 80 ? msg.data.slice(0, 80) + '…' : msg.data;47console.warn(48`[TunnelRelayTransport] Malformed frame #${this._malformedFrames} (len=${msg.data.length}): ${preview}`,49err instanceof Error ? err.message : String(err)50);51}52if (this._malformedFrames > MALFORMED_FRAMES_FORCE_CLOSE_THRESHOLD) {53console.warn('[TunnelRelayTransport] Malformed frame threshold exceeded; closing relay.');54this._tunnelService.disconnect(this._connectionId).catch(() => { /* best effort */ });55}56return;57}58this._onMessage.fire(parsed);59}));6061// Listen for relay close62this._register(this._tunnelService.onDidRelayClose((closedId: string) => {63if (closedId === this._connectionId) {64this._onClose.fire();65}66}));67}6869override dispose(): void {70// Tear down the shared-process relay connection71this._tunnelService.disconnect(this._connectionId).catch(() => { /* best effort */ });72super.dispose();73}7475send(message: ProtocolMessage | AhpServerNotification | JsonRpcResponse): void {76this._tunnelService.relaySend(this._connectionId, JSON.stringify(message)).catch(() => {77// Send failed — connection probably closed78});79}80}818283