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

diff --git a/lib/src/client.dart b/lib/src/client.dart
index 82baa6a..0568da0 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -49,9 +49,8 @@
   /// 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].
diff --git a/lib/src/peer.dart b/lib/src/peer.dart
index 810d173..c663da2 100644
--- a/lib/src/peer.dart
+++ b/lib/src/peer.dart
@@ -44,9 +44,8 @@
   /// 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].
   ///
diff --git a/lib/src/server.dart b/lib/src/server.dart
index 3ef59ed..b85a46f 100644
--- a/lib/src/server.dart
+++ b/lib/src/server.dart
@@ -56,9 +56,8 @@
   /// 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].
@@ -126,41 +125,45 @@
   /// 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;
@@ -170,22 +173,24 @@
         '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')) {
+    } catch (error, stackTrace) {
+      if (error is RpcException) {
+        if (error.code == error_code.INVALID_REQUEST ||
+            request.containsKey('id')) {
+          return error.serialize(request);
+        } else {
+          return null;
+        }
+      } else if (!request.containsKey('id')) {
         return null;
-      } else {
-        return error.serialize(request);
       }
-    });
+      final chain = new Chain.forTrace(stackTrace);
+      return new RpcException(error_code.SERVER_ERROR, getErrorMessage(error),
+          data: {
+            'full': '$error',
+            'stack': '$chain',
+          }).serialize(request);
+    }
   }
 
   /// Validates that [request] matches the JSON-RPC spec.
@@ -233,18 +238,18 @@
   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();
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 8871b56..1e2d6b1 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:stack_trace/stack_trace.dart';
 import 'package:stream_channel/stream_channel.dart';
 
 import '../error_code.dart' as error_code;
@@ -12,10 +11,6 @@
 
 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
@@ -69,8 +64,9 @@
 }
 
 /// 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);
 });
diff --git a/test/client/utils.dart b/test/client/utils.dart
index 09a200c..9b4dd79 100644
--- a/test/client/utils.dart
+++ b/test/client/utils.dart
@@ -45,10 +45,14 @@
   }
 
   /// 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]
diff --git a/test/peer_test.dart b/test/peer_test.dart
index ad7ad22..5241e15 100644
--- a/test/peer_test.dart
+++ b/test/peer_test.dart
@@ -164,8 +164,8 @@
     });
 
     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));