From d3fbe5e382db76be028a17f196117820bb0705a2 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 19 Mar 2023 12:39:57 +0100 Subject: [PATCH] BREAKING server: refactor streaming API data structures Since looking up a channel by ID should be the most often needed use case, the data structure is now more optimized towards this. The code is also simplified by using optional chaining where possible. In this vein, the server will now enforce that channel IDs are unique and not reused. Changelog: Changed --- .../backend/src/server/api/stream/index.ts | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 0ceab5d94..c93b4877b 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -31,7 +31,8 @@ export class Connection { public token?: AccessToken; private socket: WebSocket; public subscriber: StreamEventEmitter; - private channels: Channel[] = []; + // Maps IDs to the actual channels. + private channels: Record = {}; private subscribingNotes: any = {}; private cachedNotes: Packed<'Note'>[] = []; @@ -294,18 +295,23 @@ export class Connection { * @param pong `true` if a confirmation message should be sent back. */ public connectChannel(id: string, params: any, channel: string, pong = false) { - if (channels[channel].requireCredential && this.user == null) { + // When it is not possible to connect to a channel, the attempt will be ignored. + if ( + // Such a channel type does not exist. + !(channel in channels) + // The specified channel ID is already in use. + || (id in this.channels) + // Not authenticated, but authentication is required. + || (channels[channel].requireCredential && this.user == null) + // The channel is shared and already connected. + || (channels[channel].shouldShare && Object.values(this.channels).some(c => c.chName === channel)) + ) { + // TODO: send back some kind of error message? return; } - // 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視 - if (channels[channel].shouldShare && this.channels.some(c => c.chName === channel)) { - return; - } - - const ch: Channel = new channels[channel](id, this); - this.channels.push(ch); - ch.init(params); + this.channels[id] = new channels[channel](id, this); + this.channels[id].init(params); if (pong) { this.sendMessageToWs('connected', { id }); @@ -317,23 +323,16 @@ export class Connection { * @param id The unique ID of the channel to disconnect. */ public disconnectChannel(id: string) { - const channel = this.channels.find(c => c.id === id); - - if (channel) { - if (channel.dispose) channel.dispose(); - this.channels = this.channels.filter(c => c.id !== id); - } + this.channels[id]?.dispose?.(); + delete this.channels[id]; } /** * Called when a message should be sent to a specific channel. * @param data The message to be sent. */ - private onChannelMessageRequested(data: any) { - const channel = this.channels.find(c => c.id === data.id); - if (channel != null && channel.onMessage != null) { - channel.onMessage(data.type, data.body); - } + private onChannelMessageRequested(data: Record) { + this.channels[id]?.onMessage?.(data.type, data.body); } private typingOnChannel(channel: ChannelModel['id']) { @@ -417,8 +416,7 @@ export class Connection { * Dispose all currently open channels where possible. */ public dispose() { - for (const c of this.channels.filter(c => c.dispose)) { - if (c.dispose) c.dispose(); - } + Object.values(this.channels) + .forEach(c => c.dispose?.()); } }