diff --git a/streaming/index.js b/streaming/index.js index 7890a35d12b..adb4d26c64d 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -3,6 +3,7 @@ import fs from 'node:fs'; import http from 'node:http'; import path from 'node:path'; +import querystring from 'node:querystring'; import url from 'node:url'; import cors from 'cors'; @@ -92,6 +93,19 @@ const parseJSON = (json, req) => { } }; +/** + * Parses the query string from a request object. + * @param {Request?} req + */ +const parseQueryString = (req) => { + if (!req?.url) { + return undefined; + } + const url = new URL(req.url, "http://./"); + const qs = url.search.slice(1); + return querystring.parse(qs); +} + // Used for priming the counters/gauges for the various metrics that are // per-channel const CHANNEL_NAMES = [ @@ -393,8 +407,8 @@ const startServer = async () => { */ const accountFromRequest = (req) => new Promise((resolve, reject) => { const authorization = req.headers.authorization; - const location = req.url ? url.parse(req.url, true) : undefined; - const accessToken = location?.query.access_token || req.headers['sec-websocket-protocol']; + const query = parseQueryString(req); + const accessToken = query?.access_token || req.headers['sec-websocket-protocol']; if (!authorization && !accessToken) { reject(new AuthenticationError('Missing access token')); @@ -1300,8 +1314,8 @@ const startServer = async () => { * @param {import('pino').Logger} log */ function onConnection(ws, req, log) { - // Note: url.parse could throw, which would terminate the connection, so we - // increment the connected clients metric straight away when we establish + // In case the handler throws, which would terminate the connection, + // increment the connected clients metric straight away when it establishes // the connection, without waiting: metrics.connectedClients.labels({ type: 'websocket' }).inc(); @@ -1383,11 +1397,10 @@ const startServer = async () => { subscribeWebsocketToSystemChannel(session); - // Parse the URL for the connection arguments (if supplied), url.parse can throw: - const location = req.url && url.parse(req.url, true); - - if (location && location.query.stream) { - subscribeWebsocketToChannel(session, firstParam(location.query.stream), location.query); + // Parse the URL for the connection arguments (if supplied) + const query = parseQueryString(req); + if (query && query.stream) { + subscribeWebsocketToChannel(session, firstParam(query.stream), query); } }