flutter/packages/flutter_tools/lib/src/base/net.dart
Ian Hickson 449f4a6673
License update (#45373)
* Update project.pbxproj files to say Flutter rather than Chromium

Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright.

* Update the copyright notice checker to require a standard notice on all files

* Update copyrights on Dart files. (This was a mechanical commit.)

* Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine.

Some were already marked "The Flutter Authors", not clear why. Their
dates have been normalized. Some were missing the blank line after the
license. Some were randomly different in trivial ways for no apparent
reason (e.g. missing the trailing period).

* Clean up the copyrights in non-Dart files. (Manual edits.)

Also, make sure templates don't have copyrights.

* Fix some more ORGANIZATIONNAMEs
2019-11-27 15:04:02 -08:00

212 lines
5.7 KiB
Dart

// Copyright 2014 The Flutter Authors. 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 '../base/context.dart';
import '../convert.dart';
import '../globals.dart';
import 'common.dart';
import 'file_system.dart';
import 'io.dart';
import 'platform.dart';
const int kNetworkProblemExitCode = 50;
typedef HttpClientFactory = HttpClient Function();
/// Download a file from the given URL.
///
/// If a destination file is not provided, returns the bytes.
///
/// If a destination file is provided, streams the bytes to that file and
/// returns an empty list.
///
/// If [maxAttempts] is exceeded, returns null.
Future<List<int>> fetchUrl(Uri url, {
int maxAttempts,
File destFile,
}) async {
int attempts = 0;
int durationSeconds = 1;
while (true) {
attempts += 1;
_MemoryIOSink memorySink;
IOSink sink;
if (destFile == null) {
memorySink = _MemoryIOSink();
sink = memorySink;
} else {
sink = destFile.openWrite();
}
final bool result = await _attempt(
url,
destSink: sink,
);
if (result) {
return memorySink?.writes?.takeBytes() ?? <int>[];
}
if (maxAttempts != null && attempts >= maxAttempts) {
printStatus('Download failed -- retry $attempts');
return null;
}
printStatus('Download failed -- attempting retry $attempts in '
'$durationSeconds second${ durationSeconds == 1 ? "" : "s"}...');
await Future<void>.delayed(Duration(seconds: durationSeconds));
if (durationSeconds < 64) {
durationSeconds *= 2;
}
}
}
/// Check if the given URL points to a valid endpoint.
Future<bool> doesRemoteFileExist(Uri url) async => await _attempt(url, onlyHeaders: true);
// Returns true on success and false on failure.
Future<bool> _attempt(Uri url, {
IOSink destSink,
bool onlyHeaders = false,
}) async {
assert(onlyHeaders || destSink != null);
printTrace('Downloading: $url');
HttpClient httpClient;
if (context.get<HttpClientFactory>() != null) {
httpClient = context.get<HttpClientFactory>()();
} else {
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)) {
printError(error.toString());
throwToolExit(
'The value of FLUTTER_STORAGE_BASE_URL ($overrideUrl) could not be '
'parsed as a valid url. Please see https://flutter.dev/community/china '
'for an example of how to use it.\n'
'Full URL: $url',
exitCode: kNetworkProblemExitCode,);
}
printError(error.toString());
rethrow;
} on HandshakeException catch (error) {
printTrace(error.toString());
throwToolExit(
'Could not authenticate download server. You may be experiencing a man-in-the-middle attack,\n'
'your network may be compromised, or you may have malware installed on your computer.\n'
'URL: $url',
exitCode: kNetworkProblemExitCode,
);
} on SocketException catch (error) {
printTrace('Download error: $error');
return false;
} on HttpException catch (error) {
printTrace('Download error: $error');
return false;
}
assert(response != null);
// If we're making a HEAD request, we're only checking to see if the URL is
// valid.
if (onlyHeaders) {
return response.statusCode == HttpStatus.ok;
}
if (response.statusCode != HttpStatus.ok) {
if (response.statusCode > 0 && response.statusCode < 500) {
throwToolExit(
'Download failed.\n'
'URL: $url\n'
'Error: ${response.statusCode} ${response.reasonPhrase}',
exitCode: kNetworkProblemExitCode,
);
}
// 5xx errors are server errors and we can try again
printTrace('Download error: ${response.statusCode} ${response.reasonPhrase}');
return false;
}
printTrace('Received response from server, collecting bytes...');
try {
assert(destSink != null);
await response.forEach(destSink.add);
return true;
} on IOException catch (error) {
printTrace('Download error: $error');
return false;
} finally {
await destSink?.flush();
await destSink?.close();
}
}
/// An IOSink that collects whatever is written to it.
class _MemoryIOSink implements IOSink {
@override
Encoding encoding = utf8;
final BytesBuilder writes = BytesBuilder(copy: false);
@override
void add(List<int> data) {
writes.add(data);
}
@override
Future<void> addStream(Stream<List<int>> stream) {
final Completer<void> completer = Completer<void>();
stream.listen(add).onDone(completer.complete);
return completer.future;
}
@override
void writeCharCode(int charCode) {
add(<int>[charCode]);
}
@override
void write(Object obj) {
add(encoding.encode('$obj'));
}
@override
void writeln([ Object obj = '' ]) {
add(encoding.encode('$obj\n'));
}
@override
void writeAll(Iterable<dynamic> objects, [ String separator = '' ]) {
bool addSeparator = false;
for (dynamic object in objects) {
if (addSeparator) {
write(separator);
}
write(object);
addSeparator = true;
}
}
@override
void addError(dynamic error, [ StackTrace stackTrace ]) {
throw UnimplementedError();
}
@override
Future<void> get done => close();
@override
Future<void> close() async { }
@override
Future<void> flush() async { }
}