osborn 0.9.152 → 0.9.153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +47 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -298,6 +298,12 @@ wsProxy.on('error', (err, _req, socket) => {
|
|
|
298
298
|
// valid cookie are forwarded, so a forgotten agent route can never be silently
|
|
299
299
|
// swallowed by the IDE proxy. Cleared in stopIde().
|
|
300
300
|
let ideSessionToken = null;
|
|
301
|
+
// Cancellation-generation guard for startCodeServer(). Incremented by stopIde()
|
|
302
|
+
// every time the IDE is torn down. startCodeServer() captures myGen at entry;
|
|
303
|
+
// if ideStartGeneration !== myGen when the poll resolves, the start has been
|
|
304
|
+
// superseded (stopIde ran mid-flight) and the result is discarded instead of
|
|
305
|
+
// re-enabling the proxy with no participant present.
|
|
306
|
+
let ideStartGeneration = 0;
|
|
301
307
|
// Helper: parse the osborn_ide cookie from a request and return true iff it
|
|
302
308
|
// matches the current ideSessionToken (and a token exists).
|
|
303
309
|
function hasValidIdeCookie(req) {
|
|
@@ -1539,7 +1545,7 @@ function startApiServer(workingDir, port) {
|
|
|
1539
1545
|
port: 8300,
|
|
1540
1546
|
path: targetUrl.pathname + (targetUrl.search || ''),
|
|
1541
1547
|
method: req.method,
|
|
1542
|
-
headers: { ...req.headers, host: '127.0.0.1:8300' },
|
|
1548
|
+
headers: { ...req.headers, host: '127.0.0.1:8300', 'x-forwarded-host': req.headers.host },
|
|
1543
1549
|
};
|
|
1544
1550
|
const proxyReq = httpRequest(options, (proxyRes) => {
|
|
1545
1551
|
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
|
|
@@ -1602,6 +1608,13 @@ function startApiServer(workingDir, port) {
|
|
|
1602
1608
|
const isAgentRoute = AGENT_ROUTE_PREFIXES.some(p => upgradePath === p || upgradePath.startsWith(p + '/'));
|
|
1603
1609
|
if (ideProxyEnabled && hasValidIdeCookie(req) && !isAgentRoute) {
|
|
1604
1610
|
ideLastProxiedActivity = Date.now();
|
|
1611
|
+
// code-server's authenticateOrigin() compares Origin against the effective host
|
|
1612
|
+
// (X-Forwarded-Host first). changeOrigin:true rewrites Host→127.0.0.1:8300 while the
|
|
1613
|
+
// browser's Origin stays the public hostname, causing a 403 → WS 1006. Preserve the
|
|
1614
|
+
// real public host so the origin check matches.
|
|
1615
|
+
if (!req.headers['x-forwarded-host'] && req.headers.host) {
|
|
1616
|
+
req.headers['x-forwarded-host'] = req.headers.host;
|
|
1617
|
+
}
|
|
1605
1618
|
wsProxy.ws(req, socket, head, { target: IDE_TARGET });
|
|
1606
1619
|
return;
|
|
1607
1620
|
}
|
|
@@ -2339,6 +2352,9 @@ async function main() {
|
|
|
2339
2352
|
};
|
|
2340
2353
|
// Kill code-server, disable proxy, clear interval, reset IDE state.
|
|
2341
2354
|
const stopIde = (reason) => {
|
|
2355
|
+
// Bump the generation counter so any in-flight startCodeServer() knows it
|
|
2356
|
+
// has been cancelled and must not re-enable the proxy or mint a new token.
|
|
2357
|
+
ideStartGeneration++;
|
|
2342
2358
|
ideProxyEnabled = false;
|
|
2343
2359
|
ideSessionToken = null;
|
|
2344
2360
|
ideStartInProgress = null;
|
|
@@ -2412,6 +2428,11 @@ async function main() {
|
|
|
2412
2428
|
// Fresh start: kill any stale proc (shouldn't normally exist here, but be safe).
|
|
2413
2429
|
if (ideCodeServerProc)
|
|
2414
2430
|
stopIde();
|
|
2431
|
+
// Capture the current generation AFTER any preceding stopIde() call so that
|
|
2432
|
+
// if stopIde() already ran (above), myGen reflects the incremented counter.
|
|
2433
|
+
// A concurrent stopIde() after this point will increment ideStartGeneration
|
|
2434
|
+
// beyond myGen, signalling cancellation to the poll loop and success branch.
|
|
2435
|
+
const myGen = ideStartGeneration;
|
|
2415
2436
|
let resolveFn;
|
|
2416
2437
|
let rejectFn;
|
|
2417
2438
|
ideStartInProgress = new Promise((res, rej) => { resolveFn = res; rejectFn = rej; });
|
|
@@ -2462,11 +2483,13 @@ async function main() {
|
|
|
2462
2483
|
await new Promise((res, rej) => {
|
|
2463
2484
|
let attempts = 0;
|
|
2464
2485
|
const poll = setInterval(() => {
|
|
2465
|
-
// Early-exit:
|
|
2466
|
-
//
|
|
2467
|
-
|
|
2486
|
+
// Early-exit: stopIde() was called while we were waiting (generation bumped),
|
|
2487
|
+
// OR the proc we spawned is no longer the active one for another reason.
|
|
2488
|
+
// Either way, cancel the poll immediately so we don't keep hitting 8300
|
|
2489
|
+
// for a boot that has already been invalidated.
|
|
2490
|
+
if (ideStartGeneration !== myGen || ideCodeServerProc !== codeServerProc) {
|
|
2468
2491
|
clearInterval(poll);
|
|
2469
|
-
rej(new Error('code-server
|
|
2492
|
+
rej(new Error('code-server start cancelled (stopIde ran mid-poll)'));
|
|
2470
2493
|
return;
|
|
2471
2494
|
}
|
|
2472
2495
|
attempts++;
|
|
@@ -2487,6 +2510,25 @@ async function main() {
|
|
|
2487
2510
|
}, 500);
|
|
2488
2511
|
});
|
|
2489
2512
|
console.log('✅ code-server ready on 8300');
|
|
2513
|
+
// Generation guard: check again right before enabling the proxy. The poll
|
|
2514
|
+
// already checks this on each tick, but there is a narrow window between the
|
|
2515
|
+
// last tick resolving and reaching here. If stopIde() ran in that window,
|
|
2516
|
+
// ideStartGeneration will have been incremented beyond myGen — we must NOT
|
|
2517
|
+
// enable the proxy or mint a token, and we must kill the orphan process so
|
|
2518
|
+
// no zombie code-server is left running with no participant present.
|
|
2519
|
+
if (ideStartGeneration !== myGen) {
|
|
2520
|
+
console.log('🖥️ IDE start cancelled (stopIde ran while code-server was booting) — killing orphan proc');
|
|
2521
|
+
try {
|
|
2522
|
+
codeServerProc.kill();
|
|
2523
|
+
}
|
|
2524
|
+
catch { }
|
|
2525
|
+
// ideCodeServerProc may already be null (stopIde cleared it); only null it
|
|
2526
|
+
// if it still points to our proc to avoid clobbering a newer start.
|
|
2527
|
+
if (ideCodeServerProc === codeServerProc)
|
|
2528
|
+
ideCodeServerProc = null;
|
|
2529
|
+
resolveFn(); // resolve (not reject) so the promise chain exits cleanly
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2490
2532
|
// Mint the session cookie token and enable the proxy. From this point the
|
|
2491
2533
|
// /editor route will 302+Set-Cookie and the fall-through proxy will only
|
|
2492
2534
|
// forward requests that carry the valid osborn_ide cookie.
|