net.dart 2.46 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 The Chromium 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';

7
import '../base/context.dart';
8
import '../globals.dart';
9
import 'common.dart';
10
import 'io.dart';
11 12

const int kNetworkProblemExitCode = 50;
13

14
typedef HttpClient HttpClientFactory();
15

16 17
/// Download a file from the given URL and return the bytes.
Future<List<int>> fetchUrl(Uri url) async {
18 19 20 21 22 23 24 25 26 27 28 29 30
  int attempts = 0;
  int duration = 1;
  while (true) {
    attempts += 1;
    final List<int> result = await _attempt(url);
    if (result != null)
      return result;
    printStatus('Download failed -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...');
    await new Future<Null>.delayed(new Duration(seconds: duration));
    if (duration < 64)
      duration *= 2;
  }
}
31

32 33 34 35 36 37 38 39
Future<List<int>> _attempt(Uri url) async {
  printTrace('Downloading: $url');
  HttpClient httpClient;
  if (context[HttpClientFactory] != null) {
    httpClient = context[HttpClientFactory]();
  } else {
    httpClient = new HttpClient();
  }
40 41 42 43 44 45 46 47 48 49 50
  HttpClientRequest request;
  try {
    request = await httpClient.getUrl(url);
  } 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,
    );
51 52 53
  } on SocketException catch (error) {
    printTrace('Download error: $error');
    return null;
54
  }
55
  final HttpClientResponse response = await request.close();
56
  if (response.statusCode != 200) {
57 58 59 60 61 62 63 64 65 66 67
    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 null;
68
  }
69
  printTrace('Received response from server, collecting bytes...');
70
  try {
71
    final BytesBuilder responseBody = new BytesBuilder(copy: false);
72
    await response.forEach(responseBody.add);
73
    return responseBody.takeBytes();
74 75 76
  } on IOException catch (error) {
    printTrace('Download error: $error');
    return null;
77
  }
78
}