From ae8d2029f68ede5ecf40b555af12d034c7bd00bb Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Fri, 12 Apr 2024 10:40:31 -0700 Subject: [PATCH] fix: Re-dial peers when resuming from background on iOS --- packages/backend/src/backendManager.ts | 16 +++++++++ .../connections-manager.service.ts | 5 +++ .../backend/src/nest/libp2p/libp2p.service.ts | 34 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 42fa25d78b..c0466b6d2e 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -114,7 +114,21 @@ export const runBackendMobile = async () => { connectionsManager.closeSocket() }) + // I've noticed that two of these 'open' events can occur in quick + // succession and of course the event handlers are not awaited. This + // is a simple fix, but perhaps we can also look into the cause in + // more depth. + let openInProgress = false + rn_bridge.channel.on('open', async (msg: OpenServices) => { + log('Received "open" event via React Native Bridge') + + if (openInProgress) { + log('"open" event already being processed, ignoring') + return + } + openInProgress = true + const connectionsManager = app.get(ConnectionsManagerService) const torControl = app.get(TorControl) const proxyAgent = app.get<{ proxy: { port: string } }>(SOCKS_PROXY_AGENT) @@ -124,6 +138,8 @@ export const runBackendMobile = async () => { proxyAgent.proxy.port = msg.httpTunnelPort await connectionsManager.openSocket() + + openInProgress = false }) } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index f52de0f6bb..8c7291133b 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -249,6 +249,11 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI // This method is only used on iOS through rn-bridge for reacting on lifecycle changes public async openSocket() { await this.socketService.init() + await this.libp2pService?.redialPeers() + + // Tried to restart libp2p as another alternative, but received this: + // Error: Handler already registered for protocol /meshsub/1.1.0 + await this.libp2pService?.restart() } public async leaveCommunity() { diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index c4129e9888..0f29b04eb8 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -32,6 +32,7 @@ export class Libp2pService extends EventEmitter { public connectedPeers: Map = new Map() public dialedPeers: Set = new Set() private readonly logger = Logger(Libp2pService.name) + constructor( @Inject(SERVER_IO_PROVIDER) public readonly serverIoProvider: ServerIoProviderTypes, @Inject(SOCKS_PROXY_AGENT) public readonly socksProxyAgent: Agent, @@ -40,14 +41,47 @@ export class Libp2pService extends EventEmitter { super() } + public restart = async () => { + await this.libp2pInstance?.stop() + await this.libp2pInstance?.start() + } + private dialPeer = async (peerAddress: string) => { if (this.dialedPeers.has(peerAddress)) { return } this.dialedPeers.add(peerAddress) + console.log('Dialing peer:', peerAddress, this.dialedPeers) await this.libp2pInstance?.dial(multiaddr(peerAddress)) } + private hangUpPeer = async (peerAddress: string) => { + this.logger('Hanging up on peer:', peerAddress) + await this.libp2pInstance?.hangUp(multiaddr(peerAddress)) + this.dialedPeers.delete(peerAddress) + } + + /** + * Hang up existing peer connections and re-dial them. Specifically useful on + * iOS where Tor receives a new port when the app resumes from background and + * we want to close/re-open connections. + */ + public redialPeers = async () => { + this.logger('Re-dialing peers') + + // TODO: Sort peers + const dialedPeers = Array.from(this.dialedPeers) + + for (const peerAddress of dialedPeers) { + await this.hangUpPeer(peerAddress) + } + + console.log('DIALED PEERS', dialedPeers, this.dialedPeers) + + this.processInChunksService.updateData(dialedPeers) + await this.processInChunksService.process() + } + public readonly createLibp2pAddress = (address: string, peerId: string): string => { return createLibp2pAddress(address, peerId) }