Start switching hijacking to use StreamChannel.

R=kevmoo@google.com

Review URL: https://codereview.chromium.org//1640323004 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a73aa30..37b00bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.6.5
+
+* `Request.hijack()` now takes a callback that accepts a single `StreamChannel`
+  argument rather than separate `Stream` and `StreamSink` arguments. The old
+  callback signature is still supported, but should be considered deprecated.
+
+* The `HijackCallback` and `OnHijackCallback` typedefs are deprecated.
+
 ## 0.6.4+3
 
 * Support `http_parser` 2.0.0.
diff --git a/lib/src/request.dart b/lib/src/request.dart
index 29e897a..b4d0e03 100644
--- a/lib/src/request.dart
+++ b/lib/src/request.dart
@@ -6,17 +6,20 @@
 import 'dart:convert';
 
 import 'package:http_parser/http_parser.dart';
+import 'package:stream_channel/stream_channel.dart';
 
 import 'hijack_exception.dart';
 import 'message.dart';
 import 'util.dart';
 
 /// A callback provided by a Shelf handler that's passed to [Request.hijack].
+@Deprecated("Will be removed in shelf 0.7.0.")
 typedef void HijackCallback(
     Stream<List<int>> stream, StreamSink<List<int>> sink);
 
 /// A callback provided by a Shelf adapter that's used by [Request.hijack] to
 /// provide a [HijackCallback] with a socket.
+@Deprecated("Will be removed in shelf 0.7.0.")
 typedef void OnHijackCallback(HijackCallback callback);
 
 /// Represents an HTTP request to be processed by a Shelf application.
@@ -133,7 +136,8 @@
   Request(String method, Uri requestedUri, {String protocolVersion,
       Map<String, String> headers, String handlerPath, Uri url, body,
       Encoding encoding, Map<String, Object> context,
-      OnHijackCallback onHijack})
+      void onHijack(void hijack(
+          Stream<List<int>> stream, StreamSink<List<int>> sink))})
       : this._(method, requestedUri,
           protocolVersion: protocolVersion,
           headers: headers,
@@ -226,23 +230,29 @@
   ///
   /// Synchronously, this throws a [HijackException] that indicates to the
   /// adapter that it shouldn't emit a response itself. Asynchronously,
-  /// [callback] is called with a [Stream<List<int>>] and
-  /// [StreamSink<List<int>>], respectively, that provide access to the
-  /// underlying request socket.
+  /// [callback] is called with a [StreamChannel<List<int>>] that provides
+  /// access to the underlying request socket.
   ///
-  /// If the sink is closed, the stream will be closed as well. The stream and
-  /// sink may be the same object, as in the case of a `dart:io` `Socket`
-  /// object.
+  /// For backwards compatibility, if the callback takes two arguments, it will
+  /// be passed a `Stream<List<int>>` and a `StreamSink<List<int>>` separately,
+  /// but this behavior is deprecated.
   ///
   /// This may only be called when using a Shelf adapter that supports
   /// hijacking, such as the `dart:io` adapter. In addition, a given request may
   /// only be hijacked once. [canHijack] can be used to detect whether this
   /// request can be hijacked.
-  void hijack(HijackCallback callback) {
+  void hijack(Function callback) {
     if (_onHijack == null) {
       throw new StateError("This request can't be hijacked.");
     }
 
+    if (callback is ZoneUnaryCallback) {
+      var oldCallback = callback;
+      callback = (stream, sink) {
+        oldCallback(new StreamChannel<List<int>>(stream, sink));
+      };
+    }
+
     _onHijack.run(callback);
     throw const HijackException();
   }
diff --git a/pubspec.yaml b/pubspec.yaml
index 24d7d99..231c612 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shelf
-version: 0.6.4+3
+version: 0.6.5-dev
 author: Dart Team <misc@dartlang.org>
 description: Web Server Middleware for Dart
 homepage: https://github.com/dart-lang/shelf
@@ -10,7 +10,12 @@
   http_parser: '>=1.0.0 <3.0.0'
   path: '^1.0.0'
   stack_trace: '^1.0.0'
+  stream_channel: '^1.0.0'
 dev_dependencies:
   http: '>=0.9.2 <0.12.0'
   scheduled_test: '^0.12.0'
   test: '^0.12.0'
+
+dependency_overrides:
+  stream_channel:
+    git: git://github.com/dart-lang/stream_channel
diff --git a/test/hijack_test.dart b/test/hijack_test.dart
index 82b33eb..6b6c233 100644
--- a/test/hijack_test.dart
+++ b/test/hijack_test.dart
@@ -11,7 +11,7 @@
 
 void main() {
   test('hijacking a non-hijackable request throws a StateError', () {
-    expect(() => new Request('GET', LOCALHOST_URI).hijack((_, __) => null),
+    expect(() => new Request('GET', LOCALHOST_URI).hijack((_) => null),
         throwsStateError);
   });
 
@@ -29,10 +29,10 @@
       callback(streamController.stream, sinkController);
     }));
 
-    expect(() => request.hijack(expectAsync((stream, sink) {
-      expect(stream.first, completion(equals([1, 2, 3])));
-      sink.add([4, 5, 6]);
-      sink.close();
+    expect(() => request.hijack(expectAsync((channel) {
+      expect(channel.stream.first, completion(equals([1, 2, 3])));
+      channel.sink.add([4, 5, 6]);
+      channel.sink.close();
     })), throwsA(new isInstanceOf<HijackException>()));
   });
 
@@ -41,17 +41,17 @@
     var request = new Request('GET', LOCALHOST_URI,
         onHijack: expectAsync((_) => null, count: 1));
 
-    expect(() => request.hijack((_, __) => null),
+    expect(() => request.hijack((_) => null),
         throwsA(new isInstanceOf<HijackException>()));
 
-    expect(() => request.hijack((_, __) => null), throwsStateError);
+    expect(() => request.hijack((_) => null), throwsStateError);
   });
 
   group('calling change', () {
     test('hijacking a non-hijackable request throws a StateError', () {
       var request = new Request('GET', LOCALHOST_URI);
       var newRequest = request.change();
-      expect(() => newRequest.hijack((_, __) => null), throwsStateError);
+      expect(() => newRequest.hijack((_) => null), throwsStateError);
     });
 
     test('hijacking a hijackable request throws a HijackException and calls '
@@ -70,10 +70,10 @@
 
       var newRequest = request.change();
 
-      expect(() => newRequest.hijack(expectAsync((stream, sink) {
-        expect(stream.first, completion(equals([1, 2, 3])));
-        sink.add([4, 5, 6]);
-        sink.close();
+      expect(() => newRequest.hijack(expectAsync((channel) {
+        expect(channel.stream.first, completion(equals([1, 2, 3])));
+        channel.sink.add([4, 5, 6]);
+        channel.sink.close();
       })), throwsA(new isInstanceOf<HijackException>()));
     });
 
@@ -85,10 +85,10 @@
 
       var newRequest = request.change();
 
-      expect(() => newRequest.hijack((_, __) => null),
+      expect(() => newRequest.hijack((_) => null),
           throwsA(new isInstanceOf<HijackException>()));
 
-      expect(() => request.hijack((_, __) => null), throwsStateError);
+      expect(() => request.hijack((_) => null), throwsStateError);
     });
   });
 }