| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| class ServiceClient { |
| Completer<dynamic>? isolateStartedId; |
| Completer<dynamic>? isolatePausedId; |
| Completer<dynamic>? isolateResumeId; |
| |
| ServiceClient( |
| this.client, { |
| this.isolateStartedId, |
| this.isolatePausedId, |
| this.isolateResumeId, |
| }) { |
| client.listen(_onData, onError: _onError, cancelOnError: true); |
| } |
| |
| Future<Map<String, dynamic>> invokeRPC(String method, |
| [Map<String, dynamic>? params]) async { |
| final String key = _createKey(); |
| final String request = json.encode(<String, dynamic>{ |
| 'jsonrpc': '2.0', |
| 'method': method, |
| 'params': params == null ? <String, dynamic>{} : params, |
| 'id': key, |
| }); |
| client.add(request); |
| final Completer<Map<String, dynamic>> completer = |
| Completer<Map<String, dynamic>>(); |
| _outstandingRequests[key] = completer; |
| print('-> $key ($method)'); |
| return completer.future; |
| } |
| |
| String _createKey() { |
| final String key = '$_id'; |
| _id++; |
| return key; |
| } |
| |
| void _onData(dynamic message) { |
| final Map<String, dynamic> response = |
| json.decode(message as String) as Map<String, dynamic>; |
| final dynamic key = response['id']; |
| if (key != null) { |
| print('<- $key'); |
| final dynamic completer = _outstandingRequests.remove(key); |
| assert(completer != null); |
| final dynamic result = response['result']; |
| final dynamic error = response['error']; |
| if (error != null) { |
| assert(result == null); |
| completer.completeError(error); |
| } else { |
| assert(result != null); |
| completer.complete(result); |
| } |
| } else { |
| if (response['method'] == 'streamNotify') { |
| _onServiceEvent(response['params'] as Map<String, dynamic>?); |
| } |
| } |
| } |
| |
| void _onServiceEvent(Map<String, dynamic>? params) { |
| if (params == null) { |
| return; |
| } |
| final Map<String, dynamic>? event = |
| params['event'] as Map<String, dynamic>?; |
| if (event == null || event['type'] != 'Event') { |
| return; |
| } |
| final dynamic isolateId = event['isolate']['id']; |
| switch (params['streamId']) { |
| case 'Isolate': |
| if (event['kind'] == 'IsolateStart') { |
| isolateStartedId?.complete(isolateId); |
| } |
| break; |
| case 'Debug': |
| switch (event['kind']) { |
| case 'Resume': |
| isolateResumeId?.complete(isolateId); |
| break; |
| case 'PauseStart': |
| isolatePausedId?.complete(isolateId); |
| break; |
| } |
| break; |
| } |
| } |
| |
| void _onError(dynamic error) { |
| print('WebSocket error: $error'); |
| } |
| |
| final WebSocket client; |
| final Map<String, Completer<dynamic>> _outstandingRequests = |
| <String, Completer<dynamic>>{}; |
| int _id = 1; |
| } |