Chat Federation (and a lot more) WIP:
Note: websocket s2s is not working yet, still WIP. New Features * Chat Federation: * You can now connect to a remote chat with your local account. * This remote connection is done using a custom implementation of [XEP-0468: WebSocket S2S](https://xmpp.org/extensions/xep-0468.html), using some specific discovering method (so that it will work without any DNS configuration). Minor changes and fixes * Possibility to debug Prosody in development environments. * Using process.spawn instead of process.exec to launch Prosody (safer, and more optimal). * Prosody AppImage: fix path mapping: we only map necessary /etc/ subdir, so that the AppImage can access to /etc/resolv.conf, /etc/hosts, ... * Prosody AppImage: hidden debug mode to disable lua-unbound, that seems broken in some docker dev environments.
This commit is contained in:
73
server/lib/xmpp-ws-proxy/check-remote.ts
Normal file
73
server/lib/xmpp-ws-proxy/check-remote.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
// import { getBaseRouterRoute } from '../helpers'
|
||||
// import { canonicalizePluginUri } from '../uri/canonicalize'
|
||||
// import { URL } from 'url'
|
||||
// const got = require('got')
|
||||
|
||||
/**
|
||||
* FIXME: this method should not be necessary anymore, it was a proof of concept.
|
||||
*
|
||||
* This function checks that there is a valid Peertube instance behind
|
||||
* the remote url, to avoid spoofing.
|
||||
* It also ensure that we have needed serverInfos for the federation
|
||||
* (so we can also open outgoing proxyfied connection to that instance)
|
||||
* @param options server options
|
||||
* @param remoteInstanceUrl remote instance url to check (as readed in the request header)
|
||||
* @returns true if the remote instance is ok
|
||||
*/
|
||||
async function checkRemote (
|
||||
_options: RegisterServerOptions,
|
||||
_remoteInstanceUrl: any
|
||||
): Promise<boolean> {
|
||||
throw new Error('Not Implemented Yet')
|
||||
|
||||
// const logger = options.peertubeHelpers.logger
|
||||
// if (typeof remoteInstanceUrl !== 'string') {
|
||||
// logger.info('WS-Proxy-Check: Received invalid request on xmpp-websocket-proxy: invalid remoteInstanceUrl header')
|
||||
// return false
|
||||
// }
|
||||
// logger.debug(
|
||||
// `WS-Proxy-Check: Receiving request on xmpp-websocket-proxy for host ${remoteInstanceUrl}, ` +
|
||||
// 'checking the host is a valid Peertube server'
|
||||
// )
|
||||
// let url: string
|
||||
// try {
|
||||
// const u = new URL(remoteInstanceUrl)
|
||||
|
||||
// // Assuming that the path on the remote instance is the same as on this one
|
||||
// // (but canonicalized to remove the plugin version)
|
||||
// u.pathname = getBaseRouterRoute(options) + 'api/federation_server_infos'
|
||||
// url = canonicalizePluginUri(options, u.toString(), {
|
||||
// protocol: 'http',
|
||||
// removePluginVersion: true
|
||||
// })
|
||||
// } catch (_err) {
|
||||
// logger.info('WS-Proxy-Check: Invalid remote instance url provided: ' + remoteInstanceUrl)
|
||||
// return false
|
||||
// }
|
||||
|
||||
// try {
|
||||
// logger.debug('WS-Proxy-Check: We must check remote server infos using url: ' + url)
|
||||
// const response = await got(url, {
|
||||
// method: 'GET',
|
||||
// headers: {},
|
||||
// responseType: 'json'
|
||||
// }).json()
|
||||
|
||||
// if (!response) {
|
||||
// logger.info('WS-Proxy-Check: Invalid remote server options')
|
||||
// return false
|
||||
// }
|
||||
|
||||
// // FIXME/TODO
|
||||
|
||||
// return true
|
||||
// } catch (_err) {
|
||||
// logger.info('WS-Proxy-Check: Can\'t get remote instance informations using url ' + url)
|
||||
// return false
|
||||
// }
|
||||
}
|
||||
|
||||
export {
|
||||
checkRemote
|
||||
}
|
119
server/lib/xmpp-ws-proxy/server.ts
Normal file
119
server/lib/xmpp-ws-proxy/server.ts
Normal file
@ -0,0 +1,119 @@
|
||||
// import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
// import type { IncomingMessage } from 'http'
|
||||
// import type { Duplex } from 'stream'
|
||||
// import { WebSocketServer, WebSocket } from 'ws'
|
||||
// import { Socket } from 'net'
|
||||
|
||||
// FIXME: this method should not be necessary anymore, it was a proof of concept.
|
||||
|
||||
// interface ProxyLogger {
|
||||
// debug: (s: string) => void
|
||||
// info: (s: string) => void
|
||||
// error: (s: string) => void
|
||||
// }
|
||||
|
||||
// let xmppWsProxyServer: XMPPWsProxyServer | undefined
|
||||
// class XMPPWsProxyServer {
|
||||
// private readonly logger: ProxyLogger
|
||||
// private readonly options: RegisterServerOptions
|
||||
// private readonly wsProxyServer: WebSocketServer
|
||||
// private prosodyPort: number | undefined
|
||||
// private readonly connections: Map<WebSocket, Socket> = new Map<WebSocket, Socket>()
|
||||
|
||||
// constructor (options: RegisterServerOptions) {
|
||||
// const logger = options.peertubeHelpers.logger
|
||||
// this.logger = {
|
||||
// debug: s => logger.debug('XMPP-WS-PROXY: ' + s),
|
||||
// info: s => logger.info('XMPP-WS-PROXY: ' + s),
|
||||
// error: s => logger.error('XMPP-WS-PROXY: ' + s)
|
||||
// }
|
||||
// this.options = options
|
||||
|
||||
// this.wsProxyServer = new WebSocketServer({ noServer: true, perMessageDeflate: false })
|
||||
// }
|
||||
|
||||
// public handleUpgrade (request: IncomingMessage, incomingSocket: Duplex, head: Buffer): void {
|
||||
// this.wsProxyServer.handleUpgrade(request, incomingSocket, head, ws => {
|
||||
// this.handleUpgradeCallback(ws).then(() => {}, () => {})
|
||||
// })
|
||||
// }
|
||||
|
||||
// public async handleUpgradeCallback (ws: WebSocket): Promise<void> {
|
||||
// this.logger.debug('Opening a Websocket Proxy connection')
|
||||
|
||||
// const port = await this.getProsodyPort()
|
||||
// if (!port) {
|
||||
// this.logger.error('No port configured for Prosody, closing the websocket stream')
|
||||
// ws.close() // FIXME: add a code and error message
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Opening a tcp connection to local Prosody:
|
||||
// const prosodySocket = new Socket()
|
||||
// this.connections.set(ws, prosodySocket)
|
||||
// prosodySocket.connect(port, 'localhost', () => {
|
||||
// // TODO: write the remote IP in the header line.
|
||||
// prosodySocket.write('LIVECHAT-WS-PROXY\n')
|
||||
// })
|
||||
// prosodySocket.on('close', () => {
|
||||
// ws.close()
|
||||
// })
|
||||
// prosodySocket.on('data', (data) => {
|
||||
// ws.send(data)
|
||||
// })
|
||||
|
||||
// ws.on('message', (data) => {
|
||||
// // TODO: remove this log
|
||||
// this.logger.debug('Receiving raw data')
|
||||
// if (Array.isArray(data)) {
|
||||
// data.forEach(chunck => {
|
||||
// prosodySocket.write(chunck)
|
||||
// })
|
||||
// } else if (data instanceof ArrayBuffer) {
|
||||
// prosodySocket.write(Buffer.from(data))
|
||||
// } else {
|
||||
// prosodySocket.write(data)
|
||||
// }
|
||||
// })
|
||||
// ws.on('close', () => {
|
||||
// this.logger.debug('Websocket connection is closed, closing socket')
|
||||
// prosodySocket.end()
|
||||
// })
|
||||
// }
|
||||
|
||||
// private async getProsodyPort (): Promise<number> {
|
||||
// if (this.prosodyPort) {
|
||||
// return this.prosodyPort
|
||||
// }
|
||||
// const port = await this.options.settingsManager.getSetting('prosody-port') as string
|
||||
// this.prosodyPort = parseInt(port)
|
||||
// return this.prosodyPort
|
||||
// }
|
||||
|
||||
// private async closeConnections (): Promise<void> {
|
||||
// this.logger.debug('Closing XMPPWsProxyServer connections...')
|
||||
// this.connections.forEach((socket, _ws) => {
|
||||
// socket.end()
|
||||
// // ws.terminate() // not necessary, socket close event should be called
|
||||
// })
|
||||
// // FIXME: wait for all connections to be closed...
|
||||
// }
|
||||
|
||||
// static singleton (options: RegisterServerOptions): XMPPWsProxyServer {
|
||||
// if (!xmppWsProxyServer) {
|
||||
// xmppWsProxyServer = new XMPPWsProxyServer(options)
|
||||
// }
|
||||
// return xmppWsProxyServer
|
||||
// }
|
||||
|
||||
// static async destroySingleton (): Promise<void> {
|
||||
// if (!xmppWsProxyServer) { return }
|
||||
// const server = xmppWsProxyServer
|
||||
// xmppWsProxyServer = undefined
|
||||
// await server.closeConnections()
|
||||
// }
|
||||
// }
|
||||
|
||||
// export {
|
||||
// XMPPWsProxyServer
|
||||
// }
|
Reference in New Issue
Block a user