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 library json_rpc_2.client; | 5 library json_rpc_2.client; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'package:stack_trace/stack_trace.dart'; | 9 import 'package:stack_trace/stack_trace.dart'; |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 final TwoWayStream _streams; | 21 final TwoWayStream _streams; |
22 | 22 |
23 /// The next request id. | 23 /// The next request id. |
24 var _id = 0; | 24 var _id = 0; |
25 | 25 |
26 /// The current batch of requests to be sent together. | 26 /// The current batch of requests to be sent together. |
27 /// | 27 /// |
28 /// Each element is a JSON-serializable object. | 28 /// Each element is a JSON-serializable object. |
29 List _batch; | 29 List _batch; |
30 | 30 |
31 /// The map of request ids for pending requests to [Completer]s that will be | 31 /// The map of request ids to pending requests. |
32 /// completed with those requests' responses. | 32 final _pendingRequests = new Map<int, _Request>(); |
33 final _pendingRequests = new Map<int, Completer>(); | |
34 | 33 |
35 /// Returns a [Future] that completes when the connection is closed. | 34 /// Returns a [Future] that completes when the connection is closed. |
36 /// | 35 /// |
37 /// This is the same future that's returned by [listen]. | 36 /// This is the same future that's returned by [listen]. |
38 Future get done => _streams.done; | 37 Future get done => _streams.done; |
39 | 38 |
40 /// Creates a [Client] that writes requests to [requests] and reads responses | 39 /// Creates a [Client] that writes requests to [requests] and reads responses |
41 /// from [responses]. | 40 /// from [responses]. |
42 /// | 41 /// |
43 /// If [responses] is a [StreamSink] as well as a [Stream] (for example, a | 42 /// If [responses] is a [StreamSink] as well as a [Stream] (for example, a |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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] |
91 /// describing the failure. | 90 /// describing the failure. |
92 Future sendRequest(String method, [parameters]) { | 91 Future sendRequest(String method, [parameters]) { |
93 var id = _id++; | 92 var id = _id++; |
94 _send(method, parameters, id); | 93 _send(method, parameters, id); |
95 | 94 |
96 var completer = new Completer.sync(); | 95 var completer = new Completer.sync(); |
97 _pendingRequests[id] = completer; | 96 _pendingRequests[id] = new _Request(completer, new Chain.current()); |
98 return completer.future; | 97 return completer.future; |
99 } | 98 } |
100 | 99 |
101 /// Sends a JSON-RPC 2 request to invoke the given [method] without expecting | 100 /// Sends a JSON-RPC 2 request to invoke the given [method] without expecting |
102 /// a response. | 101 /// a response. |
103 /// | 102 /// |
104 /// If passed, [parameters] is the parameters for the method. This must be | 103 /// If passed, [parameters] is the parameters for the method. This must be |
105 /// either an [Iterable] (to pass parameters by position) or a [Map] with | 104 /// either an [Iterable] (to pass parameters by position) or a [Map] with |
106 /// [String] keys (to pass parameters by name). Either way, it must be | 105 /// [String] keys (to pass parameters by name). Either way, it must be |
107 /// JSON-serializable. | 106 /// JSON-serializable. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 response.forEach(_handleSingleResponse); | 163 response.forEach(_handleSingleResponse); |
165 } else { | 164 } else { |
166 _handleSingleResponse(response); | 165 _handleSingleResponse(response); |
167 } | 166 } |
168 } | 167 } |
169 | 168 |
170 /// Handles a decoded response from the server after batches have been | 169 /// Handles a decoded response from the server after batches have been |
171 /// resolved. | 170 /// resolved. |
172 void _handleSingleResponse(response) { | 171 void _handleSingleResponse(response) { |
173 if (!_isResponseValid(response)) return; | 172 if (!_isResponseValid(response)) return; |
174 var completer = _pendingRequests.remove(response["id"]); | 173 var request = _pendingRequests.remove(response["id"]); |
175 if (response.containsKey("result")) { | 174 if (response.containsKey("result")) { |
176 completer.complete(response["result"]); | 175 request.completer.complete(response["result"]); |
177 } else { | 176 } else { |
178 completer.completeError(new RpcException( | 177 request.completer.completeError(new RpcException( |
179 response["error"]["code"], | 178 response["error"]["code"], |
180 response["error"]["message"], | 179 response["error"]["message"], |
181 data: response["error"]["data"]), | 180 data: response["error"]["data"]), |
182 new Chain.current()); | 181 request.chain); |
183 } | 182 } |
184 } | 183 } |
185 | 184 |
186 /// Determines whether the server's response is valid per the spec. | 185 /// Determines whether the server's response is valid per the spec. |
187 bool _isResponseValid(response) { | 186 bool _isResponseValid(response) { |
188 if (response is! Map) return false; | 187 if (response is! Map) return false; |
189 if (response["jsonrpc"] != "2.0") return false; | 188 if (response["jsonrpc"] != "2.0") return false; |
190 if (!_pendingRequests.containsKey(response["id"])) return false; | 189 if (!_pendingRequests.containsKey(response["id"])) return false; |
191 if (response.containsKey("result")) return true; | 190 if (response.containsKey("result")) return true; |
192 | 191 |
193 if (!response.containsKey("error")) return false; | 192 if (!response.containsKey("error")) return false; |
194 var error = response["error"]; | 193 var error = response["error"]; |
195 if (error is! Map) return false; | 194 if (error is! Map) return false; |
196 if (error["code"] is! int) return false; | 195 if (error["code"] is! int) return false; |
197 if (error["message"] is! String) return false; | 196 if (error["message"] is! String) return false; |
198 return true; | 197 return true; |
199 } | 198 } |
200 } | 199 } |
| 200 |
| 201 /// A pending request to the server. |
| 202 class _Request { |
| 203 /// The completer to use to complete the response future. |
| 204 final Completer completer; |
| 205 |
| 206 /// The stack chain from where the request was made. |
| 207 final Chain chain; |
| 208 |
| 209 _Request(this.completer, this.chain); |
| 210 } |
OLD | NEW |