Skip to content

Commit

Permalink
Added strictProtocolChecks named parameter to Peer and Server c…
Browse files Browse the repository at this point in the history
…onstructors (#51)

Allows creating servers which are lenient towards misbehaving clients.
  • Loading branch information
bkonyi committed May 18, 2020
1 parent eec1081 commit d589e63
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 18 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2.2.0

* Added `strictProtocolChecks` named parameter to `Server` and `Peer`
constructors. Setting this parameter to false will result in the server not
rejecting requests missing the `jsonrpc` parameter.

## 2.1.1

* Fixed issue where throwing `RpcException.methodNotFound` in an asynchronous
Expand Down
25 changes: 21 additions & 4 deletions lib/src/peer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,27 @@ class Peer implements Client, Server {
@override
ErrorCallback get onUnhandledError => _server?.onUnhandledError;

@override
bool get strictProtocolChecks => _server.strictProtocolChecks;

/// Creates a [Peer] that communicates over [channel].
///
/// Note that the peer won't begin listening to [channel] until [Peer.listen]
/// is called.
///
/// 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})
///
/// If [strictProtocolChecks] is false, the underlying [Server] will accept
/// some requests which are not conformant with the JSON-RPC 2.0
/// specification. In particular, requests missing the `jsonrpc` parameter
/// will be accepted.
Peer(StreamChannel<String> channel,
{ErrorCallback onUnhandledError, bool strictProtocolChecks = true})
: this.withoutJson(
jsonDocument.bind(channel).transform(respondToFormatExceptions),
onUnhandledError: onUnhandledError);
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks);

/// Creates a [Peer] that communicates using decoded messages over [channel].
///
Expand All @@ -66,11 +76,18 @@ class Peer implements Client, Server {
///
/// 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})
///
/// If [strictProtocolChecks] is false, the underlying [Server] will accept
/// some requests which are not conformant with the JSON-RPC 2.0
/// specification. In particular, requests missing the `jsonrpc` parameter
/// will be accepted.
Peer.withoutJson(StreamChannel channel,
{ErrorCallback onUnhandledError, bool strictProtocolChecks = true})
: _manager = ChannelManager('Peer', channel) {
_server = Server.withoutJson(
StreamChannel(_serverIncomingForwarder.stream, channel.sink),
onUnhandledError: onUnhandledError);
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks);
_client = Client.withoutJson(
StreamChannel(_clientIncomingForwarder.stream, channel.sink));
}
Expand Down
43 changes: 32 additions & 11 deletions lib/src/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,30 @@ class Server {
/// invoked. If it is not set, the exception will be swallowed.
final ErrorCallback onUnhandledError;

/// Whether to strictly enforce the JSON-RPC 2.0 specification for received messages.
///
/// If `false`, this [Server] will accept some requests which are not conformant
/// with the JSON-RPC 2.0 specification. In particular, requests missing the
/// `jsonrpc` parameter will be accepted.
final bool strictProtocolChecks;

/// Creates a [Server] that communicates over [channel].
///
/// Note that the server won't begin listening to [requests] until
/// [Server.listen] is called.
///
/// 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})
///
/// If [strictProtocolChecks] is false, this [Server] will accept some
/// requests which are not conformant with the JSON-RPC 2.0 specification. In
/// particular, requests missing the `jsonrpc` parameter will be accepted.
Server(StreamChannel<String> channel,
{ErrorCallback onUnhandledError, bool strictProtocolChecks = true})
: this.withoutJson(
jsonDocument.bind(channel).transform(respondToFormatExceptions),
onUnhandledError: onUnhandledError);
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks);

/// Creates a [Server] that communicates using decoded messages over
/// [channel].
Expand All @@ -84,7 +97,12 @@ class Server {
///
/// 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})
///
/// If [strictProtocolChecks] is false, this [Server] will accept some
/// requests which are not conformant with the JSON-RPC 2.0 specification. In
/// particular, requests missing the `jsonrpc` parameter will be accepted.
Server.withoutJson(StreamChannel channel,
{this.onUnhandledError, this.strictProtocolChecks = true})
: _manager = ChannelManager('Server', channel);

/// Starts listening to the underlying stream.
Expand Down Expand Up @@ -217,14 +235,15 @@ class Server {
'an Array or an Object.');
}

if (!request.containsKey('jsonrpc')) {
if (strictProtocolChecks && !request.containsKey('jsonrpc')) {
throw RpcException(
error_code.INVALID_REQUEST,
'Request must '
'contain a "jsonrpc" key.');
}

if (request['jsonrpc'] != '2.0') {
if ((strictProtocolChecks || request.containsKey('jsonrpc')) &&
request['jsonrpc'] != '2.0') {
throw RpcException(
error_code.INVALID_REQUEST,
'Invalid JSON-RPC '
Expand All @@ -246,12 +265,14 @@ class Server {
'be a string, but was ${jsonEncode(method)}.');
}

var params = request['params'];
if (request.containsKey('params') && params is! List && params is! Map) {
throw RpcException(
error_code.INVALID_REQUEST,
'Request params must '
'be an Array or an Object, but was ${jsonEncode(params)}.');
if (request.containsKey('params')) {
var params = request['params'];
if (params is! List && params is! Map) {
throw RpcException(
error_code.INVALID_REQUEST,
'Request params must '
'be an Array or an Object, but was ${jsonEncode(params)}.');
}
}

var id = request['id'];
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: json_rpc_2
version: 2.1.1
version: 2.2.0
description: >-
Utilities to write a client or server using the JSON-RPC 2.0 spec.
homepage: https://github.com/dart-lang/json_rpc_2
Expand Down
17 changes: 17 additions & 0 deletions test/server/invalid_request_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,21 @@ void main() {
}
})));
});

group('strict protocol checks disabled', () {
setUp(() => controller = ServerController(strictProtocolChecks: false));

test('and no jsonrpc param', () {
expectErrorResponse(controller, {'method': 'foo', 'id': 1234},
error_code.METHOD_NOT_FOUND, 'Unknown method "foo".');
});

test('the jsonrpc version must be 2.0', () {
expectErrorResponse(
controller,
{'jsonrpc': '1.0', 'method': 'foo', 'id': 1234},
error_code.INVALID_REQUEST,
'Invalid JSON-RPC version "1.0", expected "2.0".');
});
});
}
7 changes: 5 additions & 2 deletions test/server/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ class ServerController {
json_rpc.Server get server => _server;
json_rpc.Server _server;

ServerController({json_rpc.ErrorCallback onUnhandledError}) {
ServerController(
{json_rpc.ErrorCallback onUnhandledError,
bool strictProtocolChecks = true}) {
_server = json_rpc.Server(
StreamChannel(_requestController.stream, _responseController.sink),
onUnhandledError: onUnhandledError);
onUnhandledError: onUnhandledError,
strictProtocolChecks: strictProtocolChecks);
_server.listen();
}

Expand Down

0 comments on commit d589e63

Please sign in to comment.