• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6
7import 'base/io.dart';
8import 'device.dart';
9import 'globals.dart';
10
11/// Discovers a specific service protocol on a device, and forwards the service
12/// protocol device port to the host.
13class ProtocolDiscovery {
14  ProtocolDiscovery._(
15    this.logReader,
16    this.serviceName, {
17    this.portForwarder,
18    this.hostPort,
19    this.ipv6,
20  }) : assert(logReader != null) {
21    _deviceLogSubscription = logReader.logLines.listen(_handleLine);
22  }
23
24  factory ProtocolDiscovery.observatory(
25    DeviceLogReader logReader, {
26    DevicePortForwarder portForwarder,
27    int hostPort,
28    bool ipv6 = false,
29  }) {
30    const String kObservatoryService = 'Observatory';
31    return ProtocolDiscovery._(
32      logReader,
33      kObservatoryService,
34      portForwarder: portForwarder,
35      hostPort: hostPort,
36      ipv6: ipv6,
37    );
38  }
39
40  final DeviceLogReader logReader;
41  final String serviceName;
42  final DevicePortForwarder portForwarder;
43  final int hostPort;
44  final bool ipv6;
45
46  final Completer<Uri> _completer = Completer<Uri>();
47
48  StreamSubscription<String> _deviceLogSubscription;
49
50  /// The discovered service URI.
51  Future<Uri> get uri => _completer.future;
52
53  Future<void> cancel() => _stopScrapingLogs();
54
55  Future<void> _stopScrapingLogs() async {
56    await _deviceLogSubscription?.cancel();
57    _deviceLogSubscription = null;
58  }
59
60  void _handleLine(String line) {
61    Uri uri;
62    final RegExp r = RegExp('${RegExp.escape(serviceName)} listening on ((http|\/\/)[a-zA-Z0-9:/=_\\-\.\\[\\]]+)');
63    final Match match = r.firstMatch(line);
64
65    if (match != null) {
66      try {
67        uri = Uri.parse(match[1]);
68      } catch (error, stackTrace) {
69        _stopScrapingLogs();
70        _completer.completeError(error, stackTrace);
71      }
72    }
73
74    if (uri != null) {
75      assert(!_completer.isCompleted);
76      _stopScrapingLogs();
77      _completer.complete(_forwardPort(uri));
78    }
79
80  }
81
82  Future<Uri> _forwardPort(Uri deviceUri) async {
83    printTrace('$serviceName URL on device: $deviceUri');
84    Uri hostUri = deviceUri;
85
86    if (portForwarder != null) {
87      final int actualDevicePort = deviceUri.port;
88      final int actualHostPort = await portForwarder.forward(actualDevicePort, hostPort: hostPort);
89      printTrace('Forwarded host port $actualHostPort to device port $actualDevicePort for $serviceName');
90      hostUri = deviceUri.replace(port: actualHostPort);
91    }
92
93    assert(InternetAddress(hostUri.host).isLoopback);
94    if (ipv6) {
95      hostUri = hostUri.replace(host: InternetAddress.loopbackIPv6.host);
96    }
97
98    return hostUri;
99  }
100}
101