Skip to content

Commit 2d0056e

Browse files
committed
fix: evict deleted browser routes
Drop cached browser routing entries after successful DELETE /browsers/:id responses so stale base URLs are not reused, and cover both the success and failure paths in the routing tests. Made-with: Cursor
1 parent 0e0e88f commit 2d0056e

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

src/lib/browser-routing.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class BrowserRouteCache {
3030
const BROWSER_ROUTING_SUBRESOURCES_ENV = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
3131
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl'];
3232
const BROWSER_ROUTE_CACHEABLE_PATH = /^\/(?:v\d+\/)?browsers(?:\/[^/]+)?\/?$/;
33+
const BROWSER_DELETE_BY_ID_PATH = /^\/(?:v\d+\/)?browsers\/([^/]+)\/?$/;
3334

3435
export function browserRoutingSubresourcesFromEnv(): string[] {
3536
const raw = readBrowserRoutingSubresourcesEnv();
@@ -69,6 +70,7 @@ export function createRoutingFetch(
6970
if (shouldSniff) {
7071
await sniffAndPopulateCache(response, cache);
7172
}
73+
maybeEvictDeletedBrowserRoute(request, response, apiOrigin, cache);
7274
return response;
7375
};
7476
}
@@ -78,6 +80,34 @@ function shouldSniffAndPopulateCache(request: Request, apiOrigin: string): boole
7880
return url.origin === apiOrigin && BROWSER_ROUTE_CACHEABLE_PATH.test(url.pathname);
7981
}
8082

83+
function maybeEvictDeletedBrowserRoute(
84+
request: Request,
85+
response: Response,
86+
apiOrigin: string,
87+
cache: BrowserRouteCache,
88+
): void {
89+
if (!response.ok || request.method.toUpperCase() !== 'DELETE') {
90+
return;
91+
}
92+
93+
const url = new URL(request.url);
94+
if (url.origin !== apiOrigin) {
95+
return;
96+
}
97+
98+
const match = url.pathname.match(BROWSER_DELETE_BY_ID_PATH);
99+
if (!match) {
100+
return;
101+
}
102+
103+
const sessionId = decodeURIComponent(match[1] ?? '');
104+
if (!sessionId) {
105+
return;
106+
}
107+
108+
cache.delete(sessionId);
109+
}
110+
81111
function browserRouteFromValue(value: unknown): BrowserRoute | undefined {
82112
if (!value || typeof value !== 'object') {
83113
return undefined;

tests/lib/browser-routing.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,52 @@ describe('browser routing', () => {
233233
await expect(kernel.browsers.fetch('sess-1', 'https://example.com/again')).rejects.toThrow(/route cache/);
234234
});
235235

236+
test('evicts cached route after successful browser delete by id', async () => {
237+
const calls: string[] = [];
238+
const kernel = new Kernel({
239+
apiKey: 'k',
240+
baseURL: 'https://api.example/',
241+
fetch: async (input) => {
242+
const url = normalizeURL(input);
243+
calls.push(url);
244+
return new Response(null, { status: 204 });
245+
},
246+
});
247+
248+
kernel.browserRouteCache.set({
249+
sessionId: 'sess-1',
250+
baseURL: 'http://browser-session.test/browser/kernel',
251+
jwt: 'token-abc',
252+
});
253+
254+
await kernel.browsers.deleteByID('sess-1');
255+
256+
expect(calls).toEqual(['https://api.example/browsers/sess-1']);
257+
expect(kernel.browserRouteCache.get('sess-1')).toBeUndefined();
258+
});
259+
260+
test('keeps cached route when browser delete by id fails', async () => {
261+
const kernel = new Kernel({
262+
apiKey: 'k',
263+
baseURL: 'https://api.example/',
264+
maxRetries: 0,
265+
fetch: async () => new Response('boom', { status: 500, headers: { 'content-type': 'text/plain' } }),
266+
});
267+
268+
kernel.browserRouteCache.set({
269+
sessionId: 'sess-1',
270+
baseURL: 'http://browser-session.test/browser/kernel',
271+
jwt: 'token-abc',
272+
});
273+
274+
await expect(kernel.browsers.deleteByID('sess-1')).rejects.toThrow();
275+
expect(kernel.browserRouteCache.get('sess-1')).toMatchObject({
276+
sessionId: 'sess-1',
277+
baseURL: 'http://browser-session.test/browser/kernel',
278+
jwt: 'token-abc',
279+
});
280+
});
281+
236282
test('browser.fetch supports HEAD requests', async () => {
237283
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
238284
const kernel = new Kernel({

0 commit comments

Comments
 (0)