mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-21 17:16:20 -07:00
fix(anime): harden stream proxy and session lifecycle
- Reject non-origin-form proxy targets - Release stale anime browser sessions when senders move
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
type AnimeBrowserIpcSender,
|
||||
} from './anime-browser-ipc-handlers';
|
||||
import { createAnimeBrowserRuntime, type AnimeBrowserRuntimeDeps } from './anime-browser-runtime';
|
||||
import { createAnimeBrowserSessionRegistry } from './anime-browser-sessions';
|
||||
import { createOpenConfigSettingsWindowHandler } from './config-settings-window';
|
||||
import { createCreateAnimeBrowserWindowHandler } from './setup-window-factory';
|
||||
import {
|
||||
@@ -68,7 +69,6 @@ export interface AnimeBrowserApplicationRuntime {
|
||||
export function createAnimeBrowserApplicationRuntime(
|
||||
deps: AnimeBrowserApplicationRuntimeDeps,
|
||||
): AnimeBrowserApplicationRuntime {
|
||||
const sessions = new Map<string, AnimeBrowserIpcSender>();
|
||||
let playbackState: AnimeBrowserPlaybackState | null = toAnimeBrowserPlaybackState(
|
||||
deps.getInitialPlaybackMetadata(),
|
||||
);
|
||||
@@ -114,20 +114,15 @@ export function createAnimeBrowserApplicationRuntime(
|
||||
},
|
||||
onQueueState: (state) => broadcast(IPC_CHANNELS.event.animeBrowserQueueState, state),
|
||||
});
|
||||
const sessions = createAnimeBrowserSessionRegistry((sessionId) =>
|
||||
runtime.releaseSession(sessionId),
|
||||
);
|
||||
|
||||
registerAnimeBrowserIpcHandlers({
|
||||
ipcMain: deps.ipcMain,
|
||||
runtime,
|
||||
getPlaybackState: () => playbackState,
|
||||
registerSession: (sessionId, sender) => {
|
||||
if (sessions.get(sessionId) === sender) return;
|
||||
sessions.set(sessionId, sender);
|
||||
sender.once('destroyed', () => {
|
||||
if (sessions.get(sessionId) !== sender) return;
|
||||
sessions.delete(sessionId);
|
||||
runtime.releaseSession(sessionId);
|
||||
});
|
||||
},
|
||||
registerSession: sessions.register,
|
||||
});
|
||||
|
||||
let dockIconRetained = false;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { AnimeBrowserIpcSender } from './anime-browser-ipc-handlers';
|
||||
import { createAnimeBrowserSessionRegistry } from './anime-browser-sessions';
|
||||
|
||||
test('moving a live sender to a new anime browser session releases its stale session', () => {
|
||||
const destroyedListeners: Array<() => void> = [];
|
||||
const sender: AnimeBrowserIpcSender = {
|
||||
send: () => {},
|
||||
isDestroyed: () => false,
|
||||
once: (_event, listener) => destroyedListeners.push(listener),
|
||||
};
|
||||
const released: string[] = [];
|
||||
const sessions = createAnimeBrowserSessionRegistry((sessionId) => released.push(sessionId));
|
||||
|
||||
sessions.register('old', sender);
|
||||
sessions.register('old', sender);
|
||||
assert.equal(destroyedListeners.length, 1, 'duplicate registration keeps its existing handler');
|
||||
|
||||
sessions.register('new', sender);
|
||||
assert.deepEqual(released, ['old']);
|
||||
assert.equal(sessions.get('old'), undefined);
|
||||
assert.equal(sessions.get('new'), sender);
|
||||
|
||||
for (const listener of destroyedListeners) listener();
|
||||
assert.deepEqual(released, ['old', 'new']);
|
||||
assert.equal(sessions.get('new'), undefined);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { AnimeBrowserIpcSender } from './anime-browser-ipc-handlers';
|
||||
|
||||
export interface AnimeBrowserSessionRegistry {
|
||||
get: (sessionId: string) => AnimeBrowserIpcSender | undefined;
|
||||
register: (sessionId: string, sender: AnimeBrowserIpcSender) => void;
|
||||
values: () => IterableIterator<AnimeBrowserIpcSender>;
|
||||
}
|
||||
|
||||
export function createAnimeBrowserSessionRegistry(
|
||||
releaseSession: (sessionId: string) => void,
|
||||
): AnimeBrowserSessionRegistry {
|
||||
const sessions = new Map<string, AnimeBrowserIpcSender>();
|
||||
|
||||
const register = (sessionId: string, sender: AnimeBrowserIpcSender): void => {
|
||||
if (sessions.get(sessionId) === sender) return;
|
||||
for (const [previousSessionId, registeredSender] of sessions) {
|
||||
if (previousSessionId === sessionId || registeredSender !== sender) continue;
|
||||
sessions.delete(previousSessionId);
|
||||
releaseSession(previousSessionId);
|
||||
}
|
||||
sessions.set(sessionId, sender);
|
||||
sender.once('destroyed', () => {
|
||||
if (sessions.get(sessionId) !== sender) return;
|
||||
sessions.delete(sessionId);
|
||||
releaseSession(sessionId);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
get: (sessionId) => sessions.get(sessionId),
|
||||
register,
|
||||
values: () => sessions.values(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user