| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | |
| // for details. 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:typed_data'; | |
| import 'package:async/async.dart'; | |
| import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc; | |
| import 'package:stream_channel/stream_channel.dart'; | |
| import 'package:web_socket_channel/web_socket_channel.dart'; | |
| import 'stream_manager.dart'; | |
| /// Adds support for binary events send from the VM service, which are not part | |
| /// of the official JSON RPC 2.0 specification. | |
| /// | |
| /// A binary event from the VM service has the form: | |
| /// ``` | |
| /// type BinaryEvent { | |
| /// dataOffset : uint32, | |
| /// metadata : uint8[dataOffset-4], | |
| /// data : uint8[], | |
| /// } | |
| /// ``` | |
| /// where `metadata` is the JSON body of the event. | |
| /// | |
| /// [BinaryCompatiblePeer] assumes that only stream events can contain a | |
| /// binary payload (e.g., clients cannot send a `BinaryEvent` to the VM service). | |
| class BinaryCompatiblePeer extends json_rpc.Peer { | |
| BinaryCompatiblePeer(WebSocketChannel ws, StreamManager streamManager) | |
| : super( | |
| ws.transform<String>( | |
| StreamChannelTransformer( | |
| StreamTransformer<dynamic, String>.fromHandlers( | |
| handleData: (data, EventSink<String> sink) => | |
| _transformStream(streamManager, data, sink)), | |
| StreamSinkTransformer<String, dynamic>.fromHandlers( | |
| handleData: (String data, EventSink<dynamic> sink) { | |
| sink.add(data); | |
| }, | |
| ), | |
| ), | |
| ), | |
| // Allow for requests without the jsonrpc parameter. | |
| strictProtocolChecks: false, | |
| ); | |
| static void _transformStream( | |
| StreamManager streamManager, dynamic data, EventSink<String> sink) { | |
| if (data is String) { | |
| // Non-binary messages come in as Strings. Simply forward to the sink. | |
| sink.add(data); | |
| } else if (data is Uint8List) { | |
| // Only binary events will result in `data` being of type Uint8List. We | |
| // need to manually forward them here. | |
| final bytesView = | |
| ByteData.view(data.buffer, data.offsetInBytes, data.lengthInBytes); | |
| const metadataOffset = 4; | |
| final dataOffset = bytesView.getUint32(0, Endian.little); | |
| final metadataLength = dataOffset - metadataOffset; | |
| final metadata = Utf8Decoder().convert(Uint8List.view(bytesView.buffer, | |
| bytesView.offsetInBytes + metadataOffset, metadataLength)); | |
| final decodedMetadata = json.decode(metadata); | |
| streamManager.streamNotify(decodedMetadata['params']['streamId'], data); | |
| } | |
| } | |
| } |