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 |