| // Copyright 2016 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'; |
| |
| import 'base/common.dart'; |
| import 'base/io.dart'; |
| import 'base/port_scanner.dart'; |
| import 'device.dart'; |
| import 'globals.dart'; |
| |
| /// Discovers a specific service protocol on a device, and forwards the service |
| /// protocol device port to the host. |
| class ProtocolDiscovery { |
| ProtocolDiscovery._( |
| this.logReader, |
| this.serviceName, { |
| this.portForwarder, |
| this.hostPort, |
| this.defaultHostPort, |
| this.ipv6, |
| }) : assert(logReader != null), |
| assert(portForwarder == null || defaultHostPort != null), |
| _prefix = '$serviceName listening on ' { |
| _deviceLogSubscription = logReader.logLines.listen(_handleLine); |
| } |
| |
| factory ProtocolDiscovery.observatory( |
| DeviceLogReader logReader, { |
| DevicePortForwarder portForwarder, |
| int hostPort, |
| bool ipv6: false, |
| }) { |
| const String kObservatoryService = 'Observatory'; |
| return new ProtocolDiscovery._( |
| logReader, kObservatoryService, |
| portForwarder: portForwarder, |
| hostPort: hostPort, |
| defaultHostPort: kDefaultObservatoryPort, |
| ipv6: ipv6, |
| ); |
| } |
| |
| final DeviceLogReader logReader; |
| final String serviceName; |
| final DevicePortForwarder portForwarder; |
| final int hostPort; |
| final int defaultHostPort; |
| final bool ipv6; |
| |
| final String _prefix; |
| final Completer<Uri> _completer = new Completer<Uri>(); |
| |
| StreamSubscription<String> _deviceLogSubscription; |
| |
| /// The discovered service URI. |
| Future<Uri> get uri => _completer.future; |
| |
| Future<Null> cancel() => _stopScrapingLogs(); |
| |
| Future<Null> _stopScrapingLogs() async { |
| await _deviceLogSubscription?.cancel(); |
| _deviceLogSubscription = null; |
| } |
| |
| void _handleLine(String line) { |
| Uri uri; |
| final int index = line.indexOf(_prefix + 'http://'); |
| if (index >= 0) { |
| try { |
| uri = Uri.parse(line.substring(index + _prefix.length)); |
| } catch (error) { |
| _stopScrapingLogs(); |
| _completer.completeError(error); |
| } |
| } |
| |
| if (uri != null) { |
| assert(!_completer.isCompleted); |
| _stopScrapingLogs(); |
| _completer.complete(_forwardPort(uri)); |
| } |
| } |
| |
| Future<Uri> _forwardPort(Uri deviceUri) async { |
| printTrace('$serviceName URL on device: $deviceUri'); |
| Uri hostUri = deviceUri; |
| |
| if (portForwarder != null) { |
| final int devicePort = deviceUri.port; |
| int hostPort = this.hostPort ?? await portScanner.findPreferredPort(defaultHostPort); |
| hostPort = await portForwarder.forward(devicePort, hostPort: hostPort); |
| printTrace('Forwarded host port $hostPort to device port $devicePort for $serviceName'); |
| hostUri = deviceUri.replace(port: hostPort); |
| } |
| |
| assert(new InternetAddress(hostUri.host).isLoopback); |
| if (ipv6) { |
| hostUri = hostUri.replace(host: InternetAddress.LOOPBACK_IP_V6.host); |
| } |
| |
| return hostUri; |
| } |
| } |