Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/lib/browser-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ const BROWSER_ROUTING_SUBRESOURCES_ENV = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
// Path prefixes eligible for direct-to-VM routing. "telemetry/stream" is the live
// SSE endpoint (served by the VM); "telemetry/events" is a historical read served
// by the control plane (S2) and must NOT be here.
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl', 'telemetry/stream', 'computer', 'playwright'];
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = [
'curl',
'telemetry/stream',
'computer',
'playwright',
'process',
];
const BROWSER_ROUTE_CACHEABLE_PATH = /^\/(?:v\d+\/)?browsers(?:\/[^/]+)?\/?$/;
const BROWSER_POOL_ACQUIRE_PATH = /^\/(?:v\d+\/)?browser_pools\/[^/]+\/acquire\/?$/;
const BROWSER_DELETE_BY_ID_PATH = /^\/(?:v\d+\/)?browsers\/([^/]+)\/?$/;
Expand Down
18 changes: 12 additions & 6 deletions tests/lib/browser-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,13 @@ describe('browser routing', () => {
'telemetry/stream',
'computer',
'playwright',
'process',
]);
});
});

test('allowlist matching is segment-boundary aware (telemetry/events stays on the control plane)', () => {
const prefixes = ['curl', 'telemetry/stream', 'computer', 'playwright'];
const prefixes = ['curl', 'telemetry/stream', 'computer', 'playwright', 'process'];
expect(matchesDirectVMPrefix('telemetry/stream', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('telemetry/stream/x', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('telemetry/events', prefixes)).toBe(false);
Expand All @@ -465,7 +466,8 @@ describe('browser routing', () => {
expect(matchesDirectVMPrefix('curl/raw', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('computer/screenshot', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('playwright/execute', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('process/exec', prefixes)).toBe(false);
expect(matchesDirectVMPrefix('process/exec', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('process/proc-1/stdout/stream', prefixes)).toBe(true);
expect(matchesDirectVMPrefix('fs/read', prefixes)).toBe(false);
});

Expand Down Expand Up @@ -507,7 +509,7 @@ describe('browser routing', () => {
});
});

test('routes computer screenshot and playwright execute to the VM by default', async () => {
test('routes default browser subresources to the VM', async () => {
await withBrowserRoutingEnv(undefined, async () => {
const calls: Array<{ url: string; headers: Headers }> = [];
const kernel = new Kernel({
Expand All @@ -530,13 +532,17 @@ describe('browser routing', () => {
headers: { 'content-type': 'image/png' },
});
}
if (url.includes('/process/exec')) {
return Response.json({ exit_code: 0, stdout_b64: '', stderr_b64: '' });
}
return Response.json({ success: true });
},
});

await kernel.browsers.create();
await kernel.browsers.computer.captureScreenshot('sess-1');
await kernel.browsers.playwright.execute('sess-1', { code: 'return 1' });
await kernel.browsers.process.exec('sess-1', { command: 'echo' });

expect(calls[1]?.url).toBe(
'http://browser-session.test/browser/kernel/computer/screenshot?jwt=token-abc',
Expand All @@ -546,10 +552,12 @@ describe('browser routing', () => {
'http://browser-session.test/browser/kernel/playwright/execute?jwt=token-abc',
);
expect(calls[2]?.headers.get('authorization')).toBeNull();
expect(calls[3]?.url).toBe('http://browser-session.test/browser/kernel/process/exec?jwt=token-abc');
expect(calls[3]?.headers.get('authorization')).toBeNull();
});
});

test('keeps process, fs, and telemetry/events on the API origin by default', async () => {
test('keeps fs and telemetry/events on the API origin by default', async () => {
await withBrowserRoutingEnv(undefined, async () => {
const calls: string[] = [];
const kernel = new Kernel({
Expand Down Expand Up @@ -579,12 +587,10 @@ describe('browser routing', () => {
});

await kernel.browsers.create();
await kernel.browsers.process.exec('sess-1', { command: 'echo' });
await kernel.browsers.fs.readFile('sess-1', { path: '/tmp/x' });
await kernel.browsers.telemetry.events('sess-1');

expect(calls.slice(1)).toEqual([
'https://api.example/browsers/sess-1/process/exec',
'https://api.example/browsers/sess-1/fs/read_file?path=%2Ftmp%2Fx',
'https://api.example/browsers/sess-1/telemetry/events',
]);
Expand Down
Loading