connect method

Future<void> connect(
  1. String host,
  2. int port, {
  3. String path = '/forward',
})

Connect to the forwarding server

@param host Host address @param port Port number @param path WebSocket path

Implementation

Future<void> connect(
  String host,
  int port, {
  String path = '/forward',
}) async {
  // Add a leading slash to path if not present
  if (path.isNotEmpty && !path.startsWith('/')) {
    path = '/$path';
  }

  // Check if we're already connected
  if (_ws != null && _isConnected()) {
    print('Already connected to forwarding server');
    return;
  }

  // If WebSocket exists but is not connected, close and recreate it
  if (_ws != null) {
    print('Closing existing WebSocket connection');
    try {
      await _ws!.sink.close();
    } catch (e) {
      print('Error closing WebSocket: $e');
    }
    _ws = null;
  }

  // Clear any existing reconnect timer
  _reconnectTimer?.cancel();
  _reconnectTimer = null;

  // Create a completer to handle the connection process
  final completer = Completer<void>();

  try {
    // Include clientType and clientId as query parameters
    final wsUrl =
        'ws://$host:$port$path?clientType=${clientType.name}&clientId=$clientId';
    print('Connecting to forwarding server at $wsUrl');

    // Create the WebSocket connection with more detailed error handling
    try {
      _ws = WebSocketChannel.connect(Uri.parse(wsUrl));
      print('WebSocket connection initiated');
    } catch (e, st) {
      print('Failed to create WebSocket connection: $e');
      print('Stack trace: $st');
      if (!completer.isCompleted) {
        completer.completeError(e);
      }
      return completer.future;
    }

    // Setup a listener for incoming messages
    _ws!.stream.listen(
      (dynamic message) {
        try {
          final String messageStr = message.toString();
          print('Received WebSocket message: $messageStr');
          final Map<String, dynamic> parsedMessage =
              json.decode(messageStr) as Map<String, dynamic>;

          // Emit the message as an event
          _emit('message', [parsedMessage]);

          // Handle method calls
          if (parsedMessage.containsKey('method') &&
              parsedMessage.containsKey('id')) {
            print(
              'Processing method call: ${parsedMessage['method']} with id: ${parsedMessage['id']}',
            );
            _emit('method', [
              parsedMessage['method'],
              parsedMessage['params'],
              (final dynamic result) {
                print(
                  'Respond callback called for method ${parsedMessage['method']} with result: $result',
                );
                _sendResponse(parsedMessage['id'] as String, result);
              },
            ]);
          }
          // Handle JSON-RPC responses
          else if (parsedMessage.containsKey('id')) {
            print('Processing response for id: ${parsedMessage['id']}');
            final request = _pendingRequests[parsedMessage['id']];
            if (request != null) {
              print('Found pending request for id: ${parsedMessage['id']}');
              if (parsedMessage.containsKey('error')) {
                print('Completing with error: ${parsedMessage['error']}');
                request.completer.completeError(
                  Exception(
                    parsedMessage['error']['message'] ?? 'Unknown error',
                  ),
                );
              } else {
                print('Completing with result: ${parsedMessage['result']}');
                request.completer.complete(parsedMessage['result']);
              }
              _pendingRequests.remove(parsedMessage['id']);
            } else {
              print(
                'No pending request found for id: ${parsedMessage['id']}',
              );
            }
          }
        } catch (error) {
          print('Error parsing WebSocket message: $error');
        }
      },
      onDone: () {
        print('Disconnected from forwarding server');
        _ws = null;
        _emit('disconnected');

        // Setup reconnect if not already set
        if (_reconnectTimer == null) {
          _setupReconnect(host, port, path: path);
        }
      },
      onError: (Object error) {
        print('WebSocket error: $error');
        _emit('error', [error]);
        if (!completer.isCompleted) {
          completer.completeError(error);
        }
      },
      cancelOnError: false,
    );

    // Complete the future once connection is established
    _emit('connected');

    // Start auto-reconnect if connection drops
    _setupReconnect(host, port, path: path);

    completer.complete();
  } catch (error) {
    print('Failed to create WebSocket: $error');
    if (!completer.isCompleted) {
      completer.completeError(error);
    }
  }

  return completer.future;
}