connect method
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;
}