Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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