Skip to content
This repository has been archived by the owner on Oct 28, 2024. It is now read-only.

Fix strong mode errors, warnings, and hints. #14

Merged
merged 14 commits into from
Oct 28, 2016
5 changes: 2 additions & 3 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ class Client {
/// Note that the client won't begin listening to [responses] until
/// [Client.listen] is called.
Client(StreamChannel<String> channel)
: this.withoutJson(channel
.transform(jsonDocument)
.transformStream(ignoreFormatExceptions));
: this.withoutJson(
jsonDocument.bind(channel).transformStream(ignoreFormatExceptions));

/// Creates a [Client] that communicates using decoded messages over
/// [channel].
Expand Down
5 changes: 2 additions & 3 deletions lib/src/peer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ class Peer implements Client, Server {
/// Note that the peer won't begin listening to [channel] until [Peer.listen]
/// is called.
Peer(StreamChannel<String> channel)
: this.withoutJson(channel
.transform(jsonDocument)
.transform(respondToFormatExceptions));
: this.withoutJson(
jsonDocument.bind(channel).transform(respondToFormatExceptions));

/// Creates a [Peer] that communicates using decoded messages over [channel].
///
Expand Down
91 changes: 50 additions & 41 deletions lib/src/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ class Server {
/// Note that the server won't begin listening to [requests] until
/// [Server.listen] is called.
Server(StreamChannel<String> channel)
: this.withoutJson(channel
.transform(jsonDocument)
.transform(respondToFormatExceptions));
: this.withoutJson(
jsonDocument.bind(channel).transform(respondToFormatExceptions));

/// Creates a [Server] that communicates using decoded messages over
/// [channel].
Expand Down Expand Up @@ -126,41 +125,45 @@ class Server {
/// errors by throwing an [RpcException].
Future _handleRequest(request) async {
var response;
if (request is! List) {
if (request is List) {
if (request.isEmpty) {
response = new RpcException(error_code.INVALID_REQUEST,
'A batch must contain at least one request.')
.serialize(request);
} else {
var results = await Future.wait(request.map(_handleSingleRequest));
var nonNull = results.where((result) => result != null);
if (nonNull.isEmpty) return;
response = nonNull.toList();
}
} else {
response = await _handleSingleRequest(request);
if (response == null) return;
} else if (request.isEmpty) {
response = new RpcException(
error_code.INVALID_REQUEST,
'A batch must contain at least one request.')
.serialize(request);
} else {
var results = await Future.wait(request.map(_handleSingleRequest));
var nonNull = results.where((result) => result != null);
if (nonNull.isEmpty) return;
response = nonNull.toList();
}

if (!isClosed) _manager.add(response);
}

/// Handles an individual parsed request.
Future _handleSingleRequest(request) {
return syncFuture(() {
Future _handleSingleRequest(request) async {
try {
_validateRequest(request);

var name = request['method'];
var method = _methods[name];
if (method == null) method = _tryFallbacks;

Object result;
if (method is ZeroArgumentFunction) {
if (!request.containsKey('params')) return method();
throw new RpcException.invalidParams('No parameters are allowed for '
'method "$name".');
if (request.containsKey('params')) {
throw new RpcException.invalidParams('No parameters are allowed for '
'method "$name".');
}
result = await method();
} else {
result = await method(new Parameters(name, request['params']));
}

return method(new Parameters(name, request['params']));
}).then((result) {
// A request without an id is a notification, which should not be sent a
// response, even if one is generated on the server.
if (!request.containsKey('id')) return null;
Expand All @@ -170,22 +173,28 @@ class Server {
'result': result,
'id': request['id']
};
}).catchError((error, stackTrace) {
if (error is! RpcException) {
error = new RpcException(
error_code.SERVER_ERROR, getErrorMessage(error), data: {
'full': error.toString(),
'stack': new Chain.forTrace(stackTrace).toString()
});
}

if (error.code != error_code.INVALID_REQUEST &&
!request.containsKey('id')) {
return null;
} catch (e, stackTrace) {
if (error is RpcException) {
if (error.code == error_code.INVALID_REQUEST ||
request.containsKey('id')) {
return error.serialize(request);
} else {
return null;
}
} else {
return error.serialize(request);
if (request.containsKey('id')) {
final chain = new Chain.forTrace(stackTrace);
return new RpcException(
error_code.SERVER_ERROR, getErrorMessage(error),
data: {
'full': '$error',
'stack': '$chain',
}).serialize(request);
} else {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last nit: I prefer to avoid indentation by short-circuiting more than I prefer to use positive conditions, so I'd move the return null up earlier so you don't have to indent the RpcException creation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}
});
}
}

/// Validates that [request] matches the JSON-RPC spec.
Expand Down Expand Up @@ -233,18 +242,18 @@ class Server {
Future _tryFallbacks(Parameters params) {
var iterator = _fallbacks.toList().iterator;

_tryNext() {
_tryNext() async {
if (!iterator.moveNext()) {
return new Future.error(
new RpcException.methodNotFound(params.method),
new Chain.current());
throw new RpcException.methodNotFound(params.method);
}

return syncFuture(() => iterator.current(params)).catchError((error) {
try {
return iterator.current(params);
} on RpcException catch (error) {
if (error is! RpcException) throw error;
if (error.code != error_code.METHOD_NOT_FOUND) throw error;
return _tryNext();
});
}
}

return _tryNext();
Expand Down
10 changes: 3 additions & 7 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,13 @@

import 'dart:async';

import 'package:stack_trace/stack_trace.dart';
import 'package:stream_channel/stream_channel.dart';

import '../error_code.dart' as error_code;
import 'exception.dart';

typedef ZeroArgumentFunction();

/// Like [new Future.sync], but automatically wraps the future in a
/// [Chain.track] call.
Future syncFuture(callback()) => Chain.track(new Future.sync(callback));

/// Returns a sentence fragment listing the elements of [iter].
///
/// This converts each element of [iter] to a string and separates them with
Expand Down Expand Up @@ -69,8 +64,9 @@ tryFinally(body(), whenComplete()) {
}

/// A transformer that silently drops [FormatException]s.
final ignoreFormatExceptions = new StreamTransformer.fromHandlers(
handleError: (error, stackTrace, sink) {
final ignoreFormatExceptions =
new StreamTransformer<Object, Object>.fromHandlers(
handleError: (error, stackTrace, sink) {
if (error is FormatException) return;
sink.addError(error, stackTrace);
});
Expand Down
8 changes: 6 additions & 2 deletions test/client/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ class ClientController {
}

/// Sends [response], a decoded response, to [client].
Future sendResponse(response) => sendJsonResponse(JSON.encode(response));
void sendResponse(response) {
sendJsonResponse(JSON.encode(response));
}

/// Sends [response], a JSON-encoded response, to [client].
Future sendJsonResponse(String request) => _responseController.add(request);
void sendJsonResponse(String request) {
_responseController.add(request);
}
}

/// Returns a [Future] that completes after pumping the event queue [times]
Expand Down
4 changes: 2 additions & 2 deletions test/peer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ void main() {
});

test("returns a response for malformed JSON", () {
var incomingController = new StreamController();
var outgoingController = new StreamController();
var incomingController = new StreamController<String>();
var outgoingController = new StreamController<String>();
var jsonPeer = new json_rpc.Peer(
new StreamChannel(incomingController.stream, outgoingController));

Expand Down