Unverified Commit 28bedb10 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Move http request close under try-catch (#38894)

parent c20113e2
...@@ -48,12 +48,14 @@ Future<List<int>> _attempt(Uri url, { bool onlyHeaders = false }) async { ...@@ -48,12 +48,14 @@ Future<List<int>> _attempt(Uri url, { bool onlyHeaders = false }) async {
httpClient = HttpClient(); httpClient = HttpClient();
} }
HttpClientRequest request; HttpClientRequest request;
HttpClientResponse response;
try { try {
if (onlyHeaders) { if (onlyHeaders) {
request = await httpClient.headUrl(url); request = await httpClient.headUrl(url);
} else { } else {
request = await httpClient.getUrl(url); request = await httpClient.getUrl(url);
} }
response = await request.close();
} on ArgumentError catch (error) { } on ArgumentError catch (error) {
final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL']; final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL'];
if (overrideUrl != null && url.toString().contains(overrideUrl)) { if (overrideUrl != null && url.toString().contains(overrideUrl)) {
...@@ -82,7 +84,8 @@ Future<List<int>> _attempt(Uri url, { bool onlyHeaders = false }) async { ...@@ -82,7 +84,8 @@ Future<List<int>> _attempt(Uri url, { bool onlyHeaders = false }) async {
printTrace('Download error: $error'); printTrace('Download error: $error');
return null; 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 // If we're making a HEAD request, we're only checking to see if the URL is
// valid. // valid.
if (onlyHeaders) { if (onlyHeaders) {
......
...@@ -34,7 +34,7 @@ void main() { ...@@ -34,7 +34,7 @@ void main() {
expect(testLogger.errorText, isEmpty); expect(testLogger.errorText, isEmpty);
expect(error, isNull); expect(error, isNull);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(500), HttpClientFactory: () => () => FakeHttpClient(500),
}); });
testUsingContext('retry from network error', () async { testUsingContext('retry from network error', () async {
...@@ -57,7 +57,7 @@ void main() { ...@@ -57,7 +57,7 @@ void main() {
expect(testLogger.errorText, isEmpty); expect(testLogger.errorText, isEmpty);
expect(error, isNull); expect(error, isNull);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(200), HttpClientFactory: () => () => FakeHttpClient(200),
}); });
testUsingContext('retry from SocketException', () async { testUsingContext('retry from SocketException', () async {
...@@ -81,7 +81,7 @@ void main() { ...@@ -81,7 +81,7 @@ void main() {
expect(error, isNull); expect(error, isNull);
expect(testLogger.traceText, contains('Download error: SocketException')); expect(testLogger.traceText, contains('Download error: SocketException'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClientThrowing( HttpClientFactory: () => () => FakeHttpClientThrowing(
const io.SocketException('test exception handling'), const io.SocketException('test exception handling'),
), ),
}); });
...@@ -101,7 +101,7 @@ void main() { ...@@ -101,7 +101,7 @@ void main() {
expect(error, startsWith('test failed')); expect(error, startsWith('test failed'));
expect(testLogger.traceText, contains('HandshakeException')); expect(testLogger.traceText, contains('HandshakeException'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClientThrowing( HttpClientFactory: () => () => FakeHttpClientThrowing(
const io.HandshakeException('test exception handling'), const io.HandshakeException('test exception handling'),
), ),
}); });
...@@ -122,7 +122,7 @@ void main() { ...@@ -122,7 +122,7 @@ void main() {
expect(testLogger.errorText, contains('Invalid argument')); expect(testLogger.errorText, contains('Invalid argument'));
expect(error, contains('FLUTTER_STORAGE_BASE_URL')); expect(error, contains('FLUTTER_STORAGE_BASE_URL'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClientThrowing( HttpClientFactory: () => () => FakeHttpClientThrowing(
ArgumentError('test exception handling'), ArgumentError('test exception handling'),
), ),
Platform: () => FakePlatform.fromPlatform(const LocalPlatform()) Platform: () => FakePlatform.fromPlatform(const LocalPlatform())
...@@ -152,7 +152,33 @@ void main() { ...@@ -152,7 +152,33 @@ void main() {
expect(error, isNull); expect(error, isNull);
expect(testLogger.traceText, contains('Download error: HttpException')); expect(testLogger.traceText, contains('Download error: HttpException'));
}, overrides: <Type, Generator>{ }, 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'), const io.HttpException('test exception handling'),
), ),
}); });
...@@ -178,7 +204,7 @@ void main() { ...@@ -178,7 +204,7 @@ void main() {
expect(error, isNull); expect(error, isNull);
expect(actualResult, isNull); expect(actualResult, isNull);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(500), HttpClientFactory: () => () => FakeHttpClient(500),
}); });
testUsingContext('remote file non-existant', () async { testUsingContext('remote file non-existant', () async {
...@@ -186,7 +212,7 @@ void main() { ...@@ -186,7 +212,7 @@ void main() {
final bool result = await doesRemoteFileExist(invalid); final bool result = await doesRemoteFileExist(invalid);
expect(result, false); expect(result, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(404), HttpClientFactory: () => () => FakeHttpClient(404),
}); });
testUsingContext('remote file server error', () async { testUsingContext('remote file server error', () async {
...@@ -194,7 +220,7 @@ void main() { ...@@ -194,7 +220,7 @@ void main() {
final bool result = await doesRemoteFileExist(valid); final bool result = await doesRemoteFileExist(valid);
expect(result, false); expect(result, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(500), HttpClientFactory: () => () => FakeHttpClient(500),
}); });
testUsingContext('remote file exists', () async { testUsingContext('remote file exists', () async {
...@@ -202,12 +228,12 @@ void main() { ...@@ -202,12 +228,12 @@ void main() {
final bool result = await doesRemoteFileExist(valid); final bool result = await doesRemoteFileExist(valid);
expect(result, true); expect(result, true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(200), HttpClientFactory: () => () => FakeHttpClient(200),
}); });
} }
class MockHttpClientThrowing implements io.HttpClient { class FakeHttpClientThrowing implements io.HttpClient {
MockHttpClientThrowing(this.exception); FakeHttpClientThrowing(this.exception);
final Object exception; final Object exception;
...@@ -222,19 +248,19 @@ class MockHttpClientThrowing implements io.HttpClient { ...@@ -222,19 +248,19 @@ class MockHttpClientThrowing implements io.HttpClient {
} }
} }
class MockHttpClient implements io.HttpClient { class FakeHttpClient implements io.HttpClient {
MockHttpClient(this.statusCode); FakeHttpClient(this.statusCode);
final int statusCode; final int statusCode;
@override @override
Future<io.HttpClientRequest> getUrl(Uri url) async { Future<io.HttpClientRequest> getUrl(Uri url) async {
return MockHttpClientRequest(statusCode); return FakeHttpClientRequest(statusCode);
} }
@override @override
Future<io.HttpClientRequest> headUrl(Uri url) async { Future<io.HttpClientRequest> headUrl(Uri url) async {
return MockHttpClientRequest(statusCode); return FakeHttpClientRequest(statusCode);
} }
@override @override
...@@ -243,14 +269,46 @@ class MockHttpClient implements io.HttpClient { ...@@ -243,14 +269,46 @@ class MockHttpClient implements io.HttpClient {
} }
} }
class MockHttpClientRequest implements io.HttpClientRequest { class FakeHttpClientThrowingRequest implements io.HttpClient {
MockHttpClientRequest(this.statusCode); 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; final int statusCode;
@override @override
Future<io.HttpClientResponse> close() async { Future<io.HttpClientResponse> close() async {
return MockHttpClientResponse(statusCode); return FakeHttpClientResponse(statusCode);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClientRequest - $invocation';
}
}
class FakeHttpClientRequestThrowing implements io.HttpClientRequest {
FakeHttpClientRequestThrowing(this.exception);
final Object exception;
@override
Future<io.HttpClientResponse> close() async {
throw exception;
} }
@override @override
...@@ -259,8 +317,8 @@ class MockHttpClientRequest implements io.HttpClientRequest { ...@@ -259,8 +317,8 @@ class MockHttpClientRequest implements io.HttpClientRequest {
} }
} }
class MockHttpClientResponse implements io.HttpClientResponse { class FakeHttpClientResponse implements io.HttpClientResponse {
MockHttpClientResponse(this.statusCode); FakeHttpClientResponse(this.statusCode);
@override @override
final int statusCode; final int statusCode;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment