| // 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/io.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.ipv6, | |
| }) : assert(logReader != null) { | |
| _deviceLogSubscription = logReader.logLines.listen(_handleLine); | |
| } | |
| factory ProtocolDiscovery.observatory( | |
| DeviceLogReader logReader, { | |
| DevicePortForwarder portForwarder, | |
| int hostPort, | |
| bool ipv6 = false, | |
| }) { | |
| const String kObservatoryService = 'Observatory'; | |
| return ProtocolDiscovery._( | |
| logReader, | |
| kObservatoryService, | |
| portForwarder: portForwarder, | |
| hostPort: hostPort, | |
| ipv6: ipv6, | |
| ); | |
| } | |
| final DeviceLogReader logReader; | |
| final String serviceName; | |
| final DevicePortForwarder portForwarder; | |
| final int hostPort; | |
| final bool ipv6; | |
| final Completer<Uri> _completer = Completer<Uri>(); | |
| StreamSubscription<String> _deviceLogSubscription; | |
| /// The discovered service URI. | |
| Future<Uri> get uri => _completer.future; | |
| Future<void> cancel() => _stopScrapingLogs(); | |
| Future<void> _stopScrapingLogs() async { | |
| await _deviceLogSubscription?.cancel(); | |
| _deviceLogSubscription = null; | |
| } | |
| void _handleLine(String line) { | |
| Uri uri; | |
| final RegExp r = RegExp('${RegExp.escape(serviceName)} listening on ((http|\/\/)[a-zA-Z0-9:/=\.\\[\\]]+)'); | |
| final Match match = r.firstMatch(line); | |
| if (match != null) { | |
| try { | |
| uri = Uri.parse(match[1]); | |
| } 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 actualDevicePort = deviceUri.port; | |
| final int actualHostPort = await portForwarder.forward(actualDevicePort, hostPort: hostPort); | |
| printTrace('Forwarded host port $actualHostPort to device port $actualDevicePort for $serviceName'); | |
| hostUri = deviceUri.replace(port: actualHostPort); | |
| } | |
| assert(InternetAddress(hostUri.host).isLoopback); | |
| if (ipv6) { | |
| hostUri = hostUri.replace(host: InternetAddress.loopbackIPv6.host); | |
| } | |
| return hostUri; | |
| } | |
| } |