Support --web-header option for flutter run (#136297)
Adds support for a new --web-header option to flutter run. Creates a workaround for https://github.com/flutter/flutter/issues/127902 This PR allows adding additional headers for the flutter run web server. This is useful to add headers like Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy without the use of a proxy server. These headers are required enable advanced web features. This approach provides flexibility to the developer to make use of the feature as they see fit and is backward-compatible. One tradeoff is that it increases the surface area to support for future changes to the flutter web server. https://github.com/flutter/flutter/issues/127902 is not fully addressed by this change. The solution for that task will be more opinionated. This PR creates a general-purpose workaround for anyone who needs a solution sooner while the bigger solution is developed.
This commit is contained in:
parent
b136ddc68b
commit
48eee14f0e
@ -239,6 +239,11 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
|
|||||||
final List<String> webBrowserFlags = featureFlags.isWebEnabled
|
final List<String> webBrowserFlags = featureFlags.isWebEnabled
|
||||||
? stringsArg(FlutterOptions.kWebBrowserFlag)
|
? stringsArg(FlutterOptions.kWebBrowserFlag)
|
||||||
: const <String>[];
|
: const <String>[];
|
||||||
|
|
||||||
|
final Map<String, String> webHeaders = featureFlags.isWebEnabled
|
||||||
|
? extractWebHeaders()
|
||||||
|
: const <String, String>{};
|
||||||
|
|
||||||
if (buildInfo.mode.isRelease) {
|
if (buildInfo.mode.isRelease) {
|
||||||
return DebuggingOptions.disabled(
|
return DebuggingOptions.disabled(
|
||||||
buildInfo,
|
buildInfo,
|
||||||
@ -252,6 +257,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
|
|||||||
webRunHeadless: featureFlags.isWebEnabled && boolArg('web-run-headless'),
|
webRunHeadless: featureFlags.isWebEnabled && boolArg('web-run-headless'),
|
||||||
webBrowserDebugPort: webBrowserDebugPort,
|
webBrowserDebugPort: webBrowserDebugPort,
|
||||||
webBrowserFlags: webBrowserFlags,
|
webBrowserFlags: webBrowserFlags,
|
||||||
|
webHeaders: webHeaders,
|
||||||
enableImpeller: enableImpeller,
|
enableImpeller: enableImpeller,
|
||||||
enableVulkanValidation: enableVulkanValidation,
|
enableVulkanValidation: enableVulkanValidation,
|
||||||
impellerForceGL: impellerForceGL,
|
impellerForceGL: impellerForceGL,
|
||||||
@ -298,6 +304,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
|
|||||||
webBrowserFlags: webBrowserFlags,
|
webBrowserFlags: webBrowserFlags,
|
||||||
webEnableExpressionEvaluation: featureFlags.isWebEnabled && boolArg('web-enable-expression-evaluation'),
|
webEnableExpressionEvaluation: featureFlags.isWebEnabled && boolArg('web-enable-expression-evaluation'),
|
||||||
webLaunchUrl: featureFlags.isWebEnabled ? stringArg('web-launch-url') : null,
|
webLaunchUrl: featureFlags.isWebEnabled ? stringArg('web-launch-url') : null,
|
||||||
|
webHeaders: webHeaders,
|
||||||
vmserviceOutFile: stringArg('vmservice-out-file'),
|
vmserviceOutFile: stringArg('vmservice-out-file'),
|
||||||
fastStart: argParser.options.containsKey('fast-start')
|
fastStart: argParser.options.containsKey('fast-start')
|
||||||
&& boolArg('fast-start')
|
&& boolArg('fast-start')
|
||||||
|
@ -959,6 +959,7 @@ class DebuggingOptions {
|
|||||||
this.webBrowserDebugPort,
|
this.webBrowserDebugPort,
|
||||||
this.webBrowserFlags = const <String>[],
|
this.webBrowserFlags = const <String>[],
|
||||||
this.webEnableExpressionEvaluation = false,
|
this.webEnableExpressionEvaluation = false,
|
||||||
|
this.webHeaders = const <String, String>{},
|
||||||
this.webLaunchUrl,
|
this.webLaunchUrl,
|
||||||
this.vmserviceOutFile,
|
this.vmserviceOutFile,
|
||||||
this.fastStart = false,
|
this.fastStart = false,
|
||||||
@ -986,6 +987,7 @@ class DebuggingOptions {
|
|||||||
this.webBrowserDebugPort,
|
this.webBrowserDebugPort,
|
||||||
this.webBrowserFlags = const <String>[],
|
this.webBrowserFlags = const <String>[],
|
||||||
this.webLaunchUrl,
|
this.webLaunchUrl,
|
||||||
|
this.webHeaders = const <String, String>{},
|
||||||
this.cacheSkSL = false,
|
this.cacheSkSL = false,
|
||||||
this.traceAllowlist,
|
this.traceAllowlist,
|
||||||
this.enableImpeller = ImpellerStatus.platformDefault,
|
this.enableImpeller = ImpellerStatus.platformDefault,
|
||||||
@ -1061,6 +1063,7 @@ class DebuggingOptions {
|
|||||||
required this.webBrowserDebugPort,
|
required this.webBrowserDebugPort,
|
||||||
required this.webBrowserFlags,
|
required this.webBrowserFlags,
|
||||||
required this.webEnableExpressionEvaluation,
|
required this.webEnableExpressionEvaluation,
|
||||||
|
required this.webHeaders,
|
||||||
required this.webLaunchUrl,
|
required this.webLaunchUrl,
|
||||||
required this.vmserviceOutFile,
|
required this.vmserviceOutFile,
|
||||||
required this.fastStart,
|
required this.fastStart,
|
||||||
@ -1141,6 +1144,9 @@ class DebuggingOptions {
|
|||||||
/// Allow developers to customize the browser's launch URL
|
/// Allow developers to customize the browser's launch URL
|
||||||
final String? webLaunchUrl;
|
final String? webLaunchUrl;
|
||||||
|
|
||||||
|
/// Allow developers to add custom headers to web server
|
||||||
|
final Map<String, String> webHeaders;
|
||||||
|
|
||||||
/// A file where the VM Service URL should be written after the application is started.
|
/// A file where the VM Service URL should be written after the application is started.
|
||||||
final String? vmserviceOutFile;
|
final String? vmserviceOutFile;
|
||||||
final bool fastStart;
|
final bool fastStart;
|
||||||
@ -1246,6 +1252,7 @@ class DebuggingOptions {
|
|||||||
'webBrowserFlags': webBrowserFlags,
|
'webBrowserFlags': webBrowserFlags,
|
||||||
'webEnableExpressionEvaluation': webEnableExpressionEvaluation,
|
'webEnableExpressionEvaluation': webEnableExpressionEvaluation,
|
||||||
'webLaunchUrl': webLaunchUrl,
|
'webLaunchUrl': webLaunchUrl,
|
||||||
|
'webHeaders': webHeaders,
|
||||||
'vmserviceOutFile': vmserviceOutFile,
|
'vmserviceOutFile': vmserviceOutFile,
|
||||||
'fastStart': fastStart,
|
'fastStart': fastStart,
|
||||||
'nullAssertions': nullAssertions,
|
'nullAssertions': nullAssertions,
|
||||||
@ -1297,6 +1304,7 @@ class DebuggingOptions {
|
|||||||
webBrowserDebugPort: json['webBrowserDebugPort'] as int?,
|
webBrowserDebugPort: json['webBrowserDebugPort'] as int?,
|
||||||
webBrowserFlags: (json['webBrowserFlags']! as List<dynamic>).cast<String>(),
|
webBrowserFlags: (json['webBrowserFlags']! as List<dynamic>).cast<String>(),
|
||||||
webEnableExpressionEvaluation: json['webEnableExpressionEvaluation']! as bool,
|
webEnableExpressionEvaluation: json['webEnableExpressionEvaluation']! as bool,
|
||||||
|
webHeaders: (json['webHeaders']! as Map<dynamic, dynamic>).cast<String, String>(),
|
||||||
webLaunchUrl: json['webLaunchUrl'] as String?,
|
webLaunchUrl: json['webLaunchUrl'] as String?,
|
||||||
vmserviceOutFile: json['vmserviceOutFile'] as String?,
|
vmserviceOutFile: json['vmserviceOutFile'] as String?,
|
||||||
fastStart: json['fastStart']! as bool,
|
fastStart: json['fastStart']! as bool,
|
||||||
|
@ -189,6 +189,7 @@ class WebAssetServer implements AssetReader {
|
|||||||
bool enableDds,
|
bool enableDds,
|
||||||
Uri entrypoint,
|
Uri entrypoint,
|
||||||
ExpressionCompiler? expressionCompiler,
|
ExpressionCompiler? expressionCompiler,
|
||||||
|
Map<String, String> extraHeaders,
|
||||||
NullSafetyMode nullSafetyMode, {
|
NullSafetyMode nullSafetyMode, {
|
||||||
bool testMode = false,
|
bool testMode = false,
|
||||||
DwdsLauncher dwdsLauncher = Dwds.start,
|
DwdsLauncher dwdsLauncher = Dwds.start,
|
||||||
@ -217,6 +218,10 @@ class WebAssetServer implements AssetReader {
|
|||||||
// Allow rendering in a iframe.
|
// Allow rendering in a iframe.
|
||||||
httpServer!.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
|
httpServer!.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
|
||||||
|
|
||||||
|
for (final MapEntry<String, String> header in extraHeaders.entries) {
|
||||||
|
httpServer.defaultResponseHeaders.add(header.key, header.value);
|
||||||
|
}
|
||||||
|
|
||||||
final PackageConfig packageConfig = buildInfo.packageConfig;
|
final PackageConfig packageConfig = buildInfo.packageConfig;
|
||||||
final Map<String, String> digests = <String, String>{};
|
final Map<String, String> digests = <String, String>{};
|
||||||
final Map<String, String> modules = <String, String>{};
|
final Map<String, String> modules = <String, String>{};
|
||||||
@ -653,6 +658,7 @@ class WebDevFS implements DevFS {
|
|||||||
required this.enableDds,
|
required this.enableDds,
|
||||||
required this.entrypoint,
|
required this.entrypoint,
|
||||||
required this.expressionCompiler,
|
required this.expressionCompiler,
|
||||||
|
required this.extraHeaders,
|
||||||
required this.chromiumLauncher,
|
required this.chromiumLauncher,
|
||||||
required this.nullAssertions,
|
required this.nullAssertions,
|
||||||
required this.nativeNullAssertions,
|
required this.nativeNullAssertions,
|
||||||
@ -670,6 +676,7 @@ class WebDevFS implements DevFS {
|
|||||||
final BuildInfo buildInfo;
|
final BuildInfo buildInfo;
|
||||||
final bool enableDwds;
|
final bool enableDwds;
|
||||||
final bool enableDds;
|
final bool enableDds;
|
||||||
|
final Map<String, String> extraHeaders;
|
||||||
final bool testMode;
|
final bool testMode;
|
||||||
final ExpressionCompiler? expressionCompiler;
|
final ExpressionCompiler? expressionCompiler;
|
||||||
final ChromiumLauncher? chromiumLauncher;
|
final ChromiumLauncher? chromiumLauncher;
|
||||||
@ -772,6 +779,7 @@ class WebDevFS implements DevFS {
|
|||||||
enableDds,
|
enableDds,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
expressionCompiler,
|
expressionCompiler,
|
||||||
|
extraHeaders,
|
||||||
nullSafetyMode,
|
nullSafetyMode,
|
||||||
testMode: testMode,
|
testMode: testMode,
|
||||||
);
|
);
|
||||||
|
@ -297,6 +297,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
|||||||
enableDds: debuggingOptions.enableDds,
|
enableDds: debuggingOptions.enableDds,
|
||||||
entrypoint: _fileSystem.file(target).uri,
|
entrypoint: _fileSystem.file(target).uri,
|
||||||
expressionCompiler: expressionCompiler,
|
expressionCompiler: expressionCompiler,
|
||||||
|
extraHeaders: debuggingOptions.webHeaders,
|
||||||
chromiumLauncher: _chromiumLauncher,
|
chromiumLauncher: _chromiumLauncher,
|
||||||
nullAssertions: debuggingOptions.nullAssertions,
|
nullAssertions: debuggingOptions.nullAssertions,
|
||||||
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
|
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
|
||||||
|
@ -59,6 +59,16 @@ abstract class DotEnvRegex {
|
|||||||
static final RegExp unquotedValue = RegExp(r'^([^#\n\s]*)\s*(?:\s*#\s*(.*))?$');
|
static final RegExp unquotedValue = RegExp(r'^([^#\n\s]*)\s*(?:\s*#\s*(.*))?$');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class _HttpRegex {
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
|
||||||
|
static const String _vchar = r'\x21-\x7E';
|
||||||
|
static const String _spaceOrTab = r'\x20\x09';
|
||||||
|
static const String _nonDelimiterVchar = r'\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E';
|
||||||
|
|
||||||
|
// --web-header is provided as key=value for consistency with --dart-define
|
||||||
|
static final RegExp httpHeader = RegExp('^([$_nonDelimiterVchar]+)' r'\s*=\s*' '([$_vchar$_spaceOrTab]+)' r'$');
|
||||||
|
}
|
||||||
|
|
||||||
enum ExitStatus {
|
enum ExitStatus {
|
||||||
success,
|
success,
|
||||||
warning,
|
warning,
|
||||||
@ -218,6 +228,14 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void usesWebOptions({ required bool verboseHelp }) {
|
void usesWebOptions({ required bool verboseHelp }) {
|
||||||
|
argParser.addMultiOption('web-header',
|
||||||
|
help: 'Additional key-value pairs that will added by the web server '
|
||||||
|
'as headers to all responses. Multiple headers can be passed by '
|
||||||
|
'repeating "--web-header" multiple times.',
|
||||||
|
valueHelp: 'X-Custom-Header=header-value',
|
||||||
|
splitCommas: false,
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
argParser.addOption('web-hostname',
|
argParser.addOption('web-hostname',
|
||||||
defaultsTo: 'localhost',
|
defaultsTo: 'localhost',
|
||||||
help:
|
help:
|
||||||
@ -1521,6 +1539,31 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
return dartDefinesSet.toList();
|
return dartDefinesSet.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, String> extractWebHeaders() {
|
||||||
|
final Map<String, String> webHeaders = <String, String>{};
|
||||||
|
|
||||||
|
if (argParser.options.containsKey('web-header')) {
|
||||||
|
final List<String> candidates = stringsArg('web-header');
|
||||||
|
final List<String> invalidHeaders = <String>[];
|
||||||
|
for (final String candidate in candidates) {
|
||||||
|
final Match? keyValueMatch = _HttpRegex.httpHeader.firstMatch(candidate);
|
||||||
|
if (keyValueMatch == null) {
|
||||||
|
invalidHeaders.add(candidate);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
webHeaders[keyValueMatch.group(1)!] = keyValueMatch.group(2)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidHeaders.isNotEmpty) {
|
||||||
|
throwToolExit('Invalid web headers: ${invalidHeaders.join(', ')}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return webHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
void _registerSignalHandlers(String commandPath, DateTime startTime) {
|
void _registerSignalHandlers(String commandPath, DateTime startTime) {
|
||||||
void handler(io.ProcessSignal s) {
|
void handler(io.ProcessSignal s) {
|
||||||
globals.cache.releaseLock();
|
globals.cache.releaseLock();
|
||||||
|
@ -919,6 +919,99 @@ void main() {
|
|||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('--web-header', () {
|
||||||
|
setUp(() {
|
||||||
|
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
||||||
|
fileSystem.file('pubspec.yaml').createSync();
|
||||||
|
fileSystem.file('.packages').createSync();
|
||||||
|
final FakeDevice device = FakeDevice(isLocalEmulator: true, platformType: PlatformType.android);
|
||||||
|
testDeviceManager.devices = <Device>[device];
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('can accept simple, valid values', () async {
|
||||||
|
final RunCommand command = RunCommand();
|
||||||
|
await expectLater(
|
||||||
|
() => createTestCommandRunner(command).run(<String>[
|
||||||
|
'run',
|
||||||
|
'--no-pub', '--no-hot',
|
||||||
|
'--web-header', 'foo = bar',
|
||||||
|
]), throwsToolExit());
|
||||||
|
|
||||||
|
final DebuggingOptions options = await command.createDebuggingOptions(true);
|
||||||
|
expect(options.webHeaders, <String, String>{'foo': 'bar'});
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Logger: () => BufferLogger.test(),
|
||||||
|
DeviceManager: () => testDeviceManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('throws a ToolExit when no value is provided', () async {
|
||||||
|
final RunCommand command = RunCommand();
|
||||||
|
await expectLater(
|
||||||
|
() => createTestCommandRunner(command).run(<String>[
|
||||||
|
'run',
|
||||||
|
'--no-pub', '--no-hot',
|
||||||
|
'--web-header',
|
||||||
|
'foo',
|
||||||
|
]), throwsToolExit(message: 'Invalid web headers: foo'));
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
() => command.createDebuggingOptions(true),
|
||||||
|
throwsToolExit(),
|
||||||
|
);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Logger: () => BufferLogger.test(),
|
||||||
|
DeviceManager: () => testDeviceManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('throws a ToolExit when value includes delimiter characters', () async {
|
||||||
|
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
||||||
|
fileSystem.file('pubspec.yaml').createSync();
|
||||||
|
fileSystem.file('.packages').createSync();
|
||||||
|
|
||||||
|
final RunCommand command = RunCommand();
|
||||||
|
await expectLater(
|
||||||
|
() => createTestCommandRunner(command).run(<String>[
|
||||||
|
'run',
|
||||||
|
'--no-pub', '--no-hot',
|
||||||
|
'--web-header', 'hurray/headers=flutter',
|
||||||
|
]), throwsToolExit());
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
() => command.createDebuggingOptions(true),
|
||||||
|
throwsToolExit(message: 'Invalid web headers: hurray/headers=flutter'),
|
||||||
|
);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Logger: () => BufferLogger.test(),
|
||||||
|
DeviceManager: () => testDeviceManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('accepts headers with commas in them', () async {
|
||||||
|
final RunCommand command = RunCommand();
|
||||||
|
await expectLater(
|
||||||
|
() => createTestCommandRunner(command).run(<String>[
|
||||||
|
'run',
|
||||||
|
'--no-pub', '--no-hot',
|
||||||
|
'--web-header', 'hurray=flutter,flutter=hurray',
|
||||||
|
]), throwsToolExit());
|
||||||
|
|
||||||
|
final DebuggingOptions options = await command.createDebuggingOptions(true);
|
||||||
|
expect(options.webHeaders, <String, String>{
|
||||||
|
'hurray': 'flutter,flutter=hurray'
|
||||||
|
});
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Logger: () => BufferLogger.test(),
|
||||||
|
DeviceManager: () => testDeviceManager,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('dart-defines and web-renderer options', () {
|
group('dart-defines and web-renderer options', () {
|
||||||
|
@ -680,6 +680,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullSafetyMode: NullSafetyMode.unsound,
|
nullSafetyMode: NullSafetyMode.unsound,
|
||||||
);
|
);
|
||||||
@ -792,6 +793,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullSafetyMode: NullSafetyMode.sound,
|
nullSafetyMode: NullSafetyMode.sound,
|
||||||
);
|
);
|
||||||
@ -901,6 +903,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null,
|
expressionCompiler: null,
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null,
|
chromiumLauncher: null,
|
||||||
nullSafetyMode: NullSafetyMode.sound,
|
nullSafetyMode: NullSafetyMode.sound,
|
||||||
);
|
);
|
||||||
@ -957,6 +960,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullAssertions: true,
|
nullAssertions: true,
|
||||||
nativeNullAssertions: true,
|
nativeNullAssertions: true,
|
||||||
@ -1001,6 +1005,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullSafetyMode: NullSafetyMode.sound,
|
nullSafetyMode: NullSafetyMode.sound,
|
||||||
);
|
);
|
||||||
@ -1044,6 +1049,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullSafetyMode: NullSafetyMode.sound,
|
nullSafetyMode: NullSafetyMode.sound,
|
||||||
);
|
);
|
||||||
@ -1075,6 +1081,7 @@ void main() {
|
|||||||
false,
|
false,
|
||||||
Uri.base,
|
Uri.base,
|
||||||
null,
|
null,
|
||||||
|
const <String, String>{},
|
||||||
NullSafetyMode.unsound,
|
NullSafetyMode.unsound,
|
||||||
testMode: true);
|
testMode: true);
|
||||||
|
|
||||||
@ -1082,6 +1089,37 @@ void main() {
|
|||||||
await webAssetServer.dispose();
|
await webAssetServer.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('passes on extra headers', () async {
|
||||||
|
const String extraHeaderKey = 'hurray';
|
||||||
|
const String extraHeaderValue = 'flutter';
|
||||||
|
final WebAssetServer webAssetServer = await WebAssetServer.start(
|
||||||
|
null,
|
||||||
|
'localhost',
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
const BuildInfo(
|
||||||
|
BuildMode.debug,
|
||||||
|
'',
|
||||||
|
treeShakeIcons: false,
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Uri.base,
|
||||||
|
null,
|
||||||
|
const <String, String>{
|
||||||
|
extraHeaderKey: extraHeaderValue,
|
||||||
|
},
|
||||||
|
NullSafetyMode.unsound,
|
||||||
|
testMode: true);
|
||||||
|
|
||||||
|
expect(webAssetServer.defaultResponseHeaders[extraHeaderKey], <String>[extraHeaderValue]);
|
||||||
|
|
||||||
|
await webAssetServer.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
test('WebAssetServer responds to POST requests with 404 not found', () => testbed.run(() async {
|
test('WebAssetServer responds to POST requests with 404 not found', () => testbed.run(() async {
|
||||||
final Response response = await webAssetServer.handleRequest(
|
final Response response = await webAssetServer.handleRequest(
|
||||||
Request('POST', Uri.parse('http://foobar/something')),
|
Request('POST', Uri.parse('http://foobar/something')),
|
||||||
@ -1147,6 +1185,7 @@ void main() {
|
|||||||
entrypoint: Uri.base,
|
entrypoint: Uri.base,
|
||||||
testMode: true,
|
testMode: true,
|
||||||
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
||||||
|
extraHeaders: const <String, String>{},
|
||||||
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
||||||
nullSafetyMode: NullSafetyMode.unsound,
|
nullSafetyMode: NullSafetyMode.unsound,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user