diff --git a/package-lock.json b/package-lock.json index 64b69db5..e8f1e758 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25076,6 +25076,7 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "emoji-regex": { diff --git a/server/lib/helpers.ts b/server/lib/helpers.ts index 7eef5e54..87bb88fb 100644 --- a/server/lib/helpers.ts +++ b/server/lib/helpers.ts @@ -1,5 +1,7 @@ import type { RegisterServerOptions, PeerTubeHelpers } from '@peertube/peertube-types' -import { Response } from 'express' +import type { Response } from 'express' +import type { IncomingMessage } from 'http' +import type { Duplex } from 'stream' const packagejson: any = require('../../../package.json') const version: string = packagejson.version || '' @@ -19,14 +21,34 @@ function getBaseRouterRoute (options: RegisterServerOptions): string { return options.peertubeHelpers.plugin.getBaseRouterRoute() } +interface RegisterServerWebSocketRouteOptions { + route: string + handler: (request: IncomingMessage, socket: Duplex, head: Buffer) => any +} +// getBaseWebSocketRoute() comes with Peertube 5.0.0. +type RegisterServerOptionsV5 = RegisterServerOptions & { + registerWebSocketRoute?: (options: RegisterServerWebSocketRouteOptions) => void + peertubeHelpers: { + plugin: { + getBaseWebSocketRoute?: () => string + } + } +} + /** - * Returns the base router route, but without the plugin version. + * Returns the base route for Websocket endpoint. + * This features comes with Peertube >=5.0.0. * @param options server options + * @returns the route, or undefined if the Peertube version does not provide this feature */ -function getBaseRouterCanonicalRoute (options: RegisterServerOptions): string { - let route = getBaseRouterRoute(options) - route = route.replace(pluginShortName + '/' + version + '/', pluginShortName + '/') - return route +function getBaseWebSocketRoute (options: RegisterServerOptionsV5): string | undefined { + if (!options.peertubeHelpers.plugin) { + throw new Error('Missing peertubeHelpers.plugin, have you the correct Peertube version?') + } + if (!options.peertubeHelpers.plugin.getBaseWebSocketRoute) { + return undefined + } + return options.peertubeHelpers.plugin.getBaseWebSocketRoute() } function getBaseStaticRoute (options: RegisterServerOptions): string { @@ -71,8 +93,9 @@ async function getUserNickname (options: RegisterServerOptions, user: AuthUserFi } export { + RegisterServerOptionsV5, getBaseRouterRoute, - getBaseRouterCanonicalRoute, + getBaseWebSocketRoute, getBaseStaticRoute, isUserAdmin, getUserNickname, diff --git a/server/lib/routers/webchat.ts b/server/lib/routers/webchat.ts index 617537ab..dbdfdea5 100644 --- a/server/lib/routers/webchat.ts +++ b/server/lib/routers/webchat.ts @@ -4,7 +4,9 @@ import type { ProsodyListRoomsResult, ProsodyListRoomsResultRoom } from '../../../shared/lib/types' import { createProxyServer } from 'http-proxy' -import { getBaseRouterRoute, getBaseRouterCanonicalRoute, getBaseStaticRoute, isUserAdmin } from '../helpers' +import { + RegisterServerOptionsV5, getBaseRouterRoute, getBaseWebSocketRoute, getBaseStaticRoute, isUserAdmin +} from '../helpers' import { asyncMiddleware } from '../middlewares/async' import { getProsodyDomain } from '../prosody/config/domain' import { getAPIKey } from '../apikey' @@ -14,7 +16,6 @@ import * as path from 'path' const got = require('got') const fs = require('fs').promises -// const proxy = require('express-http-proxy') interface ProsodyProxyInfo { host: string @@ -23,15 +24,11 @@ interface ProsodyProxyInfo { let currentProsodyProxyInfo: ProsodyProxyInfo | null = null let currentHttpBindProxy: ReturnType | null = null let currentWebsocketProxy: ReturnType | null = null -interface CurrentWebsocketUpgradeEvent { - server: any - listener: Function -} -let currentWebsocketUpgradeEvent: CurrentWebsocketUpgradeEvent | null = null -async function initWebchatRouter (options: RegisterServerOptions): Promise { +async function initWebchatRouter (options: RegisterServerOptionsV5): Promise { const { getRouter, + registerWebSocketRoute, peertubeHelpers, settingsManager } = options @@ -50,6 +47,10 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise { - try { + + // Peertube >=5.0.0: Adding the websocket route. + if (registerWebSocketRoute) { + registerWebSocketRoute({ + route: '/xmpp-websocket', + handler: (request, socket, head) => { if (!currentWebsocketProxy) { - res.status(404) - res.send('Not found') - return + throw new Error('There is no current websocket proxy, should not get here.') } - - // Now, we need to attach an listener for the update event on the server... - // But we don't have access to the server. - // To get this, we will wait for the first request, and then get the server from there! - if (!currentWebsocketUpgradeEvent) { - peertubeHelpers.logger.info( - 'Here is the first websocket proxy connection, we are binding the upgrade listener...' - ) - /** - * Get the server object to subscribe to server events; - * 'upgrade' for websocket and 'close' for graceful shutdown - * - * NOTE: - * req.socket: node >= 13 - * req.connection: node < 13 (Remove this when node 12/13 support is dropped) - * - * This code was inspired by: - * https://github.com/chimurai/http-proxy-middleware, file /src/http-proxy-middleware.ts#L53 - */ - const s: any = (req.socket ?? req.connection) - const server = 'server' in s ? s.server : null - if (!server) { - peertubeHelpers.logger.error('Cant access to the ExpressJS server, wont be able to handle websocket.') - } else { - currentWebsocketUpgradeEvent = { - server, - listener: (req: any, socket: any, head: any) => { - // We are not the only websocket server! Peertube has its own. We must match the url. - if (/webchat\/xmpp-websocket/.test(req.url)) { - peertubeHelpers.logger.info('Got an http upgrade event that match the correct path') - currentWebsocketProxy?.ws(req, socket, head) - } - } - } - server.on('upgrade', currentWebsocketUpgradeEvent.listener) - } - } - - req.url = 'xmpp-websocket' - currentWebsocketProxy.web(req, res) - } catch (err) { - next(err) + currentWebsocketProxy.ws(request, socket, head) } - } - ) + }) + } router.get('/prosody-list-rooms', asyncMiddleware( async (req: Request, res: Response, _next: NextFunction) => { @@ -356,10 +311,6 @@ async function disableProxyRoute ({ peertubeHelpers }: RegisterServerOptions): P currentWebsocketProxy.close() currentWebsocketProxy = null } - if (currentWebsocketUpgradeEvent) { - peertubeHelpers.logger.info('Removing the upgrade listener') - currentWebsocketUpgradeEvent.server.off('upgrade', currentWebsocketUpgradeEvent.listener) - } } catch (err) { peertubeHelpers.logger.error('Seems that the http bind proxy close has failed: ' + (err as string)) }