mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
[dds] Support IPv6 for DAP with --ipv6 flag
Change-Id: I07c66b79e832f200bf5f27c20930e1ba6a2ebda7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/205261 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
parent
8e4e4c56aa
commit
8f01499793
|
@ -105,6 +105,9 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
|
|||
/// yet been made.
|
||||
DartDevelopmentService? _dds;
|
||||
|
||||
/// Whether to use IPv6 for DAP/Debugger services.
|
||||
final bool ipv6;
|
||||
|
||||
/// Whether to enable DDS for launched applications.
|
||||
final bool enableDds;
|
||||
|
||||
|
@ -121,6 +124,7 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
|
|||
|
||||
DartDebugAdapter(
|
||||
ByteStreamServerChannel channel, {
|
||||
this.ipv6 = false,
|
||||
this.enableDds = true,
|
||||
this.enableAuthCodes = true,
|
||||
this.logger,
|
||||
|
@ -194,8 +198,8 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
|
|||
logger?.call('Starting a DDS instance for $uri');
|
||||
final dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||
uri,
|
||||
// TODO(dantup): Allow this to be disabled?
|
||||
enableAuthCodes: true,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
ipv6: ipv6,
|
||||
);
|
||||
_dds = dds;
|
||||
uri = dds.wsUri!;
|
||||
|
|
|
@ -43,11 +43,13 @@ class DartCliDebugAdapter extends DartDebugAdapter<DartLaunchRequestArguments> {
|
|||
|
||||
DartCliDebugAdapter(
|
||||
ByteStreamServerChannel channel, {
|
||||
bool ipv6 = false,
|
||||
bool enableDds = true,
|
||||
bool enableAuthCodes = true,
|
||||
Logger? logger,
|
||||
}) : super(
|
||||
channel,
|
||||
ipv6: ipv6,
|
||||
enableDds: enableDds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
|
@ -109,7 +111,7 @@ class DartCliDebugAdapter extends DartDebugAdapter<DartLaunchRequestArguments> {
|
|||
final vmServiceInfoFile = _vmServiceInfoFile;
|
||||
final vmArgs = <String>[
|
||||
if (debug) ...[
|
||||
'--enable-vm-service=${args.vmServicePort ?? 0}',
|
||||
'--enable-vm-service=${args.vmServicePort ?? 0}${ipv6 ? '/::1' : ''}',
|
||||
'--pause_isolates_on_start=true',
|
||||
if (!enableAuthCodes) '--disable-service-auth-codes'
|
||||
],
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'protocol_stream_transformers.dart';
|
|||
/// A DAP server that binds to a port and runs in multi-session mode.
|
||||
class DapServer {
|
||||
final ServerSocket _socket;
|
||||
final bool ipv6;
|
||||
final bool enableDds;
|
||||
final bool enableAuthCodes;
|
||||
final Logger? logger;
|
||||
|
@ -24,6 +25,7 @@ class DapServer {
|
|||
|
||||
DapServer._(
|
||||
this._socket, {
|
||||
this.ipv6 = false,
|
||||
this.enableDds = true,
|
||||
this.enableAuthCodes = true,
|
||||
this.logger,
|
||||
|
@ -55,6 +57,7 @@ class DapServer {
|
|||
final channel = ByteStreamServerChannel(_input, _output, logger);
|
||||
final adapter = DartCliDebugAdapter(
|
||||
channel,
|
||||
ipv6: ipv6,
|
||||
enableDds: enableDds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
|
@ -70,15 +73,19 @@ class DapServer {
|
|||
|
||||
/// Starts a DAP Server listening on [host]:[port].
|
||||
static Future<DapServer> create({
|
||||
String host = 'localhost',
|
||||
String? host,
|
||||
int port = 0,
|
||||
bool ipv6 = false,
|
||||
bool enableDdds = true,
|
||||
bool enableAuthCodes = true,
|
||||
Logger? logger,
|
||||
}) async {
|
||||
final _socket = await ServerSocket.bind(host, port);
|
||||
final hostFallback =
|
||||
ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4;
|
||||
final _socket = await ServerSocket.bind(host ?? hostFallback, port);
|
||||
return DapServer._(
|
||||
_socket,
|
||||
ipv6: ipv6,
|
||||
enableDds: enableDdds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_client.dart';
|
||||
import 'test_scripts.dart';
|
||||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
|
@ -12,11 +13,7 @@ main() {
|
|||
group('debug mode breakpoints', () {
|
||||
test('stops at a line breakpoint', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
@ -24,11 +21,7 @@ void main(List<String> args) async {
|
|||
|
||||
test('stops at a line breakpoint and can be resumed', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:dds/src/dap/adapters/dart.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_client.dart';
|
||||
import 'test_scripts.dart';
|
||||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
|
@ -30,10 +31,7 @@ void main(List<String> args) {
|
|||
|
||||
test('evaluates expressions with complex results', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}''');
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
@ -57,10 +55,7 @@ void main(List<String> args) {
|
|||
'evaluates complex expressions expressions with evaluateToStringInDebugViews=true',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}''');
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:dds/src/dap/protocol_generated.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'test_client.dart';
|
||||
import 'test_scripts.dart';
|
||||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
|
@ -47,11 +48,7 @@ void main(List<String> args) async {
|
|||
|
||||
test('provides a list of threads', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
@ -63,11 +60,7 @@ void main(List<String> args) async {
|
|||
|
||||
test('runs with DDS', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
@ -77,9 +70,7 @@ void main(List<String> args) async {
|
|||
}, timeout: Timeout.none);
|
||||
|
||||
test('runs with auth codes enabled', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) {}
|
||||
''');
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
expect(_hasAuthCode(outputEvents.first), isTrue);
|
||||
|
@ -90,11 +81,7 @@ void main(List<String> args) {}
|
|||
group('debug mode', () {
|
||||
test('runs without DDS', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
@ -103,9 +90,7 @@ void main(List<String> args) async {
|
|||
});
|
||||
|
||||
test('runs with auth tokens disabled', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) {}
|
||||
''');
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
expect(_hasAuthCode(outputEvents.first), isFalse);
|
||||
|
@ -113,18 +98,39 @@ void main(List<String> args) {}
|
|||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}, additionalArgs: ['--no-dds', '--no-auth-codes']);
|
||||
|
||||
testDap((dap) async {
|
||||
group('debug mode', () {
|
||||
test('can run with ipv6', () async {
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
final vmServiceUri = _extractVmServiceUri(outputEvents.first);
|
||||
|
||||
// Check DAP server host.
|
||||
expect(dap.server.host, equals('::1'));
|
||||
// Check VM Service/DDS host.
|
||||
expect(vmServiceUri.host, equals('::1'));
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}, additionalArgs: ['--ipv6']);
|
||||
}
|
||||
|
||||
/// Extracts the VM Service URI from the "Connecting to ..." banner output by
|
||||
/// the DAP server upon connection.
|
||||
Uri _extractVmServiceUri(OutputEventBody vmConnectionBanner) {
|
||||
// TODO(dantup): Change this to use the dart.debuggerUris custom event
|
||||
// if implemented (whch VS Code also needs).
|
||||
final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
|
||||
final match = vmServiceUriPattern.firstMatch(vmConnectionBanner.output);
|
||||
return Uri.parse(match!.group(1)!);
|
||||
}
|
||||
|
||||
/// Checks for the presence of an auth token in a VM Service URI in the
|
||||
/// "Connecting to VM Service" [OutputEvent].
|
||||
bool _hasAuthCode(OutputEventBody vmConnection) {
|
||||
// TODO(dantup): Change this to use the dart.debuggerUris custom event
|
||||
// if implemented (whch VS Code also needs).
|
||||
final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
|
||||
final authCodePattern = RegExp(r'ws://127.0.0.1:\d+/[\w=]{5,15}/ws');
|
||||
|
||||
final vmServiceUri =
|
||||
vmServiceUriPattern.firstMatch(vmConnection.output)!.group(1);
|
||||
|
||||
return vmServiceUri != null && authCodePattern.hasMatch(vmServiceUri);
|
||||
bool _hasAuthCode(OutputEventBody vmConnectionBanner) {
|
||||
final vmServiceUri = _extractVmServiceUri(vmConnectionBanner);
|
||||
final authCodePattern = RegExp(r'^/[\w=]{5,15}/ws');
|
||||
return authCodePattern.hasMatch(vmServiceUri.path);
|
||||
}
|
||||
|
|
|
@ -305,8 +305,8 @@ class DapTestClient {
|
|||
/// Creates a [DapTestClient] that connects the server listening on
|
||||
/// [host]:[port].
|
||||
static Future<DapTestClient> connect(
|
||||
String host,
|
||||
int port, {
|
||||
String host = 'localhost',
|
||||
bool captureVmServiceTraffic = false,
|
||||
Logger? logger,
|
||||
}) async {
|
||||
|
|
17
pkg/dds/test/dap/integration/test_scripts.dart
Normal file
17
pkg/dds/test/dap/integration/test_scripts.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// A simple empty Dart script that should run with no output and no errors.
|
||||
const emptyProgram = '''
|
||||
void main(List<String> args) {}
|
||||
''';
|
||||
|
||||
/// A simple Dart script that should run with no errors and contains a comment
|
||||
/// marker '// BREAKPOINT' for use in tests that require stopping at a breakpoint
|
||||
/// but require no other context.
|
||||
const simpleBreakpointProgram = r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''';
|
|
@ -110,7 +110,7 @@ class DapTestSession {
|
|||
var attempt = 1;
|
||||
while (attempt++ <= 100) {
|
||||
try {
|
||||
return await DapTestClient.connect(server.port);
|
||||
return await DapTestClient.connect(server.host, server.port);
|
||||
} catch (e) {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ Future<void> main(List<String> arguments) async {
|
|||
class DapCommand extends Command {
|
||||
static const argHost = 'host';
|
||||
static const argPort = 'port';
|
||||
static const argIpv6 = 'ipv6';
|
||||
static const argDds = 'dds';
|
||||
static const argAuthCodes = 'auth-codes';
|
||||
static const argVerbose = 'verbose';
|
||||
|
@ -39,8 +40,9 @@ class DapCommand extends Command {
|
|||
argParser
|
||||
..addOption(
|
||||
argHost,
|
||||
defaultsTo: 'localhost',
|
||||
help: 'The hostname/IP to bind the server to',
|
||||
help: 'The hostname/IP to bind the server to. If not supplied, will'
|
||||
' use the appropriate loopback address depending on whether'
|
||||
' --ipv6 is set',
|
||||
)
|
||||
..addOption(
|
||||
argPort,
|
||||
|
@ -48,6 +50,10 @@ class DapCommand extends Command {
|
|||
defaultsTo: '0',
|
||||
help: 'The port to bind the server to',
|
||||
)
|
||||
..addFlag(
|
||||
argIpv6,
|
||||
help: 'Whether to bind DAP/VM Service/DDS to IPv6 addresses',
|
||||
)
|
||||
..addFlag(
|
||||
argDds,
|
||||
defaultsTo: true,
|
||||
|
@ -69,10 +75,12 @@ class DapCommand extends Command {
|
|||
final args = argResults!;
|
||||
final port = int.parse(args[argPort]);
|
||||
final host = args[argHost];
|
||||
final ipv6 = args[argIpv6] as bool;
|
||||
|
||||
final server = await DapServer.create(
|
||||
host: host,
|
||||
port: port,
|
||||
ipv6: ipv6,
|
||||
enableDdds: args[argDds],
|
||||
enableAuthCodes: args[argAuthCodes],
|
||||
logger: args[argVerbose] ? print : null,
|
||||
|
|
Loading…
Reference in a new issue