diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 7594b269a8..9fba304d8e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -3,6 +3,7 @@
/build/ @natemcmaster
/eng/ @natemcmaster
+/src/Components/ @SteveSandersonMS
/src/DefaultBuilder/ @tratcher
/src/Hosting/ @tratcher
/src/Http/ @tratcher
diff --git a/build.ps1 b/build.ps1
index fc4830731a..2d08643422 100644
--- a/build.ps1
+++ b/build.ps1
@@ -102,6 +102,11 @@ param(
[Parameter(ParameterSetName = 'Groups')]
[switch]$Installers,
+ # By default, Windows builds will use MSBuild.exe. Passing this will force the build to run on
+ # dotnet.exe instead, which may cause issues if you invoke build on a project unsupported by
+ # MSBuild for .NET Core
+ [switch]$ForceCoreMsbuild,
+
# Other lifecycle targets
[switch]$Help, # Show help
@@ -269,6 +274,9 @@ Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1')
try {
Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $RepoRoot -ConfigFile $ConfigFile -CI:$CI
+ if ($ForceCoreMsbuild) {
+ $global:KoreBuildSettings.MSBuildType = 'core'
+ }
Invoke-KoreBuildCommand 'default-build' @MSBuildArguments
}
finally {
diff --git a/build/Publish.targets b/build/Publish.targets
index 54d9b39641..fa3e8c9d77 100644
--- a/build/Publish.targets
+++ b/build/Publish.targets
@@ -1,130 +1,13 @@
-
-
- GetFilesToPublish;
- PublishToAzureFeed;
- PublishToMyGet;
-
-
+
+
-
-
-
-
-
- <_FilesToPublish Include="$(InstallersOutputPath)*.txt">
- text/plain
-
-
- <_FilesToPublish Include="$(InstallersOutputPath)*.version">
- text/plain
- no-cache, no-store, must-revalidate
-
-
- <_FilesToPublish Include="$(InstallersOutputPath)*.svg">
- no-cache, no-store, must-revalidate
- image/svg+xml
-
-
-
- <_FilesToPublish Include="$(InstallersOutputPath)*" Exclude="@(_FilesToPublish)" />
-
-
- <_FilesToPublish Include="$(ProductPackageOutputPath)*.jar;$(ProductPackageOutputPath)*.pom">
- aspnetcore/jar/$(PackageVersion)/
-
-
-
-
- %(_FilesToPublish.BlobBasePath)%(_FilesToPublish.FileName)%(_FilesToPublish.Extension)
- aspnetcore/Runtime/$(PackageVersion)/%(_FilesToPublish.FileName)%(_FilesToPublish.Extension)
-
- <_FilesToPublish Remove="@(_FilesToPublish)" />
-
-
-
-
-
-
-
-
-
-
-
- NonShipping=true
-
-
- NonShipping=true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(PublishMyGetNpmRegistryUrl.Replace("https:", "")):_authToken
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/build/SharedFrameworkOnly.props b/build/SharedFrameworkOnly.props
index 4c3969a169..73b030b5f7 100644
--- a/build/SharedFrameworkOnly.props
+++ b/build/SharedFrameworkOnly.props
@@ -11,6 +11,10 @@
+
+
+
+
diff --git a/build/external-dependencies.props b/build/external-dependencies.props
index 494ba861db..e9b08f2897 100644
--- a/build/external-dependencies.props
+++ b/build/external-dependencies.props
@@ -28,6 +28,7 @@
+
diff --git a/build/publish/Publish.csproj b/build/publish/Publish.csproj
new file mode 100644
index 0000000000..a136fe7910
--- /dev/null
+++ b/build/publish/Publish.csproj
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+ netcoreapp3.0
+ true
+ $(ArtifactsDir)manifests\
+ https://maestro-prod.westus2.cloudapp.azure.com
+
+
+
+
+
+
+
+
+
+
+
+ GetFilesToPublish;
+ GenerateBuildAssetManifest;
+ PublishToAzureFeed;
+ PublishToMyGet;
+ PublishToBuildAssetRegistry;
+
+
+
+
+
+
+
+
+ <_FilesToPublish Include="$(InstallersOutputPath)*.txt">
+ text/plain
+
+
+ <_FilesToPublish Include="$(InstallersOutputPath)*.version">
+ text/plain
+ no-cache, no-store, must-revalidate
+
+
+ <_FilesToPublish Include="$(InstallersOutputPath)*.svg">
+ no-cache, no-store, must-revalidate
+ image/svg+xml
+
+
+
+ <_FilesToPublish Include="$(InstallersOutputPath)*" Exclude="@(_FilesToPublish)" />
+
+
+ <_FilesToPublish Include="$(ProductPackageOutputPath)*.jar;$(ProductPackageOutputPath)*.pom">
+ aspnetcore/jar/$(PackageVersion)/
+
+
+
+
+ aspnetcore/npm/$(PackageVersion)/
+
+ <_FilesToPublish Include="@(NpmPackageToPublish)" />
+
+
+
+ %(_FilesToPublish.BlobBasePath)%(_FilesToPublish.FileName)%(_FilesToPublish.Extension)
+ aspnetcore/Runtime/$(PackageVersion)/%(_FilesToPublish.FileName)%(_FilesToPublish.Extension)
+
+ <_FilesToPublish Remove="@(_FilesToPublish)" />
+
+
+
+
+
+
+
+
+ NonShipping=true
+
+
+ NonShipping=true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(PublishMyGetNpmRegistryUrl.Replace("https:", "")):_authToken
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/tasks/PublishToAzureBlob.cs b/build/tasks/PublishToAzureBlob.cs
index 86150d0be3..71f1b1c29a 100644
--- a/build/tasks/PublishToAzureBlob.cs
+++ b/build/tasks/PublishToAzureBlob.cs
@@ -1,6 +1,7 @@
// 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.
+#if BUILD_AZ_TASKS
using System;
using System.Collections.Generic;
using System.IO;
@@ -142,3 +143,4 @@ namespace RepoTasks
}
}
}
+#endif
diff --git a/build/tasks/RepoTasks.csproj b/build/tasks/RepoTasks.csproj
index 82c90e29a2..06c08355a3 100644
--- a/build/tasks/RepoTasks.csproj
+++ b/build/tasks/RepoTasks.csproj
@@ -5,13 +5,14 @@
netcoreapp2.2
net461
$(DefineConstants);BUILD_MSI_TASKS
+ $(DefineConstants);BUILD_AZ_TASKS
-
+
diff --git a/build/tasks/RepoTasks.tasks b/build/tasks/RepoTasks.tasks
index 43323cf2e2..e073e49173 100644
--- a/build/tasks/RepoTasks.tasks
+++ b/build/tasks/RepoTasks.tasks
@@ -9,6 +9,6 @@
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 810775dee9..c11c8325a8 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -37,227 +37,231 @@
https://github.com/aspnet/EntityFrameworkCore
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
-
+
+ https://github.com/aspnet/Extensions
+ 000000
+
+
https://github.com/aspnet/Extensions
000000
@@ -355,11 +359,11 @@
-
+
https://github.com/aspnet/Extensions
000000
-
+
https://github.com/aspnet/Extensions
000000
diff --git a/eng/Versions.props b/eng/Versions.props
index d0e0fa9d3e..fd7bd4c55c 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -35,64 +35,65 @@
4.6.0-preview.18619.1
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
- 3.0.0-preview.19059.4
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
+ 3.0.0-preview.19059.5
3.0.0-preview.18604.3
@@ -105,7 +106,7 @@
3.0.0-preview.18604.3
- 3.0.0-preview-19057-06
+ 3.0.0-preview-19064-09
diff --git a/src/Azure/Azure.sln b/src/Azure/Azure.sln
index b26a534edd..f3724c7bc7 100644
--- a/src/Azure/Azure.sln
+++ b/src/Azure/Azure.sln
@@ -73,6 +73,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.TestHo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization", "..\Security\Authorization\Core\src\Microsoft.AspNetCore.Authorization.csproj", "{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.OpenIdConnect", "..\Security\Authentication\OpenIdConnect\src\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj", "{F44054A2-DAC9-467F-B899-F35F9DCDAE9C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.OAuth", "..\Security\Authentication\OAuth\src\Microsoft.AspNetCore.Authentication.OAuth.csproj", "{406DF28A-0B58-408E-96B0-2D373EE36352}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication", "..\Security\Authentication\Core\src\Microsoft.AspNetCore.Authentication.csproj", "{A5E7BA46-B76B-467A-88FA-38E04D0A42FC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -383,6 +389,42 @@ Global
{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x64.Build.0 = Release|Any CPU
{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x86.ActiveCfg = Release|Any CPU
{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E}.Release|x86.Build.0 = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x64.Build.0 = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x64.ActiveCfg = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x64.Build.0 = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C}.Release|x86.Build.0 = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x64.Build.0 = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Debug|x86.Build.0 = Debug|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|Any CPU.Build.0 = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x64.ActiveCfg = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x64.Build.0 = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x86.ActiveCfg = Release|Any CPU
+ {406DF28A-0B58-408E-96B0-2D373EE36352}.Release|x86.Build.0 = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x64.Build.0 = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Debug|x86.Build.0 = Debug|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x64.ActiveCfg = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x64.Build.0 = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x86.ActiveCfg = Release|Any CPU
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -418,6 +460,9 @@ Global
{38027842-48A7-4A64-A44F-004BAF0AB450} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
{C520D48E-87A0-463D-B4CF-3E6B68F8F4D0} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
{C57DFBC2-A887-44B4-A149-7ABFA6D98F7E} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
+ {F44054A2-DAC9-467F-B899-F35F9DCDAE9C} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
+ {406DF28A-0B58-408E-96B0-2D373EE36352} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
+ {A5E7BA46-B76B-467A-88FA-38E04D0A42FC} = {84622717-F98A-4DE2-806E-1EF89C45C0EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81AADD49-473B-43ED-8A08-F6B7A058AA39}
diff --git a/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs b/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs
index dc8e5f8271..d724ee2561 100644
--- a/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.cs
+++ b/src/Azure/AzureAD/test/FunctionalTests/WebAuthenticationTests.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 Microsoft.AspNetCore.Authorization;
using System.Net;
@@ -11,7 +11,9 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
+using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Xunit;
@@ -158,5 +160,60 @@ namespace Microsoft.AspNetCore.Authentication.AzureAD.FunctionalTests
// Assert
Assert.Equal(expectedStatusCode, response.StatusCode);
}
+
+ [Fact]
+ public async Task ADB2C_EndToEnd_PasswordReset()
+ {
+ var client = Factory.WithWebHostBuilder(builder => builder.ConfigureTestServices(
+ services =>
+ {
+ services
+ .AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
+ .AddAzureADB2C(o =>
+ {
+ o.Instance = "https://login.microsoftonline.com/tfp/";
+ o.ClientId = "ClientId";
+ o.CallbackPath = "/signin-oidc";
+ o.Domain = "test.onmicrosoft.com";
+ o.SignUpSignInPolicyId = "B2C_1_SiUpIn";
+ o.ResetPasswordPolicyId = "B2C_1_SSPR";
+ o.EditProfilePolicyId = "B2C_1_SiPe";
+ });
+
+ services.Configure(AzureADB2CDefaults.OpenIdScheme, o =>
+ {
+ o.Configuration = new OpenIdConnectConfiguration()
+ {
+ Issuer = "https://www.example.com",
+ TokenEndpoint = "https://www.example.com/token",
+ AuthorizationEndpoint = "https://www.example.com/authorize",
+ EndSessionEndpoint = "https://www.example.com/logout"
+ };
+ // CookieContainer doesn't allow cookies from other paths
+ o.CorrelationCookie.Path = "/";
+ o.NonceCookie.Path = "/";
+ });
+
+ services.AddMvc(o => o.Filters.Add(
+ new AuthorizeFilter(new AuthorizationPolicyBuilder(new[] { AzureADB2CDefaults.AuthenticationScheme })
+ .RequireAuthenticatedUser().Build())));
+ })).CreateClient(new WebApplicationFactoryClientOptions() { AllowAutoRedirect = false });
+
+ var response = await client.GetAsync("/api/get");
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+
+ var location = response.Headers.Location;
+ Assert.StartsWith("https://www.example.com/authorize", location.AbsoluteUri);
+ var queryString = location.Query;
+ var query = QueryHelpers.ParseQuery(queryString);
+ var state = query["state"];
+ Assert.False(StringValues.IsNullOrEmpty(state));
+
+ // Mock Authorization response
+ response = await client.GetAsync($"/signin-oidc?error=access_denied&error_description=AADB2C90118&state={state}");
+
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.Equal("/AzureADB2C/Account/ResetPassword/AzureADB2C", response.Headers.Location.OriginalString);
+ }
}
}
diff --git a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj
index 73b832bf22..25e2626cfe 100644
--- a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj
+++ b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/AzureAD.WebSite.csproj
@@ -1,4 +1,4 @@
-
+
netcoreapp3.0
@@ -9,16 +9,8 @@
-
+
-
-
-
-
-
-
-
-
diff --git a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs
index 01046c73a0..998a9ab324 100644
--- a/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.cs
+++ b/src/Azure/AzureAD/test/testassets/AzureAD.WebSite/Program.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;
@@ -23,55 +23,8 @@ namespace AzureAD.WebSite
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
- var builder = new WebHostBuilder()
- .UseKestrel((builderContext, options) =>
- {
- options.Configure(builderContext.Configuration.GetSection("Kestrel"));
- })
- .UseContentRoot(Directory.GetCurrentDirectory())
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- var env = hostingContext.HostingEnvironment;
-
- config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
-
- if (env.IsDevelopment())
- {
- var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
- if (appAssembly != null)
- {
- config.AddUserSecrets(appAssembly, optional: true);
- }
- }
-
- config.AddEnvironmentVariables();
-
- if (args != null)
- {
- config.AddCommandLine(args);
- }
- })
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- logging.AddConsole();
- logging.AddDebug();
- })
- .UseIISIntegration()
- .UseDefaultServiceProvider((context, options) =>
- {
- options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
- });
-
- if (args != null)
- {
- builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
- }
-
- builder.UseStartup();
-
- return builder;
+ return WebHost.CreateDefaultBuilder()
+ .UseStartup();
}
}
}
diff --git a/src/Components/.editorconfig b/src/Components/.editorconfig
index 0d238f8e41..f9d02415ce 100644
--- a/src/Components/.editorconfig
+++ b/src/Components/.editorconfig
@@ -25,3 +25,31 @@ indent_size = 2
[*.{xml,csproj,config,*proj,targets,props}]
indent_size = 2
+
+# Dotnet code style settings:
+[*.cs]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+
+# Don't use this. qualifier
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+
+# use int x = .. over Int32
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+
+# use int.MaxValue over Int32.MaxValue
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Require var all the time.
+csharp_style_var_for_built_in_types = true:suggestion
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_elsewhere = true:suggestion
+
+# Newline settings
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
diff --git a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
index c68831682f..76cb6f8495 100644
--- a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
+++ b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
@@ -22,6 +22,9 @@
+
+
+
@@ -37,7 +40,6 @@
-
diff --git a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Microsoft.AspNetCore.Blazor.csproj b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Microsoft.AspNetCore.Blazor.csproj
index b01c0d97d0..1aa8b3eba5 100644
--- a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Microsoft.AspNetCore.Blazor.csproj
+++ b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Microsoft.AspNetCore.Blazor.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Rendering/WebAssemblyRenderer.cs b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Rendering/WebAssemblyRenderer.cs
index e6b4de5dc6..93941e4d34 100644
--- a/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Rendering/WebAssemblyRenderer.cs
+++ b/src/Components/blazor/src/Microsoft.AspNetCore.Blazor/Rendering/WebAssemblyRenderer.cs
@@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
/// Provides mechanisms for rendering instances in a
/// web browser, dispatching events to them, and refreshing the UI as required.
///
- public class WebAssemblyRenderer : Renderer, IDisposable
+ public class WebAssemblyRenderer : Renderer
{
private readonly int _webAssemblyRendererId;
@@ -71,11 +71,10 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
RenderRootComponent(componentId);
}
- ///
- /// Disposes the instance.
- ///
- public void Dispose()
+ ///
+ protected override void Dispose(bool disposing)
{
+ base.Dispose(disposing);
RendererRegistry.Current.TryRemove(_webAssemblyRendererId);
}
diff --git a/src/Components/build/dependencies.props b/src/Components/build/dependencies.props
index 9e3ba7f2cf..13fac9d3ab 100644
--- a/src/Components/build/dependencies.props
+++ b/src/Components/build/dependencies.props
@@ -12,7 +12,7 @@
3.0.0-alpha1-10605
3.0.0-alpha1-10605
3.0.0-alpha1-10605
- 3.0.0-preview-19057-06
+ 3.0.0-preview-19064-09
0.10.1
3.0.0-alpha1-10605
0.8.0-preview-19064-0339
@@ -20,7 +20,8 @@
2.1.2
0.8.0-preview1-20181126.1
-
- 0.8.0-preview1-20181126.4
+ 3.0.0-preview.19059.5
+
+ 0.8.0-preview1-20181126.4
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Microsoft.AspNetCore.Components.Browser.csproj b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Microsoft.AspNetCore.Components.Browser.csproj
index d56001821d..1d49c29df7 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Microsoft.AspNetCore.Components.Browser.csproj
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Microsoft.AspNetCore.Components.Browser.csproj
@@ -2,7 +2,7 @@
netstandard2.0
- Support for rendering ASP.NET Core components in browsers.
+ Support for rendering ASP.NET Core components for browsers.
true
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Properties/AssemblyInfo.cs b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Properties/AssemblyInfo.cs
index b513292596..a311581a18 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Browser/Properties/AssemblyInfo.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Browser/Properties/AssemblyInfo.cs
@@ -2,3 +2,5 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Build/targets/RazorCompilation.targets b/src/Components/src/Microsoft.AspNetCore.Components.Build/targets/RazorCompilation.targets
index f3c62f1e56..2dbfe3ce78 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Build/targets/RazorCompilation.targets
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Build/targets/RazorCompilation.targets
@@ -12,6 +12,7 @@
false
false
false
+ <_RazorComponentInclude>**\*.cshtml
@@ -24,8 +25,4 @@
-
-
-
-
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/CircuitHost.cs b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/CircuitHost.cs
index 8c483daf8b..ea62df438c 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/CircuitHost.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/CircuitHost.cs
@@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
internal class CircuitHost : IDisposable
{
- private static AsyncLocal _current = new AsyncLocal();
+ private static readonly AsyncLocal _current = new AsyncLocal();
///
/// Gets the current , if any.
@@ -24,23 +24,18 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
public static CircuitHost Current => _current.Value;
///
- /// Sets the current .
+ /// Sets the current .
///
- /// The .
+ /// The .
///
/// Calling will store the circuit
/// and other related values such as the and
/// in the local execution context. Application code should not need to call this method,
- /// it is primarily used by the Server-Side Blazor infrastructure.
+ /// it is primarily used by the Server-Side Components infrastructure.
///
public static void SetCurrentCircuitHost(CircuitHost circuitHost)
{
- if (circuitHost == null)
- {
- throw new ArgumentNullException(nameof(circuitHost));
- }
-
- _current.Value = circuitHost;
+ _current.Value = circuitHost ?? throw new ArgumentNullException(nameof(circuitHost));
Microsoft.JSInterop.JSRuntime.SetCurrentJSRuntime(circuitHost.JSRuntime);
RendererRegistry.SetCurrentRendererRegistry(circuitHost.RendererRegistry);
@@ -134,6 +129,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
public void Dispose()
{
Scope.Dispose();
+ Renderer.Dispose();
}
private void AssertInitialized()
diff --git a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteRenderer.cs b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteRenderer.cs
index e60b9643e1..b81673f8ca 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteRenderer.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components.Server/Circuits/RemoteRenderer.cs
@@ -120,11 +120,10 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering
}
}
- ///
- /// Disposes the instance.
- ///
- public void Dispose()
+ ///
+ protected override void Dispose(bool disposing)
{
+ base.Dispose(true);
_rendererRegistry.TryRemove(_id);
}
diff --git a/src/Components/src/Microsoft.AspNetCore.Components/Microsoft.AspNetCore.Components.csproj b/src/Components/src/Microsoft.AspNetCore.Components/Microsoft.AspNetCore.Components.csproj
index 58295c79da..038d8d67ef 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components/Microsoft.AspNetCore.Components.csproj
+++ b/src/Components/src/Microsoft.AspNetCore.Components/Microsoft.AspNetCore.Components.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs b/src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs
index ff8e2b0f8e..3dde251101 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs
@@ -12,19 +12,17 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// Provides mechanisms for rendering hierarchies of instances,
/// dispatching events to them, and notifying when the user interface is being updated.
///
- public abstract class Renderer
+ public abstract class Renderer : IDisposable
{
private readonly ComponentFactory _componentFactory;
- private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it
- private readonly Dictionary _componentStateById
- = new Dictionary();
-
+ private readonly Dictionary _componentStateById = new Dictionary();
private readonly RenderBatchBuilder _batchBuilder = new RenderBatchBuilder();
- private bool _isBatchInProgress;
-
- private int _lastEventHandlerId = 0;
private readonly Dictionary _eventBindings = new Dictionary();
+ private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it
+ private bool _isBatchInProgress;
+ private int _lastEventHandlerId = 0;
+
///
/// Constructs an instance of .
///
@@ -175,7 +173,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
if (frame.AttributeValue is MulticastDelegate @delegate)
{
- _eventBindings.Add(id, new EventHandlerInvoker(@delegate));
+ _eventBindings.Add(id, new EventHandlerInvoker(@delegate));
}
frame = frame.WithAttributeEventHandlerId(id);
@@ -295,5 +293,44 @@ namespace Microsoft.AspNetCore.Components.Rendering
RemoveEventHandlerIds(eventHandlerIdsClone, Task.CompletedTask));
}
}
+
+ ///
+ /// Releases all resources currently used by this instance.
+ ///
+ /// if this method is being invoked by , otherwise .
+ protected virtual void Dispose(bool disposing)
+ {
+ List exceptions = null;
+
+ foreach (var componentState in _componentStateById.Values)
+ {
+ if (componentState.Component is IDisposable disposable)
+ {
+ try
+ {
+ disposable.Dispose();
+ }
+ catch (Exception exception)
+ {
+ // Capture exceptions thrown by individual components and rethrow as an aggregate.
+ exceptions = exceptions ?? new List();
+ exceptions.Add(exception);
+ }
+ }
+ }
+
+ if (exceptions != null)
+ {
+ throw new AggregateException(exceptions);
+ }
+ }
+
+ ///
+ /// Releases all resources currently used by this instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ }
}
}
diff --git a/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Circuits/CircuitHostTest.cs b/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Circuits/CircuitHostTest.cs
new file mode 100644
index 0000000000..27a35bfa73
--- /dev/null
+++ b/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Circuits/CircuitHostTest.cs
@@ -0,0 +1,61 @@
+// 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 Microsoft.AspNetCore.Components.Browser;
+using Microsoft.AspNetCore.Components.Browser.Rendering;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.JSInterop;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Components.Server.Circuits
+{
+ public class CircuitHostTest
+ {
+ [Fact]
+ public void Dispose_DisposesResources()
+ {
+ // Arrange
+ var serviceScope = new Mock();
+ var clientProxy = Mock.Of();
+ var renderRegistry = new RendererRegistry();
+ var jsRuntime = Mock.Of();
+ var syncContext = new CircuitSynchronizationContext();
+
+ var remoteRenderer = new TestRemoteRenderer(
+ Mock.Of(),
+ renderRegistry,
+ jsRuntime,
+ clientProxy,
+ syncContext);
+
+ var circuitHost = new CircuitHost(serviceScope.Object, clientProxy, renderRegistry, remoteRenderer, configure: _ => { }, jsRuntime: jsRuntime, synchronizationContext: syncContext);
+
+ // Act
+ circuitHost.Dispose();
+
+ // Assert
+ serviceScope.Verify(s => s.Dispose(), Times.Once());
+ Assert.True(remoteRenderer.Disposed);
+ }
+
+ private class TestRemoteRenderer : RemoteRenderer
+ {
+ public TestRemoteRenderer(IServiceProvider serviceProvider, RendererRegistry rendererRegistry, IJSRuntime jsRuntime, IClientProxy client, SynchronizationContext syncContext)
+ : base(serviceProvider, rendererRegistry, jsRuntime, client, syncContext)
+ {
+ }
+
+ public bool Disposed { get; set; }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ Disposed = true;
+ }
+ }
+ }
+}
diff --git a/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Microsoft.AspNetCore.Components.Server.Test.csproj b/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Microsoft.AspNetCore.Components.Server.Test.csproj
index 152cb7df88..204aecef37 100644
--- a/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Microsoft.AspNetCore.Components.Server.Test.csproj
+++ b/src/Components/test/Microsoft.AspNetCore.Components.Server.Test/Microsoft.AspNetCore.Components.Server.Test.csproj
@@ -1,4 +1,4 @@
-
+
netcoreapp3.0
@@ -7,6 +7,7 @@
+
diff --git a/src/Components/test/Microsoft.AspNetCore.Components.Test/RendererTest.cs b/src/Components/test/Microsoft.AspNetCore.Components.Test/RendererTest.cs
index 5fe8b79097..57eb16707d 100644
--- a/src/Components/test/Microsoft.AspNetCore.Components.Test/RendererTest.cs
+++ b/src/Components/test/Microsoft.AspNetCore.Components.Test/RendererTest.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
@@ -1139,6 +1138,78 @@ namespace Microsoft.AspNetCore.Components.Test
Assert.Equal(2, numEventsFired);
}
+ [Fact]
+ public void DisposingRenderer_DisposesTopLevelComponents()
+ {
+ // Arrange
+ var renderer = new TestRenderer();
+ var component = new DisposableComponent();
+ renderer.AssignRootComponentId(component);
+
+ // Act
+ renderer.Dispose();
+
+ // Assert
+ Assert.True(component.Disposed);
+ }
+
+ [Fact]
+ public void DisposingRenderer_DisposesNestedComponents()
+ {
+ // Arrange
+ var renderer = new TestRenderer();
+ var component = new TestComponent(builder =>
+ {
+ builder.AddContent(0, "Hello");
+ builder.OpenComponent(1);
+ builder.CloseComponent();
+ });
+ var componentId = renderer.AssignRootComponentId(component);
+ component.TriggerRender();
+ var batch = renderer.Batches.Single();
+ var componentFrame = batch.ReferenceFrames
+ .Single(frame => frame.FrameType == RenderTreeFrameType.Component);
+ var nestedComponent = Assert.IsType(componentFrame.Component);
+
+ // Act
+ renderer.Dispose();
+
+ // Assert
+ Assert.True(component.Disposed);
+ Assert.True(nestedComponent.Disposed);
+ }
+
+ [Fact]
+ public void DisposingRenderer_CapturesExceptionsFromAllRegisteredComponents()
+ {
+ // Arrange
+ var renderer = new TestRenderer();
+ var exception1 = new Exception();
+ var exception2 = new Exception();
+ var component = new TestComponent(builder =>
+ {
+ builder.AddContent(0, "Hello");
+ builder.OpenComponent(1);
+ builder.AddAttribute(1, nameof(DisposableComponent.DisposeAction), (Action)(() => throw exception1));
+ builder.CloseComponent();
+
+ builder.OpenComponent(2);
+ builder.AddAttribute(1, nameof(DisposableComponent.DisposeAction), (Action)(() => throw exception2));
+ builder.CloseComponent();
+ });
+ var componentId = renderer.AssignRootComponentId(component);
+ component.TriggerRender();
+
+ // Act &A Assert
+ var aggregate = Assert.Throws(renderer.Dispose);
+
+ // All components must be disposed even if some throw as part of being diposed.
+ Assert.True(component.Disposed);
+ Assert.Equal(2, aggregate.InnerExceptions.Count);
+ Assert.Contains(exception1, aggregate.InnerExceptions);
+ Assert.Contains(exception2, aggregate.InnerExceptions);
+ }
+
private class NoOpRenderer : Renderer
{
public NoOpRenderer() : base(new TestServiceProvider())
@@ -1152,7 +1223,7 @@ namespace Microsoft.AspNetCore.Components.Test
=> Task.CompletedTask;
}
- private class TestComponent : IComponent
+ private class TestComponent : IComponent, IDisposable
{
private RenderHandle _renderHandle;
private RenderFragment _renderFragment;
@@ -1172,6 +1243,10 @@ namespace Microsoft.AspNetCore.Components.Test
public void TriggerRender()
=> _renderHandle.Render(_renderFragment);
+
+ public bool Disposed { get; private set; }
+
+ void IDisposable.Dispose() => Disposed = true;
}
private class MessageComponent : AutoRenderComponent
@@ -1398,6 +1473,24 @@ namespace Microsoft.AspNetCore.Components.Test
}
}
+ private class DisposableComponent : AutoRenderComponent, IDisposable
+ {
+ public bool Disposed { get; private set; }
+
+ [Parameter]
+ public Action DisposeAction { get; private set; }
+
+ public void Dispose()
+ {
+ Disposed = true;
+ DisposeAction?.Invoke();
+ }
+
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ }
+ }
+
class TestAsyncRenderer : TestRenderer
{
public Task NextUpdateDisplayReturnTask { get; set; }
diff --git a/src/Framework/Microsoft.AspNetCore.App.props b/src/Framework/Microsoft.AspNetCore.App.props
index 3be5f12ffe..d2dc4ccd41 100644
--- a/src/Framework/Microsoft.AspNetCore.App.props
+++ b/src/Framework/Microsoft.AspNetCore.App.props
@@ -19,6 +19,8 @@
+
+
@@ -127,6 +129,7 @@
+
diff --git a/src/Hosting/TestHost/src/AsyncStreamWrapper.cs b/src/Hosting/TestHost/src/AsyncStreamWrapper.cs
new file mode 100644
index 0000000000..32f1548d35
--- /dev/null
+++ b/src/Hosting/TestHost/src/AsyncStreamWrapper.cs
@@ -0,0 +1,128 @@
+// 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.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.TestHost
+{
+ internal class AsyncStreamWrapper : Stream
+ {
+ private Stream _inner;
+ private Func _allowSynchronousIO;
+
+ internal AsyncStreamWrapper(Stream inner, Func allowSynchronousIO)
+ {
+ _inner = inner;
+ _allowSynchronousIO = allowSynchronousIO;
+ }
+
+ public override bool CanRead => _inner.CanRead;
+
+ public override bool CanSeek => _inner.CanSeek;
+
+ public override bool CanWrite => _inner.CanWrite;
+
+ public override long Length => _inner.Length;
+
+ public override long Position { get => _inner.Position; set => _inner.Position = value; }
+
+ public override void Flush()
+ {
+ // Not blocking Flush because things like StreamWriter.Dispose() always call it.
+ _inner.Flush();
+ }
+
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ return _inner.FlushAsync(cancellationToken);
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (!_allowSynchronousIO())
+ {
+ throw new InvalidOperationException("Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true.");
+ }
+
+ return _inner.Read(buffer, offset, count);
+ }
+
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return _inner.ReadAsync(buffer, offset, count, cancellationToken);
+ }
+
+ public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
+ {
+ return _inner.ReadAsync(buffer, cancellationToken);
+ }
+
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ return _inner.BeginRead(buffer, offset, count, callback, state);
+ }
+
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ return _inner.EndRead(asyncResult);
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return _inner.Seek(offset, origin);
+ }
+
+ public override void SetLength(long value)
+ {
+ _inner.SetLength(value);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (!_allowSynchronousIO())
+ {
+ throw new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.");
+ }
+
+ _inner.Write(buffer, offset, count);
+ }
+
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ return _inner.BeginWrite(buffer, offset, count, callback, state);
+ }
+
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ _inner.EndWrite(asyncResult);
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return _inner.WriteAsync(buffer, offset, count, cancellationToken);
+ }
+
+ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
+ {
+ return _inner.WriteAsync(buffer, cancellationToken);
+ }
+
+ public override void Close()
+ {
+ _inner.Close();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _inner.Dispose();
+ }
+
+ public override ValueTask DisposeAsync()
+ {
+ return _inner.DisposeAsync();
+ }
+ }
+}
diff --git a/src/Hosting/TestHost/src/ClientHandler.cs b/src/Hosting/TestHost/src/ClientHandler.cs
index 5471e23d19..c947623220 100644
--- a/src/Hosting/TestHost/src/ClientHandler.cs
+++ b/src/Hosting/TestHost/src/ClientHandler.cs
@@ -43,6 +43,8 @@ namespace Microsoft.AspNetCore.TestHost
_pathBase = pathBase;
}
+ internal bool AllowSynchronousIO { get; set; }
+
///
/// This adapts HttpRequestMessages to ASP.NET Core requests, dispatches them through the pipeline, and returns the
/// associated HttpResponseMessage.
@@ -59,7 +61,7 @@ namespace Microsoft.AspNetCore.TestHost
throw new ArgumentNullException(nameof(request));
}
- var contextBuilder = new HttpContextBuilder(_application);
+ var contextBuilder = new HttpContextBuilder(_application, AllowSynchronousIO);
Stream responseBody = null;
var requestContent = request.Content ?? new StreamContent(Stream.Null);
@@ -110,7 +112,7 @@ namespace Microsoft.AspNetCore.TestHost
// This body may have been consumed before, rewind it.
body.Seek(0, SeekOrigin.Begin);
}
- req.Body = body;
+ req.Body = new AsyncStreamWrapper(body, () => contextBuilder.AllowSynchronousIO);
responseBody = context.Response.Body;
});
diff --git a/src/Hosting/TestHost/src/HttpContextBuilder.cs b/src/Hosting/TestHost/src/HttpContextBuilder.cs
index c576628b65..69acf27591 100644
--- a/src/Hosting/TestHost/src/HttpContextBuilder.cs
+++ b/src/Hosting/TestHost/src/HttpContextBuilder.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;
@@ -11,7 +11,7 @@ using static Microsoft.AspNetCore.Hosting.Internal.HostingApplication;
namespace Microsoft.AspNetCore.TestHost
{
- internal class HttpContextBuilder
+ internal class HttpContextBuilder : IHttpBodyControlFeature
{
private readonly IHttpApplication _application;
private readonly HttpContext _httpContext;
@@ -23,24 +23,28 @@ namespace Microsoft.AspNetCore.TestHost
private bool _pipelineFinished;
private Context _testContext;
- internal HttpContextBuilder(IHttpApplication application)
+ internal HttpContextBuilder(IHttpApplication application, bool allowSynchronousIO)
{
_application = application ?? throw new ArgumentNullException(nameof(application));
+ AllowSynchronousIO = allowSynchronousIO;
_httpContext = new DefaultHttpContext();
var request = _httpContext.Request;
request.Protocol = "HTTP/1.1";
request.Method = HttpMethods.Get;
+ _httpContext.Features.Set(this);
_httpContext.Features.Set(_responseFeature);
var requestLifetimeFeature = new HttpRequestLifetimeFeature();
requestLifetimeFeature.RequestAborted = _requestAbortedSource.Token;
_httpContext.Features.Set(requestLifetimeFeature);
- _responseStream = new ResponseStream(ReturnResponseMessageAsync, AbortRequest);
+ _responseStream = new ResponseStream(ReturnResponseMessageAsync, AbortRequest, () => AllowSynchronousIO);
_responseFeature.Body = _responseStream;
}
+ public bool AllowSynchronousIO { get; set; }
+
internal void Configure(Action configureContext)
{
if (configureContext == null)
@@ -136,4 +140,4 @@ namespace Microsoft.AspNetCore.TestHost
_responseTcs.TrySetException(exception);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Hosting/TestHost/src/ResponseStream.cs b/src/Hosting/TestHost/src/ResponseStream.cs
index b2ababa182..7563beb4c3 100644
--- a/src/Hosting/TestHost/src/ResponseStream.cs
+++ b/src/Hosting/TestHost/src/ResponseStream.cs
@@ -24,13 +24,15 @@ namespace Microsoft.AspNetCore.TestHost
private Func _onFirstWriteAsync;
private bool _firstWrite;
private Action _abortRequest;
+ private Func _allowSynchronousIO;
private Pipe _pipe = new Pipe();
- internal ResponseStream(Func onFirstWriteAsync, Action abortRequest)
+ internal ResponseStream(Func onFirstWriteAsync, Action abortRequest, Func allowSynchronousIO)
{
_onFirstWriteAsync = onFirstWriteAsync ?? throw new ArgumentNullException(nameof(onFirstWriteAsync));
_abortRequest = abortRequest ?? throw new ArgumentNullException(nameof(abortRequest));
+ _allowSynchronousIO = allowSynchronousIO ?? throw new ArgumentNullException(nameof(allowSynchronousIO));
_firstWrite = true;
_writeLock = new SemaphoreSlim(1, 1);
}
@@ -144,6 +146,11 @@ namespace Microsoft.AspNetCore.TestHost
// Write with count 0 will still trigger OnFirstWrite
public override void Write(byte[] buffer, int offset, int count)
{
+ if (!_allowSynchronousIO())
+ {
+ throw new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.");
+ }
+
// The Pipe Write method requires calling FlushAsync to notify the reader. Call WriteAsync instead.
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
}
diff --git a/src/Hosting/TestHost/src/TestServer.cs b/src/Hosting/TestHost/src/TestServer.cs
index 6c45c0b21b..c5668185ac 100644
--- a/src/Hosting/TestHost/src/TestServer.cs
+++ b/src/Hosting/TestHost/src/TestServer.cs
@@ -77,6 +77,14 @@ namespace Microsoft.AspNetCore.TestHost
public IFeatureCollection Features { get; }
+ ///
+ /// Gets or sets a value that controls whether synchronous IO is allowed for the and
+ ///
+ ///
+ /// Defaults to true.
+ ///
+ public bool AllowSynchronousIO { get; set; } = true;
+
private IHttpApplication Application
{
get => _application ?? throw new InvalidOperationException("The server has not been started or no web application was configured.");
@@ -85,7 +93,7 @@ namespace Microsoft.AspNetCore.TestHost
public HttpMessageHandler CreateHandler()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
- return new ClientHandler(pathBase, Application);
+ return new ClientHandler(pathBase, Application) { AllowSynchronousIO = AllowSynchronousIO };
}
public HttpClient CreateClient()
@@ -96,7 +104,7 @@ namespace Microsoft.AspNetCore.TestHost
public WebSocketClient CreateWebSocketClient()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
- return new WebSocketClient(pathBase, Application);
+ return new WebSocketClient(pathBase, Application) { AllowSynchronousIO = AllowSynchronousIO };
}
///
@@ -120,7 +128,7 @@ namespace Microsoft.AspNetCore.TestHost
throw new ArgumentNullException(nameof(configureContext));
}
- var builder = new HttpContextBuilder(Application);
+ var builder = new HttpContextBuilder(Application, AllowSynchronousIO);
builder.Configure(context =>
{
var request = context.Request;
@@ -138,6 +146,7 @@ namespace Microsoft.AspNetCore.TestHost
request.PathBase = pathBase;
});
builder.Configure(configureContext);
+ // TODO: Wrap the request body if any?
return await builder.SendAsync(cancellationToken).ConfigureAwait(false);
}
diff --git a/src/Hosting/TestHost/src/WebSocketClient.cs b/src/Hosting/TestHost/src/WebSocketClient.cs
index e3deb670a5..cec96ab4ce 100644
--- a/src/Hosting/TestHost/src/WebSocketClient.cs
+++ b/src/Hosting/TestHost/src/WebSocketClient.cs
@@ -46,10 +46,12 @@ namespace Microsoft.AspNetCore.TestHost
set;
}
+ internal bool AllowSynchronousIO { get; set; }
+
public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken)
{
WebSocketFeature webSocketFeature = null;
- var contextBuilder = new HttpContextBuilder(_application);
+ var contextBuilder = new HttpContextBuilder(_application, AllowSynchronousIO);
contextBuilder.Configure(context =>
{
var request = context.Request;
@@ -131,4 +133,4 @@ namespace Microsoft.AspNetCore.TestHost
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Hosting/TestHost/test/ClientHandlerTests.cs b/src/Hosting/TestHost/test/ClientHandlerTests.cs
index f287ebd053..012867dd28 100644
--- a/src/Hosting/TestHost/test/ClientHandlerTests.cs
+++ b/src/Hosting/TestHost/test/ClientHandlerTests.cs
@@ -92,13 +92,12 @@ namespace Microsoft.AspNetCore.TestHost
public async Task ResubmitRequestWorks()
{
int requestCount = 1;
- var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
+ var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
{
- int read = context.Request.Body.Read(new byte[100], 0, 100);
+ int read = await context.Request.Body.ReadAsync(new byte[100], 0, 100);
Assert.Equal(11, read);
context.Response.Headers["TestHeader"] = "TestValue:" + requestCount++;
- return Task.FromResult(0);
}));
HttpMessageInvoker invoker = new HttpMessageInvoker(handler);
diff --git a/src/Hosting/TestHost/test/HttpContextBuilderTests.cs b/src/Hosting/TestHost/test/HttpContextBuilderTests.cs
index f04a2f16f9..88126de9e8 100644
--- a/src/Hosting/TestHost/test/HttpContextBuilderTests.cs
+++ b/src/Hosting/TestHost/test/HttpContextBuilderTests.cs
@@ -109,6 +109,7 @@ namespace Microsoft.AspNetCore.TestHost
{
c.Response.Headers["TestHeader"] = "TestValue";
var bytes = Encoding.UTF8.GetBytes("BodyStarted" + Environment.NewLine);
+ c.Features.Get().AllowSynchronousIO = true;
c.Response.Body.Write(bytes, 0, bytes.Length);
await block.Task;
bytes = Encoding.UTF8.GetBytes("BodyFinished");
diff --git a/src/Hosting/TestHost/test/TestClientTests.cs b/src/Hosting/TestHost/test/TestClientTests.cs
index 3101c2965f..7b86c18978 100644
--- a/src/Hosting/TestHost/test/TestClientTests.cs
+++ b/src/Hosting/TestHost/test/TestClientTests.cs
@@ -87,8 +87,8 @@ namespace Microsoft.AspNetCore.TestHost
public async Task PutAsyncWorks()
{
// Arrange
- RequestDelegate appDelegate = ctx =>
- ctx.Response.WriteAsync(new StreamReader(ctx.Request.Body).ReadToEnd() + " PUT Response");
+ RequestDelegate appDelegate = async ctx =>
+ await ctx.Response.WriteAsync(await new StreamReader(ctx.Request.Body).ReadToEndAsync() + " PUT Response");
var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate));
var server = new TestServer(builder);
var client = server.CreateClient();
@@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.TestHost
{
// Arrange
RequestDelegate appDelegate = async ctx =>
- await ctx.Response.WriteAsync(new StreamReader(ctx.Request.Body).ReadToEnd() + " POST Response");
+ await ctx.Response.WriteAsync(await new StreamReader(ctx.Request.Body).ReadToEndAsync() + " POST Response");
var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate));
var server = new TestServer(builder);
var client = server.CreateClient();
@@ -132,16 +132,15 @@ namespace Microsoft.AspNetCore.TestHost
}
var builder = new WebHostBuilder();
- RequestDelegate app = (ctx) =>
+ RequestDelegate app = async ctx =>
{
var disposable = new TestDisposable();
ctx.Response.RegisterForDispose(disposable);
- ctx.Response.Body.Write(data, 0, 1024);
+ await ctx.Response.Body.WriteAsync(data, 0, 1024);
Assert.False(disposable.IsDisposed);
- ctx.Response.Body.Write(data, 1024, 1024);
- return Task.FromResult(0);
+ await ctx.Response.Body.WriteAsync(data, 1024, 1024);
};
builder.Configure(appBuilder => appBuilder.Run(app));
diff --git a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj
index 380a4bd959..2f17e52019 100644
--- a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj
+++ b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj
@@ -22,7 +22,6 @@ Microsoft.AspNetCore.Http.HttpResponse
-
diff --git a/src/Http/Http.Features/src/IRequestBodyPipeFeature.cs b/src/Http/Http.Features/src/IRequestBodyPipeFeature.cs
index 0c996ff691..503c205276 100644
--- a/src/Http/Http.Features/src/IRequestBodyPipeFeature.cs
+++ b/src/Http/Http.Features/src/IRequestBodyPipeFeature.cs
@@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Http.Features
public interface IRequestBodyPipeFeature
{
///
- /// A representing the request body, if any.
+ /// A representing the request body, if any.
///
PipeReader RequestBodyPipe { get; set; }
}
diff --git a/src/Http/Http.Features/test/Microsoft.AspNetCore.Http.Features.Tests.csproj b/src/Http/Http.Features/test/Microsoft.AspNetCore.Http.Features.Tests.csproj
index c83d286a00..6a80fe588a 100644
--- a/src/Http/Http.Features/test/Microsoft.AspNetCore.Http.Features.Tests.csproj
+++ b/src/Http/Http.Features/test/Microsoft.AspNetCore.Http.Features.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.0;net461
+ netcoreapp3.0
diff --git a/src/Http/Http/src/BufferSegment.cs b/src/Http/Http/src/BufferSegment.cs
index f0dcdc5077..735a4a39e0 100644
--- a/src/Http/Http/src/BufferSegment.cs
+++ b/src/Http/Http/src/BufferSegment.cs
@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace System.IO.Pipelines
{
- public sealed class BufferSegment : ReadOnlySequenceSegment
+ internal sealed class BufferSegment : ReadOnlySequenceSegment
{
private IMemoryOwner _memoryOwner;
private BufferSegment _next;
diff --git a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
index 1575488b80..d091f7d690 100644
--- a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
+++ b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
@@ -1,4 +1,4 @@
-
+
ASP.NET Core default HTTP feature implementations.
@@ -19,7 +19,6 @@
-
diff --git a/src/Http/Http/src/Properties/AssemblyInfo.cs b/src/Http/Http/src/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..2b8d94f4a5
--- /dev/null
+++ b/src/Http/Http/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Http.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Http/Http/src/ReadOnlyPipeStream.cs b/src/Http/Http/src/ReadOnlyPipeStream.cs
new file mode 100644
index 0000000000..7585947d2c
--- /dev/null
+++ b/src/Http/Http/src/ReadOnlyPipeStream.cs
@@ -0,0 +1,240 @@
+// 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.Buffers;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO.Pipelines
+{
+ ///
+ /// Represents a read-only Stream backed by a PipeReader
+ ///
+ public class ReadOnlyPipeStream : Stream
+ {
+ private readonly PipeReader _pipeReader;
+ private bool _allowSynchronousIO = true;
+
+ ///
+ /// Creates a new ReadOnlyPipeStream
+ ///
+ /// The PipeReader to read from.
+ public ReadOnlyPipeStream(PipeReader pipeReader) :
+ this(pipeReader, allowSynchronousIO: true)
+ {
+ }
+
+ ///
+ /// Creates a new ReadOnlyPipeStream
+ ///
+ /// The PipeReader to read from.
+ /// Whether synchronous IO is allowed.
+ public ReadOnlyPipeStream(PipeReader pipeReader, bool allowSynchronousIO)
+ {
+ _allowSynchronousIO = allowSynchronousIO;
+ _pipeReader = pipeReader;
+ }
+
+ ///
+ public override bool CanSeek => false;
+
+ ///
+ public override bool CanRead => true;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => throw new NotSupportedException();
+
+ ///
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public override int WriteTimeout
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ => throw new NotSupportedException();
+
+ ///
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ => throw new NotSupportedException();
+
+ ///
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (!_allowSynchronousIO)
+ {
+ ThrowHelper.ThrowInvalidOperationException_SynchronousReadsDisallowed();
+ }
+ return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
+ }
+
+ ///
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ var task = ReadAsync(buffer, offset, count, default, state);
+ if (callback != null)
+ {
+ task.ContinueWith(t => callback.Invoke(t));
+ }
+ return task;
+ }
+
+ ///
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ return ((Task)asyncResult).GetAwaiter().GetResult();
+ }
+
+ private Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state)
+ {
+ var tcs = new TaskCompletionSource(state);
+ var task = ReadAsync(buffer, offset, count, cancellationToken);
+ task.ContinueWith((task2, state2) =>
+ {
+ var tcs2 = (TaskCompletionSource)state2;
+ if (task2.IsCanceled)
+ {
+ tcs2.SetCanceled();
+ }
+ else if (task2.IsFaulted)
+ {
+ tcs2.SetException(task2.Exception);
+ }
+ else
+ {
+ tcs2.SetResult(task2.Result);
+ }
+ }, tcs, cancellationToken);
+ return tcs.Task;
+ }
+
+ ///
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ return ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken).AsTask();
+ }
+
+ ///
+ public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default)
+ {
+ return ReadAsyncInternal(destination, cancellationToken);
+ }
+
+ private async ValueTask ReadAsyncInternal(Memory buffer, CancellationToken cancellationToken)
+ {
+ while (true)
+ {
+ var result = await _pipeReader.ReadAsync(cancellationToken);
+ var readableBuffer = result.Buffer;
+ var readableBufferLength = readableBuffer.Length;
+
+ var consumed = readableBuffer.End;
+ var actual = 0;
+ try
+ {
+ if (readableBufferLength != 0)
+ {
+ actual = (int)Math.Min(readableBufferLength, buffer.Length);
+
+ var slice = actual == readableBufferLength ? readableBuffer : readableBuffer.Slice(0, actual);
+ consumed = slice.End;
+ slice.CopyTo(buffer.Span);
+
+ return actual;
+ }
+
+ if (result.IsCompleted)
+ {
+ return 0;
+ }
+ }
+ finally
+ {
+ _pipeReader.AdvanceTo(consumed);
+ }
+ }
+ }
+
+ ///
+ public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ if (bufferSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufferSize));
+ }
+
+ return CopyToAsyncInternal(destination, cancellationToken);
+ }
+
+ private async Task CopyToAsyncInternal(Stream destination, CancellationToken cancellationToken)
+ {
+ while (true)
+ {
+ var result = await _pipeReader.ReadAsync(cancellationToken);
+ var readableBuffer = result.Buffer;
+ var readableBufferLength = readableBuffer.Length;
+
+ try
+ {
+ if (readableBufferLength != 0)
+ {
+ foreach (var memory in readableBuffer)
+ {
+ await destination.WriteAsync(memory, cancellationToken);
+ }
+ }
+
+ if (result.IsCompleted)
+ {
+ return;
+ }
+ }
+ finally
+ {
+ _pipeReader.AdvanceTo(readableBuffer.End);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Http/Http/src/StreamPipeReader.cs b/src/Http/Http/src/StreamPipeReader.cs
index 9d9e64caca..09295f2807 100644
--- a/src/Http/Http/src/StreamPipeReader.cs
+++ b/src/Http/Http/src/StreamPipeReader.cs
@@ -25,7 +25,8 @@ namespace System.IO.Pipelines
private readonly MemoryPool _pool;
private CancellationTokenSource _internalTokenSource;
- private bool _isCompleted;
+ private bool _isReaderCompleted;
+ private bool _isWriterCompleted;
private ExceptionDispatchInfo _exceptionInfo;
private BufferSegment _readHead;
@@ -182,12 +183,12 @@ namespace System.IO.Pipelines
///
public override void Complete(Exception exception = null)
{
- if (_isCompleted)
+ if (_isReaderCompleted)
{
return;
}
- _isCompleted = true;
+ _isReaderCompleted = true;
if (exception != null)
{
_exceptionInfo = ExceptionDispatchInfo.Capture(exception);
@@ -248,6 +249,11 @@ namespace System.IO.Pipelines
_readTail.End += length;
_bufferedBytes += length;
+
+ if (length == 0)
+ {
+ _isWriterCompleted = true;
+ }
}
catch (OperationCanceledException)
{
@@ -275,7 +281,7 @@ namespace System.IO.Pipelines
private void ThrowIfCompleted()
{
- if (_isCompleted)
+ if (_isReaderCompleted)
{
ThrowHelper.ThrowInvalidOperationException_NoReadingAllowed();
}
@@ -357,7 +363,7 @@ namespace System.IO.Pipelines
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsCompletedOrThrow()
{
- if (!_isCompleted)
+ if (!_isWriterCompleted)
{
return false;
}
diff --git a/src/Http/Http/src/StreamPipeWriter.cs b/src/Http/Http/src/StreamPipeWriter.cs
index 6926f1e9b9..adfafe4455 100644
--- a/src/Http/Http/src/StreamPipeWriter.cs
+++ b/src/Http/Http/src/StreamPipeWriter.cs
@@ -65,7 +65,7 @@ namespace System.IO.Pipelines
}
///
- /// Gets the inner stream that is being read from.
+ /// Gets the inner stream that is being written to.
///
public Stream InnerStream => _writingStream;
diff --git a/src/Http/Http/src/ThrowHelper.cs b/src/Http/Http/src/ThrowHelper.cs
index e671d9f6ee..1ae116b646 100644
--- a/src/Http/Http/src/ThrowHelper.cs
+++ b/src/Http/Http/src/ThrowHelper.cs
@@ -19,5 +19,17 @@ namespace System.IO.Pipelines
public static void ThrowInvalidOperationException_NoDataRead() => throw CreateInvalidOperationException_NoDataRead();
[MethodImpl(MethodImplOptions.NoInlining)]
public static Exception CreateInvalidOperationException_NoDataRead() => new InvalidOperationException("No data has been read into the StreamPipeReader.");
+
+ public static void ThrowInvalidOperationException_SynchronousReadsDisallowed() => throw CreateInvalidOperationException_SynchronousReadsDisallowed();
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Exception CreateInvalidOperationException_SynchronousReadsDisallowed() => new InvalidOperationException("Synchronous operations are disallowed. Call ReadAsync or set allowSynchronousIO to true instead.");
+
+ public static void ThrowInvalidOperationException_SynchronousWritesDisallowed() => throw CreateInvalidOperationException_SynchronousWritesDisallowed();
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Exception CreateInvalidOperationException_SynchronousWritesDisallowed() => new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set allowSynchronousIO to true instead.");
+
+ public static void ThrowInvalidOperationException_SynchronousFlushesDisallowed() => throw CreateInvalidOperationException_SynchronousFlushesDisallowed();
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Exception CreateInvalidOperationException_SynchronousFlushesDisallowed() => new InvalidOperationException("Synchronous operations are disallowed. Call FlushAsync or set allowSynchronousIO to true instead.");
}
}
diff --git a/src/Http/Http/src/WriteOnlyPipeStream.cs b/src/Http/Http/src/WriteOnlyPipeStream.cs
new file mode 100644
index 0000000000..0f5121cc2f
--- /dev/null
+++ b/src/Http/Http/src/WriteOnlyPipeStream.cs
@@ -0,0 +1,162 @@
+// 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.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO.Pipelines
+{
+ ///
+ /// Represents a WriteOnlyStream backed by a PipeWriter
+ ///
+ public class WriteOnlyPipeStream : Stream
+ {
+ private PipeWriter _pipeWriter;
+ private bool _allowSynchronousIO = true;
+
+ ///
+ /// Creates a new WriteOnlyStream
+ ///
+ /// The PipeWriter to write to.
+ public WriteOnlyPipeStream(PipeWriter pipeWriter) :
+ this(pipeWriter, allowSynchronousIO: true)
+ {
+ }
+
+ ///
+ /// Creates a new WriteOnlyStream
+ ///
+ /// The PipeWriter to write to.
+ /// Whether synchronous IO is allowed.
+ public WriteOnlyPipeStream(PipeWriter pipeWriter, bool allowSynchronousIO)
+ {
+ _pipeWriter = pipeWriter;
+ _allowSynchronousIO = allowSynchronousIO;
+ }
+
+ ///
+ public override bool CanSeek => false;
+
+ ///
+ public override bool CanRead => false;
+
+ ///
+ public override bool CanWrite => true;
+
+ ///
+ public override long Length => throw new NotSupportedException();
+
+ ///
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public override int ReadTimeout
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ => throw new NotSupportedException();
+
+ ///
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ => throw new NotSupportedException();
+
+ ///
+ public override void Flush()
+ {
+ if (!_allowSynchronousIO)
+ {
+ ThrowHelper.ThrowInvalidOperationException_SynchronousFlushesDisallowed();
+ }
+
+ FlushAsync(default).GetAwaiter().GetResult();
+ }
+
+ ///
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ await _pipeWriter.FlushAsync(cancellationToken);
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (!_allowSynchronousIO)
+ {
+ ThrowHelper.ThrowInvalidOperationException_SynchronousWritesDisallowed();
+ }
+ WriteAsync(buffer, offset, count, default).GetAwaiter().GetResult();
+ }
+
+ ///
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ var task = WriteAsync(buffer, offset, count, default, state);
+ if (callback != null)
+ {
+ task.ContinueWith(t => callback.Invoke(t));
+ }
+ return task;
+ }
+
+ ///
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ ((Task