import { EventEmitter } from "events" ; import * as http from "http" ; import * as util from "util" ; import * as core from "@fluidframework/server-services-core" ; import { Lumberjack } from "@fluidframework/server-services-telemetry" ; import { clone } from "lodash" ; import Redis from "ioredis" ; import { Namespace , Server , Socket } from "socket.io" ; import { createAdapter } from "@socket.io/redis-adapter" ; import type { Adapter } from "socket.io-adapter" ; import * as winston from "winston" ; import * as redisSocketIoAdapter from "./redisSocketIoAdapter" ; import { SocketIORedisConnection , SocketIoRedisSubscriptionConnection } from "./socketIoRedisConnection" ; class SocketIoSocket implements core . IWebSocket { public get id ( ) : string { return this . socket . id ; } constructor ( private readonly socket ) { } public on ( event , listener ) { this . socket . on ( event , listener ) ; } public async join ( id ) { return this . socket . join ( id ) ; } public async emit ( event , ... args ) { this . socket . emit ( event , ... args ) ; } public async emitToRoom ( roomId , event , ... args ) { this . socket . nsp . to ( roomId ) . emit ( event , ... args ) ; } public disconnect ( close ? ) { this . socket . disconnect ( close ) ; } } class SocketIoServer implements core . IWebSocketServer { private readonly events = new EventEmitter ( ) ; constructor ( private readonly io , private readonly pub , private readonly sub ) { this . io . on ( "connection" , ( socket ) => { const webSocket = new SocketIoSocket ( socket ) ; this . events . emit ( "connection" , webSocket ) ; } ) ; } public on ( event , listener ) { this . events . on ( event , listener ) ; } public async close ( ) { const pubClosedP = util . promisify ( ( ( callback ) => this . pub . quit ( callback ) ) as any ) ( ) ; const subClosedP = util . promisify ( ( ( callback ) => this . sub . quit ( callback ) ) as any ) ( ) ; const ioClosedP = util . promisify ( ( ( callback ) => this . io . close ( callback ) ) as any ) ( ) ; await Promise . all ( [ pubClosedP , subClosedP , ioClosedP ] ) ; } } export function create ( redisConfig , server , socketIoAdapterConfig ? ) { const options = { host : redisConfig . host , port : redisConfig . port , password : redisConfig . pass } ; if ( redisConfig . tls ) { options . tls = { servername : redisConfig . host } ; } const pub = new Redis ( clone ( options ) ) ; const sub = new Redis ( clone ( options ) ) ; pub . on ( "error" , ( err ) => { winston . error ( "Error with Redis pub connection: " , err ) ; Lumberjack . error ( "Error with Redis pub connection" , undefined , err ) ; } ) ; sub . on ( "error" , ( err ) => { winston . error ( "Error with Redis sub connection: " , err ) ; Lumberjack . error ( "Error with Redis sub connection" , undefined , err ) ; } ) ; let adapter ; if ( socketIoAdapterConfig ?. enableCustomSocketIoAdapter ) { const socketIoRedisOptions = { pubConnection : new SocketIORedisConnection ( pub ) , subConnection : new SocketIoRedisSubscriptionConnection ( sub ) } ; redisSocketIoAdapter . RedisSocketIoAdapter . setup ( socketIoRedisOptions , socketIoAdapterConfig ?. shouldDisableDefaultNamespace ) ; adapter = redisSocketIoAdapter . RedisSocketIoAdapter as any ; } else { adapter = createAdapter ( pub , sub ) ; } const io = new Server ( server , { allowEIO3 : true , perMessageDeflate : true , transports : [ "websocket" ] , cors : { origin : true , credentials : true } , adapter } ) ; return new SocketIoServer ( io , pub , sub ) ; }