Skip to content

Latest commit

 

History

History
127 lines (100 loc) · 4.52 KB

File metadata and controls

127 lines (100 loc) · 4.52 KB

SSH Connection Safety Analysis and Recommendations

Critical Safety Issues Identified

1. Connection Reuse Problems

  • Multiple managers (ServerManager, ThumbnailManagerRemote, CreateTorrent) maintain separate connection pools
  • Connections can become stale or invalid without proper detection
  • No centralized validation of connection health
  • Risk of reusing connections to wrong servers or in invalid states

2. Complex State Management

  • Heavy use of await withCheckedContinuation patterns that can deadlock
  • Multiple locks and async operations that increase complexity
  • Connection state scattered across multiple variables
  • Timeout-based reconnection logic that can fail

3. Insufficient Error Recovery

  • When connections fail, cleanup isn't always complete
  • Dangling references to broken connections
  • Race conditions in connection establishment

4. Thread Safety Issues

  • Connection pools accessed from multiple threads without proper synchronization
  • State changes not properly coordinated between different managers

Recommended Solution: Create-and-Destroy Pattern

Benefits

  • Simplified State Management: No need to track connection state across operations
  • Guaranteed Cleanup: Connections are always closed when operations complete
  • No Stale Connections: Fresh connection for each operation eliminates reuse issues
  • Better Error Recovery: Failed connections don't affect future operations
  • Thread Safety: No shared state to coordinate between threads

Implementation Changes Made

1. Simplified SSHConnection class

  • Removed complex timeout and reconnection logic
  • Eliminated async continuation patterns that could deadlock
  • Simplified connect/disconnect methods
  • Removed automatic registration with connection manager

2. Created SSHConnectionHelpers.swift

  • Added static methods for common operations (executeCommand, downloadFile, etc.)
  • Each method creates fresh connection, performs operation, and cleans up automatically
  • Added SafeSSHManager actor for cases requiring multiple operations

3. Updated ServerManager

  • Removed connection pooling
  • Added wrapper methods that use the safe helper functions

Migration Guide

Before (Unsafe):

// BAD: Persistent connection that can become stale
@State private var sshConnection: SSHConnection?

func someOperation() async {
    if sshConnection == nil {
        sshConnection = SSHConnection(server: server)
    }
    try await sshConnection?.connect()
    // ... use connection ...
    // Connection may not be properly cleaned up
}

After (Safe):

// GOOD: Create-and-destroy pattern
func someOperation() async throws {
    try await SSHConnection.withConnection(server: server) { connection in
        try await connection.connect()
        // ... use connection ...
        // Automatic cleanup guaranteed
    }
}

// OR even better, use helper methods:
func someOperation() async throws {
    let result = try await SSHConnection.executeCommand(on: server, command: "ls -la")
    // Connection automatically created, used, and destroyed
}

For Multiple Operations:

// GOOD: Use SafeSSHManager for multiple operations
let manager = SafeSSHManager(server: server)
defer { await manager.cleanup() }

try await manager.connect()
try await manager.executeCommand("command1")
try await manager.executeCommand("command2")
// Cleanup guaranteed by defer

Files That Need Updates

  1. CreateTorrent.swift - Remove persistent sshConnection property
  2. ThumbnailManagerRemote.swift - Remove connection pooling
  3. Any other views/managers that cache SSH connections

Performance Considerations

  • Network Overhead: Creating new connections has some overhead
  • Mitigation: For operations requiring multiple commands, use SafeSSHManager or withConnection
  • Trade-off: Slight performance cost for significantly improved reliability and safety

Testing Recommendations

  1. Test app backgrounding/foregrounding cycles
  2. Test network connectivity changes
  3. Test rapid connection requests
  4. Test server configuration changes
  5. Test memory pressure scenarios

Conclusion

The create-and-destroy pattern eliminates the root causes of your SSH connection crashes:

  • No reuse of stale connections
  • No connections to wrong servers
  • Proper cleanup on all failure paths
  • Simplified state management

This approach prioritizes reliability over minor performance optimizations, which is the right choice for a production app where crashes are unacceptable.