blob: f2530a33f5703778678390cb21e146601912f721 [file] [log] [blame]
// 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.
import 'dart:async';
import 'dart:io';
import 'package:dds/src/dap/adapters/dart.dart';
import 'package:pedantic/pedantic.dart';
import 'adapters/dart_cli.dart';
import 'logging.dart';
import 'protocol_stream.dart';
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;
final _channels = <ByteStreamServerChannel>{};
final _adapters = <DartDebugAdapter>{};
DapServer._(
this._socket, {
this.ipv6 = false,
this.enableDds = true,
this.enableAuthCodes = true,
this.logger,
}) {
_socket.listen(_acceptConnection);
}
String get host => _socket.address.host;
int get port => _socket.port;
Future<void> stop() async {
_channels.forEach((client) => client.close());
await _socket.close();
}
void _acceptConnection(Socket client) {
final address = client.remoteAddress;
logger?.call('Accepted connection from $address');
client.done.then((_) {
logger?.call('Connection from $address closed');
});
_createAdapter(client.transform(Uint8ListTransformer()), client);
}
void _createAdapter(Stream<List<int>> _input, StreamSink<List<int>> _output) {
// TODO(dantup): This is hard-coded to DartCliDebugAdapter but will
// ultimately need to support having a factory passed in to support
// tests and/or being used in flutter_tools.
final channel = ByteStreamServerChannel(_input, _output, logger);
final adapter = DartCliDebugAdapter(
channel,
ipv6: ipv6,
enableDds: enableDds,
enableAuthCodes: enableAuthCodes,
logger: logger,
);
_channels.add(channel);
_adapters.add(adapter);
unawaited(channel.closed.then((_) {
_channels.remove(channel);
_adapters.remove(adapter);
adapter.shutdown();
}));
}
/// Starts a DAP Server listening on [host]:[port].
static Future<DapServer> create({
String? host,
int port = 0,
bool ipv6 = false,
bool enableDdds = true,
bool enableAuthCodes = true,
Logger? logger,
}) async {
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,
);
}
}