dds.dart 3.53 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:async';

7 8 9
import 'package:dds/dds.dart' as dds;
import 'package:meta/meta.dart';

10
import 'common.dart';
11
import 'context.dart';
12 13 14
import 'io.dart' as io;
import 'logger.dart';

15
// TODO(fujino): This should be direct injected, rather than mutable global state.
16 17
@visibleForTesting
Future<dds.DartDevelopmentService> Function(
18 19
  Uri remoteVmServiceUri, {
  bool enableAuthCodes,
20
  bool ipv6,
21
  Uri? serviceUri,
22
  List<String> cachedUserTags,
23
  dds.UriConverter? uriConverter,
24 25
}) ddsLauncherCallback = dds.DartDevelopmentService.startDartDevelopmentService;

26 27 28
/// Helper class to launch a [dds.DartDevelopmentService]. Allows for us to
/// mock out this functionality for testing purposes.
class DartDevelopmentService {
29
  dds.DartDevelopmentService? _ddsInstance;
30

31 32
  Uri? get uri => _ddsInstance?.uri ?? _existingDdsUri;
  Uri? _existingDdsUri;
33

34 35 36
  Future<void> get done => _completer.future;
  final Completer<void> _completer = Completer<void>();

37
  Future<void> startDartDevelopmentService(
38 39 40 41 42
    Uri observatoryUri, {
    required Logger logger,
    int? hostPort,
    bool? ipv6,
    bool? disableServiceAuthCodes,
43
    bool cacheStartupProfile = false,
44
  }) async {
45 46
    final Uri ddsUri = Uri(
      scheme: 'http',
47
      host: ((ipv6 ?? false) ? io.InternetAddress.loopbackIPv6 : io.InternetAddress.loopbackIPv4).host,
48
      port: hostPort ?? 0,
49 50 51 52 53 54
    );
    logger.printTrace(
      'Launching a Dart Developer Service (DDS) instance at $ddsUri, '
      'connecting to VM service at $observatoryUri.',
    );
    try {
55
      _ddsInstance = await ddsLauncherCallback(
56 57
          observatoryUri,
          serviceUri: ddsUri,
58
          enableAuthCodes: disableServiceAuthCodes != true,
59
          ipv6: ipv6 ?? false,
60 61
          // Enables caching of CPU samples collected during application startup.
          cachedUserTags: cacheStartupProfile ? const <String>['AppStartUp'] : const <String>[],
62
          uriConverter: context.get<dds.UriConverter>(),
63
        );
64
      unawaited(_ddsInstance?.done.whenComplete(() {
65 66 67 68
        if (!_completer.isCompleted) {
          _completer.complete();
        }
      }));
69
      logger.printTrace('DDS is listening at ${_ddsInstance?.uri}.');
70
    } on dds.DartDevelopmentServiceException catch (e) {
71
      logger.printTrace('Warning: Failed to start DDS: ${e.message}');
72
      if (e.errorCode == dds.DartDevelopmentServiceException.existingDdsInstanceError) {
73 74 75 76 77
        try {
          _existingDdsUri = Uri.parse(
            e.message.split(' ').firstWhere((String e) => e.startsWith('http'))
          );
        } on StateError {
78 79 80
          if (e.message.contains('Existing VM service clients prevent DDS from taking control.')) {
            throwToolExit('${e.message}. Please rebuild your application with a newer version of Flutter.');
          }
81 82
          logger.printError(
            'DDS has failed to start and there is not an existing DDS instance '
83 84
            'available to connect to. Please file an issue at https://github.com/flutter/flutter/issues '
            'with the following error message:\n\n ${e.message}.'
85
          );
86 87
          // DDS was unable to start for an unknown reason. Raise a StateError
          // so it can be reported by the crash reporter.
88 89
          throw StateError(e.message);
        }
90
      }
91 92 93
      if (!_completer.isCompleted) {
        _completer.complete();
      }
94 95 96 97
      rethrow;
    }
  }

98
  Future<void> shutdown() async => _ddsInstance?.shutdown();
99
}