Merge pull request dart-lang/json_rpc_2#28 from bcko/patch-1
Update .gitignore to new `dart_tool` pub cache
diff --git a/pkgs/json_rpc_2/CHANGELOG.md b/pkgs/json_rpc_2/CHANGELOG.md
index 3e2d662..878e1f0 100644
--- a/pkgs/json_rpc_2/CHANGELOG.md
+++ b/pkgs/json_rpc_2/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 2.1.0
+
+* `Server` and related classes can now take an `onUnhandledError` callback to
+ notify callers of unhandled exceptions.
+
+## 2.0.10
+
+* Allow `stream_channel` version 2.x
+
## 2.0.8
* Updated SDK version to 2.0.0-dev.17.0
diff --git a/pkgs/json_rpc_2/README.md b/pkgs/json_rpc_2/README.md
index e495264..0567d20 100644
--- a/pkgs/json_rpc_2/README.md
+++ b/pkgs/json_rpc_2/README.md
@@ -14,7 +14,11 @@
main() async {
var socket = IOWebSocketChannel.connect('ws://localhost:4321');
- var server = new json_rpc.Server(socket);
+
+ // The socket is a StreamChannel<dynamic> because it might emit binary
+ // List<int>s, but JSON RPC 2 only works with Strings so we assert it only
+ // emits those by casting it.
+ var server = new json_rpc.Server(socket.cast<String>());
// Any string may be used as a method name. JSON-RPC 2.0 methods are
// case-sensitive.
diff --git a/pkgs/json_rpc_2/analysis_options.yaml b/pkgs/json_rpc_2/analysis_options.yaml
deleted file mode 100644
index a10d4c5..0000000
--- a/pkgs/json_rpc_2/analysis_options.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-analyzer:
- strong-mode: true
diff --git a/pkgs/json_rpc_2/lib/src/peer.dart b/pkgs/json_rpc_2/lib/src/peer.dart
index 18cac07..7176763 100644
--- a/pkgs/json_rpc_2/lib/src/peer.dart
+++ b/pkgs/json_rpc_2/lib/src/peer.dart
@@ -39,13 +39,20 @@
Future get done => _manager.done;
bool get isClosed => _manager.isClosed;
+ @override
+ ErrorCallback get onUnhandledError => _server?.onUnhandledError;
+
/// Creates a [Peer] that communicates over [channel].
///
/// Note that the peer won't begin listening to [channel] until [Peer.listen]
/// is called.
- Peer(StreamChannel<String> channel)
+ ///
+ /// Unhandled exceptions in callbacks will be forwarded to [onUnhandledError].
+ /// If this is not provided, unhandled exceptions will be swallowed.
+ Peer(StreamChannel<String> channel, {ErrorCallback onUnhandledError})
: this.withoutJson(
- jsonDocument.bind(channel).transform(respondToFormatExceptions));
+ jsonDocument.bind(channel).transform(respondToFormatExceptions),
+ onUnhandledError: onUnhandledError);
/// Creates a [Peer] that communicates using decoded messages over [channel].
///
@@ -54,10 +61,14 @@
///
/// Note that the peer won't begin listening to [channel] until
/// [Peer.listen] is called.
- Peer.withoutJson(StreamChannel channel)
+ ///
+ /// Unhandled exceptions in callbacks will be forwarded to [onUnhandledError].
+ /// If this is not provided, unhandled exceptions will be swallowed.
+ Peer.withoutJson(StreamChannel channel, {ErrorCallback onUnhandledError})
: _manager = new ChannelManager("Peer", channel) {
_server = new Server.withoutJson(
- new StreamChannel(_serverIncomingForwarder.stream, channel.sink));
+ new StreamChannel(_serverIncomingForwarder.stream, channel.sink),
+ onUnhandledError: onUnhandledError);
_client = new Client.withoutJson(
new StreamChannel(_clientIncomingForwarder.stream, channel.sink));
}
diff --git a/pkgs/json_rpc_2/lib/src/server.dart b/pkgs/json_rpc_2/lib/src/server.dart
index 0aa4c2f..5c3b132 100644
--- a/pkgs/json_rpc_2/lib/src/server.dart
+++ b/pkgs/json_rpc_2/lib/src/server.dart
@@ -15,6 +15,9 @@
import 'parameters.dart';
import 'utils.dart';
+/// A callback for unhandled exceptions.
+typedef ErrorCallback = void Function(dynamic error, dynamic stackTrace);
+
/// A JSON-RPC 2.0 server.
///
/// A server exposes methods that are called by requests, to which it provides
@@ -51,13 +54,24 @@
/// endpoint closes the connection.
bool get isClosed => _manager.isClosed;
+ /// A callback that is fired on unhandled exceptions.
+ ///
+ /// In the case where a user provided callback results in an exception that
+ /// cannot be properly routed back to the client, this handler will be
+ /// invoked. If it is not set, the exception will be swallowed.
+ final ErrorCallback onUnhandledError;
+
/// Creates a [Server] that communicates over [channel].
///
/// Note that the server won't begin listening to [requests] until
/// [Server.listen] is called.
- Server(StreamChannel<String> channel)
+ ///
+ /// Unhandled exceptions in callbacks will be forwarded to [onUnhandledError].
+ /// If this is not provided, unhandled exceptions will be swallowed.
+ Server(StreamChannel<String> channel, {ErrorCallback onUnhandledError})
: this.withoutJson(
- jsonDocument.bind(channel).transform(respondToFormatExceptions));
+ jsonDocument.bind(channel).transform(respondToFormatExceptions),
+ onUnhandledError: onUnhandledError);
/// Creates a [Server] that communicates using decoded messages over
/// [channel].
@@ -67,7 +81,10 @@
///
/// Note that the server won't begin listening to [requests] until
/// [Server.listen] is called.
- Server.withoutJson(StreamChannel channel)
+ ///
+ /// Unhandled exceptions in callbacks will be forwarded to [onUnhandledError].
+ /// If this is not provided, unhandled exceptions will be swallowed.
+ Server.withoutJson(StreamChannel channel, {this.onUnhandledError})
: _manager = new ChannelManager("Server", channel);
/// Starts listening to the underlying stream.
@@ -175,9 +192,11 @@
request.containsKey('id')) {
return error.serialize(request);
} else {
+ onUnhandledError?.call(error, stackTrace);
return null;
}
} else if (!request.containsKey('id')) {
+ onUnhandledError?.call(error, stackTrace);
return null;
}
final chain = new Chain.forTrace(stackTrace);
diff --git a/pkgs/json_rpc_2/pubspec.yaml b/pkgs/json_rpc_2/pubspec.yaml
index 77b52c0..40c0293 100644
--- a/pkgs/json_rpc_2/pubspec.yaml
+++ b/pkgs/json_rpc_2/pubspec.yaml
@@ -1,12 +1,16 @@
name: json_rpc_2
-version: 2.0.8
+version: 2.1.0
author: Dart Team <misc@dartlang.org>
-description: An implementation of the JSON-RPC 2.0 spec.
-homepage: http://github.com/dart-lang/json_rpc_2
-dependencies:
- stack_trace: '>=0.9.1 <2.0.0'
- stream_channel: '^1.1.0'
-dev_dependencies:
- test: "^0.12.28"
+description: >-
+ Utilities to write a client or server using the JSON-RPC 2.0 spec.
+homepage: https://github.com/dart-lang/json_rpc_2
+
environment:
- sdk: ">=2.0.0-dev.17.0 <2.0.0"
+ sdk: ">=2.0.0 <3.0.0"
+
+dependencies:
+ stack_trace: ^1.0.0
+ stream_channel: ">=1.1.0 <3.0.0"
+
+dev_dependencies:
+ test: ^1.0.0
diff --git a/pkgs/json_rpc_2/test/client/client_test.dart b/pkgs/json_rpc_2/test/client/client_test.dart
index 1648df0..358247d 100644
--- a/pkgs/json_rpc_2/test/client/client_test.dart
+++ b/pkgs/json_rpc_2/test/client/client_test.dart
@@ -69,7 +69,7 @@
test("sends a synchronous batch of requests", () {
controller.expectRequest((request) {
- expect(request, new isInstanceOf<List>());
+ expect(request, new TypeMatcher<List>());
expect(request, hasLength(3));
expect(request[0], equals({'jsonrpc': '2.0', 'method': 'foo'}));
expect(
@@ -101,7 +101,7 @@
test("sends an asynchronous batch of requests", () {
controller.expectRequest((request) {
- expect(request, new isInstanceOf<List>());
+ expect(request, new TypeMatcher<List>());
expect(request, hasLength(3));
expect(request[0], equals({'jsonrpc': '2.0', 'method': 'foo'}));
expect(
@@ -157,7 +157,7 @@
expect(controller.client.sendRequest("foo", {'param': 'value'}),
throwsA(predicate((exception) {
- expect(exception, new isInstanceOf<json_rpc.RpcException>());
+ expect(exception, new TypeMatcher<json_rpc.RpcException>());
expect(exception.code, equals(error_code.SERVER_ERROR));
expect(exception.message, equals('you are bad at requests'));
expect(exception.data, equals('some junk'));
diff --git a/pkgs/json_rpc_2/test/peer_test.dart b/pkgs/json_rpc_2/test/peer_test.dart
index 94c49e6..491526f 100644
--- a/pkgs/json_rpc_2/test/peer_test.dart
+++ b/pkgs/json_rpc_2/test/peer_test.dart
@@ -184,4 +184,24 @@
incoming.add({"completely": "wrong"});
});
});
+
+ test("can notify on unhandled errors for if the method throws", () async {
+ Exception exception = Exception('test exception');
+ var incomingController = new StreamController();
+ var outgoingController = new StreamController();
+ final Completer<Exception> completer = Completer<Exception>();
+ peer = new json_rpc.Peer.withoutJson(
+ new StreamChannel(incomingController.stream, outgoingController),
+ onUnhandledError: (error, stack) {
+ completer.complete(error);
+ },
+ );
+ peer
+ ..registerMethod('foo', () => throw exception)
+ ..listen();
+
+ incomingController.add({'jsonrpc': '2.0', 'method': 'foo'});
+ Exception receivedException = await completer.future;
+ expect(receivedException, equals(exception));
+ });
}
diff --git a/pkgs/json_rpc_2/test/server/server_test.dart b/pkgs/json_rpc_2/test/server/server_test.dart
index c2f87ab..b9ba504 100644
--- a/pkgs/json_rpc_2/test/server/server_test.dart
+++ b/pkgs/json_rpc_2/test/server/server_test.dart
@@ -70,7 +70,7 @@
'data': {
'request': {'jsonrpc': '2.0', 'method': 'foo', 'id': 1234},
'full': 'FormatException: bad format',
- 'stack': new isInstanceOf<String>()
+ 'stack': new TypeMatcher<String>()
}
}
}));
@@ -168,7 +168,7 @@
'data': {
'request': {'jsonrpc': '2.0', 'method': 'foo', 'id': 1234},
'full': 'FormatException: bad format',
- 'stack': new isInstanceOf<String>()
+ 'stack': new TypeMatcher<String>()
}
}
}));
diff --git a/pkgs/json_rpc_2/test/server/utils.dart b/pkgs/json_rpc_2/test/server/utils.dart
index f383f89..ef089ad 100644
--- a/pkgs/json_rpc_2/test/server/utils.dart
+++ b/pkgs/json_rpc_2/test/server/utils.dart
@@ -23,9 +23,10 @@
json_rpc.Server get server => _server;
json_rpc.Server _server;
- ServerController() {
+ ServerController({json_rpc.ErrorCallback onUnhandledError}) {
_server = new json_rpc.Server(
- new StreamChannel(_requestController.stream, _responseController.sink));
+ new StreamChannel(_requestController.stream, _responseController.sink),
+ onUnhandledError: onUnhandledError);
_server.listen();
}
@@ -64,7 +65,7 @@
/// `invalid_params` error code.
Matcher throwsInvalidParams(String message) {
return throwsA(predicate((error) {
- expect(error, new isInstanceOf<json_rpc.RpcException>());
+ expect(error, new TypeMatcher<json_rpc.RpcException>());
expect(error.code, equals(error_code.INVALID_PARAMS));
expect(error.message, equals(message));
return true;