Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 2 : // for details. All rights reserved. Use of this source code is governed by a 3 : // BSD-style license that can be found in the LICENSE file. 4 : 5 : import 'dart:async'; 6 : 7 : import 'package:async/async.dart'; 8 : 9 : import '../stream_channel.dart'; 10 : 11 : /// A class that multiplexes multiple virtual channels across a single 12 : /// underlying transport layer. 13 : /// 14 : /// This should be connected to another [MultiChannel] on the other end of the 15 : /// underlying channel. It starts with a single default virtual channel, 16 : /// accessible via [stream] and [sink]. Additional virtual channels can be 17 : /// created with [virtualChannel]. 18 : /// 19 : /// When a virtual channel is created by one endpoint, the other must connect to 20 : /// it before messages may be sent through it. The first endpoint passes its 21 : /// [VirtualChannel.id] to the second, which then creates a channel from that id 22 : /// also using [virtualChannel]. For example: 23 : /// 24 : /// ```dart 25 : /// // First endpoint 26 : /// var virtual = multiChannel.virtualChannel(); 27 : /// multiChannel.sink.add({ 28 : /// "channel": virtual.id 29 : /// }); 30 : /// 31 : /// // Second endpoint 32 : /// multiChannel.stream.listen((message) { 33 : /// var virtual = multiChannel.virtualChannel(message["channel"]); 34 : /// // ... 35 : /// }); 36 : /// ``` 37 : /// 38 : /// Sending errors across a [MultiChannel] is not supported. Any errors from the 39 : /// underlying stream will be reported only via the default 40 : /// [MultiChannel.stream]. 41 : /// 42 : /// Each virtual channel may be closed individually. When all of them are 43 : /// closed, the underlying [StreamSink] is closed automatically. 44 : abstract class MultiChannel<T> implements StreamChannel<T> { 45 : /// The default input stream. 46 : /// 47 : /// This connects to the remote [sink]. 48 : @override 49 : Stream<T> get stream; 50 : 51 : /// The default output stream. 52 : /// 53 : /// This connects to the remote [stream]. If this is closed, the remote 54 : /// [stream] will close, but other virtual channels will remain open and new 55 : /// virtual channels may be opened. 56 : @override 57 : StreamSink<T> get sink; 58 : 59 : /// Creates a new [MultiChannel] that sends and receives messages over 60 : /// [inner]. 61 : /// 62 : /// The inner channel must take JSON-like objects. 63 22 : factory MultiChannel(StreamChannel<dynamic> inner) => _MultiChannel<T>(inner); 64 : 65 : /// Creates a new virtual channel. 66 : /// 67 : /// If [id] is not passed, this creates a virtual channel from scratch. Before 68 : /// it's used, its [VirtualChannel.id] must be sent to the remote endpoint 69 : /// where [virtualChannel] should be called with that id. 70 : /// 71 : /// If [id] is passed, this creates a virtual channel corresponding to the 72 : /// channel with that id on the remote channel. 73 : /// 74 : /// Throws an [ArgumentError] if a virtual channel already exists for [id]. 75 : /// Throws a [StateError] if the underlying channel is closed. 76 : VirtualChannel<T> virtualChannel([int? id]); 77 : } 78 : 79 : /// The implementation of [MultiChannel]. 80 : /// 81 : /// This is private so that [VirtualChannel] can inherit from [MultiChannel] 82 : /// without having to implement all the private members. 83 : class _MultiChannel<T> extends StreamChannelMixin<T> 84 : implements MultiChannel<T> { 85 : /// The inner channel over which all communication is conducted. 86 : /// 87 : /// This will be `null` if the underlying communication channel is closed. 88 : StreamChannel<dynamic>? _inner; 89 : 90 : /// The subscription to [_inner].stream. 91 : StreamSubscription<dynamic>? _innerStreamSubscription; 92 : 93 11 : @override 94 33 : Stream<T> get stream => _mainController.foreign.stream; 95 11 : @override 96 33 : StreamSink<T> get sink => _mainController.foreign.sink; 97 : 98 : /// The controller for this channel. 99 : final _mainController = StreamChannelController<T>(sync: true); 100 : 101 : /// A map from input IDs to [StreamChannelController]s that should be used to 102 : /// communicate over those channels. 103 : final _controllers = <int, StreamChannelController<T>>{}; 104 : 105 : /// Input IDs of controllers in [_controllers] that we've received messages 106 : /// for but that have not yet had a local [virtualChannel] created. 107 : final _pendingIds = <int>{}; 108 : 109 : /// Input IDs of virtual channels that used to exist but have since been 110 : /// closed. 111 : final _closedIds = <int>{}; 112 : 113 : /// The next id to use for a local virtual channel. 114 : /// 115 : /// Ids are used to identify virtual channels. Each message is tagged with an 116 : /// id; the receiving [MultiChannel] uses this id to look up which 117 : /// [VirtualChannel] the message should be dispatched to. 118 : /// 119 : /// The id scheme for virtual channels is somewhat complicated. This is 120 : /// necessary to ensure that there are no conflicts even when both endpoints 121 : /// have virtual channels with the same id; since both endpoints can send and 122 : /// receive messages across each virtual channel, a naïve scheme would make it 123 : /// impossible to tell whether a message was from a channel that originated in 124 : /// the remote endpoint or a reply on a channel that originated in the local 125 : /// endpoint. 126 : /// 127 : /// The trick is that each endpoint only uses odd ids for its own channels. 128 : /// When sending a message over a channel that was created by the remote 129 : /// endpoint, the channel's id plus one is used. This way each [MultiChannel] 130 : /// knows that if an incoming message has an odd id, it's coming from a 131 : /// channel that was originally created remotely, but if it has an even id, 132 : /// it's coming from a channel that was originally created locally. 133 : var _nextId = 1; 134 : 135 11 : _MultiChannel(StreamChannel<dynamic> inner) : _inner = inner { 136 : // The default connection is a special case which has id 0 on both ends. 137 : // This allows it to begin connected without having to send over an id. 138 33 : _controllers[0] = _mainController; 139 44 : _mainController.local.stream.listen( 140 55 : (message) => _inner!.sink.add(<Object?>[0, message]), 141 0 : onDone: () => _closeChannel(0, 0)); 142 : 143 66 : _innerStreamSubscription = _inner!.stream.cast<List>().listen((message) { 144 11 : var id = message[0] as int; 145 : 146 : // If the channel was closed before an incoming message was processed, 147 : // ignore that message. 148 22 : if (_closedIds.contains(id)) return; 149 : 150 22 : var controller = _controllers.putIfAbsent(id, () { 151 : // If we receive a message for a controller that doesn't have a local 152 : // counterpart yet, create a controller for it to buffer incoming 153 : // messages for when a local connection is created. 154 0 : _pendingIds.add(id); 155 0 : return StreamChannelController(sync: true); 156 : }); 157 : 158 22 : if (message.length > 1) { 159 44 : controller.local.sink.add(message[1] as T); 160 : } else { 161 : // A message without data indicates that the channel has been closed. We 162 : // can just close the sink here without doing any more cleanup, because 163 : // the sink closing will cause the stream to emit a done event which 164 : // will trigger more cleanup. 165 0 : controller.local.sink.close(); 166 : } 167 : }, 168 11 : onDone: _closeInnerChannel, 169 44 : onError: _mainController.local.sink.addError); 170 : } 171 : 172 11 : @override 173 : VirtualChannel<T> virtualChannel([int? id]) { 174 : int inputId; 175 : int outputId; 176 : if (id != null) { 177 : // Since the user is passing in an id, we're connected to a remote 178 : // VirtualChannel. This means messages they send over this channel will 179 : // have the original odd id, but our replies will have an even id. 180 : inputId = id; 181 11 : outputId = id + 1; 182 : } else { 183 : // Since we're generating an id, we originated this VirtualChannel. This 184 : // means messages we send over this channel will have the original odd id, 185 : // but the remote channel's replies will have an even id. 186 22 : inputId = _nextId + 1; 187 11 : outputId = _nextId; 188 22 : _nextId += 2; 189 : } 190 : 191 : // If the inner channel has already closed, create new virtual channels in a 192 : // closed state. 193 11 : if (_inner == null) { 194 0 : return VirtualChannel._(this, inputId, Stream.empty(), NullStreamSink()); 195 : } 196 : 197 : late StreamChannelController<T> controller; 198 22 : if (_pendingIds.remove(inputId)) { 199 : // If we've already received messages for this channel, use the controller 200 : // where those messages are buffered. 201 0 : controller = _controllers[inputId]!; 202 22 : } else if (_controllers.containsKey(inputId) || 203 22 : _closedIds.contains(inputId)) { 204 0 : throw ArgumentError('A virtual channel with id $id already exists.'); 205 : } else { 206 11 : controller = StreamChannelController(sync: true); 207 22 : _controllers[inputId] = controller; 208 : } 209 : 210 33 : controller.local.stream.listen( 211 55 : (message) => _inner!.sink.add(<Object?>[outputId, message]), 212 0 : onDone: () => _closeChannel(inputId, outputId)); 213 11 : return VirtualChannel._( 214 44 : this, outputId, controller.foreign.stream, controller.foreign.sink); 215 : } 216 : 217 : /// Closes the virtual channel for which incoming messages have [inputId] and 218 : /// outgoing messages have [outputId]. 219 0 : void _closeChannel(int inputId, int outputId) { 220 0 : _closedIds.add(inputId); 221 0 : var controller = _controllers.remove(inputId)!; 222 0 : controller.local.sink.close(); 223 : 224 0 : if (_inner == null) return; 225 : 226 : // A message without data indicates that the virtual channel has been 227 : // closed. 228 0 : _inner!.sink.add([outputId]); 229 0 : if (_controllers.isEmpty) _closeInnerChannel(); 230 : } 231 : 232 : /// Closes the underlying communication channel. 233 0 : void _closeInnerChannel() { 234 0 : _inner!.sink.close(); 235 0 : _innerStreamSubscription!.cancel(); 236 0 : _inner = null; 237 : 238 : // Convert this to a list because the close is dispatched synchronously, and 239 : // that could conceivably remove a controller from [_controllers]. 240 0 : for (var controller in List.from(_controllers.values)) { 241 0 : controller.local.sink.close(); 242 : } 243 0 : _controllers.clear(); 244 : } 245 : } 246 : 247 : /// A virtual channel created by [MultiChannel]. 248 : /// 249 : /// This implements [MultiChannel] for convenience. 250 : /// [VirtualChannel.virtualChannel] is semantically identical to the parent's 251 : /// [MultiChannel.virtualChannel]. 252 : class VirtualChannel<T> extends StreamChannelMixin<T> 253 : implements MultiChannel<T> { 254 : /// The [MultiChannel] that created this. 255 : final MultiChannel<T> _parent; 256 : 257 : /// The identifier for this channel. 258 : /// 259 : /// This can be sent across the [MultiChannel] to provide the remote endpoint 260 : /// a means to connect to this channel. Nothing about this is guaranteed 261 : /// except that it will be JSON-serializable. 262 : final int id; 263 : 264 : @override 265 : final Stream<T> stream; 266 : @override 267 : final StreamSink<T> sink; 268 : 269 11 : VirtualChannel._(this._parent, this.id, this.stream, this.sink); 270 : 271 0 : @override 272 0 : VirtualChannel<T> virtualChannel([id]) => _parent.virtualChannel(id); 273 : }