/******************************************************************************** * Copyright (c) 2026 CrossBreeze, EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the MIT License which is available in the project root. * * SPDX-License-Identifier: MIT ********************************************************************************/ import { type Channel, CommandService, type ConnectionHandler, type Disposable, ILogger, type MessageProvider, MessageService } from '@theia/core'; import * as net from 'node:net'; import { Clock } from '../common/clock'; /** Resolved configuration for a {@link AbstractSocketForwardingConnectionHandler}. * A head-specific subclass derives these from its own (adopter-facing) options * and passes them to `super(...)`. */ export interface SocketForwardingConnectionHandlerOptions { /** Theia service path the browser frontend opens a channel to. */ readonly path: string; /** Command id whose return value is the target server's listening port (the * server publishes it on the LSP connection at startup). */ readonly portCommand: string; /** Short bracket prefix for this head's diagnostic logs, rendered as * `[] …`. */ readonly logComponent: string; /** Human-readable name of the server this head connects to — used in log and * error messages. */ readonly serverName: string; readonly findPortTimeout?: number; readonly findPortAttempts?: number; readonly connectTimeoutMs?: number; /** * Optional diagnostic hook called immediately after the outbound * `net.Socket` to the server is created, BEFORE `socket.connect()` is * invoked. Adopters attach `'data'` / `'close'` listeners here for * byte-level observability when debugging wire-level issues; attaching * before `connect()` guarantees the very first bytes are observed. */ readonly onSocketCreated?: (socket: net.Socket) => void; } /** * Cross-head base for the Theia backend half of a protocol head's transport: * it bridges a Theia browser-frontend {@link Channel} to a server's TCP socket * by relaying bytes between the two. * * Theia delivers each frontend connection to a registered `ConnectionHandler` * keyed by its `path`. The handler asks the language server (via a registered * command) for the target server's listening port, opens a `net.Socket` to that * port, and forwards the Theia channel onto the socket. The frontend terminates * the same wire protocol the server speaks, so the backend performs no semantic * re-proxy — it relays bytes only and stays oblivious to the server's method set. * * The byte forwarder that bridges the channel and the socket is * {@link forwardToSocketConnection}; both heads use {@link SocketChannelForwarder}, * and the port discovery, buffer-and-replay race fix and connect orchestration * here are shared. */ export declare abstract class AbstractSocketForwardingConnectionHandler implements ConnectionHandler { protected messageService: MessageService; protected commandService: CommandService; protected readonly logger: ILogger; /** The clock each {@link SocketChannelForwarder} bounds its flush on: the container's {@link Clock}, or a `SystemClock`. */ protected readonly clock: Clock; readonly path: string; protected readonly portCommand: string; protected readonly logComponent: string; protected readonly serverName: string; protected readonly findPortTimeout: number; protected readonly findPortAttempts: number; protected readonly connectTimeoutMs: number; protected readonly onSocketCreated?: (socket: net.Socket) => void; constructor(options: SocketForwardingConnectionHandlerOptions); onConnection(connection: Channel): void; protected initializeServerConnection(channel: Channel): Promise; /** Tell the user this handler gave up; `message` is the reason it logged. */ protected reportConnectFailure(message: string): void; /** Poll the port command until it answers, its attempts run out, or `signal` aborts. */ protected findPort(signal?: AbortSignal): Promise; protected connectToServer(channel: Channel, port: number, preForwardBuffer?: { bufferSub: Disposable; buffered: MessageProvider[]; }): Promise; /** * Bridge the Theia frontend `clientChannel` and the server `socket` so bytes * relay both ways. Called once, after the socket is created and the * pre-forward buffer is about to be replayed. */ protected forwardToSocketConnection(clientChannel: Channel, socket: net.Socket): Disposable; /** * Re-fire pre-forward buffered `MessageProvider`s on the channel's * `onMessageEmitter` so the now-subscribed forwarder picks them up in arrival * order. `MessageProvider` is a thunk (`() => ReadBuffer`) — buffering does * not consume the read position, so replay produces the same bytes the * forwarder would have seen if wired earlier. * * The emitter is public on `AbstractChannel`; the `Channel` interface * carries just its `Event`, so any other channel has its messages logged as * dropped. A Theia rename of the emitter fails to compile here. */ protected replayBufferedMessages(channel: Channel, buffered: MessageProvider[]): void; } //# sourceMappingURL=abstract-socket-forwarding-connection-handler.d.ts.map