Make failing HTTP requests thrown when using dart:io.

Previous behavior was to expose the response content anyway,
which was generally empty on a 404 error.

R=sgjesse@google.com

Review-Url: https://codereview.chromium.org//2638403002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa4ec0f..faf031d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 # Changelog
 
+## 2.1.0
+- Make failing HTTP requests throw an `HttpException`.
+
 ## 2.0.2
 - Update README.md.
 
diff --git a/lib/src/io_io.dart b/lib/src/io_io.dart
index d4edfea..1056d75 100644
--- a/lib/src/io_io.dart
+++ b/lib/src/io_io.dart
@@ -4,8 +4,12 @@
 
 import "dart:async" show Future, Stream;
 import "dart:convert" show Encoding, LATIN1, UTF8;
-import "dart:io" show
-    File, HttpClient, HttpClientResponse, HttpClientRequest, HttpHeaders;
+import "dart:io" show File,
+                      HttpStatus,
+                      HttpClient,
+                      HttpClientResponse,
+                      HttpClientRequest,
+                      HttpHeaders;
 
 import "package:typed_data/typed_buffers.dart" show Uint8Buffer;
 
@@ -17,6 +21,7 @@
   }
   if (uri.scheme == "http" || uri.scheme == "https") {
     HttpClientResponse response = await _httpGetBytes(uri);
+    _throwIfFailed(response, uri);
     yield* response;
     return;
   }
@@ -34,6 +39,7 @@
   }
   if (uri.scheme == "http" || uri.scheme == "https") {
     HttpClientResponse response = await _httpGetBytes(uri);
+    _throwIfFailed(response, uri);
     int length = response.contentLength;
     if (length < 0) length = 0;
     var buffer = new Uint8Buffer(length);
@@ -62,6 +68,7 @@
       request.headers.set(HttpHeaders.ACCEPT_CHARSET, encoding.name);
     }
     HttpClientResponse response = await request.close();
+    _throwIfFailed(response, uri);
     encoding ??= Encoding.getByName(response.headers.contentType?.charset);
     if (encoding == null || encoding == LATIN1) {
       // Default to LATIN-1 if no encoding found.
@@ -88,3 +95,10 @@
     request.headers.set(HttpHeaders.ACCEPT, "application/octet-stream, */*");
   return request.close();
 }
+
+void _throwIfFailed(HttpClientResponse response, Uri uri) {
+  var statusCode = response.statusCode;
+  if (statusCode < HttpStatus.OK || statusCode > HttpStatus.NO_CONTENT) {
+    throw new HttpException(response.reasonPhrase, uri: uri);
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 460b422..7a182b2 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: resource
-version: 2.0.2
+version: 2.1.0
 description: Reading resource data from (package and other) files.
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/resource
diff --git a/test/loader_http_test.dart b/test/loader_http_test.dart
index 114ec6a..7795f58 100644
--- a/test/loader_http_test.dart
+++ b/test/loader_http_test.dart
@@ -20,6 +20,12 @@
     int port = server.port;
     uri = Uri.parse("http://localhost:$port/default.html");
     server.forEach((HttpRequest request) {
+      if (request.uri.path.endsWith(".not")) {
+        request.response
+          ..statusCode = HttpStatus.NOT_FOUND
+          ..close();
+        return;
+      }
       var encodings = request.headers[HttpHeaders.ACCEPT_CHARSET];
       var encoding = parseAcceptCharset(encodings);
       request.response.headers.contentType =
@@ -61,6 +67,24 @@
     expect(buffer, content.codeUnits);
   });
 
+  test("not found - String", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var badUri = uri.resolve("file.not");  // .not makes server fail.
+    expect(loader.readAsString(badUri), throws);
+  });
+
+  test("not found - bytes", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var badUri = uri.resolve("file.not");  // .not makes server fail.
+    expect(loader.readAsBytes(badUri), throws);
+  });
+
+  test("not found - byte stream", () async {
+    var loader = ResourceLoader.defaultLoader;
+    var badUri = uri.resolve("file.not");  // .not makes server fail.
+    expect(loader.openRead(badUri).length, throws);
+  });
+
   tearDown(() {
     server.close();
     server = null;