Files
orca/src/main/network/electron-proxy-request-guard.test.ts
T
Brennan BensonandJinwoo-H d9870c6c75 fix(browser): apply the app-wide HTTP proxy to embedded browser sessions (#15536)
* fix(browser): apply the app-wide HTTP proxy to embedded browser sessions

The proxy setting was only ever written to `session.defaultSession`, but browser
guests run on their own `persist:orca-*` partitions. Any host reachable only via
the configured proxy failed to load in an embedded tab, landing on
`chrome-error://chromewebdata/`, while the same setting worked everywhere else.

Adds a per-session applier alongside the existing defaultSession path, keyed by a
WeakMap so one session's applied config can't suppress another's, and applies it
to every browser partition through the single installer they all pass through.
Startup awaits an explicit sweep so the first guest navigation can't race the
installer's fire-and-forget write, and a settings change re-sweeps so toggling
the proxy takes effect without a restart.

Env-var fallback and the system-proxy probe mirror the defaultSession behaviour,
so a browser partition resolves the proxy the same way the rest of the app does.

Fixes STA-4779

* fix(browser): await per-session proxy readiness

* fix(proxy): preserve loopback and authenticate

* fix(proxy): settle browser partition update races

* fix(proxy): close partition policy races

* fix(proxy): order settings and release removed sessions

* test(browser): await partition proxy readiness

* fix(proxy): cancel removed partition retries

* refactor(proxy): keep OpenCode rate limits out of scope

* fix(proxy): preserve sessionless host policy

* fix(proxy): gate requests on policy readiness

* fix(proxy): retire deleted browser sessions

* fix(proxy): close retired browser guests

* fix(proxy): retain retired session guards

* fix(proxy): retain retired partition policies

* fix(browser): retry transient proxy application failures

* fix(browser): release deleted partition installer state

* fix(proxy): retry delayed transient failures

* fix(proxy): preserve route session authority after rebase

* fix(proxy): clear retired session credentials

* fix(proxy): retire failed browser profiles

* fix(proxy): harden failed session cleanup

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
2026-08-30 04:53:20 -04:00

145 lines
4.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { installElectronProxyRequestGuard } from './electron-proxy-request-guard'
import {
applyProxySettingsToSession,
resetSessionProxyApplicationForTests,
retireProxySessionApplication
} from './proxy-settings'
function createSession() {
let listener:
| ((details: unknown, callback: (result: { cancel?: boolean }) => void) => void)
| null = null
const proxySession = {
resolveProxy: vi.fn(async () => 'DIRECT'),
setProxy: vi.fn(async () => {}),
closeAllConnections: vi.fn(async () => {}),
webRequest: {
onBeforeRequest: vi.fn(
(
next:
| ((details: unknown, callback: (result: { cancel?: boolean }) => void) => void)
| null
) => {
listener = next
}
)
}
}
return {
proxySession,
request: (callback: (result: { cancel?: boolean }) => void) => listener?.({}, callback)
}
}
describe('default-session proxy request guard', () => {
beforeEach(() => vi.clearAllMocks())
it('holds renderer requests until a delayed proxy transition settles', async () => {
const { proxySession, request } = createSession()
let finishWrite: (() => void) | undefined
proxySession.setProxy.mockImplementationOnce(
() => new Promise<void>((resolve) => (finishWrite = resolve))
)
resetSessionProxyApplicationForTests(proxySession)
installElectronProxyRequestGuard(proxySession as never)
const applying = applyProxySettingsToSession(
proxySession,
{ httpProxyUrl: 'http://proxy.example:8080' },
{ env: {} }
)
const callback = vi.fn()
request(callback)
expect(callback).not.toHaveBeenCalled()
await vi.waitFor(() => expect(proxySession.setProxy).toHaveBeenCalledOnce())
finishWrite?.()
await applying
await vi.waitFor(() => expect(callback).toHaveBeenCalledWith({}))
})
it('keeps renderer requests blocked across a transient retry', async () => {
const { proxySession, request } = createSession()
let finishRetry: (() => void) | undefined
proxySession.setProxy
.mockRejectedValueOnce(new Error('transient proxy failure'))
.mockImplementationOnce(() => new Promise<void>((resolve) => (finishRetry = resolve)))
resetSessionProxyApplicationForTests(proxySession)
installElectronProxyRequestGuard(proxySession as never)
const applying = applyProxySettingsToSession(
proxySession,
{ httpProxyUrl: 'http://proxy.example:8080' },
{ env: {} }
)
const callback = vi.fn()
request(callback)
await vi.waitFor(() => expect(proxySession.setProxy).toHaveBeenCalledTimes(2))
expect(callback).not.toHaveBeenCalled()
finishRetry?.()
await applying
await vi.waitFor(() => expect(callback).toHaveBeenCalledWith({}))
})
it('recovers after two transient failures without another settings transition', async () => {
const { proxySession, request } = createSession()
proxySession.setProxy
.mockRejectedValueOnce(new Error('first transient proxy failure'))
.mockRejectedValueOnce(new Error('second transient proxy failure'))
resetSessionProxyApplicationForTests(proxySession)
installElectronProxyRequestGuard(proxySession as never)
const applying = applyProxySettingsToSession(
proxySession,
{ httpProxyUrl: 'http://proxy.example:8080' },
{ env: {} }
)
const callback = vi.fn()
request(callback)
await vi.waitFor(() => expect(proxySession.setProxy).toHaveBeenCalledTimes(2))
expect(callback).not.toHaveBeenCalled()
await applying
expect(proxySession.setProxy).toHaveBeenCalledTimes(3)
await vi.waitFor(() => expect(callback).toHaveBeenCalledWith({}))
})
it('cancels renderer requests after proxy application fails', async () => {
const { proxySession, request } = createSession()
proxySession.setProxy.mockRejectedValue(new Error('proxy apply failed'))
resetSessionProxyApplicationForTests(proxySession)
installElectronProxyRequestGuard(proxySession as never)
await expect(
applyProxySettingsToSession(
proxySession,
{ httpProxyUrl: 'http://proxy.example:8080' },
{ env: {} }
)
).rejects.toThrow('proxy apply failed')
const callback = vi.fn()
request(callback)
expect(callback).toHaveBeenCalledWith({ cancel: true })
})
it('cancels session requests after permanent retirement without a WebContents', async () => {
const { proxySession, request } = createSession()
resetSessionProxyApplicationForTests(proxySession)
installElectronProxyRequestGuard(proxySession as never)
await applyProxySettingsToSession(
proxySession,
{ httpProxyUrl: 'http://proxy.example:8080' },
{ env: {} }
)
await retireProxySessionApplication(proxySession)
const callback = vi.fn()
request(callback)
expect(callback).toHaveBeenCalledWith({ cancel: true })
})
})