Migrated http_read_test from SDK. (#43)

diff --git a/test/http_read_test.dart b/test/http_read_test.dart
new file mode 100644
index 0000000..989fd1b
--- /dev/null
+++ b/test/http_read_test.dart
@@ -0,0 +1,216 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:http_io/http_io.dart';
+import 'package:test/test.dart';
+
+class IsolatedHttpServer {
+  IsolatedHttpServer()
+      : _statusPort = new ReceivePort(),
+        _serverPort = null;
+
+  void setServerStartedHandler(void startedCallback(int port)) {
+    _startedCallback = startedCallback;
+  }
+
+  void start([bool chunkedEncoding = false]) {
+    ReceivePort receivePort = new ReceivePort();
+    Isolate.spawn(startIsolatedHttpServer, receivePort.sendPort);
+    receivePort.first.then((port) {
+      _serverPort = port;
+
+      if (chunkedEncoding) {
+        // Send chunked encoding message to the server.
+        port.send([
+          new IsolatedHttpServerCommand.chunkedEncoding(),
+          _statusPort.sendPort
+        ]);
+      }
+
+      // Send server start message to the server.
+      var command = new IsolatedHttpServerCommand.start();
+      port.send([command, _statusPort.sendPort]);
+    });
+
+    // Handle status messages from the server.
+    _statusPort.listen((var status) {
+      if (status.isStarted) {
+        _startedCallback(status.port);
+      }
+    });
+  }
+
+  void shutdown() {
+    // Send server stop message to the server.
+    _serverPort
+        .send([new IsolatedHttpServerCommand.stop(), _statusPort.sendPort]);
+    _statusPort.close();
+  }
+
+  ReceivePort _statusPort; // Port for receiving messages from the server.
+  SendPort _serverPort; // Port for sending messages to the server.
+  var _startedCallback;
+}
+
+class IsolatedHttpServerCommand {
+  static const START = 0;
+  static const STOP = 1;
+  static const CHUNKED_ENCODING = 2;
+
+  IsolatedHttpServerCommand.start() : _command = START;
+  IsolatedHttpServerCommand.stop() : _command = STOP;
+  IsolatedHttpServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
+
+  bool get isStart => _command == START;
+  bool get isStop => _command == STOP;
+  bool get isChunkedEncoding => _command == CHUNKED_ENCODING;
+
+  int _command;
+}
+
+class IsolatedHttpServerStatus {
+  static const STARTED = 0;
+  static const STOPPED = 1;
+  static const ERROR = 2;
+
+  IsolatedHttpServerStatus.started(this._port) : _state = STARTED;
+  IsolatedHttpServerStatus.stopped() : _state = STOPPED;
+  IsolatedHttpServerStatus.error() : _state = ERROR;
+
+  bool get isStarted => _state == STARTED;
+  bool get isStopped => _state == STOPPED;
+  bool get isError => _state == ERROR;
+
+  int get port => _port;
+
+  int _state;
+  int _port;
+}
+
+void startIsolatedHttpServer(Object replyToObj) {
+  SendPort replyTo = replyToObj;
+  var server = new TestServer();
+  server.init();
+  replyTo.send(server.dispatchSendPort);
+}
+
+class TestServer {
+  // Echo the request content back to the response.
+  void _echoHandler(HttpRequest request) {
+    var response = request.response;
+    if (request.method != 'POST') {
+      response.addError('POST expected, got: ${request.method}');
+    }
+    response.contentLength = request.contentLength;
+    request.pipe(response);
+  }
+
+  // Return a 404.
+  void _notFoundHandler(HttpRequest request) {
+    var response = request.response;
+    response.statusCode = HttpStatus.NOT_FOUND;
+    response.headers.set("Content-Type", "text/html; charset=UTF-8");
+    response.write("Page not found");
+    response.close();
+  }
+
+  void init() {
+    // Setup request handlers.
+    _requestHandlers = new Map();
+    _requestHandlers["/echo"] = _echoHandler;
+    _dispatchPort = new ReceivePort();
+    _dispatchPort.listen(dispatch);
+  }
+
+  SendPort get dispatchSendPort => _dispatchPort.sendPort;
+
+  void dispatch(message) {
+    IsolatedHttpServerCommand command = message[0];
+    SendPort replyTo = message[1];
+    if (command.isStart) {
+      try {
+        HttpServer.bind("127.0.0.1", 0).then((server) {
+          _server = server;
+          _server.listen(_requestReceivedHandler);
+          replyTo.send(new IsolatedHttpServerStatus.started(_server.port));
+        });
+      } catch (e) {
+        replyTo.send(new IsolatedHttpServerStatus.error());
+      }
+    } else if (command.isStop) {
+      _server.close();
+      _dispatchPort.close();
+      replyTo.send(new IsolatedHttpServerStatus.stopped());
+    } else if (command.isChunkedEncoding) {
+      _chunkedEncoding = true;
+    }
+  }
+
+  void _requestReceivedHandler(HttpRequest request) {
+    var requestHandler = _requestHandlers[request.uri.path];
+    if (requestHandler != null) {
+      requestHandler(request);
+    } else {
+      _notFoundHandler(request);
+    }
+  }
+
+  HttpServer _server; // HTTP server instance.
+  ReceivePort _dispatchPort;
+  Map _requestHandlers;
+  bool _chunkedEncoding = false;
+}
+
+Future<Null> testRead(bool chunkedEncoding) {
+  final completer = new Completer<Null>();
+  String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  final int kMessageCount = 10;
+
+  IsolatedHttpServer server = new IsolatedHttpServer();
+
+  void runTest(int port) {
+    int count = 0;
+    HttpClient httpClient = new HttpClient();
+    void sendRequest() {
+      httpClient.post("127.0.0.1", port, "/echo").then((request) {
+        if (chunkedEncoding) {
+          request.write(data.substring(0, 10));
+          request.write(data.substring(10, data.length));
+        } else {
+          request.contentLength = data.length;
+          request.add(data.codeUnits);
+        }
+        return request.close();
+      }).then((response) {
+        expect(HttpStatus.OK, equals(response.statusCode));
+        List<int> body = new List<int>();
+        response.listen(body.addAll, onDone: () {
+          expect(data, equals(new String.fromCharCodes(body)));
+          count++;
+          if (count < kMessageCount) {
+            sendRequest();
+          } else {
+            httpClient.close();
+            server.shutdown();
+            completer.complete();
+          }
+        });
+      });
+    }
+
+    sendRequest();
+  }
+
+  server.setServerStartedHandler(runTest);
+  server.start(chunkedEncoding);
+  return completer.future;
+}
+
+void main() {
+  test('readChunked', () => testRead(true));
+  test('readNotChunked', () => testRead(false));
+}