Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkgs/json_rpc_2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.0.0-tentative

* Enable tests for Web-platforms.
* Added a test for peer failure in "like a client" scenario. Without this fix, `peer.sendRequest()` never completes and the test times out.
* The fix consists in forwarding errors to both the client and the server.

## 4.0.0

* Add custom ID generator option to clients, which allows for `String` ids.
Expand Down
7 changes: 7 additions & 0 deletions pkgs/json_rpc_2/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
platforms:
- vm
- chrome

compilers:
- dart2js
- dart2wasm
81 changes: 44 additions & 37 deletions pkgs/json_rpc_2/lib/src/peer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ class Peer implements Client, Server {
bool strictProtocolChecks = true,
Object Function()? idGenerator,
}) : this.withoutJson(
jsonDocument.bind(channel).transform(respondToFormatExceptions),
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks,
idGenerator: idGenerator);
jsonDocument.bind(channel).transform(respondToFormatExceptions),
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks,
idGenerator: idGenerator,
);

/// Creates a [Peer] that communicates using decoded messages over [_channel].
///
Expand All @@ -93,20 +94,21 @@ class Peer implements Client, Server {
/// If [idGenerator] is passed, it will be called to generate an ID for each
/// request. Defaults to an auto-incrementing `int`. The value returned must
/// be either an `int` or `String`.
Peer.withoutJson(this._channel,
{ErrorCallback? onUnhandledError,
bool strictProtocolChecks = true,
Object Function()? idGenerator}) {
Peer.withoutJson(
this._channel, {
ErrorCallback? onUnhandledError,
bool strictProtocolChecks = true,
Object Function()? idGenerator,
}) {
_server = Server.withoutJson(
StreamChannel(_serverIncomingForwarder.stream, _channel.sink),
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks);
StreamChannel(_serverIncomingForwarder.stream, _channel.sink),
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks,
);
_client = Client.withoutJson(
StreamChannel(
_clientIncomingForwarder.stream,
_channel.sink,
),
idGenerator: idGenerator);
StreamChannel(_clientIncomingForwarder.stream, _channel.sink),
idGenerator: idGenerator,
);
}

// Client methods.
Expand Down Expand Up @@ -138,30 +140,35 @@ class Peer implements Client, Server {
Future listen() {
_client.listen();
_server.listen();
_channel.stream.listen((message) {
if (message is Map) {
if (message.containsKey('result') || message.containsKey('error')) {
_clientIncomingForwarder.add(message);
} else {
_serverIncomingForwarder.add(message);
}
} else if (message is List &&
message.isNotEmpty &&
message.first is Map) {
if (message.first.containsKey('result') ||
message.first.containsKey('error')) {
_clientIncomingForwarder.add(message);
_channel.stream.listen(
(message) {
if (message is Map) {
if (message.containsKey('result') || message.containsKey('error')) {
_clientIncomingForwarder.add(message);
} else {
_serverIncomingForwarder.add(message);
}
} else if (message is List &&
message.isNotEmpty &&
message.first is Map) {
if (message.first.containsKey('result') ||
message.first.containsKey('error')) {
_clientIncomingForwarder.add(message);
} else {
_serverIncomingForwarder.add(message);
}
} else {
// Non-Map and -List messages are ill-formed, so we pass them to the
// server since it knows how to send error responses.
_serverIncomingForwarder.add(message);
}
} else {
// Non-Map and -List messages are ill-formed, so we pass them to the
// server since it knows how to send error responses.
_serverIncomingForwarder.add(message);
}
}, onError: (Object error, StackTrace stackTrace) {
_serverIncomingForwarder.addError(error, stackTrace);
}, onDone: close);
},
onError: (Object error, StackTrace stackTrace) {
_serverIncomingForwarder.addError(error, stackTrace);
_clientIncomingForwarder.addError(error, stackTrace);
},
onDone: close,
);
return done;
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/json_rpc_2/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_rpc_2
version: 4.0.0
version: 4.0.0-tentative
description: >-
Utilities to write a client or server using the JSON-RPC 2.0 spec.
repository: https://github.com/dart-lang/tools/tree/main/pkgs/json_rpc_2
Expand Down
21 changes: 21 additions & 0 deletions pkgs/json_rpc_2/test/peer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ void main() {
peer.sendRequest('foo', {'bar': 'baz'}), completion(equals('qux')));
});

test('can send a message and receive an error', () {
expect(outgoing.first.then((request) {
expect(
request,
equals({
'jsonrpc': '2.0',
'method': 'foo',
'params': {'bar': 'baz'},
'id': 0
}));
incoming.addError(Exception('failure'));
}), completes);

peer.listen().ignore();

expect(
peer.sendRequest('foo', {'bar': 'baz'}),
throwsA(isA<StateError>().having(($) => $.message, 'message',
contains('client closed with pending request "foo"'))));
});

test('can send a batch of messages and receive a batch of responses', () {
expect(outgoing.first.then((request) {
expect(
Expand Down