diff --git a/.azure/pipelines/blazor-daily-tests.yml b/.azure/pipelines/blazor-daily-tests.yml deleted file mode 100644 index 537751bfed6b..000000000000 --- a/.azure/pipelines/blazor-daily-tests.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Uses Scheduled Triggers, which aren't supported in YAML yet. -# https://docs.microsoft.com/en-us/azure/devops/pipelines/build/triggers?view=vsts&tabs=yaml#scheduled - -# Daily Tests for Blazor -# These use Sauce Labs resources, hence they run daily rather than per-commit. - -# We just need one Windows machine because all it does is trigger SauceLabs. -variables: - SAUCE_CONNECT_DOWNLOAD_ON_INSTALL: true - E2ETESTS_SauceTest: true - E2ETESTS_Sauce__TunnelIdentifier: 'blazor-e2e-sc-proxy-tunnel' - E2ETESTS_Sauce__HostName: 'sauce.local' -jobs: -- template: jobs/default-build.yml - parameters: - buildDirectory: src/Components - isTestingJob: true - agentOs: Windows - jobName: BlazorDailyTests - jobDisplayName: "Blazor Daily Tests" - afterBuild: - - # macOS/Safari - - script: 'dotnet test --filter "StandaloneAppTest"' - workingDirectory: 'src/Components/test/E2ETest' - displayName: 'Run Blazor tests - macOS/Safari' - condition: succeededOrFailed() - env: - # Secrets need to be explicitly mapped to env variables. - E2ETESTS_Sauce__Username: '$(asplab-sauce-labs-username)' - E2ETESTS_Sauce__AccessKey: '$(asplab-sauce-labs-access-key)' - # Set platform/browser configuration. - E2ETESTS_Sauce__TestName: 'Blazor Daily Tests - macOS/Safari' - E2ETESTS_Sauce__PlatformName: 'macOS 10.14' - E2ETESTS_Sauce__BrowserName: 'Safari' - # Need to explicitly set version here because some older versions don't support timeouts in Safari. - E2ETESTS_Sauce__SeleniumVersion: '3.4.0' - - # Android/Chrome - - script: 'dotnet test --filter "StandaloneAppTest"' - workingDirectory: 'src/Components/test/E2ETest' - displayName: 'Run Blazor tests - Android/Chrome' - condition: succeededOrFailed() - env: - # Secrets need to be explicitly mapped to env variables. - E2ETESTS_Sauce__Username: '$(asplab-sauce-labs-username)' - E2ETESTS_Sauce__AccessKey: '$(asplab-sauce-labs-access-key)' - # Set platform/browser configuration. - E2ETESTS_Sauce__TestName: 'Blazor Daily Tests - Android/Chrome' - E2ETESTS_Sauce__PlatformName: 'Android' - E2ETESTS_Sauce__PlatformVersion: '10.0' - E2ETESTS_Sauce__BrowserName: 'Chrome' - E2ETESTS_Sauce__DeviceName: 'Android GoogleAPI Emulator' - E2ETESTS_Sauce__DeviceOrientation: 'portrait' - E2ETESTS_Sauce__AppiumVersion: '1.9.1' - artifacts: - - name: Windows_Logs - path: ../../artifacts/log/ - publishOnError: true \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets index a91198a327ce..34e36b4b06bd 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -131,7 +131,7 @@ - $(AspNetCoreMajorMinorVersion).0.0 + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).0.0 diff --git a/eng/Versions.props b/eng/Versions.props index 0261b4b23076..4fcd41e8baaf 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -29,18 +29,18 @@ true true - $(AspNetCoreMajorMinorVersion).$(AspNetCorePatchVersion) + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion) $(VersionPrefix) - $(AspNetCoreMajorMinorVersion).0 + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).0 0.3.$(AspNetCorePatchVersion) $([MSBuild]::Add(10, $(AspNetCoreMajorVersion))) $(AspNetCoreMinorVersion) $(AspNetCorePatchVersion) - $(AspNetCoreMajorMinorVersion).$([MSBuild]::Subtract($(AspNetCorePatchVersion), 1)) + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$([MSBuild]::Subtract($(AspNetCorePatchVersion), 1)) diff --git a/eng/helix/content/RunTests/RunTestsOptions.cs b/eng/helix/content/RunTests/RunTestsOptions.cs index 5bb6b46a308f..bd611ef46ee5 100644 --- a/eng/helix/content/RunTests/RunTestsOptions.cs +++ b/eng/helix/content/RunTests/RunTestsOptions.cs @@ -60,11 +60,6 @@ public static RunTestsOptions Parse(string[] args) aliases: new string[] { "--helixTimeout" }, description: "The timeout duration of the Helix job") { Argument = new Argument(), Required = true }, - - new Option( - aliases: new string[] { "--source" }, - description: "The restore sources to use during testing") - { Argument = new Argument() { Arity = ArgumentArity.ZeroOrMore }, Required = true } }; var parseResult = command.Parse(args); @@ -78,7 +73,6 @@ public static RunTestsOptions Parse(string[] args) options.AspNetRuntime = parseResult.ValueForOption("--aspnetruntime"); options.AspNetRef = parseResult.ValueForOption("--aspnetref"); options.Timeout = TimeSpan.Parse(parseResult.ValueForOption("--helixTimeout")); - options.Source = parseResult.ValueForOption>("--source"); options.HELIX_WORKITEM_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_ROOT"); options.Path = Environment.GetEnvironmentVariable("PATH"); options.DotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); @@ -97,7 +91,6 @@ public static RunTestsOptions Parse(string[] args) public string HELIX_WORKITEM_ROOT { get; set;} public string DotnetRoot { get; set; } public string Path { get; set; } - public IEnumerable Source { get; set; } public TimeSpan Timeout { get; set; } } } diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index 8722306e0993..4eb8c903e2c5 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -126,16 +126,13 @@ await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); - foreach (var restoreSource in Options.Source) - { - await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", - $"nuget add source {restoreSource} --configfile NuGet.config", - environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, - throwOnError: false, - cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); - } + await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", + "nuget add source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --configfile NuGet.config", + environmentVariables: EnvironmentVariables, + outputDataReceived: Console.WriteLine, + errorDataReceived: Console.Error.WriteLine, + throwOnError: false, + cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); // Write nuget sources to console, useful for debugging purposes await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", @@ -250,7 +247,7 @@ public async Task RunTestsAsync() { // Timeout test run 5 minutes before the Helix job would timeout var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); - var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; + var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; if (Options.Quarantined) { Console.WriteLine("Running quarantined tests."); @@ -349,7 +346,7 @@ public void UploadResults() else { Console.WriteLine("No dmps found in TestResults"); - } + } } } } diff --git a/eng/helix/content/runtests.cmd b/eng/helix/content/runtests.cmd index a3f1491d2f50..af984dd88274 100644 --- a/eng/helix/content/runtests.cmd +++ b/eng/helix/content/runtests.cmd @@ -30,11 +30,11 @@ echo "Invoking InstallDotNet.ps1 %$arch% %$sdkVersion% %$runtimeVersion% %DOTNET powershell.exe -NoProfile -ExecutionPolicy unrestricted -file InstallDotNet.ps1 %$arch% %$sdkVersion% %$runtimeVersion% %DOTNET_ROOT% set exit_code=0 -echo "Restore: dotnet restore RunTests\RunTests.csproj --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json --source https://api.nuget.org/v3/index.json --ignore-failed-sources" -dotnet restore RunTests\RunTests.csproj --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json --source https://api.nuget.org/v3/index.json --ignore-failed-sources +echo "Restore: dotnet restore RunTests\RunTests.csproj --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://api.nuget.org/v3/index.json --ignore-failed-sources..." +dotnet restore RunTests\RunTests.csproj --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://api.nuget.org/v3/index.json --ignore-failed-sources -echo "Running tests: dotnet run --project RunTests\RunTests.csproj -- --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --ef %$ef% --aspnetruntime %$aspnetruntime% --aspnetref %$aspnetref% --helixTimeout %$helixTimeout%" -dotnet run --project RunTests\RunTests.csproj -- --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --ef %$ef% --aspnetruntime %$aspnetruntime% --aspnetref %$aspnetref% --helixTimeout %$helixTimeout% +echo "Running tests: dotnet run --project RunTests\RunTests.csproj -- --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --ef %$ef% --aspnetruntime %$aspnetruntime% --aspnetref %$aspnetref% --helixTimeout %$helixTimeout%..." +dotnet run --project RunTests\RunTests.csproj -- --target %$target% --runtime %$aspRuntimeVersion% --queue %$queue% --arch %$arch% --quarantined %$quarantined% --ef %$ef% --aspnetruntime %$aspnetruntime% --aspnetref %$aspnetref% --helixTimeout %$helixTimeout% if errorlevel neq 0 ( set exit_code=%errorlevel% ) diff --git a/src/Components/Components/src/ComponentBase.cs b/src/Components/Components/src/ComponentBase.cs index 6d4eb6109554..dd80af0fd3bf 100644 --- a/src/Components/Components/src/ComponentBase.cs +++ b/src/Components/Components/src/ComponentBase.cs @@ -200,8 +200,9 @@ void IComponent.Attach(RenderHandle renderHandle) /// A that completes when the component has finished updating and rendering itself. /// /// - /// Parameters are passed when is called. It is not required that - /// the caller supply a parameter value for all of the parameters that are logically understood by the component. + /// The method should be passed the entire set of parameter values each + /// time is called. It not required that the caller supply a parameter + /// value for all parameters that are logically understood by the component. /// /// /// The default implementation of will set the value of each property diff --git a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj index 8b58eb02dd68..2d7aabeee71c 100644 --- a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj +++ b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -12,4 +12,8 @@ + + + + diff --git a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs index 14e1600b9f33..cbc07b36a92c 100644 --- a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs +++ b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs @@ -78,7 +78,7 @@ TResult IJSUnmarshalledRuntime.InvokeUnmarshalled(string id var callInfo = new JSCallInfo { FunctionIdentifier = identifier, - ResultType = ResultTypeFromGeneric() + ResultType = JSCallResultTypeHelper.FromGeneric() }; var result = InternalCalls.InvokeJS(out var exception, ref callInfo, arg0, arg1, arg2); diff --git a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor index b24da1366bd6..388e74549331 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor @@ -137,7 +137,7 @@ ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke("returnArray").Select(x => x.Source).ToArray()); } - var jsObjectReference = await JSRuntime.InvokeAsync("returnJSObjectReference"); + var jsObjectReference = await JSRuntime.InvokeAsync("returnJSObjectReference"); ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync("identity", "Invoked from JSObjectReference"); ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync("nested.add", 2, 3)).ToString(); ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString(); @@ -151,7 +151,7 @@ JSObjectReferenceInvokeNonFunctionException = e; } - var module = await JSRuntime.InvokeAsync("import", "./js/testmodule.js"); + var module = await JSRuntime.InvokeAsync("import", "./js/testmodule.js"); ReturnValues["jsObjectReferenceModule"] = await module.InvokeAsync("identity", "Returned from module!"); if (shouldSupportSyncInterop) @@ -192,7 +192,7 @@ { var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); - var jsInProcObjectReference = inProcRuntime.Invoke("returnJSObjectReference"); + var jsInProcObjectReference = inProcRuntime.Invoke("returnJSObjectReference"); ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke("identity", "Invoked from JSInProcessObjectReference"); } diff --git a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs index 8667bdc2c61f..55fc6e8fc679 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs +++ b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs @@ -415,20 +415,20 @@ public static int ExtractNonSerializedValue(DotNetObjectReference objec } [JSInvokable] - public static JSObjectReference RoundTripJSObjectReference(JSObjectReference jsObjectReference) + public static IJSObjectReference RoundTripJSObjectReference(IJSObjectReference jsObjectReference) { return jsObjectReference; } [JSInvokable] - public static async Task RoundTripJSObjectReferenceAsync(JSObjectReference jSObjectReference) + public static async Task RoundTripJSObjectReferenceAsync(IJSObjectReference jSObjectReference) { await Task.Yield(); return jSObjectReference; } [JSInvokable] - public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectReference jsObjectReference) + public static string InvokeDisposedJSObjectReferenceException(IJSInProcessObjectReference jsObjectReference) { try { @@ -442,7 +442,7 @@ public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectR } [JSInvokable] - public static async Task InvokeDisposedJSObjectReferenceExceptionAsync(JSObjectReference jsObjectReference) + public static async Task InvokeDisposedJSObjectReferenceExceptionAsync(IJSObjectReference jsObjectReference) { try { diff --git a/src/Framework/Directory.Build.props b/src/Framework/Directory.Build.props index 13342f6cde8e..7e8014e865f1 100644 --- a/src/Framework/Directory.Build.props +++ b/src/Framework/Directory.Build.props @@ -7,8 +7,8 @@ PlatformManifest.txt $(ArtifactsObjDir)$(PlatformManifestFileName) - - $(AspNetCoreMajorMinorVersion).0 + + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).0 $(ReferencePackSharedFxVersion)-$(VersionSuffix) diff --git a/src/Installers/Debian/Runtime/Debian.Runtime.debproj b/src/Installers/Debian/Runtime/Debian.Runtime.debproj index c780e24d2bf9..70931ff3a4ed 100644 --- a/src/Installers/Debian/Runtime/Debian.Runtime.debproj +++ b/src/Installers/Debian/Runtime/Debian.Runtime.debproj @@ -8,7 +8,7 @@ $(SharedFrameworkLayoutRoot) - $(RuntimeInstallerBaseName)-$(AspNetCoreMajorMinorVersion) + $(RuntimeInstallerBaseName)-$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) $(MicrosoftNETCoreAppRuntimeVersion) diff --git a/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj b/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj index 5e4282a631f3..43eba9135887 100644 --- a/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj +++ b/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj @@ -8,10 +8,10 @@ $(TargetingPackLayoutRoot) - $(TargetingPackInstallerBaseName)-$(AspNetCoreMajorMinorVersion) + $(TargetingPackInstallerBaseName)-$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) ASP.NET Core Targeting Pack - Provides a default set of APIs for building an ASP.NET Core $(AspNetCoreMajorMinorVersion) application. Contains reference assemblies, documentation, and other design-time assets. + Provides a default set of APIs for building an ASP.NET Core $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) application. Contains reference assemblies, documentation, and other design-time assets. $(MicrosoftNETCoreAppRefPackageVersion) diff --git a/src/Installers/Rpm/Rpm.Runtime.Common.targets b/src/Installers/Rpm/Rpm.Runtime.Common.targets index 18a9975c0fe6..dea8c0148d67 100644 --- a/src/Installers/Rpm/Rpm.Runtime.Common.targets +++ b/src/Installers/Rpm/Rpm.Runtime.Common.targets @@ -4,8 +4,8 @@ - $(RuntimeInstallerBaseName)-$(AspNetCoreMajorMinorVersion) - $(AspNetCoreMajorMinorVersion).$(AspNetCorePatchVersion) + $(RuntimeInstallerBaseName)-$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion) 1 @@ -24,6 +24,6 @@ - + diff --git a/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj b/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj index 688e9de95efe..38f5fbf78bb6 100644 --- a/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj +++ b/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj @@ -8,10 +8,10 @@ - $(TargetingPackInstallerBaseName)-$(AspNetCoreMajorMinorVersion) + $(TargetingPackInstallerBaseName)-$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) ASP.NET Core Targeting Pack - Provides a default set of APIs for building an ASP.NET Core $(AspNetCoreMajorMinorVersion) application. Contains reference assemblies, documentation, and other design-time assets. + Provides a default set of APIs for building an ASP.NET Core $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) application. Contains reference assemblies, documentation, and other design-time assets. @@ -21,7 +21,7 @@ - + diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props index cf5ef5f48827..d08b147e4ae5 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props +++ b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props @@ -15,7 +15,7 @@ $(BUILD_MINOR) - 1$(AspNetCoreMajorMinorVersion).$(BUILD_MAJOR) + 1$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(BUILD_MAJOR) $(ANCMFolderVersion).0 - $(AspNetCoreMajorMinorVersion).$(AspNetCorePatchVersion).0 + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion).0 <_FileRevisionVersion>$(VersionSuffixDateStamp) <_FileRevisionVersion Condition=" '$(_FileRevisionVersion)' == '' ">42424 - $(AspNetCoreMajorMinorVersion).$(AspNetCorePatchVersion).$(_FileRevisionVersion) + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion).$(_FileRevisionVersion) $(DefineConstants);MajorVersion=$(AspNetCoreMajorVersion) $(DefineConstants);MinorVersion=$(AspNetCoreMinorVersion) @@ -56,7 +56,7 @@ the upgrade code. Bundle upgrades pivot on Major.Minor.Patch changes. For example, 3.0.1-preview 1 can upgrade to 3.0.1-preview 8, but 3.0.1 cannot upgrade to 3.0.2 or 3.1. --> - $(AspNetCoreMajorMinorVersion).$(AspNetCorePatchVersion);$(Platform) + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$(AspNetCorePatchVersion);$(Platform) diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs new file mode 100644 index 000000000000..2c3cbbde6926 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + /// + /// Represents a reference to a JavaScript object whose functions can be invoked synchronously. + /// + public interface IJSInProcessObjectReference : IJSObjectReference + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + TValue Invoke(string identifier, params object?[]? args); + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs new file mode 100644 index 000000000000..43e67f0cb18b --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Represents a reference to a JavaScript object. + /// + public interface IJSObjectReference : IAsyncDisposable + { + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different, or no timeout, + /// consider using . + /// + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + ValueTask InvokeAsync(string identifier, object?[]? args); + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args); + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs index f26efa92a138..d18f7092b614 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs @@ -7,17 +7,21 @@ namespace Microsoft.JSInterop.Infrastructure { - internal sealed class JSObjectReferenceJsonConverter - : JsonConverter where TJSObjectReference : JSObjectReference + internal sealed class JSObjectReferenceJsonConverter : JsonConverter + where TInterface : class, IJSObjectReference + where TImplementation : JSObjectReference, TInterface { - private readonly Func _jsObjectReferenceFactory; + private readonly Func _jsObjectReferenceFactory; - public JSObjectReferenceJsonConverter(Func jsObjectReferenceFactory) + public JSObjectReferenceJsonConverter(Func jsObjectReferenceFactory) { _jsObjectReferenceFactory = jsObjectReferenceFactory; } - public override TJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override bool CanConvert(Type typeToConvert) + => typeToConvert == typeof(TInterface) || typeToConvert == typeof(TImplementation); + + public override TInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { long id = -1; @@ -49,10 +53,10 @@ public JSObjectReferenceJsonConverter(Func jsObjectRef return _jsObjectReferenceFactory(id); } - public override void Write(Utf8JsonWriter writer, TJSObjectReference value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) { writer.WriteStartObject(); - writer.WriteNumber(JSObjectReference.IdKey, value.Id); + writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id); writer.WriteEndObject(); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs index bd97a8f7a071..44dcba72874d 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs @@ -5,10 +5,7 @@ namespace Microsoft.JSInterop { - /// - /// Represents a reference to a JavaScript object whose functions can be invoked synchronously. - /// - public class JSInProcessObjectReference : JSObjectReference + internal class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference { private readonly JSInProcessRuntime _jsRuntime; @@ -17,15 +14,8 @@ internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : bas _jsRuntime = jsRuntime; } - /// - /// Invokes the specified JavaScript function synchronously. - /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. - /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. [return: MaybeNull] - public TValue Invoke(string identifier, params object[] args) + public TValue Invoke(string identifier, params object?[]? args) { ThrowIfDisposed(); diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReferenceExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReferenceExtensions.cs new file mode 100644 index 000000000000..57b9cc66fe46 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReferenceExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + public static class JSInProcessObjectReferenceExtensions + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + public static void InvokeVoid(this IJSInProcessObjectReference jsObjectReference, string identifier, params object?[] args) + { + if (jsObjectReference == null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + jsObjectReference.Invoke(identifier, args); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs index 092f29533058..2912c2db066a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs @@ -17,7 +17,7 @@ public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime /// protected JSInProcessRuntime() { - JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter( + JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter( id => new JSInProcessObjectReference(this, id))); } @@ -27,7 +27,7 @@ internal TValue Invoke(string identifier, long targetInstanceId, params var resultJson = InvokeJS( identifier, JsonSerializer.Serialize(args, JsonSerializerOptions), - ResultTypeFromGeneric(), + JSCallResultTypeHelper.FromGeneric(), targetInstanceId); if (resultJson is null) diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs index ef98c2dfc924..ea87657a07b3 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs @@ -8,77 +8,37 @@ namespace Microsoft.JSInterop { - /// - /// Represents a reference to a JavaScript object. - /// - public class JSObjectReference : IAsyncDisposable + internal class JSObjectReference : IJSObjectReference { - internal static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId"); + public static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId"); private readonly JSRuntime _jsRuntime; private bool _disposed; - internal long Id { get; } + public long Id { get; } - internal JSObjectReference(JSRuntime jsRuntime, long id) + public JSObjectReference(JSRuntime jsRuntime, long id) { _jsRuntime = jsRuntime; Id = id; } - /// - /// Invokes the specified JavaScript function asynchronously. - /// - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. - /// JSON-serializable arguments. - /// A that represents the asynchronous invocation operation. - public async ValueTask InvokeVoidAsync(string identifier, params object[] args) - { - await InvokeAsync(identifier, args); - } - - /// - /// Invokes the specified JavaScript function asynchronously. - /// - /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different, or no timeout, - /// consider using . - /// - /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. - /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. - public ValueTask InvokeAsync(string identifier, params object[] args) + public ValueTask InvokeAsync(string identifier, object?[]? args) { ThrowIfDisposed(); return _jsRuntime.InvokeAsync(Id, identifier, args); } - /// - /// Invokes the specified JavaScript function asynchronously. - /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. - /// - /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts - /// () from being applied. - /// - /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. - public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, params object[] args) + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) { ThrowIfDisposed(); return _jsRuntime.InvokeAsync(Id, identifier, cancellationToken, args); } - /// - /// Disposes the , freeing its resources and disabling it from further use. - /// - /// A representing the completion of the operation. public async ValueTask DisposeAsync() { if (!_disposed) @@ -89,9 +49,6 @@ public async ValueTask DisposeAsync() } } - /// - /// Throws an exception if this instance has been disposed. - /// protected void ThrowIfDisposed() { if (_disposed) diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs new file mode 100644 index 000000000000..ee5d658cf6b9 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs @@ -0,0 +1,139 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Extensions for . + /// + public static class JSObjectReferenceExtensions + { + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + await jsObjectReference.InvokeAsync(identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different timeout, or no timeout, + /// consider using . + /// + /// + /// The . + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public static ValueTask InvokeAsync(this IJSObjectReference jsObjectReference, string identifier, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + return jsObjectReference.InvokeAsync(identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public static ValueTask InvokeAsync(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + return jsObjectReference.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + await jsObjectReference.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeAsync(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + return await jsObjectReference.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args) + { + if (jsObjectReference is null) + { + throw new ArgumentNullException(nameof(jsObjectReference)); + } + + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + await jsObjectReference.InvokeAsync(identifier, cancellationToken, args); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 7e69eb3d2192..25a885a7b9f5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -37,7 +37,7 @@ protected JSRuntime() Converters = { new DotNetObjectReferenceJsonConverterFactory(this), - new JSObjectReferenceJsonConverter(id => new JSObjectReference(this, id)), + new JSObjectReferenceJsonConverter(id => new JSObjectReference(this, id)), } }; } @@ -52,17 +52,6 @@ protected JSRuntime() /// protected TimeSpan? DefaultAsyncTimeout { get; set; } - /// - /// Creates a from the given generic type. - /// - /// - /// The type of the result of the relevant JS interop call. - /// - protected static JSCallResultType ResultTypeFromGeneric() - => typeof(TResult) == typeof(JSObjectReference) || typeof(TResult) == typeof(JSInProcessObjectReference) ? - JSCallResultType.JSObjectReference : - JSCallResultType.Default; - /// /// Invokes the specified JavaScript function asynchronously. /// @@ -134,7 +123,7 @@ internal ValueTask InvokeAsync( var argsJson = args?.Any() == true ? JsonSerializer.Serialize(args, JsonSerializerOptions) : null; - var resultType = ResultTypeFromGeneric(); + var resultType = JSCallResultTypeHelper.FromGeneric(); BeginInvokeJS(taskId, identifier, argsJson, resultType, targetInstanceId); diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index ebdf3cd31902..46679918338b 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -13,6 +13,10 @@ + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs index 75f4388c15a6..0b7948cf56e7 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs @@ -18,7 +18,7 @@ public void Read_Throws_IfJsonIsMissingJSObjectIdProperty() var json = "{}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); Assert.Equal("Required property __jsObjectId not found.", ex.Message); } @@ -29,7 +29,7 @@ public void Read_Throws_IfJsonContainsUnknownContent() var json = "{\"foo\":2}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); Assert.Equal("Unexcepted JSON property foo.", ex.Message); } @@ -40,7 +40,7 @@ public void Read_Throws_IfJsonIsIncomplete() var json = $"{{\"__jsObjectId\":5"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); Assert.IsAssignableFrom(ex); } @@ -51,7 +51,7 @@ public void Read_Throws_IfJSObjectIdAppearsMultipleTimes() var json = $"{{\"__jsObjectId\":3,\"__jsObjectId\":7}}"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); Assert.IsAssignableFrom(ex); } @@ -63,7 +63,7 @@ public void Read_ReadsJson() var json = $"{{\"__jsObjectId\":{expectedId}}}"; // Act - var deserialized = JsonSerializer.Deserialize(json, JsonSerializerOptions)!; + var deserialized = (JSObjectReference)JsonSerializer.Deserialize(json, JsonSerializerOptions)!; // Assert Assert.Equal(expectedId, deserialized?.Id); @@ -76,7 +76,7 @@ public void Write_WritesValidJson() var jsObjectRef = new JSObjectReference(JSRuntime, 7); // Act - var json = JsonSerializer.Serialize(jsObjectRef, JsonSerializerOptions); + var json = JsonSerializer.Serialize((IJSObjectReference)jsObjectRef, JsonSerializerOptions); // Assert Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json); diff --git a/src/Middleware/CORS/src/Infrastructure/CorsPolicy.cs b/src/Middleware/CORS/src/Infrastructure/CorsPolicy.cs index c620cfd187f8..54f48fe16a7c 100644 --- a/src/Middleware/CORS/src/Infrastructure/CorsPolicy.cs +++ b/src/Middleware/CORS/src/Infrastructure/CorsPolicy.cs @@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure /// public class CorsPolicy { - private Func _isOriginAllowed; private TimeSpan? _preflightMaxAge; /// @@ -21,7 +20,7 @@ public class CorsPolicy /// public CorsPolicy() { - _isOriginAllowed = DefaultIsOriginAllowed; + IsOriginAllowed = DefaultIsOriginAllowed; } /// @@ -72,26 +71,10 @@ public bool AllowAnyOrigin } } - /// - /// Gets a value indicating if is the default function that is set in the CorsPolicy constructor. - /// - internal bool IsDefaultIsOriginAllowed { get; private set; } = true; - /// /// Gets or sets a function which evaluates whether an origin is allowed. /// - public Func IsOriginAllowed - { - get - { - return _isOriginAllowed; - } - set - { - _isOriginAllowed = value; - IsDefaultIsOriginAllowed = false; - } - } + public Func IsOriginAllowed { get; set; } /// /// Gets the headers that the resource might use and can be exposed. diff --git a/src/Middleware/CORS/src/Infrastructure/CorsService.cs b/src/Middleware/CORS/src/Infrastructure/CorsService.cs index ff1bcdb49da2..8ed5ca318ceb 100644 --- a/src/Middleware/CORS/src/Infrastructure/CorsService.cs +++ b/src/Middleware/CORS/src/Infrastructure/CorsService.cs @@ -119,7 +119,7 @@ private static void PopulateResult(HttpContext context, CorsPolicy policy, CorsR { var origin = headers[CorsConstants.Origin]; result.AllowedOrigin = origin; - result.VaryByOrigin = policy.Origins.Count > 1 || !policy.IsDefaultIsOriginAllowed; + result.VaryByOrigin = policy.Origins.Count > 1; } result.SupportsCredentials = policy.SupportsCredentials; diff --git a/src/Middleware/CORS/test/UnitTests/CorsPolicyTests.cs b/src/Middleware/CORS/test/UnitTests/CorsPolicyTests.cs index af8ab5de742c..e99b04096c8e 100644 --- a/src/Middleware/CORS/test/UnitTests/CorsPolicyTests.cs +++ b/src/Middleware/CORS/test/UnitTests/CorsPolicyTests.cs @@ -25,20 +25,6 @@ public void Default_Constructor() Assert.Empty(corsPolicy.Origins); Assert.Null(corsPolicy.PreflightMaxAge); Assert.NotNull(corsPolicy.IsOriginAllowed); - Assert.True(corsPolicy.IsDefaultIsOriginAllowed); - } - - [Fact] - public void IsDefaultIsOriginAllowed_IsFalseAfterSettingIsOriginAllowed() - { - // Arrange - var policy = new CorsPolicy(); - - // Act - policy.IsOriginAllowed = origin => true; - - // Assert - Assert.False(policy.IsDefaultIsOriginAllowed); } [Fact] @@ -85,4 +71,4 @@ public void ToString_ReturnsThePropertyValues() policyString); } } -} +} \ No newline at end of file diff --git a/src/Middleware/CORS/test/UnitTests/CorsServiceTests.cs b/src/Middleware/CORS/test/UnitTests/CorsServiceTests.cs index e47ed0261465..e9bf95531365 100644 --- a/src/Middleware/CORS/test/UnitTests/CorsServiceTests.cs +++ b/src/Middleware/CORS/test/UnitTests/CorsServiceTests.cs @@ -223,23 +223,6 @@ public void EvaluatePolicy_AllowMultipleOrigins_VariesByOrigin() Assert.True(result.VaryByOrigin); } - [Fact] - public void EvaluatePolicy_SetIsOriginAllowed_VariesByOrigin() - { - // Arrange - var corsService = GetCorsService(); - var requestContext = GetHttpContext(origin: "http://example.com"); - var policy = new CorsPolicy(); - policy.IsOriginAllowed = origin => true; - - // Act - var result = corsService.EvaluatePolicy(requestContext, policy); - - // Assert - Assert.Equal("http://example.com", result.AllowedOrigin); - Assert.True(result.VaryByOrigin); - } - [Fact] public void EvaluatePolicy_NoExposedHeaders_NoAllowExposedHeaders() { diff --git a/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs b/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs index 6c14cca67fc0..4defbc5d2ada 100644 --- a/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs +++ b/src/Mvc/Mvc.Core/src/Builder/ControllerEndpointRouteBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -506,10 +506,18 @@ public static void MapDynamicControllerRoute(this IEndpointRouteBu EnsureControllerServices(endpoints); // Called for side-effect to make sure that the data source is registered. - var controllerDataSource = GetOrCreateDataSource(endpoints); - - // The data source is just used to share the common order with conventionally routed actions. - controllerDataSource.AddDynamicControllerEndpoint(endpoints, pattern, typeof(TTransformer), state); + GetOrCreateDataSource(endpoints).CreateInertEndpoints = true; + + endpoints.Map( + pattern, + context => + { + throw new InvalidOperationException("This endpoint is not expected to be executed directly."); + }) + .Add(b => + { + b.Metadata.Add(new DynamicControllerRouteValueTransformerMetadata(typeof(TTransformer), state)); + }); } private static DynamicControllerMetadata CreateDynamicControllerMetadata(string action, string controller, string area) diff --git a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs index 4f5593e86acd..82181cd034d1 100644 --- a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs +++ b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs @@ -269,7 +269,6 @@ internal static void AddMvcCoreServices(IServiceCollection services) // // Endpoint Routing / Endpoints // - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/Mvc/Mvc.Core/src/DependencyInjection/OrderedEndpointsSequenceProvider.cs b/src/Mvc/Mvc.Core/src/DependencyInjection/OrderedEndpointsSequenceProvider.cs deleted file mode 100644 index 132f7dfb6d80..000000000000 --- a/src/Mvc/Mvc.Core/src/DependencyInjection/OrderedEndpointsSequenceProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Mvc.Infrastructure -{ - internal class OrderedEndpointsSequenceProvider - { - private object Lock = new object(); - - // In traditional conventional routing setup, the routes defined by a user have a order - // defined by how they are added into the list. We would like to maintain the same order when building - // up the endpoints too. - // - // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'. - // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world. - private int _current = 1; - - public int GetNext() - { - lock (Lock) - { - return _current++; - } - } - } -} diff --git a/src/Mvc/Mvc.Core/src/Routing/ControllerActionEndpointDataSource.cs b/src/Mvc/Mvc.Core/src/Routing/ControllerActionEndpointDataSource.cs index fb9288b9cf84..8a27c982dc98 100644 --- a/src/Mvc/Mvc.Core/src/Routing/ControllerActionEndpointDataSource.cs +++ b/src/Mvc/Mvc.Core/src/Routing/ControllerActionEndpointDataSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -15,19 +15,27 @@ namespace Microsoft.AspNetCore.Mvc.Routing internal class ControllerActionEndpointDataSource : ActionEndpointDataSourceBase { private readonly ActionEndpointFactory _endpointFactory; - private readonly OrderedEndpointsSequenceProvider _orderSequence; private readonly List _routes; + private int _order; + public ControllerActionEndpointDataSource( IActionDescriptorCollectionProvider actions, - ActionEndpointFactory endpointFactory, - OrderedEndpointsSequenceProvider orderSequence) + ActionEndpointFactory endpointFactory) : base(actions) { _endpointFactory = endpointFactory; - _orderSequence = orderSequence; + _routes = new List(); + // In traditional conventional routing setup, the routes defined by a user have a order + // defined by how they are added into the list. We would like to maintain the same order when building + // up the endpoints too. + // + // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'. + // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world. + _order = 1; + DefaultBuilder = new ControllerActionEndpointConventionBuilder(Lock, Conventions); // IMPORTANT: this needs to be the last thing we do in the constructor. @@ -51,7 +59,7 @@ public ControllerActionEndpointConventionBuilder AddRoute( lock (Lock) { var conventions = new List>(); - _routes.Add(new ConventionalRouteEntry(routeName, pattern, defaults, constraints, dataTokens, _orderSequence.GetNext(), conventions)); + _routes.Add(new ConventionalRouteEntry(routeName, pattern, defaults, constraints, dataTokens, _order++, conventions)); return new ControllerActionEndpointConventionBuilder(Lock, conventions); } } @@ -100,27 +108,6 @@ protected override List CreateEndpoints(IReadOnlyList - { - throw new InvalidOperationException("This endpoint is not expected to be executed directly."); - }) - .Add(b => - { - ((RouteEndpointBuilder)b).Order = order; - b.Metadata.Add(new DynamicControllerRouteValueTransformerMetadata(transformerType, state)); - }); - } - } } } diff --git a/src/Mvc/Mvc.Core/test/Routing/ControllerActionEndpointDataSourceTest.cs b/src/Mvc/Mvc.Core/test/Routing/ControllerActionEndpointDataSourceTest.cs index 6001dab41545..deb97532ce6b 100644 --- a/src/Mvc/Mvc.Core/test/Routing/ControllerActionEndpointDataSourceTest.cs +++ b/src/Mvc/Mvc.Core/test/Routing/ControllerActionEndpointDataSourceTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -385,7 +385,7 @@ private static bool SupportsLinkGeneration(RouteEndpoint endpoint) private protected override ActionEndpointDataSourceBase CreateDataSource(IActionDescriptorCollectionProvider actions, ActionEndpointFactory endpointFactory) { - return new ControllerActionEndpointDataSource(actions, endpointFactory, new OrderedEndpointsSequenceProvider()); + return new ControllerActionEndpointDataSource(actions, endpointFactory); } protected override ActionDescriptor CreateActionDescriptor( diff --git a/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs b/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs index bd0c27186b46..a1f48fc00ee2 100644 --- a/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs +++ b/src/Mvc/Mvc.RazorPages/src/Builder/RazorPagesEndpointRouteBuilderExtensions.cs @@ -337,9 +337,18 @@ public static void MapDynamicPageRoute(this IEndpointRouteBuilder EnsureRazorPagesServices(endpoints); // Called for side-effect to make sure that the data source is registered. - var dataSource = GetOrCreateDataSource(endpoints); + GetOrCreateDataSource(endpoints).CreateInertEndpoints = true; - dataSource.AddDynamicPageEndpoint(endpoints, pattern, typeof(TTransformer), state); + endpoints.Map( + pattern, + context => + { + throw new InvalidOperationException("This endpoint is not expected to be executed directly."); + }) + .Add(b => + { + b.Metadata.Add(new DynamicPageRouteValueTransformerMetadata(typeof(TTransformer), state)); + }); } private static DynamicPageMetadata CreateDynamicPageMetadata(string page, string area) diff --git a/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageActionEndpointDataSource.cs b/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageActionEndpointDataSource.cs index cefb410c73ce..d7501266a43a 100644 --- a/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageActionEndpointDataSource.cs +++ b/src/Mvc/Mvc.RazorPages/src/Infrastructure/PageActionEndpointDataSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -8,23 +8,18 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing; namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { internal class PageActionEndpointDataSource : ActionEndpointDataSourceBase { private readonly ActionEndpointFactory _endpointFactory; - private readonly OrderedEndpointsSequenceProvider _orderSequence; - public PageActionEndpointDataSource( - IActionDescriptorCollectionProvider actions, - ActionEndpointFactory endpointFactory, - OrderedEndpointsSequenceProvider orderedEndpoints) + public PageActionEndpointDataSource(IActionDescriptorCollectionProvider actions, ActionEndpointFactory endpointFactory) : base(actions) { _endpointFactory = endpointFactory; - _orderSequence = orderedEndpoints; + DefaultBuilder = new PageActionEndpointConventionBuilder(Lock, Conventions); // IMPORTANT: this needs to be the last thing we do in the constructor. @@ -52,27 +47,6 @@ protected override List CreateEndpoints(IReadOnlyList - { - throw new InvalidOperationException("This endpoint is not expected to be executed directly."); - }) - .Add(b => - { - ((RouteEndpointBuilder)b).Order = order; - b.Metadata.Add(new DynamicPageRouteValueTransformerMetadata(transformerType, state)); - }); - } - } } } diff --git a/src/Mvc/Mvc.RazorPages/test/Infrastructure/PageActionEndpointDataSourceTest.cs b/src/Mvc/Mvc.RazorPages/test/Infrastructure/PageActionEndpointDataSourceTest.cs index 291b3a801e00..a74d388c7fc9 100644 --- a/src/Mvc/Mvc.RazorPages/test/Infrastructure/PageActionEndpointDataSourceTest.cs +++ b/src/Mvc/Mvc.RazorPages/test/Infrastructure/PageActionEndpointDataSourceTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -92,7 +92,7 @@ public void Endpoints_AppliesConventions() private protected override ActionEndpointDataSourceBase CreateDataSource(IActionDescriptorCollectionProvider actions, ActionEndpointFactory endpointFactory) { - return new PageActionEndpointDataSource(actions, endpointFactory, new OrderedEndpointsSequenceProvider()); + return new PageActionEndpointDataSource(actions, endpointFactory); } protected override ActionDescriptor CreateActionDescriptor( diff --git a/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/ControllerActionEndpointDatasourceBenchmark.cs b/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/ControllerActionEndpointDatasourceBenchmark.cs index 8b965022950b..c551bedb2ecc 100644 --- a/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/ControllerActionEndpointDatasourceBenchmark.cs +++ b/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/ControllerActionEndpointDatasourceBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -110,8 +110,7 @@ private ControllerActionEndpointDataSource CreateDataSource(IActionDescriptorCol { var dataSource = new ControllerActionEndpointDataSource( actionDescriptorCollectionProvider, - new ActionEndpointFactory(new MockRoutePatternTransformer()), - new OrderedEndpointsSequenceProvider()); + new ActionEndpointFactory(new MockRoutePatternTransformer())); return dataSource; } diff --git a/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs b/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs index f74e4dd7874f..98416f442ac9 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs +++ b/src/Mvc/test/Mvc.FunctionalTests/HtmlGenerationTest.cs @@ -12,7 +12,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -62,6 +61,8 @@ public static TheoryData WebPagesData { "EmployeeList", "/HtmlGeneration_Home/EmployeeList" }, // Testing the EnvironmentTagHelper { "Environment", null }, + // Testing the ImageTagHelper + { "Image", null }, // Testing InputTagHelper with File { "Input", null }, // Testing the LinkTagHelper @@ -137,13 +138,6 @@ public async Task HtmlGenerationWebSite_GeneratesExpectedResults(string action, } } - [Fact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25206")] - public async Task HtmlGenerationWebSite_GeneratesExpectedResults_WithImageData() - { - await HtmlGenerationWebSite_GeneratesExpectedResults("image", antiforgeryPath: null); - } - [Fact] public async Task HtmlGenerationWebSite_LinkGeneration_With21CompatibilityBehavior() { diff --git a/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicOrderTest.cs b/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicOrderTest.cs deleted file mode 100644 index ce0b22f21442..000000000000 --- a/src/Mvc/test/Mvc.FunctionalTests/RoutingDynamicOrderTest.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Json; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; -using RoutingWebSite; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.FunctionalTests -{ - public class RoutingDynamicOrderTest : IClassFixture> - { - public RoutingDynamicOrderTest(MvcTestFixture fixture) - { - Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); - } - - private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => builder.UseStartup(); - - public WebApplicationFactory Factory { get; } - - [Fact] - public async Task PrefersAttributeRoutesOverDynamicControllerRoutes() - { - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.AttributeRouteDynamicRoute)); - - var client = factory.CreateClient(); - - // Arrange - var url = "http://localhost/attribute-dynamic-order/Controller=Home,Action=Index"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("AttributeRouteSlug", content.RouteName); - } - - [Fact] - public async Task DynamicRoutesAreMatchedInDefinitionOrderOverPrecedence() - { - AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", isEnabled: true); - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.MultipleDynamicRoute)); - - var client = factory.CreateClient(); - - // Arrange - var url = "http://localhost/dynamic-order/specific/Controller=Home,Action=Index"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(content.RouteValues.TryGetValue("identifier", out var identifier)); - Assert.Equal("slug", identifier); - } - - [Fact] - public async Task ConventionalRoutesDefinedEarlierWinOverDynamicControllerRoutes() - { - AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", isEnabled: true); - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.ConventionalRouteDynamicRoute)); - - var client = factory.CreateClient(); - - // Arrange - var url = "http://localhost/conventional-dynamic-order-before"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.False(content.RouteValues.TryGetValue("identifier", out var identifier)); - } - - [Fact] - public async Task ConventionalRoutesDefinedLaterLooseToDynamicControllerRoutes() - { - AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", isEnabled: true); - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.ConventionalRouteDynamicRoute)); - - var client = factory.CreateClient(); - - // Arrange - var url = "http://localhost/conventional-dynamic-order-after"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(content.RouteValues.TryGetValue("identifier", out var identifier)); - Assert.Equal("slug", identifier); - } - - [Fact] - public async Task DynamicPagesDefinedEarlierWinOverDynamicControllers() - { - AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", isEnabled: true); - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.DynamicControllerAndPages)); - - var client = factory.CreateClient(); - // Arrange - var url = "http://localhost/dynamic-order-page-controller-before"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadAsStringAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("Hello from dynamic page: /DynamicPagebefore", content); - } - - [Fact] - public async Task DynamicPagesDefinedLaterLooseOverDynamicControllers() - { - AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", isEnabled: true); - var factory = Factory - .WithWebHostBuilder(b => b.UseSetting("Scenario", RoutingWebSite.StartupForDynamicOrder.DynamicOrderScenarios.DynamicControllerAndPages)); - - var client = factory.CreateClient(); - - // Arrange - var url = "http://localhost/dynamic-order-page-controller-after"; - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // Act - var response = await client.SendAsync(request); - var content = await response.Content.ReadFromJsonAsync(); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(content.RouteValues.TryGetValue("identifier", out var identifier)); - Assert.Equal("controller", identifier); - } - - private record RouteInfo(string RouteName, IDictionary RouteValues); - } -} diff --git a/src/Mvc/test/WebSites/RoutingWebSite/Controllers/DynamicOrderController.cs b/src/Mvc/test/WebSites/RoutingWebSite/Controllers/DynamicOrderController.cs deleted file mode 100644 index d58b9fc24c31..000000000000 --- a/src/Mvc/test/WebSites/RoutingWebSite/Controllers/DynamicOrderController.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Mvc.RoutingWebSite.Controllers -{ - public class DynamicOrderController : Controller - { - private readonly TestResponseGenerator _generator; - - public DynamicOrderController(TestResponseGenerator generator) - { - _generator = generator; - } - - [HttpGet("attribute-dynamic-order/{**slug}", Name = "AttributeRouteSlug")] - public IActionResult Get(string slug) - { - return _generator.Generate(Url.RouteUrl("AttributeRouteSlug", new { slug })); - } - - [HttpGet] - public IActionResult Index() - { - return _generator.Generate(Url.RouteUrl(null, new { controller = "DynamicOrder", action = "Index" })); - } - } -} diff --git a/src/Mvc/test/WebSites/RoutingWebSite/Pages/DynamicPage.cshtml b/src/Mvc/test/WebSites/RoutingWebSite/Pages/DynamicPage.cshtml index 7580432bcbf9..f1d271bc6292 100644 --- a/src/Mvc/test/WebSites/RoutingWebSite/Pages/DynamicPage.cshtml +++ b/src/Mvc/test/WebSites/RoutingWebSite/Pages/DynamicPage.cshtml @@ -1,3 +1,3 @@ @page @model RoutingWebSite.Pages.DynamicPageModel -Hello from dynamic page: @Url.Page("")@RouteData.Values["identifier"] \ No newline at end of file +Hello from dynamic page: @Url.Page("") \ No newline at end of file diff --git a/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicOrder.cs b/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicOrder.cs deleted file mode 100644 index 2d3c96c57986..000000000000 --- a/src/Mvc/test/WebSites/RoutingWebSite/StartupForDynamicOrder.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace RoutingWebSite -{ - // For by tests for dynamic routing to pages/controllers - public class StartupForDynamicOrder - { - public static class DynamicOrderScenarios - { - public const string AttributeRouteDynamicRoute = nameof(AttributeRouteDynamicRoute); - public const string MultipleDynamicRoute = nameof(MultipleDynamicRoute); - public const string ConventionalRouteDynamicRoute = nameof(ConventionalRouteDynamicRoute); - public const string DynamicControllerAndPages = nameof(DynamicControllerAndPages); - } - - public IConfiguration Configuration { get; } - - public StartupForDynamicOrder(IConfiguration configuration) - { - Configuration = configuration; - } - - public void ConfigureServices(IServiceCollection services) - { - services - .AddMvc() - .AddNewtonsoftJson() - .SetCompatibilityVersion(CompatibilityVersion.Latest); - - services.AddTransient(); - services.AddScoped(); - services.AddSingleton(); - - // Used by some controllers defined in this project. - services.Configure(options => options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer)); - } - - public void Configure(IApplicationBuilder app) - { - var scenario = Configuration.GetValue("Scenario"); - app.UseRouting(); - app.UseEndpoints(endpoints => - { - // Route order definition is important for all these routes: - switch (scenario) - { - case DynamicOrderScenarios.AttributeRouteDynamicRoute: - endpoints.MapDynamicControllerRoute("attribute-dynamic-order/{**slug}", new TransformerState() { Identifier = "slug" }); - endpoints.MapControllers(); - break; - case DynamicOrderScenarios.ConventionalRouteDynamicRoute: - endpoints.MapControllerRoute(null, "{**conventional-dynamic-order-before:regex(^((?!conventional\\-dynamic\\-order\\-after).)*$)}", new { controller = "DynamicOrder", action = "Index" }); - endpoints.MapDynamicControllerRoute("{conventional-dynamic-order}", new TransformerState() { Identifier = "slug" }); - endpoints.MapControllerRoute(null, "conventional-dynamic-order-after", new { controller = "DynamicOrder", action = "Index" }); - break; - case DynamicOrderScenarios.MultipleDynamicRoute: - endpoints.MapDynamicControllerRoute("dynamic-order/{**slug}", new TransformerState() { Identifier = "slug" }); - endpoints.MapDynamicControllerRoute("dynamic-order/specific/{**slug}", new TransformerState() { Identifier = "specific" }); - break; - case DynamicOrderScenarios.DynamicControllerAndPages: - endpoints.MapDynamicPageRoute("{**dynamic-order-page-controller-before:regex(^((?!dynamic\\-order\\-page\\-controller\\-after).)*$)}", new TransformerState() { Identifier = "before", ForPages = true }); - endpoints.MapDynamicControllerRoute("{dynamic-order-page-controller}", new TransformerState() { Identifier = "controller" }); - endpoints.MapDynamicPageRoute("dynamic-order-page-controller-after", new TransformerState() { Identifier = "after", ForPages = true }); - break; - default: - throw new InvalidOperationException("Invalid scenario configuration."); - } - }); - - app.Map("/afterrouting", b => b.Run(c => - { - return c.Response.WriteAsync("Hello from middleware after routing"); - })); - } - - private class TransformerState - { - public string Identifier { get; set; } - public bool ForPages { get; set; } - } - - private class Transformer : DynamicRouteValueTransformer - { - // Turns a format like `controller=Home,action=Index` into an RVD - public override ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) - { - var kvps = ((string)values?["slug"])?.Split("/")?.LastOrDefault()?.Split(",") ?? Array.Empty(); - - // Go to index by default if the route doesn't follow the slug pattern, we want to make sure always match to - // test the order is applied - var state = (TransformerState)State; - var results = new RouteValueDictionary(); - if (!state.ForPages) - { - results["controller"] = "Home"; - results["action"] = "Index"; - } - else - { - results["Page"] = "/DynamicPage"; - } - - foreach (var kvp in kvps) - { - var split = kvp.Split("="); - if (split.Length == 2) - { - results[split[0]] = split[1]; - } - } - - results["identifier"] = ((TransformerState)State).Identifier; - - return new ValueTask(results); - } - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj index f7a25baba975..b61cd6f6d328 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj @@ -2,7 +2,7 @@ $(DefaultNetCoreTargetFramework) - Microsoft.DotNet.Web.ProjectTemplates.$(AspNetCoreMajorMinorVersion) + Microsoft.DotNet.Web.ProjectTemplates.$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) ASP.NET Core Web Template Pack for Microsoft Template Engine $(RepoRoot)src\Components\WebAssembly\ diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json index 020748fbc433..3d3cb33674fe 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json @@ -26,6 +26,31 @@ "sources": [ { "modifiers": [ + { + "condition": "(!OrganizationalAuth && !IndividualAuth)", + "exclude": [ + "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml", + "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml" + ] + }, + { + "condition": "(OrganizationalAuth || IndividualB2CAuth)", + "rename": { + "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml": "Areas/Identity/Pages/Shared/_LoginPartial.cshtml" + }, + "exclude": [ + "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml" + ] + }, + { + "condition": "(IndividualLocalAuth)", + "rename": { + "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml": "Areas/Identity/Pages/Shared/_LoginPartial.cshtml" + }, + "exclude": [ + "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml" + ] + }, { "condition": "(!IndividualLocalAuth || UseLocalDB)", "exclude": [ @@ -131,7 +156,6 @@ { "condition": "(!GenerateApi)", "exclude": [ - "Services/DownstreamWebApi.cs", "Pages/CallWebApi.razor" ] }, diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml new file mode 100644 index 000000000000..a4f854aac31f --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml @@ -0,0 +1,27 @@ +@using Microsoft.AspNetCore.Identity +@inject SignInManager SignInManager +@inject UserManager UserManager +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml similarity index 100% rename from src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml rename to src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor index 48dcfafe899e..4fe5a889de3e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor @@ -4,7 +4,6 @@ @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web -@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using BlazorServerWeb_CSharp @using BlazorServerWeb_CSharp.Shared diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj index e9341c91b1f9..62e1c07e7db8 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj @@ -2,7 +2,7 @@ $(DefaultNetCoreTargetFramework) - Microsoft.DotNet.Web.Spa.ProjectTemplates.$(AspNetCoreMajorMinorVersion) + Microsoft.DotNet.Web.Spa.ProjectTemplates.$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) Single Page Application templates for ASP.NET Core $(PackageTags);spa diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/ClientApp/src/api-authorization/login-menu/login-menu.component.ts b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/ClientApp/src/api-authorization/login-menu/login-menu.component.ts index 5853edff2442..d1fa469908f5 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/ClientApp/src/api-authorization/login-menu/login-menu.component.ts +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/ClientApp/src/api-authorization/login-menu/login-menu.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { AuthorizeService } from '../authorize.service'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; @Component({ selector: 'app-login-menu', diff --git a/src/ProjectTemplates/test/WorkerTemplateTest.cs b/src/ProjectTemplates/test/WorkerTemplateTest.cs index f272d4840ff2..1656ebb0896b 100644 --- a/src/ProjectTemplates/test/WorkerTemplateTest.cs +++ b/src/ProjectTemplates/test/WorkerTemplateTest.cs @@ -25,7 +25,6 @@ public WorkerTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelpe [OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")] [InlineData("C#")] [InlineData("F#")] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25404")] public async Task WorkerTemplateAsync(string language) { Project = await ProjectFactory.GetOrCreateProject( diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index 8c9cece3e303..ef55c4ec5311 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -30,6 +30,7 @@ #define CS_ENABLED L"enabled" #define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook" #define CS_ASPNETCORE_HANDLER_STACK_SIZE L"stackSize" +#define CS_ASPNETCORE_SUPPRESS_RECYCLE_ON_STARTUP_TIMEOUT L"suppressRecycleOnStartupTimeout" #define CS_ASPNETCORE_DETAILEDERRORS L"ASPNETCORE_DETAILEDERRORS" #define CS_ASPNETCORE_ENVIRONMENT L"ASPNETCORE_ENVIRONMENT" #define CS_DOTNET_ENVIRONMENT L"DOTNET_ENVIRONMENT" diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index c2ff5e0a7d59..157f0dda2265 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -66,6 +66,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true"); m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true"); m_strStackSize = find_element(handlerSettings, CS_ASPNETCORE_HANDLER_STACK_SIZE).value_or(L"1048576"); + m_fSuppressRecycleOnStartupTimeout = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_SUPPRESS_RECYCLE_ON_STARTUP_TIMEOUT).value_or(L"false"), L"true"); m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000; m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h index 61a79664b2f6..fa0e19e84952 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h @@ -118,6 +118,12 @@ class InProcessOptions: NonCopyable return m_strStackSize; } + bool + QuerySuppressRecycleOnStartupTimeout() const + { + return m_fSuppressRecycleOnStartupTimeout; + } + InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite); static @@ -139,6 +145,7 @@ class InProcessOptions: NonCopyable bool m_fWindowsAuthEnabled; bool m_fBasicAuthEnabled; bool m_fAnonymousAuthEnabled; + bool m_fSuppressRecycleOnStartupTimeout; DWORD m_dwStartupTimeLimitInMS; DWORD m_dwShutdownTimeLimitInMS; DWORD m_dwMaxRequestBodySize; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index 4f86cde3b852..1dd8a42c4b14 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -169,7 +169,14 @@ IN_PROCESS_APPLICATION::LoadManagedApplication(ErrorContext& errorContext) errorContext.errorReason = format("ASP.NET Core app failed to start after %d milliseconds", m_pConfig->QueryStartupTimeLimitInMS()); m_waitForShutdown = false; - StopClr(); + if (m_pConfig->QuerySuppressRecycleOnStartupTimeout()) + { + StopClr(); + } + else + { + Stop(/* fServerInitiated */false); + } throw InvalidOperationException(format(L"Managed server didn't initialize after %u ms.", m_pConfig->QueryStartupTimeLimitInMS())); } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 220489dba689..01a7243f10f8 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -453,9 +453,10 @@ public async Task RemoveInProcessReference_FailedToFindRequestHandler() } [ConditionalFact] + [RequiresNewHandler] public async Task StartupTimeoutIsApplied() { - // From what I can tell, this failure is due to ungraceful shutdown. + // From what we can tell, this failure is due to ungraceful shutdown. // The error could be the same as https://github.com/dotnet/core-setup/issues/4646 // But can't be certain without another repro. using (AppVerifier.Disable(DeployerSelector.ServerType, 0x300)) @@ -470,11 +471,45 @@ public async Task StartupTimeoutIsApplied() var response = await deploymentResult.HttpClient.GetAsync("/"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + // Startup timeout now recycles process. + deploymentResult.AssertWorkerProcessStop(); + + EventLogHelpers.VerifyEventLogEvent(deploymentResult, + EventLogHelpers.InProcessFailedToStart(deploymentResult, "Managed server didn't initialize after 1000 ms."), + Logger); + + if (DeployerSelector.HasNewHandler) + { + var responseContent = await response.Content.ReadAsStringAsync(); + Assert.Contains("500.37", responseContent); + } + } + } + + [ConditionalFact] + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H1, SkipReason = "Shutdown hangs https://github.com/dotnet/aspnetcore/issues/25107")] + public async Task StartupTimeoutIsApplied_DisableRecycleOnStartupTimeout() + { + // From what we can tell, this failure is due to ungraceful shutdown. + // The error could be the same as https://github.com/dotnet/core-setup/issues/4646 + // But can't be certain without another repro. + using (AppVerifier.Disable(DeployerSelector.ServerType, 0x300)) + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(Fixture.InProcessTestSite); + deploymentParameters.TransformArguments((a, _) => $"{a} Hang"); + deploymentParameters.WebConfigActionList.Add( + WebConfigHelpers.AddOrModifyAspNetCoreSection("startupTimeLimit", "1")); + deploymentParameters.HandlerSettings["suppressRecycleOnStartupTimeout"] = "true"; + var deploymentResult = await DeployAsync(deploymentParameters); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + StopServer(gracefulShutdown: false); - EventLogHelpers.VerifyEventLogEvents(deploymentResult, - EventLogHelpers.InProcessFailedToStart(deploymentResult, "Managed server didn't initialize after 1000 ms.") - ); + EventLogHelpers.VerifyEventLogEvent(deploymentResult, + EventLogHelpers.InProcessFailedToStart(deploymentResult, "Managed server didn't initialize after 1000 ms."), + Logger); if (DeployerSelector.HasNewHandler) { diff --git a/src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs b/src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs index 06ca82a8a947..d749de79f8a9 100644 --- a/src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs +++ b/src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs @@ -353,7 +353,19 @@ internal class CertificateConfig public CertificateConfig(IConfigurationSection configSection) { ConfigSection = configSection; - ConfigSection.Bind(this); + + // Bind explictly to preserve linkability + Path = configSection[nameof(Path)]; + KeyPath = configSection[nameof(KeyPath)]; + Password = configSection[nameof(Password)]; + Subject = configSection[nameof(Subject)]; + Store = configSection[nameof(Store)]; + Location = configSection[nameof(Location)]; + + if (bool.TryParse(configSection[nameof(AllowInvalid)], out var value)) + { + AllowInvalid = value; + } } // For testing diff --git a/src/Servers/Kestrel/Core/src/Systemd/KestrelServerOptionsSystemdExtensions.cs b/src/Servers/Kestrel/Core/src/Systemd/KestrelServerOptionsSystemdExtensions.cs index bf8f0fe69437..6def39159de2 100644 --- a/src/Servers/Kestrel/Core/src/Systemd/KestrelServerOptionsSystemdExtensions.cs +++ b/src/Servers/Kestrel/Core/src/Systemd/KestrelServerOptionsSystemdExtensions.cs @@ -11,12 +11,11 @@ namespace Microsoft.AspNetCore.Hosting public static class KestrelServerOptionsSystemdExtensions { // SD_LISTEN_FDS_START https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html - private const int SdListenFdsStart = 3; + private const ulong SdListenFdsStart = 3; private const string ListenPidEnvVar = "LISTEN_PID"; - private const string ListenFdsEnvVar = "LISTEN_FDS"; /// - /// Open file descriptors (starting from SD_LISTEN_FDS_START) initialized by systemd socket-based activation logic if available. + /// Open file descriptor (SD_LISTEN_FDS_START) initialized by systemd socket-based activation logic if available. /// /// /// The . @@ -27,7 +26,7 @@ public static KestrelServerOptions UseSystemd(this KestrelServerOptions options) } /// - /// Open file descriptors (starting from SD_LISTEN_FDS_START) initialized by systemd socket-based activation logic if available. + /// Open file descriptor (SD_LISTEN_FDS_START) initialized by systemd socket-based activation logic if available. /// Specify callback to configure endpoint-specific settings. /// /// @@ -37,16 +36,7 @@ public static KestrelServerOptions UseSystemd(this KestrelServerOptions options, { if (string.Equals(Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture), Environment.GetEnvironmentVariable(ListenPidEnvVar), StringComparison.Ordinal)) { - // This matches sd_listen_fds behavior that requires %LISTEN_FDS% to be present and in range [1;INT_MAX-SD_LISTEN_FDS_START] - if (int.TryParse(Environment.GetEnvironmentVariable(ListenFdsEnvVar), NumberStyles.None, NumberFormatInfo.InvariantInfo, out var listenFds) - && listenFds > 0 - && listenFds <= int.MaxValue - SdListenFdsStart) - { - for (var handle = SdListenFdsStart; handle < SdListenFdsStart + listenFds; ++handle) - { - options.ListenHandle((ulong)handle, configure); - } - } + options.ListenHandle(SdListenFdsStart, configure); } return options; diff --git a/src/Servers/Kestrel/test/BindTests/AddressRegistrationTests.cs b/src/Servers/Kestrel/test/BindTests/AddressRegistrationTests.cs index da7058d331d2..5980ff8f32d6 100644 --- a/src/Servers/Kestrel/test/BindTests/AddressRegistrationTests.cs +++ b/src/Servers/Kestrel/test/BindTests/AddressRegistrationTests.cs @@ -125,7 +125,6 @@ public async Task RegisterIPEndPoint_Port443_Success(IPEndPoint endpoint, string [ConditionalTheory] [MemberData(nameof(AddressRegistrationDataIPv6))] [IPv6SupportedCondition] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25403")] public async Task RegisterAddresses_IPv6_Success(string addressInput, string[] testUrls) { await RegisterAddresses_Success(addressInput, testUrls); diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs index 3ef0c16d2a0e..2e2e2c48bb6a 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs @@ -61,6 +61,8 @@ public void UseHttpsDefaultsToDefaultCert() } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25542")] + public async Task UseHttpsWithAsyncCallbackDoeNotFallBackToDefaultCert() { var loggerProvider = new HandshakeErrorLoggerProvider(); @@ -81,8 +83,10 @@ public async Task UseHttpsWithAsyncCallbackDoeNotFallBackToDefaultCert() { var ex = await Assert.ThrowsAnyAsync(() => sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null, - enabledSslProtocols: SslProtocols.Tls, + enabledSslProtocols: SslProtocols.None, checkCertificateRevocation: false)); + + Logger.LogTrace(ex, "AuthenticateAsClientAsync Exception"); } } diff --git a/src/Shared/JSInterop/JSCallResultTypeHelper.cs b/src/Shared/JSInterop/JSCallResultTypeHelper.cs new file mode 100644 index 000000000000..a03e4308d1ab --- /dev/null +++ b/src/Shared/JSInterop/JSCallResultTypeHelper.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + internal static class JSCallResultTypeHelper + { + public static JSCallResultType FromGeneric() + => typeof(TResult) == typeof(IJSObjectReference) || typeof(TResult) == typeof(IJSInProcessObjectReference) ? + JSCallResultType.JSObjectReference : + JSCallResultType.Default; + } +} diff --git a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj index 3fd4728dc69a..504f2e454992 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj +++ b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj @@ -17,7 +17,6 @@ - diff --git a/src/SignalR/common/Http.Connections/src/Internal/TaskExtensions.cs b/src/SignalR/common/Http.Connections/src/Internal/TaskExtensions.cs index d48aab38b8d4..efbbb4a2391a 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/TaskExtensions.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/TaskExtensions.cs @@ -21,6 +21,6 @@ public static async Task NoThrow(this Task task) // Observe exception public void GetResult() { _ = _task.Exception; } public void OnCompleted(Action continuation) => _task.GetAwaiter().OnCompleted(continuation); - public void UnsafeOnCompleted(Action continuation) => _task.GetAwaiter().UnsafeOnCompleted(continuation); + public void UnsafeOnCompleted(Action continuation) => OnCompleted(continuation); } } diff --git a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj index 314ab57e6e36..a6224d57a4d0 100644 --- a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj +++ b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj @@ -23,7 +23,6 @@ - diff --git a/src/SignalR/common/Shared/TimerAwaitable.cs b/src/SignalR/common/Shared/TimerAwaitable.cs index bbf6d71ce7b4..959e2ef31d9f 100644 --- a/src/SignalR/common/Shared/TimerAwaitable.cs +++ b/src/SignalR/common/Shared/TimerAwaitable.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Internal { @@ -20,9 +19,9 @@ internal class TimerAwaitable : IDisposable, ICriticalNotifyCompletion private readonly TimeSpan _period; private readonly TimeSpan _dueTime; - private readonly object _lockObj = new object(); private bool _disposed; private bool _running = true; + private object _lockObj = new object(); public TimerAwaitable(TimeSpan dueTime, TimeSpan period) { @@ -43,20 +42,39 @@ public void Start() if (_timer == null) { - // This fixes the cycle by using a WeakReference to the state object. The object graph now looks like this: - // Timer -> TimerHolder -> TimerQueueTimer -> WeakReference -> Timer -> ... - // If TimerAwaitable falls out of scope, the timer should be released. - _timer = NonCapturingTimer.Create(state => + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer + bool restoreFlow = false; + try { - var weakRef = (WeakReference)state!; - if (weakRef.TryGetTarget(out var thisRef)) + if (!ExecutionContext.IsFlowSuppressed()) { - thisRef.Tick(); + ExecutionContext.SuppressFlow(); + restoreFlow = true; } - }, - state: new WeakReference(this), - dueTime: _dueTime, - period: _period); + + // This fixes the cycle by using a WeakReference to the state object. The object graph now looks like this: + // Timer -> TimerHolder -> TimerQueueTimer -> WeakReference -> Timer -> ... + // If TimerAwaitable falls out of scope, the timer should be released. + _timer = new Timer(state => + { + var weakRef = (WeakReference)state!; + if (weakRef.TryGetTarget(out var thisRef)) + { + thisRef.Tick(); + } + }, + new WeakReference(this), + _dueTime, + _period); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + { + ExecutionContext.RestoreFlow(); + } + } } } } diff --git a/src/SignalR/server/StackExchangeRedis/src/Internal/AckHandler.cs b/src/SignalR/server/StackExchangeRedis/src/Internal/AckHandler.cs index a4fe2ed31294..1a9d264097d9 100644 --- a/src/SignalR/server/StackExchangeRedis/src/Internal/AckHandler.cs +++ b/src/SignalR/server/StackExchangeRedis/src/Internal/AckHandler.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.SignalR.StackExchangeRedis.Internal { @@ -20,7 +19,26 @@ internal class AckHandler : IDisposable public AckHandler() { - _timer = NonCapturingTimer.Create(state => ((AckHandler)state).CheckAcks(), state: this, dueTime: _ackInterval, period: _ackInterval); + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer + bool restoreFlow = false; + try + { + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + _timer = new Timer(state => ((AckHandler)state).CheckAcks(), state: this, dueTime: _ackInterval, period: _ackInterval); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + { + ExecutionContext.RestoreFlow(); + } + } } public Task CreateAck(int id) diff --git a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj index bec487bcd1f9..562720dfb15b 100644 --- a/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj +++ b/src/SignalR/server/StackExchangeRedis/src/Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj @@ -1,4 +1,4 @@ - + Provides scale-out support for ASP.NET Core SignalR using a Redis server and the StackExchange.Redis client. @@ -8,7 +8,6 @@ - diff --git a/src/Tools/dotnet-user-secrets/test/SecretManagerTests.cs b/src/Tools/dotnet-user-secrets/test/SecretManagerTests.cs index 036378036ca5..1eab5c7c6be0 100644 --- a/src/Tools/dotnet-user-secrets/test/SecretManagerTests.cs +++ b/src/Tools/dotnet-user-secrets/test/SecretManagerTests.cs @@ -79,7 +79,7 @@ public void SupportsRelativePaths() Assert.Contains(Resources.FormatMessage_Project_File_Path(Path.Combine(cwd, "..", "TestProject.csproj")), _console.GetOutput()); } - [ConditionalTheory(Skip = "https://github.com/dotnet/aspnetcore/issues/25109")] + [Theory] [InlineData(true)] [InlineData(false)] public void SetSecrets(bool fromCurrentDirectory) @@ -192,7 +192,7 @@ public void Remove_Non_Existing_Secret() Assert.Contains("Cannot find 'secret1' in the secret store.", _console.GetOutput()); } - [ConditionalFact(Skip = "https://github.com/dotnet/aspnetcore/issues/25109")] + [Fact] public void Remove_Is_Case_Insensitive() { var projectPath = _fixture.GetTempSecretProject(); @@ -266,7 +266,7 @@ public void List_Empty_Secrets_File() Assert.Contains(Resources.Error_No_Secrets_Found, _console.GetOutput()); } - [ConditionalTheory(Skip = "https://github.com/dotnet/aspnetcore/issues/25109")] + [Theory] [InlineData(true)] [InlineData(false)] public void Clear_Secrets(bool fromCurrentDirectory)