From 5aceeb31e7b1fd72a5e05cebc9a2e3d4584d4f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Wed, 16 Jul 2025 01:04:06 +0200 Subject: [PATCH 1/2] Return request errors for /streaming in-band instead of in HTTP Closes #591 --- .../server/api/StreamingApiServerService.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 231ba7bfbf..b6b930a434 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -116,6 +116,7 @@ export class StreamingApiServerService implements OnApplicationShutdown { let user: MiLocalUser | null = null; let app: MiAccessToken | null = null; + let dieInstantly: [number, string] | null = null; // https://datatracker.ietf.org/doc/html/rfc6750.html#section-2.1 // Note that the standard WHATWG WebSocket API does not support setting any headers, @@ -132,21 +133,16 @@ export class StreamingApiServerService implements OnApplicationShutdown { } } catch (e) { if (e instanceof AuthenticationError) { - socket.write([ - 'HTTP/1.1 401 Unauthorized', - 'WWW-Authenticate: Bearer realm="Misskey", error="invalid_token", error_description="Failed to authenticate"', - ].join('\r\n') + '\r\n\r\n'); + dieInstantly = [4000, 'Failed to authenticate']; } else { socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n'); + socket.destroy(); + return; } - socket.destroy(); - return; } if (user?.isSuspended) { - socket.write('HTTP/1.1 403 Forbidden\r\n\r\n'); - socket.destroy(); - return; + dieInstantly = [4001, 'User suspended']; } // ServerServices sets `trustProxy: true`, which inside fastify/request.js ends up calling `proxyAddr` in this way, so we do the same. @@ -231,6 +227,11 @@ export class StreamingApiServerService implements OnApplicationShutdown { ws.terminate(); }); + if (dieInstantly !== null) { + ws.close(...dieInstantly); + return; + } + this.#wss.emit('connection', ws, request, { stream, user, app, }); From 155d2920b637f76a10d66cb5c3d5aa3e8dec0eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Wed, 16 Jul 2025 18:43:00 +0200 Subject: [PATCH 2/2] Dispose of the stream even if it gets closed early --- packages/backend/src/server/api/StreamingApiServerService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index b6b930a434..783f6af34e 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -220,6 +220,8 @@ export class StreamingApiServerService implements OnApplicationShutdown { if (connectionsForClient.size < 1) { this.#connectionsByClient.delete(limitActor); } + + stream.dispose(); }); ws.once('error', (e) => {