Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:async'; | 5 import 'dart:async'; |
| 6 | 6 |
| 7 import 'package:stack_trace/stack_trace.dart'; | 7 import 'package:stack_trace/stack_trace.dart'; |
| 8 import 'package:stream_channel/stream_channel.dart'; | |
| 8 | 9 |
| 10 import 'channel_manager.dart'; | |
| 9 import 'exception.dart'; | 11 import 'exception.dart'; |
| 10 import 'two_way_stream.dart'; | |
| 11 import 'utils.dart'; | 12 import 'utils.dart'; |
| 12 | 13 |
| 13 /// A JSON-RPC 2.0 client. | 14 /// A JSON-RPC 2.0 client. |
| 14 /// | 15 /// |
| 15 /// A client calls methods on a server and handles the server's responses to | 16 /// A client calls methods on a server and handles the server's responses to |
| 16 /// those method calls. Methods can be called with [sendRequest], or with | 17 /// those method calls. Methods can be called with [sendRequest], or with |
| 17 /// [sendNotification] if no response is expected. | 18 /// [sendNotification] if no response is expected. |
| 18 class Client { | 19 class Client { |
| 19 final TwoWayStream _streams; | 20 final ChannelManager _manager; |
| 20 | 21 |
| 21 /// The next request id. | 22 /// The next request id. |
| 22 var _id = 0; | 23 var _id = 0; |
| 23 | 24 |
| 24 /// The current batch of requests to be sent together. | 25 /// The current batch of requests to be sent together. |
| 25 /// | 26 /// |
| 26 /// Each element is a JSON-serializable object. | 27 /// Each element is a JSON-serializable object. |
| 27 List _batch; | 28 List _batch; |
| 28 | 29 |
| 29 /// The map of request ids to pending requests. | 30 /// The map of request ids to pending requests. |
| 30 final _pendingRequests = new Map<int, _Request>(); | 31 final _pendingRequests = new Map<int, _Request>(); |
| 31 | 32 |
| 32 /// Returns a [Future] that completes when the connection is closed. | 33 /// Returns a [Future] that completes when the underlying connection is |
| 34 /// closed. | |
| 33 /// | 35 /// |
| 34 /// This is the same future that's returned by [listen]. | 36 /// This is the same future that's returned by [listen] and [close]. It may |
| 35 Future get done => _streams.done; | 37 /// fire before [close] is called if the remote endpoint closes the |
|
Bob Nystrom
2016/02/02 17:54:15
fire -> complete
| |
| 38 /// connection. | |
| 39 Future get done => _manager.done; | |
| 36 | 40 |
| 37 /// Whether the connection is closed. | 41 /// Whether the underlying connection is closed. |
| 38 bool get isClosed => _streams.isClosed; | 42 /// |
| 43 /// Note that this will be `true` before [close] is called if the remote | |
| 44 /// endpoint closes the connection. | |
| 45 bool get isClosed => _manager.isClosed; | |
| 39 | 46 |
| 40 /// Creates a [Client] that writes requests to [requests] and reads responses | 47 /// Creates a [Client] that communicates over [channel]. |
| 41 /// from [responses]. | |
| 42 /// | |
| 43 /// If [responses] is a [StreamSink] as well as a [Stream] (for example, a | |
| 44 /// `WebSocket`), [requests] may be omitted. | |
| 45 /// | 48 /// |
| 46 /// Note that the client won't begin listening to [responses] until | 49 /// Note that the client won't begin listening to [responses] until |
| 47 /// [Client.listen] is called. | 50 /// [Client.listen] is called. |
| 48 Client(Stream<String> responses, [StreamSink<String> requests]) | 51 Client(StreamChannel<String> channel) |
| 49 : _streams = new TwoWayStream( | 52 : this.withoutJson(channel |
| 50 "Client", responses, "responses", requests, "requests"); | 53 .transform(jsonDocument) |
| 54 .transformStream(ignoreFormatExceptions)); | |
| 51 | 55 |
| 52 /// Creates a [Client] that writes decoded responses to [responses] and reads | 56 /// Creates a [Client] that communicates using decoded messages over |
| 53 /// decoded requests from [requests]. | 57 /// [channel]. |
| 54 /// | 58 /// |
| 55 /// Unlike [new Client], this doesn't read or write JSON strings. Instead, it | 59 /// Unlike [new Client], this doesn't read or write JSON strings. Instead, it |
| 56 /// reads and writes decoded maps or lists. | 60 /// reads and writes decoded maps or lists. |
| 57 /// | 61 /// |
| 58 /// If [responses] is a [StreamSink] as well as a [Stream], [requests] may be | |
| 59 /// omitted. | |
| 60 /// | |
| 61 /// Note that the client won't begin listening to [responses] until | 62 /// Note that the client won't begin listening to [responses] until |
| 62 /// [Client.listen] is called. | 63 /// [Client.listen] is called. |
| 63 Client.withoutJson(Stream responses, [StreamSink requests]) | 64 Client.withoutJson(StreamChannel channel) |
| 64 : _streams = new TwoWayStream.withoutJson( | 65 : _manager = new ChannelManager("Client", channel); |
| 65 "Client", responses, "responses", requests, "requests"); | |
| 66 | 66 |
| 67 /// Starts listening to the underlying stream. | 67 /// Starts listening to the underlying stream. |
| 68 /// | 68 /// |
| 69 /// Returns a [Future] that will complete when the stream is closed or when it | 69 /// Returns a [Future] that will complete when the connection is closed or |
| 70 /// has an error. | 70 /// when it has an error. This is the same as [done]. |
| 71 /// | 71 /// |
| 72 /// [listen] may only be called once. | 72 /// [listen] may only be called once. |
| 73 Future listen() => _streams.listen(_handleResponse); | 73 Future listen() => _manager.listen(_handleResponse); |
| 74 | 74 |
| 75 /// Closes the server's request sink and response subscription. | 75 /// Closes the underlying connection. |
| 76 /// | 76 /// |
| 77 /// Returns a [Future] that completes when all resources have been released. | 77 /// Returns a [Future] that completes when all resources have been released. |
| 78 /// | 78 /// This is the same as [done]. |
| 79 /// A client can't be closed before [listen] has been called. | 79 Future close() => _manager.close(); |
| 80 Future close() => _streams.close(); | |
| 81 | 80 |
| 82 /// Sends a JSON-RPC 2 request to invoke the given [method]. | 81 /// Sends a JSON-RPC 2 request to invoke the given [method]. |
| 83 /// | 82 /// |
| 84 /// If passed, [parameters] is the parameters for the method. This must be | 83 /// If passed, [parameters] is the parameters for the method. This must be |
| 85 /// either an [Iterable] (to pass parameters by position) or a [Map] with | 84 /// either an [Iterable] (to pass parameters by position) or a [Map] with |
| 86 /// [String] keys (to pass parameters by name). Either way, it must be | 85 /// [String] keys (to pass parameters by name). Either way, it must be |
| 87 /// JSON-serializable. | 86 /// JSON-serializable. |
| 88 /// | 87 /// |
| 89 /// If the request succeeds, this returns the response result as a decoded | 88 /// If the request succeeds, this returns the response result as a decoded |
| 90 /// JSON-serializable object. If it fails, it throws an [RpcException] | 89 /// JSON-serializable object. If it fails, it throws an [RpcException] |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 var message = { | 124 var message = { |
| 126 "jsonrpc": "2.0", | 125 "jsonrpc": "2.0", |
| 127 "method": method | 126 "method": method |
| 128 }; | 127 }; |
| 129 if (id != null) message["id"] = id; | 128 if (id != null) message["id"] = id; |
| 130 if (parameters != null) message["params"] = parameters; | 129 if (parameters != null) message["params"] = parameters; |
| 131 | 130 |
| 132 if (_batch != null) { | 131 if (_batch != null) { |
| 133 _batch.add(message); | 132 _batch.add(message); |
| 134 } else { | 133 } else { |
| 135 _streams.add(message); | 134 _manager.add(message); |
| 136 } | 135 } |
| 137 } | 136 } |
| 138 | 137 |
| 139 /// Runs [callback] and batches any requests sent until it returns. | 138 /// Runs [callback] and batches any requests sent until it returns. |
| 140 /// | 139 /// |
| 141 /// A batch of requests is sent in a single message on the underlying stream, | 140 /// A batch of requests is sent in a single message on the underlying stream, |
| 142 /// and the responses are likewise sent back in a single message. | 141 /// and the responses are likewise sent back in a single message. |
| 143 /// | 142 /// |
| 144 /// [callback] may be synchronous or asynchronous. If it returns a [Future], | 143 /// [callback] may be synchronous or asynchronous. If it returns a [Future], |
| 145 /// requests will be batched until that Future returns; otherwise, requests | 144 /// requests will be batched until that Future returns; otherwise, requests |
| 146 /// will only be batched while synchronously executing [callback]. | 145 /// will only be batched while synchronously executing [callback]. |
| 147 /// | 146 /// |
| 148 /// If this is called in the context of another [withBatch] call, it just | 147 /// If this is called in the context of another [withBatch] call, it just |
| 149 /// invokes [callback] without creating another batch. This means that | 148 /// invokes [callback] without creating another batch. This means that |
| 150 /// responses are batched until the first batch ends. | 149 /// responses are batched until the first batch ends. |
| 151 withBatch(callback()) { | 150 withBatch(callback()) { |
| 152 if (_batch != null) return callback(); | 151 if (_batch != null) return callback(); |
| 153 | 152 |
| 154 _batch = []; | 153 _batch = []; |
| 155 return tryFinally(callback, () { | 154 return tryFinally(callback, () { |
| 156 _streams.add(_batch); | 155 _manager.add(_batch); |
| 157 _batch = null; | 156 _batch = null; |
| 158 }); | 157 }); |
| 159 } | 158 } |
| 160 | 159 |
| 161 /// Handles a decoded response from the server. | 160 /// Handles a decoded response from the server. |
| 162 void _handleResponse(response) { | 161 void _handleResponse(response) { |
| 163 if (response is List) { | 162 if (response is List) { |
| 164 response.forEach(_handleSingleResponse); | 163 response.forEach(_handleSingleResponse); |
| 165 } else { | 164 } else { |
| 166 _handleSingleResponse(response); | 165 _handleSingleResponse(response); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 /// A pending request to the server. | 201 /// A pending request to the server. |
| 203 class _Request { | 202 class _Request { |
| 204 /// The completer to use to complete the response future. | 203 /// The completer to use to complete the response future. |
| 205 final Completer completer; | 204 final Completer completer; |
| 206 | 205 |
| 207 /// The stack chain from where the request was made. | 206 /// The stack chain from where the request was made. |
| 208 final Chain chain; | 207 final Chain chain; |
| 209 | 208 |
| 210 _Request(this.completer, this.chain); | 209 _Request(this.completer, this.chain); |
| 211 } | 210 } |
| OLD | NEW |