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 () => { continueBtn.addEventListener('click', async () => {
buttonRow.style.display = 'none'; buttonRow.style.display = 'none';
this._tabListContainer.style.display = 'none';
try { try {
const selectedTab = this._selectedTab; const selectedTab = this._selectedTab;
if (!selectedTab) { if (!selectedTab) {

View File

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

View File

@ -123,8 +123,13 @@ export class CDPRelayServer {
} }
stop(): void { stop(): void {
this._closePlaywrightConnection('Server stopped'); this.closeConnections('Server stopped');
this._closeExtensionConnection('Server stopped'); this._wss.close();
}
closeConnections(reason: string) {
this._closePlaywrightConnection(reason);
this._closeExtensionConnection(reason);
} }
private _onConnection(ws: WebSocket, request: http.IncomingMessage): void { private _onConnection(ws: WebSocket, request: http.IncomingMessage): void {
@ -141,6 +146,11 @@ export class CDPRelayServer {
} }
private _handlePlaywrightConnection(ws: WebSocket): void { 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; this._playwrightConnection = ws;
ws.on('message', async data => { ws.on('message', async data => {
try { 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> { private async _obtainBrowser(clientInfo: { name: string, version: string }): Promise<playwright.Browser> {
await this._relay.ensureExtensionConnectionForMCPContext(clientInfo); await this._relay.ensureExtensionConnectionForMCPContext(clientInfo);
const browser = await playwright.chromium.connectOverCDP(this._relay.cdpEndpoint()); 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) { export async function runWithExtension(config: FullConfig, abortController: AbortController) {
const contextFactory = await startCDPRelayServer(config.browser.launchOptions.channel || 'chrome', 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); await mcpTransport.start(serverBackendFactory, config.server);
} }