chore(extension): reject second http connection (#766)

This commit is contained in:
Yury Semikhatsky 2025-07-25 14:46:48 -07:00 committed by GitHub
parent 6710a78641
commit dbf113d5e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 3 deletions

View File

@ -69,6 +69,7 @@ class ConnectPage {
continueBtn.addEventListener('click', async () => {
buttonRow.style.display = 'none';
this._tabListContainer.style.display = 'none';
try {
const selectedTab = this._selectedTab;
if (!selectedTab) {

View File

@ -30,6 +30,8 @@ import type { Tool } from './tools/tool.js';
export class BrowserServerBackend implements ServerBackend {
name = 'Playwright';
version = packageJSON.version;
onclose?: () => void;
private _tools: Tool[];
private _context: Context;
private _sessionLog: SessionLog | undefined;
@ -61,6 +63,7 @@ export class BrowserServerBackend implements ServerBackend {
}
serverClosed() {
this.onclose?.();
void this._context.dispose().catch(logUnhandledError);
}
}

View File

@ -123,8 +123,13 @@ export class CDPRelayServer {
}
stop(): void {
this._closePlaywrightConnection('Server stopped');
this._closeExtensionConnection('Server stopped');
this.closeConnections('Server stopped');
this._wss.close();
}
closeConnections(reason: string) {
this._closePlaywrightConnection(reason);
this._closeExtensionConnection(reason);
}
private _onConnection(ws: WebSocket, request: http.IncomingMessage): void {
@ -141,6 +146,11 @@ export class CDPRelayServer {
}
private _handlePlaywrightConnection(ws: WebSocket): void {
if (this._playwrightConnection) {
debugLogger('Rejecting second Playwright connection');
ws.close(1000, 'Another CDP client already connected');
return;
}
this._playwrightConnection = ws;
ws.on('message', async data => {
try {
@ -311,6 +321,11 @@ class ExtensionContextFactory implements BrowserContextFactory {
};
}
clientDisconnected() {
this._relay.closeConnections('MCP client disconnected');
this._browserPromise = undefined;
}
private async _obtainBrowser(clientInfo: { name: string, version: string }): Promise<playwright.Browser> {
await this._relay.ensureExtensionConnectionForMCPContext(clientInfo);
const browser = await playwright.chromium.connectOverCDP(this._relay.cdpEndpoint());

View File

@ -22,6 +22,18 @@ import type { FullConfig } from '../config.js';
export async function runWithExtension(config: FullConfig, abortController: AbortController) {
const contextFactory = await startCDPRelayServer(config.browser.launchOptions.channel || 'chrome', abortController);
const serverBackendFactory = () => new BrowserServerBackend(config, contextFactory);
let backend: BrowserServerBackend | undefined;
const serverBackendFactory = () => {
if (backend)
throw new Error('Another MCP client is still connected. Only one connection at a time is allowed.');
backend = new BrowserServerBackend(config, contextFactory);
backend.onclose = () => {
contextFactory.clientDisconnected();
backend = undefined;
};
return backend;
};
await mcpTransport.start(serverBackendFactory, config.server);
}