blob: 785db4a8a6ecb7abaa1c4d6ac6dd48167d4cfcad [file] [log] [blame]
// Copyright (c) 2019, 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.
// @dart = 2.9
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';
import 'package:sse/server/sse_handler.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import 'data/build_result.dart';
import 'src/connections/app_connection.dart';
import 'src/connections/debug_connection.dart';
import 'src/events.dart';
import 'src/handlers/dev_handler.dart';
import 'src/handlers/injector.dart';
import 'src/handlers/socket_connections.dart';
import 'src/loaders/strategy.dart';
import 'src/readers/asset_reader.dart';
import 'src/servers/devtools.dart';
import 'src/servers/extension_backend.dart';
import 'src/services/expression_compiler.dart';
import 'src/utilities/dart_uri.dart';
export 'src/connections/app_connection.dart' show AppConnection;
export 'src/connections/debug_connection.dart' show DebugConnection;
export 'src/debugging/metadata/provider.dart'
show MetadataProvider, AbsoluteImportUriException;
export 'src/events.dart' show DwdsEvent;
export 'src/handlers/dev_handler.dart' show AppConnectionException;
export 'src/handlers/socket_connections.dart';
export 'src/loaders/build_runner_require.dart'
show BuildRunnerRequireStrategyProvider;
export 'src/loaders/frontend_server_require.dart'
show FrontendServerRequireStrategyProvider;
export 'src/loaders/legacy.dart' show LegacyStrategy;
export 'src/loaders/require.dart' show RequireStrategy;
export 'src/loaders/strategy.dart' show LoadStrategy, ReloadConfiguration;
export 'src/readers/asset_reader.dart' show AssetReader;
export 'src/readers/frontend_server_asset_reader.dart'
show FrontendServerAssetReader;
export 'src/readers/proxy_server_asset_reader.dart' show ProxyServerAssetReader;
export 'src/servers/devtools.dart';
export 'src/services/chrome_proxy_service.dart' show ChromeDebugException;
export 'src/services/expression_compiler.dart'
show ExpressionCompilationResult, ExpressionCompiler, ModuleInfo;
export 'src/services/expression_compiler_service.dart'
show ExpressionCompilerService;
typedef ConnectionProvider = Future<ChromeConnection> Function();
typedef UrlEncoder = Future<String> Function(String url);
/// The Dart Web Debug Service.
class Dwds {
static final _logger = Logger('DWDS');
final Middleware middleware;
final Handler handler;
final DevTools _devTools;
final DevHandler _devHandler;
final AssetReader _assetReader;
final bool _enableDebugging;
Dwds._(
this.middleware,
this._devTools,
this._devHandler,
this._assetReader,
this._enableDebugging,
) : handler = _devHandler.handler;
Stream<AppConnection> get connectedApps => _devHandler.connectedApps;
Stream<DwdsEvent> get events => eventStream;
StreamController<DebugConnection> get extensionDebugConnections =>
_devHandler.extensionDebugConnections;
Future<void> stop() async {
await _devTools?.close();
await _devHandler.close();
await _assetReader.close();
}
Future<DebugConnection> debugConnection(AppConnection appConnection) async {
if (!_enableDebugging) throw StateError('Debugging is not enabled.');
final dwdsStats = DwdsStats(DateTime.now());
var appDebugServices =
await _devHandler.loadAppServices(appConnection, dwdsStats);
await appDebugServices.chromeProxyService.isInitialized;
return DebugConnection(appDebugServices);
}
static Future<Dwds> start({
@required AssetReader assetReader,
@required Stream<BuildResult> buildResults,
@required ConnectionProvider chromeConnection,
@required LoadStrategy loadStrategy,
@required bool enableDebugging,
// TODO(annagrin): make expressionCompiler argument required
// [issue 881](https://github.com/dart-lang/webdev/issues/881)
ExpressionCompiler expressionCompiler,
bool enableDebugExtension,
String hostname,
bool useSseForDebugProxy,
bool useSseForDebugBackend,
bool useSseForInjectedClient,
UrlEncoder urlEncoder,
bool spawnDds,
// TODO(elliette): DevTools is inconsistently capitalized throughout this
// file. Change all occurances of devtools/Devtools to devTools/DevTools.
bool enableDevtoolsLaunch,
DevtoolsLauncher devtoolsLauncher,
bool launchDevToolsInNewWindow,
Uri sdkDir,
Uri librariesPath,
bool emitDebugEvents,
}) async {
hostname ??= 'localhost';
enableDebugging ??= true;
enableDebugExtension ??= false;
useSseForDebugProxy ??= true;
useSseForDebugBackend ??= true;
useSseForInjectedClient ??= true;
enableDevtoolsLaunch ??= true;
launchDevToolsInNewWindow ??= true;
spawnDds ??= true;
globalLoadStrategy = loadStrategy;
emitDebugEvents ??= true;
await DartUri.initialize(sdkDir: sdkDir, librariesPath: librariesPath);
DevTools devTools;
Future<String> extensionUri;
ExtensionBackend extensionBackend;
if (enableDebugExtension) {
final handler = useSseForDebugBackend
? SseSocketHandler(SseHandler(Uri.parse('/\$debug'),
// Proxy servers may actively kill long standing connections.
// Allow for clients to reconnect in a short window. Making the
// window too long may cause issues if the user closes a debug
// session and initites a new one during the keepAlive window.
keepAlive: const Duration(seconds: 5)))
: WebSocketSocketHandler();
extensionBackend = await ExtensionBackend.start(handler, hostname);
extensionUri = Future.value(Uri(
scheme: useSseForDebugBackend ? 'http' : 'ws',
host: extensionBackend.hostname,
port: extensionBackend.port,
path: r'$debug')
.toString());
if (urlEncoder != null) extensionUri = urlEncoder(await extensionUri);
}
var serveDevTools = devtoolsLauncher != null;
if (serveDevTools) {
devTools = await devtoolsLauncher(hostname);
var uri =
Uri(scheme: 'http', host: devTools.hostname, port: devTools.port);
_logger.info('Serving DevTools at $uri\n');
}
var injected = DwdsInjector(
loadStrategy,
useSseForInjectedClient: useSseForInjectedClient,
extensionUri: extensionUri,
enableDevtoolsLaunch: enableDevtoolsLaunch,
emitDebugEvents: emitDebugEvents,
);
var devHandler = DevHandler(
chromeConnection,
buildResults,
devTools,
assetReader,
loadStrategy,
hostname,
extensionBackend,
urlEncoder,
useSseForDebugProxy,
useSseForInjectedClient,
serveDevTools,
expressionCompiler,
injected,
spawnDds,
launchDevToolsInNewWindow,
);
return Dwds._(
injected.middleware,
devTools,
devHandler,
assetReader,
enableDebugging,
);
}
}