[flutter_tool] Move http request close under try-catch (#38894)
diff --git a/packages/flutter_tools/lib/src/base/net.dart b/packages/flutter_tools/lib/src/base/net.dart
index 3466eda..df14055 100644
--- a/packages/flutter_tools/lib/src/base/net.dart
+++ b/packages/flutter_tools/lib/src/base/net.dart
@@ -48,12 +48,14 @@
httpClient = HttpClient();
}
HttpClientRequest request;
+ HttpClientResponse response;
try {
if (onlyHeaders) {
request = await httpClient.headUrl(url);
} else {
request = await httpClient.getUrl(url);
}
+ response = await request.close();
} on ArgumentError catch (error) {
final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL'];
if (overrideUrl != null && url.toString().contains(overrideUrl)) {
@@ -82,7 +84,8 @@
printTrace('Download error: $error');
return null;
}
- final HttpClientResponse response = await request.close();
+ assert(response != null);
+
// If we're making a HEAD request, we're only checking to see if the URL is
// valid.
if (onlyHeaders) {
diff --git a/packages/flutter_tools/test/general.shard/base/net_test.dart b/packages/flutter_tools/test/general.shard/base/net_test.dart
index 99e1d3b..a2ce132 100644
--- a/packages/flutter_tools/test/general.shard/base/net_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/net_test.dart
@@ -34,7 +34,7 @@
expect(testLogger.errorText, isEmpty);
expect(error, isNull);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(500),
+ HttpClientFactory: () => () => FakeHttpClient(500),
});
testUsingContext('retry from network error', () async {
@@ -57,7 +57,7 @@
expect(testLogger.errorText, isEmpty);
expect(error, isNull);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(200),
+ HttpClientFactory: () => () => FakeHttpClient(200),
});
testUsingContext('retry from SocketException', () async {
@@ -81,7 +81,7 @@
expect(error, isNull);
expect(testLogger.traceText, contains('Download error: SocketException'));
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClientThrowing(
+ HttpClientFactory: () => () => FakeHttpClientThrowing(
const io.SocketException('test exception handling'),
),
});
@@ -101,7 +101,7 @@
expect(error, startsWith('test failed'));
expect(testLogger.traceText, contains('HandshakeException'));
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClientThrowing(
+ HttpClientFactory: () => () => FakeHttpClientThrowing(
const io.HandshakeException('test exception handling'),
),
});
@@ -122,7 +122,7 @@
expect(testLogger.errorText, contains('Invalid argument'));
expect(error, contains('FLUTTER_STORAGE_BASE_URL'));
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClientThrowing(
+ HttpClientFactory: () => () => FakeHttpClientThrowing(
ArgumentError('test exception handling'),
),
Platform: () => FakePlatform.fromPlatform(const LocalPlatform())
@@ -152,7 +152,33 @@
expect(error, isNull);
expect(testLogger.traceText, contains('Download error: HttpException'));
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClientThrowing(
+ HttpClientFactory: () => () => FakeHttpClientThrowing(
+ const io.HttpException('test exception handling'),
+ ),
+ });
+
+ testUsingContext('retry from HttpException when request throws', () async {
+ String error;
+ FakeAsync().run((FakeAsync time) {
+ fetchUrl(Uri.parse('http://example.invalid/')).then((List<int> value) {
+ error = 'test completed unexpectedly';
+ }, onError: (dynamic exception) {
+ error = 'test failed unexpectedly: $exception';
+ });
+ expect(testLogger.statusText, '');
+ time.elapse(const Duration(milliseconds: 10000));
+ expect(testLogger.statusText,
+ 'Download failed -- attempting retry 1 in 1 second...\n'
+ 'Download failed -- attempting retry 2 in 2 seconds...\n'
+ 'Download failed -- attempting retry 3 in 4 seconds...\n'
+ 'Download failed -- attempting retry 4 in 8 seconds...\n',
+ );
+ });
+ expect(testLogger.errorText, isEmpty);
+ expect(error, isNull);
+ expect(testLogger.traceText, contains('Download error: HttpException'));
+ }, overrides: <Type, Generator>{
+ HttpClientFactory: () => () => FakeHttpClientThrowingRequest(
const io.HttpException('test exception handling'),
),
});
@@ -178,7 +204,7 @@
expect(error, isNull);
expect(actualResult, isNull);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(500),
+ HttpClientFactory: () => () => FakeHttpClient(500),
});
testUsingContext('remote file non-existant', () async {
@@ -186,7 +212,7 @@
final bool result = await doesRemoteFileExist(invalid);
expect(result, false);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(404),
+ HttpClientFactory: () => () => FakeHttpClient(404),
});
testUsingContext('remote file server error', () async {
@@ -194,7 +220,7 @@
final bool result = await doesRemoteFileExist(valid);
expect(result, false);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(500),
+ HttpClientFactory: () => () => FakeHttpClient(500),
});
testUsingContext('remote file exists', () async {
@@ -202,12 +228,12 @@
final bool result = await doesRemoteFileExist(valid);
expect(result, true);
}, overrides: <Type, Generator>{
- HttpClientFactory: () => () => MockHttpClient(200),
+ HttpClientFactory: () => () => FakeHttpClient(200),
});
}
-class MockHttpClientThrowing implements io.HttpClient {
- MockHttpClientThrowing(this.exception);
+class FakeHttpClientThrowing implements io.HttpClient {
+ FakeHttpClientThrowing(this.exception);
final Object exception;
@@ -222,19 +248,19 @@
}
}
-class MockHttpClient implements io.HttpClient {
- MockHttpClient(this.statusCode);
+class FakeHttpClient implements io.HttpClient {
+ FakeHttpClient(this.statusCode);
final int statusCode;
@override
Future<io.HttpClientRequest> getUrl(Uri url) async {
- return MockHttpClientRequest(statusCode);
+ return FakeHttpClientRequest(statusCode);
}
@override
Future<io.HttpClientRequest> headUrl(Uri url) async {
- return MockHttpClientRequest(statusCode);
+ return FakeHttpClientRequest(statusCode);
}
@override
@@ -243,14 +269,30 @@
}
}
-class MockHttpClientRequest implements io.HttpClientRequest {
- MockHttpClientRequest(this.statusCode);
+class FakeHttpClientThrowingRequest implements io.HttpClient {
+ FakeHttpClientThrowingRequest(this.exception);
+
+ final Object exception;
+
+ @override
+ Future<io.HttpClientRequest> getUrl(Uri url) async {
+ return FakeHttpClientRequestThrowing(exception);
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ throw 'io.HttpClient - $invocation';
+ }
+}
+
+class FakeHttpClientRequest implements io.HttpClientRequest {
+ FakeHttpClientRequest(this.statusCode);
final int statusCode;
@override
Future<io.HttpClientResponse> close() async {
- return MockHttpClientResponse(statusCode);
+ return FakeHttpClientResponse(statusCode);
}
@override
@@ -259,8 +301,24 @@
}
}
-class MockHttpClientResponse implements io.HttpClientResponse {
- MockHttpClientResponse(this.statusCode);
+class FakeHttpClientRequestThrowing implements io.HttpClientRequest {
+ FakeHttpClientRequestThrowing(this.exception);
+
+ final Object exception;
+
+ @override
+ Future<io.HttpClientResponse> close() async {
+ throw exception;
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ throw 'io.HttpClientRequest - $invocation';
+ }
+}
+
+class FakeHttpClientResponse implements io.HttpClientResponse {
+ FakeHttpClientResponse(this.statusCode);
@override
final int statusCode;