Replace .Sources package references with local code (#4801)
Replaces package references the following packages with local code: Microsoft.AspNetCore.Certificates.Generation.Sources Microsoft.Extensions.CopyOnWriteDictionary.Sources Microsoft.Extensions.ClosedGenericMatcher.Sources Microsoft.Extensions.ObjectMethodExecutor.Sources Microsoft.Extensions.PropertyActivator.Sources Microsoft.Extensions.PropertyHelper.Sources Microsoft.Extensions.RazorViews.Sources Microsoft.Extensions.SecurityHelper.Sources Microsoft.Extensions.StackTrace.Sources Microsoft.Extensions.WebEncoders.Sources
This commit is contained in:
parent
96ea326664
commit
13ae0057fb
|
|
@ -26,14 +26,12 @@
|
||||||
<PropertyGroup Label="Package Versions: Pinned">
|
<PropertyGroup Label="Package Versions: Pinned">
|
||||||
<!-- Packages that came from aspnet/Extensions. -->
|
<!-- Packages that came from aspnet/Extensions. -->
|
||||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.1</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.1</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsCachingAbstractionsPackageVersion>2.1.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
|
<MicrosoftExtensionsCachingAbstractionsPackageVersion>2.1.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.2</MicrosoftExtensionsCachingMemoryPackageVersion>
|
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.2</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||||
<MicrosoftExtensionsCachingRedisPackageVersion>2.1.2</MicrosoftExtensionsCachingRedisPackageVersion>
|
<MicrosoftExtensionsCachingRedisPackageVersion>2.1.2</MicrosoftExtensionsCachingRedisPackageVersion>
|
||||||
<MicrosoftExtensionsCachingSqlServerPackageVersion>2.1.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
<MicrosoftExtensionsCachingSqlServerPackageVersion>2.1.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
||||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
|
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
|
||||||
|
|
@ -47,7 +45,6 @@
|
||||||
<MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
|
<MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
|
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationXmlPackageVersion>2.1.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
|
<MicrosoftExtensionsConfigurationXmlPackageVersion>2.1.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>
|
||||||
|
|
@ -73,22 +70,14 @@
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>2.1.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
|
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>2.1.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
|
||||||
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsObjectPoolPackageVersion>2.1.6</MicrosoftExtensionsObjectPoolPackageVersion>
|
<MicrosoftExtensionsObjectPoolPackageVersion>2.1.6</MicrosoftExtensionsObjectPoolPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
|
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.6</MicrosoftExtensionsPrimitivesPackageVersion>
|
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.6</MicrosoftExtensionsPrimitivesPackageVersion>
|
||||||
<MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsStackTraceSourcesPackageVersion>2.1.1</MicrosoftExtensionsStackTraceSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
|
|
||||||
|
|
||||||
<!-- These dependencies are temporary while we refactor package refs into project refs. -->
|
<!-- These dependencies are temporary while we refactor package refs into project refs. -->
|
||||||
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,12 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Packages from aspnet/Extensions -->
|
<!-- Packages from aspnet/Extensions -->
|
||||||
<ExternalDependency Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
<ExternalDependency Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" AllMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" AllMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="$(MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion)" AllMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="$(MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion)" AllMetapackage="true" />
|
||||||
|
|
@ -36,7 +34,6 @@
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Specification.Tests" Version="$(MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Specification.Tests" Version="$(MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
|
|
@ -62,21 +59,13 @@
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.PropertyActivator.Sources" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.SecurityHelper.Sources" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
<ExternalDependency Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
||||||
<ExternalDependency Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
|
|
||||||
<ExternalDependency Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
<ExternalDependency Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" AllMetapackage="true" AppMetapackage="true" />
|
||||||
|
|
||||||
<!-- These dependencies are temporary while we refactor package refs into project refs. -->
|
<!-- These dependencies are temporary while we refactor package refs into project refs. -->
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
$(RepositoryRoot)src\Html\**\*.*proj;
|
$(RepositoryRoot)src\Html\**\*.*proj;
|
||||||
$(RepositoryRoot)src\Servers\**\*.csproj;
|
$(RepositoryRoot)src\Servers\**\*.csproj;
|
||||||
$(RepositoryRoot)src\Servers\**\*.pkgproj;
|
$(RepositoryRoot)src\Servers\**\*.pkgproj;
|
||||||
|
$(RepositoryRoot)src\Shared\**\*.*proj;
|
||||||
$(RepositoryRoot)src\Tools\**\*.*proj;
|
$(RepositoryRoot)src\Tools\**\*.*proj;
|
||||||
$(RepositoryRoot)src\Middleware\**\*.*proj;
|
$(RepositoryRoot)src\Middleware\**\*.*proj;
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ and are generated based on the last package release.
|
||||||
<!-- These dependencies must use version variables because they may be overriden by ProdCon builds. -->
|
<!-- These dependencies must use version variables because they may be overriden by ProdCon builds. -->
|
||||||
<ItemGroup Label="ProdCon dependencies">
|
<ItemGroup Label="ProdCon dependencies">
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
|
||||||
|
|
@ -34,14 +33,12 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
||||||
|
|
@ -56,12 +53,7 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
|
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
|
||||||
|
|
@ -71,6 +63,7 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
|
<LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
<LatestPackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
<LatestPackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
||||||
|
|
@ -79,6 +72,7 @@ and are generated based on the last package release.
|
||||||
|
|
||||||
<ItemGroup Label="External dependencies">
|
<ItemGroup Label="External dependencies">
|
||||||
<LatestPackageReference Include="BenchmarkDotNet" Version="0.10.13" />
|
<LatestPackageReference Include="BenchmarkDotNet" Version="0.10.13" />
|
||||||
|
<LatestPackageReference Include="FSharp.Core" Version="4.2.1" />
|
||||||
<LatestPackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
<LatestPackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
<LatestPackageReference Include="Moq" Version="4.10.0" />
|
<LatestPackageReference Include="Moq" Version="4.10.0" />
|
||||||
<!-- This version is required by MSBuild tasks or Visual Studio extensions. -->
|
<!-- This version is required by MSBuild tasks or Visual Studio extensions. -->
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,6 @@
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.14.2</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
|
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.14.2</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
|
||||||
<MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.2.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>
|
<MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.2.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,5 @@
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
|
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
<None Include="Assets\*.*" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="Assets\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||||
<Compile Remove="Assets\**\*.*" />
|
<Compile Remove="Assets\**\*.*" />
|
||||||
<Compile Include="Assets\RuntimeInformationMiddleware.cs" />
|
<Compile Include="Assets\RuntimeInformationMiddleware.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)Process\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
|
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="$(MicrosoftAzureManagementFluentPackageVersion)" />
|
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="$(MicrosoftAzureManagementFluentPackageVersion)" />
|
||||||
<PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStoragePackageVersion)" />
|
<PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStoragePackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector</Description>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\..\shared\src\*.cs" />
|
<Compile Include="..\..\shared\src\*.cs" />
|
||||||
</ItemGroup>
|
<Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@
|
||||||
<PackageTags>aspnetcore;json;jsonpatch</PackageTags>
|
<PackageTags>aspnetcore;json;jsonpatch</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="Newtonsoft.Json" />
|
<Reference Include="Newtonsoft.Json" />
|
||||||
<Reference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@
|
||||||
<PackageTags>aspnetcore;hosting</PackageTags>
|
<PackageTags>aspnetcore;hosting</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
|
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
|
||||||
|
|
@ -20,8 +25,6 @@
|
||||||
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging" />
|
<Reference Include="Microsoft.Extensions.Logging" />
|
||||||
<Reference Include="Microsoft.Extensions.Options" />
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
<Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.Extensions.StackTrace.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
|
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
|
||||||
<Reference Include="System.Diagnostics.DiagnosticSource" />
|
<Reference Include="System.Diagnostics.DiagnosticSource" />
|
||||||
<Reference Include="System.Reflection.Metadata" />
|
<Reference Include="System.Reflection.Metadata" />
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)Process\*.cs" />
|
||||||
<EmbeddedResource Include="Deployers\RemoteWindowsDeployer\RemotePSSessionHelper.ps1;Deployers\RemoteWindowsDeployer\StartServer.ps1;Deployers\RemoteWindowsDeployer\StopServer.ps1" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
|
<EmbeddedResource Include="Deployers\RemoteWindowsDeployer\RemotePSSessionHelper.ps1;Deployers\RemoteWindowsDeployer\StartServer.ps1;Deployers\RemoteWindowsDeployer\StopServer.ps1" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
@ -25,7 +26,6 @@
|
||||||
<Reference Include="Microsoft.Extensions.Logging" />
|
<Reference Include="Microsoft.Extensions.Logging" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Console" />
|
<Reference Include="Microsoft.Extensions.Logging.Console" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Testing" />
|
<Reference Include="Microsoft.Extensions.Logging.Testing" />
|
||||||
<Reference Include="Microsoft.Extensions.Process.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.NETCore.Windows.ApiSets" />
|
<Reference Include="Microsoft.NETCore.Windows.ApiSets" />
|
||||||
<Reference Include="Serilog.Extensions.Logging" />
|
<Reference Include="Serilog.Extensions.Logging" />
|
||||||
<Reference Include="Serilog.Sinks.File" />
|
<Reference Include="Serilog.Sinks.File" />
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,13 @@
|
||||||
<PackageTags>aspnetcore</PackageTags>
|
<PackageTags>aspnetcore</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
|
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
|
||||||
<Reference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.Extensions.ObjectPool" />
|
<Reference Include="Microsoft.Extensions.ObjectPool" />
|
||||||
<Reference Include="Microsoft.Extensions.Options" />
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
<Reference Include="Microsoft.Net.Http.Headers" />
|
<Reference Include="Microsoft.Net.Http.Headers" />
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,11 @@ Microsoft.AspNetCore.Routing.RouteData</Description>
|
||||||
<PackageTags>aspnetcore;routing</PackageTags>
|
<PackageTags>aspnetcore;routing</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ Microsoft.AspNetCore.Routing.RouteCollection</Description>
|
||||||
<PackageTags>aspnetcore;routing</PackageTags>
|
<PackageTags>aspnetcore;routing</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
|
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
||||||
|
|
@ -18,6 +22,5 @@ Microsoft.AspNetCore.Routing.RouteCollection</Description>
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.ObjectPool" />
|
<Reference Include="Microsoft.Extensions.ObjectPool" />
|
||||||
<Reference Include="Microsoft.Extensions.Options" />
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
<Reference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" />
|
<Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Net.Http.Headers" />
|
<Reference Include="Microsoft.Net.Http.Headers" />
|
||||||
<Reference Include="System.Text.Encodings.Web" />
|
<Reference Include="System.Text.Encodings.Web" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,6 @@
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.1.1</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftNETSdkRazorPackageVersion>2.1.1</MicrosoftNETSdkRazorPackageVersion>
|
<MicrosoftNETSdkRazorPackageVersion>2.1.1</MicrosoftNETSdkRazorPackageVersion>
|
||||||
<MicrosoftOwinSecurityInteropPackageVersion>2.1.1</MicrosoftOwinSecurityInteropPackageVersion>
|
<MicrosoftOwinSecurityInteropPackageVersion>2.1.1</MicrosoftOwinSecurityInteropPackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
|
<Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
||||||
<Reference Include="Microsoft.EntityFrameworkCore.Relational" />
|
<Reference Include="Microsoft.EntityFrameworkCore.Relational" />
|
||||||
<Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
|
<Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -20,8 +22,6 @@
|
||||||
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
|
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.Options" />
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
<Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.Extensions.StackTrace.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
|
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
|
||||||
<Reference Include="System.Diagnostics.DiagnosticSource" />
|
<Reference Include="System.Diagnostics.DiagnosticSource" />
|
||||||
<Reference Include="System.Reflection.Metadata" />
|
<Reference Include="System.Reflection.Metadata" />
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,8 @@
|
||||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.1</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.1</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||||
<MicrosoftCodeAnalysisRazorPackageVersion>2.1.1</MicrosoftCodeAnalysisRazorPackageVersion>
|
<MicrosoftCodeAnalysisRazorPackageVersion>2.1.1</MicrosoftCodeAnalysisRazorPackageVersion>
|
||||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
|
<MicrosoftExtensionsConfigurationPackageVersion>2.1.1</MicrosoftExtensionsConfigurationPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.1.0</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.1.0</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||||
|
|
@ -88,13 +86,9 @@
|
||||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
|
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.1.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.1</MicrosoftExtensionsPrimitivesPackageVersion>
|
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.1</MicrosoftExtensionsPrimitivesPackageVersion>
|
||||||
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@ Microsoft.AspNetCore.Mvc.IActionResult</Description>
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Routing.Abstractions" Version="$(MicrosoftAspNetCoreRoutingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Routing.Abstractions" Version="$(MicrosoftAspNetCoreRoutingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
|
<PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,15 @@
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(RepositoryRoot)\src\Shared\RangeHelper\**\*.cs" />
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)RangeHelper\**\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -30,17 +35,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" Version="$(MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" Version="$(MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
||||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourcePackageVersion)" />
|
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourcePackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,14 @@
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="$(SystemComponentModelAnnotationsPackageVersion)" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="$(SystemComponentModelAnnotationsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc;json</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc;json</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="$(MicrosoftAspNetCoreJsonPatchPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="$(MicrosoftAspNetCoreJsonPatchPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,16 @@ Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer</Description>
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc;localization</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc;localization</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="$(MicrosoftAspNetCoreLocalizationPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="$(MicrosoftAspNetCoreLocalizationPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@
|
||||||
<Serviceable>true</Serviceable>
|
<Serviceable>true</Serviceable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.ViewFeatures\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.ViewFeatures\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
|
||||||
|
|
||||||
|
|
@ -19,10 +25,7 @@
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpPackageVersion)" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec" DependsOnTargets="BuiltProjectOutputGroup;DebugSymbolsProjectOutputGroup;DocumentationProjectOutputGroup">
|
<Target Name="PopulateNuspec" BeforeTargets="GenerateNuspec" DependsOnTargets="BuiltProjectOutputGroup;DebugSymbolsProjectOutputGroup;DocumentationProjectOutputGroup">
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,17 @@
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc;cshtml;razor</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc;cshtml;razor</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor\Microsoft.AspNetCore.Mvc.Razor.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="compiler\resources\**\*" />
|
<EmbeddedResource Include="compiler\resources\**\*" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -21,6 +22,5 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,14 @@ Microsoft.AspNetCore.Mvc.ViewComponent</Description>
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.DataAnnotations\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.DataAnnotations\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />
|
||||||
|
|
@ -22,12 +30,7 @@ Microsoft.AspNetCore.Mvc.ViewComponent</Description>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="$(MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="$(MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyActivator.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyActivatorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
||||||
<PackageReference Include="Newtonsoft.Json.Bson" Version="$(NewtonsoftJsonBsonPackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json.Bson" Version="$(NewtonsoftJsonBsonPackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,16 @@ System.Web.Http.ApiController</Description>
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc;aspnetwebapi</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc;aspnetwebapi</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Formatters.Json\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Formatters.Json\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@
|
||||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.1</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(MicrosoftAspNetCoreHtmlAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@
|
||||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -8,6 +8,10 @@
|
||||||
<PackageTags>aspnetcore;authentication;security</PackageTags>
|
<PackageTags>aspnetcore;authentication;security</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(MicrosoftAspNetCoreAuthenticationCorePackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="$(MicrosoftAspNetCoreAuthenticationCorePackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="$(MicrosoftAspNetCoreDataProtectionPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="$(MicrosoftAspNetCoreDataProtectionPackageVersion)" />
|
||||||
|
|
@ -15,7 +19,6 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,16 @@
|
||||||
<PackageTags>aspnetcore;authorization</PackageTags>
|
<PackageTags>aspnetcore;authorization</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Authorization\Microsoft.AspNetCore.Authorization.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Authorization\Microsoft.AspNetCore.Authorization.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="$(MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="$(MicrosoftAspNetCoreAuthenticationAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(KestrelSharedSourceRoot)src\ThrowHelper.cs" />
|
<Compile Include="$(KestrelSharedSourceRoot)src\ThrowHelper.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" />
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
|
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,720 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
|
{
|
||||||
|
internal class CertificateManager
|
||||||
|
{
|
||||||
|
public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
|
||||||
|
public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate";
|
||||||
|
|
||||||
|
public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2";
|
||||||
|
public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate";
|
||||||
|
|
||||||
|
private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1";
|
||||||
|
private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication";
|
||||||
|
|
||||||
|
private const string LocalhostHttpsDnsName = "localhost";
|
||||||
|
private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName;
|
||||||
|
|
||||||
|
private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing";
|
||||||
|
|
||||||
|
public const int RSAMinimumKeySizeInBits = 2048;
|
||||||
|
|
||||||
|
private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1);
|
||||||
|
private const string CertificateSubjectRegex = "CN=(.*[^,]+).*";
|
||||||
|
private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain";
|
||||||
|
private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db";
|
||||||
|
private const string MacOSFindCertificateCommandLine = "security";
|
||||||
|
#if NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
|
private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain;
|
||||||
|
#endif
|
||||||
|
private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)";
|
||||||
|
private const string MacOSRemoveCertificateTrustCommandLine = "sudo";
|
||||||
|
private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}";
|
||||||
|
private const string MacOSDeleteCertificateCommandLine = "sudo";
|
||||||
|
private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}";
|
||||||
|
private const string MacOSTrustCertificateCommandLine = "sudo";
|
||||||
|
#if NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
|
private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " ";
|
||||||
|
#endif
|
||||||
|
private const int UserCancelledErrorCode = 1223;
|
||||||
|
|
||||||
|
public IList<X509Certificate2> ListCertificates(
|
||||||
|
CertificatePurpose purpose,
|
||||||
|
StoreName storeName,
|
||||||
|
StoreLocation location,
|
||||||
|
bool isValid,
|
||||||
|
bool requireExportable = true)
|
||||||
|
{
|
||||||
|
var certificates = new List<X509Certificate2>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var store = new X509Store(storeName, location))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadOnly);
|
||||||
|
certificates.AddRange(store.Certificates.OfType<X509Certificate2>());
|
||||||
|
IEnumerable<X509Certificate2> matchingCertificates = certificates;
|
||||||
|
switch (purpose)
|
||||||
|
{
|
||||||
|
case CertificatePurpose.All:
|
||||||
|
matchingCertificates = matchingCertificates
|
||||||
|
.Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid));
|
||||||
|
break;
|
||||||
|
case CertificatePurpose.HTTPS:
|
||||||
|
matchingCertificates = matchingCertificates
|
||||||
|
.Where(c => HasOid(c, AspNetHttpsOid));
|
||||||
|
break;
|
||||||
|
case CertificatePurpose.Signing:
|
||||||
|
matchingCertificates = matchingCertificates
|
||||||
|
.Where(c => HasOid(c, AspNetIdentityOid));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isValid)
|
||||||
|
{
|
||||||
|
// Ensure the certificate hasn't expired, has a private key and its exportable
|
||||||
|
// (for container/unix scenarios).
|
||||||
|
var now = DateTimeOffset.Now;
|
||||||
|
matchingCertificates = matchingCertificates
|
||||||
|
.Where(c => c.NotBefore <= now &&
|
||||||
|
now <= c.NotAfter &&
|
||||||
|
(!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to enumerate the certificates early to prevent dispoisng issues.
|
||||||
|
matchingCertificates = matchingCertificates.ToList();
|
||||||
|
|
||||||
|
var certificatesToDispose = certificates.Except(matchingCertificates);
|
||||||
|
DisposeCertificates(certificatesToDispose);
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
|
||||||
|
return (IList<X509Certificate2>)matchingCertificates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
DisposeCertificates(certificates);
|
||||||
|
certificates.Clear();
|
||||||
|
return certificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasOid(X509Certificate2 certificate, string oid) =>
|
||||||
|
certificate.Extensions.OfType<X509Extension>()
|
||||||
|
.Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal));
|
||||||
|
#if !XPLAT
|
||||||
|
bool IsExportable(X509Certificate2 c) =>
|
||||||
|
((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
|
||||||
|
rsaPrivateKey.CspKeyContainerInfo.Exportable) ||
|
||||||
|
(c.GetRSAPrivateKey() is RSACng cngPrivateKey &&
|
||||||
|
cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport));
|
||||||
|
#else
|
||||||
|
// Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as
|
||||||
|
// System.Security.Cryptography.Cng is not pat of the shared framework and we don't
|
||||||
|
// want to bring the dependency in on CLI scenarios. This functionality will be used
|
||||||
|
// on CLI scenarios as part of the first run experience, so checking the exportability
|
||||||
|
// of the certificate is not important.
|
||||||
|
bool IsExportable(X509Certificate2 c) =>
|
||||||
|
((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
|
||||||
|
rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeCertificates(IEnumerable<X509Certificate2> disposables)
|
||||||
|
{
|
||||||
|
foreach (var disposable in disposables)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
|
|
||||||
|
public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
|
||||||
|
{
|
||||||
|
var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName);
|
||||||
|
var extensions = new List<X509Extension>();
|
||||||
|
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
sanBuilder.AddDnsName(LocalhostHttpsDnsName);
|
||||||
|
|
||||||
|
var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true);
|
||||||
|
var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
|
||||||
|
new OidCollection() {
|
||||||
|
new Oid(
|
||||||
|
ServerAuthenticationEnhancedKeyUsageOid,
|
||||||
|
ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
|
||||||
|
},
|
||||||
|
critical: true);
|
||||||
|
|
||||||
|
var basicConstraints = new X509BasicConstraintsExtension(
|
||||||
|
certificateAuthority: false,
|
||||||
|
hasPathLengthConstraint: false,
|
||||||
|
pathLengthConstraint: 0,
|
||||||
|
critical: true);
|
||||||
|
|
||||||
|
var aspNetHttpsExtension = new X509Extension(
|
||||||
|
new AsnEncodedData(
|
||||||
|
new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName),
|
||||||
|
Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)),
|
||||||
|
critical: false);
|
||||||
|
|
||||||
|
extensions.Add(basicConstraints);
|
||||||
|
extensions.Add(keyUsage);
|
||||||
|
extensions.Add(enhancedKeyUsage);
|
||||||
|
extensions.Add(sanBuilder.Build(critical: true));
|
||||||
|
extensions.Add(aspNetHttpsExtension);
|
||||||
|
|
||||||
|
var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
certificate.FriendlyName = AspNetHttpsOidFriendlyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
|
||||||
|
{
|
||||||
|
var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName);
|
||||||
|
var extensions = new List<X509Extension>();
|
||||||
|
|
||||||
|
var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true);
|
||||||
|
var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
|
||||||
|
new OidCollection() {
|
||||||
|
new Oid(
|
||||||
|
ServerAuthenticationEnhancedKeyUsageOid,
|
||||||
|
ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
|
||||||
|
},
|
||||||
|
critical: true);
|
||||||
|
|
||||||
|
var basicConstraints = new X509BasicConstraintsExtension(
|
||||||
|
certificateAuthority: false,
|
||||||
|
hasPathLengthConstraint: false,
|
||||||
|
pathLengthConstraint: 0,
|
||||||
|
critical: true);
|
||||||
|
|
||||||
|
var aspNetIdentityExtension = new X509Extension(
|
||||||
|
new AsnEncodedData(
|
||||||
|
new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName),
|
||||||
|
Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)),
|
||||||
|
critical: false);
|
||||||
|
|
||||||
|
extensions.Add(basicConstraints);
|
||||||
|
extensions.Add(keyUsage);
|
||||||
|
extensions.Add(enhancedKeyUsage);
|
||||||
|
extensions.Add(aspNetIdentityExtension);
|
||||||
|
|
||||||
|
var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
certificate.FriendlyName = AspNetIdentityOidFriendlyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 CreateSelfSignedCertificate(
|
||||||
|
X500DistinguishedName subject,
|
||||||
|
IEnumerable<X509Extension> extensions,
|
||||||
|
DateTimeOffset notBefore,
|
||||||
|
DateTimeOffset notAfter)
|
||||||
|
{
|
||||||
|
var key = CreateKeyMaterial(RSAMinimumKeySizeInBits);
|
||||||
|
|
||||||
|
var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
|
foreach (var extension in extensions)
|
||||||
|
{
|
||||||
|
request.CertificateExtensions.Add(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.CreateSelfSigned(notBefore, notAfter);
|
||||||
|
|
||||||
|
RSA CreateKeyMaterial(int minimumKeySize)
|
||||||
|
{
|
||||||
|
var rsa = RSA.Create(minimumKeySize);
|
||||||
|
if (rsa.KeySize < minimumKeySize)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location)
|
||||||
|
{
|
||||||
|
var imported = certificate;
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
// On non OSX systems we need to export the certificate and import it so that the transient
|
||||||
|
// key that we generated gets persisted.
|
||||||
|
var export = certificate.Export(X509ContentType.Pkcs12, "");
|
||||||
|
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
|
||||||
|
Array.Clear(export, 0, export.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
imported.FriendlyName = certificate.FriendlyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var store = new X509Store(name, location))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
store.Add(imported);
|
||||||
|
store.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return imported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password)
|
||||||
|
{
|
||||||
|
if (Path.GetDirectoryName(path) != "")
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includePrivateKey)
|
||||||
|
{
|
||||||
|
var bytes = certificate.Export(X509ContentType.Pkcs12, password);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(path, bytes);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Array.Clear(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var bytes = certificate.Export(X509ContentType.Cert);
|
||||||
|
File.WriteAllBytes(path, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrustCertificate(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
// Strip certificate of the private key if any.
|
||||||
|
var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert));
|
||||||
|
|
||||||
|
if (!IsTrusted(publicCertificate))
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
TrustCertificateOnWindows(certificate, publicCertificate);
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
TrustCertificateOnMac(publicCertificate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrustCertificateOnMac(X509Certificate2 publicCertificate)
|
||||||
|
{
|
||||||
|
var tmpFile = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null);
|
||||||
|
using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile))
|
||||||
|
{
|
||||||
|
process.WaitForExit();
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("There was an error trusting the certificate.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(tmpFile))
|
||||||
|
{
|
||||||
|
File.Delete(tmpFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// We don't care if we can't delete the temp file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate)
|
||||||
|
{
|
||||||
|
publicCertificate.FriendlyName = certificate.FriendlyName;
|
||||||
|
|
||||||
|
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
store.Add(publicCertificate);
|
||||||
|
}
|
||||||
|
catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode)
|
||||||
|
{
|
||||||
|
throw new UserCancelledTrustException();
|
||||||
|
}
|
||||||
|
store.Close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTrusted(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false)
|
||||||
|
.Any(c => c.Thumbprint == certificate.Thumbprint);
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout);
|
||||||
|
if (!subjectMatch.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'.");
|
||||||
|
}
|
||||||
|
var subject = subjectMatch.Groups[1].Value;
|
||||||
|
using (var checkTrustProcess = Process.Start(new ProcessStartInfo(
|
||||||
|
MacOSFindCertificateCommandLine,
|
||||||
|
string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject))
|
||||||
|
{
|
||||||
|
RedirectStandardOutput = true
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
var output = checkTrustProcess.StandardOutput.ReadToEnd();
|
||||||
|
checkTrustProcess.WaitForExit();
|
||||||
|
var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout);
|
||||||
|
var hashes = matches.OfType<Match>().Select(m => m.Groups[1].Value).ToList();
|
||||||
|
return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName)
|
||||||
|
{
|
||||||
|
CleanupCertificates(CertificatePurpose.HTTPS, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanupCertificates(CertificatePurpose purpose, string subject)
|
||||||
|
{
|
||||||
|
// On OS X we don't have a good way to manage trusted certificates in the system keychain
|
||||||
|
// so we do everything by invoking the native toolchain.
|
||||||
|
// This has some limitations, like for example not being able to identify our custom OID extension. For that
|
||||||
|
// matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates.
|
||||||
|
// To do this, we list the certificates that we can identify on the current user personal store and we invoke
|
||||||
|
// the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates,
|
||||||
|
// we remove the certificates from the local user store to finish up the cleanup.
|
||||||
|
var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false);
|
||||||
|
foreach (var certificate in certificates)
|
||||||
|
{
|
||||||
|
RemoveCertificate(certificate, RemoveLocations.All);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null)
|
||||||
|
{
|
||||||
|
var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||||
|
ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) :
|
||||||
|
ListCertificates(purpose, storeName, storeLocation, isValid: false);
|
||||||
|
var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject);
|
||||||
|
|
||||||
|
var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted;
|
||||||
|
|
||||||
|
foreach (var certificate in certificates)
|
||||||
|
{
|
||||||
|
RemoveCertificate(certificate, removeLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeCertificates(certificates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations)
|
||||||
|
{
|
||||||
|
switch (locations)
|
||||||
|
{
|
||||||
|
case RemoveLocations.Undefined:
|
||||||
|
throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location.");
|
||||||
|
case RemoveLocations.Local:
|
||||||
|
RemoveCertificateFromUserStore(certificate);
|
||||||
|
break;
|
||||||
|
case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux):
|
||||||
|
RemoveCertificateFromTrustedRoots(certificate);
|
||||||
|
break;
|
||||||
|
case RemoveLocations.All:
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
RemoveCertificateFromTrustedRoots(certificate);
|
||||||
|
}
|
||||||
|
RemoveCertificateFromUserStore(certificate);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("Invalid location.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveCertificateFromUserStore(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
var matching = store.Certificates
|
||||||
|
.OfType<X509Certificate2>()
|
||||||
|
.Single(c => c.SerialNumber == certificate.SerialNumber);
|
||||||
|
|
||||||
|
store.Remove(matching);
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
var matching = store.Certificates
|
||||||
|
.OfType<X509Certificate2>()
|
||||||
|
.Single(c => c.SerialNumber == certificate.SerialNumber);
|
||||||
|
|
||||||
|
store.Remove(matching);
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RemoveCertificateTrustRule(certificate);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// We don't care if we fail to remove the trust rule if
|
||||||
|
// for some reason the certificate became untrusted.
|
||||||
|
// The delete command will fail if the certificate is
|
||||||
|
// trusted.
|
||||||
|
}
|
||||||
|
RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveCertificateTrustRule(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
var certificatePath = Path.GetTempFileName();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var certBytes = certificate.Export(X509ContentType.Cert);
|
||||||
|
File.WriteAllBytes(certificatePath, certBytes);
|
||||||
|
var processInfo = new ProcessStartInfo(
|
||||||
|
MacOSRemoveCertificateTrustCommandLine,
|
||||||
|
string.Format(
|
||||||
|
MacOSRemoveCertificateTrustCommandLineArgumentsFormat,
|
||||||
|
certificatePath
|
||||||
|
));
|
||||||
|
using (var process = Process.Start(processInfo))
|
||||||
|
{
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(certificatePath))
|
||||||
|
{
|
||||||
|
File.Delete(certificatePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// We don't care about failing to do clean-up on a temp file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
var processInfo = new ProcessStartInfo(
|
||||||
|
MacOSDeleteCertificateCommandLine,
|
||||||
|
string.Format(
|
||||||
|
MacOSDeleteCertificateCommandLineArgumentsFormat,
|
||||||
|
certificate.Thumbprint.ToUpperInvariant(),
|
||||||
|
keyChain
|
||||||
|
))
|
||||||
|
{
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = Process.Start(processInfo))
|
||||||
|
{
|
||||||
|
var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd();
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'.
|
||||||
|
|
||||||
|
{output}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
|
||||||
|
DateTimeOffset notBefore,
|
||||||
|
DateTimeOffset notAfter,
|
||||||
|
string path = null,
|
||||||
|
bool trust = false,
|
||||||
|
bool includePrivateKey = false,
|
||||||
|
string password = null,
|
||||||
|
string subject = LocalhostHttpsDistinguishedName)
|
||||||
|
{
|
||||||
|
return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate(
|
||||||
|
DateTimeOffset notBefore,
|
||||||
|
DateTimeOffset notAfter,
|
||||||
|
string path = null,
|
||||||
|
bool trust = false,
|
||||||
|
bool includePrivateKey = false,
|
||||||
|
string password = null,
|
||||||
|
string subject = IdentityDistinguishedName)
|
||||||
|
{
|
||||||
|
return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnsureCertificateResult EnsureValidCertificateExists(
|
||||||
|
DateTimeOffset notBefore,
|
||||||
|
DateTimeOffset notAfter,
|
||||||
|
CertificatePurpose purpose,
|
||||||
|
string path = null,
|
||||||
|
bool trust = false,
|
||||||
|
bool includePrivateKey = false,
|
||||||
|
string password = null,
|
||||||
|
string subjectOverride = null)
|
||||||
|
{
|
||||||
|
if (purpose == CertificatePurpose.All)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The certificate must have a specific purpose.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat(
|
||||||
|
ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true));
|
||||||
|
|
||||||
|
certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride);
|
||||||
|
|
||||||
|
var result = EnsureCertificateResult.Succeeded;
|
||||||
|
|
||||||
|
X509Certificate2 certificate = null;
|
||||||
|
if (certificates.Count() > 0)
|
||||||
|
{
|
||||||
|
certificate = certificates.FirstOrDefault();
|
||||||
|
result = EnsureCertificateResult.ValidCertificatePresent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (purpose)
|
||||||
|
{
|
||||||
|
case CertificatePurpose.All:
|
||||||
|
throw new InvalidOperationException("The certificate must have a specific purpose.");
|
||||||
|
case CertificatePurpose.HTTPS:
|
||||||
|
certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride);
|
||||||
|
break;
|
||||||
|
case CertificatePurpose.Signing:
|
||||||
|
certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException("The certificate must have a purpose.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return EnsureCertificateResult.ErrorCreatingTheCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ExportCertificate(certificate, path, includePrivateKey, password);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return EnsureCertificateResult.ErrorExportingTheCertificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TrustCertificate(certificate);
|
||||||
|
}
|
||||||
|
catch (UserCancelledTrustException)
|
||||||
|
{
|
||||||
|
return EnsureCertificateResult.UserCancelledTrustStep;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return EnsureCertificateResult.FailedToTrustTheCertificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UserCancelledTrustException : Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum RemoveLocations
|
||||||
|
{
|
||||||
|
Undefined,
|
||||||
|
Local,
|
||||||
|
Trusted,
|
||||||
|
All
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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.Certificates.Generation
|
||||||
|
{
|
||||||
|
internal enum CertificatePurpose
|
||||||
|
{
|
||||||
|
All,
|
||||||
|
HTTPS,
|
||||||
|
Signing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
#if NETCOREAPP2_0 || NETCOREAPP2_1
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
|
{
|
||||||
|
internal enum EnsureCertificateResult
|
||||||
|
{
|
||||||
|
Succeeded = 1,
|
||||||
|
ValidCertificatePresent,
|
||||||
|
ErrorCreatingTheCertificate,
|
||||||
|
ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore,
|
||||||
|
ErrorExportingTheCertificate,
|
||||||
|
FailedToTrustTheCertificate,
|
||||||
|
UserCancelledTrustStep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper related to generic interface definitions and implementing classes.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ClosedGenericMatcher
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determine whether <paramref name="queryType"/> is or implements a closed generic <see cref="Type"/>
|
||||||
|
/// created from <paramref name="interfaceType"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryType">The <see cref="Type"/> of interest.</param>
|
||||||
|
/// <param name="interfaceType">The open generic <see cref="Type"/> to match. Usually an interface.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The closed generic <see cref="Type"/> created from <paramref name="interfaceType"/> that
|
||||||
|
/// <paramref name="queryType"/> is or implements. <c>null</c> if the two <see cref="Type"/>s have no such
|
||||||
|
/// relationship.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method will return <paramref name="queryType"/> if <paramref name="interfaceType"/> is
|
||||||
|
/// <c>typeof(KeyValuePair{,})</c>, and <paramref name="queryType"/> is
|
||||||
|
/// <c>typeof(KeyValuePair{string, object})</c>.
|
||||||
|
/// </remarks>
|
||||||
|
public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
|
||||||
|
{
|
||||||
|
if (queryType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(queryType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interfaceType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(interfaceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsGenericInstantiation(queryType, interfaceType))
|
||||||
|
{
|
||||||
|
// queryType matches (i.e. is a closed generic type created from) the open generic type.
|
||||||
|
return queryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise check all interfaces the type implements for a match.
|
||||||
|
// - If multiple different generic instantiations exists, we want the most derived one.
|
||||||
|
// - If that doesn't break the tie, then we sort alphabetically so that it's deterministic.
|
||||||
|
//
|
||||||
|
// We do this by looking at interfaces on the type, and recursing to the base type
|
||||||
|
// if we don't find any matches.
|
||||||
|
return GetGenericInstantiation(queryType, interfaceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsGenericInstantiation(Type candidate, Type interfaceType)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
candidate.GetTypeInfo().IsGenericType &&
|
||||||
|
candidate.GetGenericTypeDefinition() == interfaceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type GetGenericInstantiation(Type queryType, Type interfaceType)
|
||||||
|
{
|
||||||
|
Type bestMatch = null;
|
||||||
|
var interfaces = queryType.GetInterfaces();
|
||||||
|
foreach (var @interface in interfaces)
|
||||||
|
{
|
||||||
|
if (IsGenericInstantiation(@interface, interfaceType))
|
||||||
|
{
|
||||||
|
if (bestMatch == null)
|
||||||
|
{
|
||||||
|
bestMatch = @interface;
|
||||||
|
}
|
||||||
|
else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0)
|
||||||
|
{
|
||||||
|
bestMatch = @interface;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There are two matches at this level of the class hierarchy, but @interface is after
|
||||||
|
// bestMatch in the sort order.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestMatch != null)
|
||||||
|
{
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseType will be null for object and interfaces, which means we've reached 'bottom'.
|
||||||
|
var baseType = queryType?.GetTypeInfo().BaseType;
|
||||||
|
if (baseType == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetGenericInstantiation(baseType, interfaceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal class CopyOnWriteDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||||
|
{
|
||||||
|
private readonly IDictionary<TKey, TValue> _sourceDictionary;
|
||||||
|
private readonly IEqualityComparer<TKey> _comparer;
|
||||||
|
private IDictionary<TKey, TValue> _innerDictionary;
|
||||||
|
|
||||||
|
public CopyOnWriteDictionary(
|
||||||
|
IDictionary<TKey, TValue> sourceDictionary,
|
||||||
|
IEqualityComparer<TKey> comparer)
|
||||||
|
{
|
||||||
|
if (sourceDictionary == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(sourceDictionary));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comparer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(comparer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_sourceDictionary = sourceDictionary;
|
||||||
|
_comparer = comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<TKey, TValue> ReadDictionary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _innerDictionary ?? _sourceDictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<TKey, TValue> WriteDictionary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_innerDictionary == null)
|
||||||
|
{
|
||||||
|
_innerDictionary = new Dictionary<TKey, TValue>(_sourceDictionary,
|
||||||
|
_comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _innerDictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ICollection<TKey> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual ICollection<TValue> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool IsReadOnly
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual TValue this[TKey key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary[key];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
WriteDictionary[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool ContainsKey(TKey key)
|
||||||
|
{
|
||||||
|
return ReadDictionary.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
WriteDictionary.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Remove(TKey key)
|
||||||
|
{
|
||||||
|
return WriteDictionary.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TryGetValue(TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
return ReadDictionary.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Add(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
WriteDictionary.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Clear()
|
||||||
|
{
|
||||||
|
WriteDictionary.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Contains(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
return ReadDictionary.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
ReadDictionary.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
return WriteDictionary.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||||
|
{
|
||||||
|
return ReadDictionary.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal struct CopyOnWriteDictionaryHolder<TKey, TValue>
|
||||||
|
{
|
||||||
|
private readonly Dictionary<TKey, TValue> _source;
|
||||||
|
private Dictionary<TKey, TValue> _copy;
|
||||||
|
|
||||||
|
public CopyOnWriteDictionaryHolder(Dictionary<TKey, TValue> source)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
_source = source;
|
||||||
|
_copy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CopyOnWriteDictionaryHolder(CopyOnWriteDictionaryHolder<TKey, TValue> source)
|
||||||
|
{
|
||||||
|
_source = source._copy ?? source._source;
|
||||||
|
_copy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasBeenCopied => _copy != null;
|
||||||
|
|
||||||
|
public Dictionary<TKey, TValue> ReadDictionary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_copy != null)
|
||||||
|
{
|
||||||
|
return _copy;
|
||||||
|
}
|
||||||
|
else if (_source != null)
|
||||||
|
{
|
||||||
|
return _source;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default-Constructor case
|
||||||
|
_copy = new Dictionary<TKey, TValue>();
|
||||||
|
return _copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<TKey, TValue> WriteDictionary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_copy == null && _source == null)
|
||||||
|
{
|
||||||
|
// Default-Constructor case
|
||||||
|
_copy = new Dictionary<TKey, TValue>();
|
||||||
|
}
|
||||||
|
else if (_copy == null)
|
||||||
|
{
|
||||||
|
_copy = new Dictionary<TKey, TValue>(_source, _source.Comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<TKey, TValue>.KeyCollection Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<TKey, TValue>.ValueCollection Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue this[TKey key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ReadDictionary[key];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
WriteDictionary[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(TKey key)
|
||||||
|
{
|
||||||
|
return ReadDictionary.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
WriteDictionary.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TKey key)
|
||||||
|
{
|
||||||
|
return WriteDictionary.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
return ReadDictionary.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
((ICollection<KeyValuePair<TKey, TValue>>)WriteDictionary).Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
WriteDictionary.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
return ((ICollection<KeyValuePair<TKey, TValue>>)ReadDictionary).Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
((ICollection<KeyValuePair<TKey, TValue>>)ReadDictionary).CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||||
|
{
|
||||||
|
return ((ICollection<KeyValuePair<TKey, TValue>>)WriteDictionary).Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return ReadDictionary.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal struct AwaitableInfo
|
||||||
|
{
|
||||||
|
public Type AwaiterType { get; }
|
||||||
|
public PropertyInfo AwaiterIsCompletedProperty { get; }
|
||||||
|
public MethodInfo AwaiterGetResultMethod { get; }
|
||||||
|
public MethodInfo AwaiterOnCompletedMethod { get; }
|
||||||
|
public MethodInfo AwaiterUnsafeOnCompletedMethod { get; }
|
||||||
|
public Type ResultType { get; }
|
||||||
|
public MethodInfo GetAwaiterMethod { get; }
|
||||||
|
|
||||||
|
public AwaitableInfo(
|
||||||
|
Type awaiterType,
|
||||||
|
PropertyInfo awaiterIsCompletedProperty,
|
||||||
|
MethodInfo awaiterGetResultMethod,
|
||||||
|
MethodInfo awaiterOnCompletedMethod,
|
||||||
|
MethodInfo awaiterUnsafeOnCompletedMethod,
|
||||||
|
Type resultType,
|
||||||
|
MethodInfo getAwaiterMethod)
|
||||||
|
{
|
||||||
|
AwaiterType = awaiterType;
|
||||||
|
AwaiterIsCompletedProperty = awaiterIsCompletedProperty;
|
||||||
|
AwaiterGetResultMethod = awaiterGetResultMethod;
|
||||||
|
AwaiterOnCompletedMethod = awaiterOnCompletedMethod;
|
||||||
|
AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod;
|
||||||
|
ResultType = resultType;
|
||||||
|
GetAwaiterMethod = getAwaiterMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo)
|
||||||
|
{
|
||||||
|
// Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347
|
||||||
|
|
||||||
|
// Awaitable must have method matching "object GetAwaiter()"
|
||||||
|
var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m =>
|
||||||
|
m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& m.GetParameters().Length == 0
|
||||||
|
&& m.ReturnType != null);
|
||||||
|
if (getAwaiterMethod == null)
|
||||||
|
{
|
||||||
|
awaitableInfo = default(AwaitableInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var awaiterType = getAwaiterMethod.ReturnType;
|
||||||
|
|
||||||
|
// Awaiter must have property matching "bool IsCompleted { get; }"
|
||||||
|
var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p =>
|
||||||
|
p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& p.PropertyType == typeof(bool)
|
||||||
|
&& p.GetMethod != null);
|
||||||
|
if (isCompletedProperty == null)
|
||||||
|
{
|
||||||
|
awaitableInfo = default(AwaitableInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Awaiter must implement INotifyCompletion
|
||||||
|
var awaiterInterfaces = awaiterType.GetInterfaces();
|
||||||
|
var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion));
|
||||||
|
if (!implementsINotifyCompletion)
|
||||||
|
{
|
||||||
|
awaitableInfo = default(AwaitableInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INotifyCompletion supplies a method matching "void OnCompleted(Action action)"
|
||||||
|
var iNotifyCompletionMap = awaiterType
|
||||||
|
.GetTypeInfo()
|
||||||
|
.GetRuntimeInterfaceMap(typeof(INotifyCompletion));
|
||||||
|
var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m =>
|
||||||
|
m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& m.ReturnType == typeof(void)
|
||||||
|
&& m.GetParameters().Length == 1
|
||||||
|
&& m.GetParameters()[0].ParameterType == typeof(Action));
|
||||||
|
|
||||||
|
// Awaiter optionally implements ICriticalNotifyCompletion
|
||||||
|
var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));
|
||||||
|
MethodInfo unsafeOnCompletedMethod;
|
||||||
|
if (implementsICriticalNotifyCompletion)
|
||||||
|
{
|
||||||
|
// ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)"
|
||||||
|
var iCriticalNotifyCompletionMap = awaiterType
|
||||||
|
.GetTypeInfo()
|
||||||
|
.GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion));
|
||||||
|
unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m =>
|
||||||
|
m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& m.ReturnType == typeof(void)
|
||||||
|
&& m.GetParameters().Length == 1
|
||||||
|
&& m.GetParameters()[0].ParameterType == typeof(Action));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsafeOnCompletedMethod = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Awaiter must have method matching "void GetResult" or "T GetResult()"
|
||||||
|
var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m =>
|
||||||
|
m.Name.Equals("GetResult")
|
||||||
|
&& m.GetParameters().Length == 0);
|
||||||
|
if (getResultMethod == null)
|
||||||
|
{
|
||||||
|
awaitableInfo = default(AwaitableInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
awaitableInfo = new AwaitableInfo(
|
||||||
|
awaiterType,
|
||||||
|
isCompletedProperty,
|
||||||
|
getResultMethod,
|
||||||
|
onCompletedMethod,
|
||||||
|
unsafeOnCompletedMethod,
|
||||||
|
getResultMethod.ReturnType,
|
||||||
|
getAwaiterMethod);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal struct CoercedAwaitableInfo
|
||||||
|
{
|
||||||
|
public AwaitableInfo AwaitableInfo { get; }
|
||||||
|
public Expression CoercerExpression { get; }
|
||||||
|
public Type CoercerResultType { get; }
|
||||||
|
public bool RequiresCoercion => CoercerExpression != null;
|
||||||
|
|
||||||
|
public CoercedAwaitableInfo(AwaitableInfo awaitableInfo)
|
||||||
|
{
|
||||||
|
AwaitableInfo = awaitableInfo;
|
||||||
|
CoercerExpression = null;
|
||||||
|
CoercerResultType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo)
|
||||||
|
{
|
||||||
|
CoercerExpression = coercerExpression;
|
||||||
|
CoercerResultType = coercerResultType;
|
||||||
|
AwaitableInfo = coercedAwaitableInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info)
|
||||||
|
{
|
||||||
|
if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo))
|
||||||
|
{
|
||||||
|
info = new CoercedAwaitableInfo(directlyAwaitableInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's not directly awaitable, but maybe we can coerce it.
|
||||||
|
// Currently we support coercing FSharpAsync<T>.
|
||||||
|
if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type,
|
||||||
|
out var coercerExpression,
|
||||||
|
out var coercerResultType))
|
||||||
|
{
|
||||||
|
if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo))
|
||||||
|
{
|
||||||
|
info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info = default(CoercedAwaitableInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,340 @@
|
||||||
|
// 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.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal class ObjectMethodExecutor
|
||||||
|
{
|
||||||
|
private readonly object[] _parameterDefaultValues;
|
||||||
|
private readonly MethodExecutorAsync _executorAsync;
|
||||||
|
private readonly MethodExecutor _executor;
|
||||||
|
|
||||||
|
private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor =
|
||||||
|
typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] {
|
||||||
|
typeof(object), // customAwaitable
|
||||||
|
typeof(Func<object, object>), // getAwaiterMethod
|
||||||
|
typeof(Func<object, bool>), // isCompletedMethod
|
||||||
|
typeof(Func<object, object>), // getResultMethod
|
||||||
|
typeof(Action<object, Action>), // onCompletedMethod
|
||||||
|
typeof(Action<object, Action>) // unsafeOnCompletedMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues)
|
||||||
|
{
|
||||||
|
if (methodInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(methodInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo = methodInfo;
|
||||||
|
MethodParameters = methodInfo.GetParameters();
|
||||||
|
TargetTypeInfo = targetTypeInfo;
|
||||||
|
MethodReturnType = methodInfo.ReturnType;
|
||||||
|
|
||||||
|
var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo);
|
||||||
|
|
||||||
|
IsMethodAsync = isAwaitable;
|
||||||
|
AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null;
|
||||||
|
|
||||||
|
// Upstream code may prefer to use the sync-executor even for async methods, because if it knows
|
||||||
|
// that the result is a specific Task<T> where T is known, then it can directly cast to that type
|
||||||
|
// and await it without the extra heap allocations involved in the _executorAsync code path.
|
||||||
|
_executor = GetExecutor(methodInfo, targetTypeInfo);
|
||||||
|
|
||||||
|
if (IsMethodAsync)
|
||||||
|
{
|
||||||
|
_executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
_parameterDefaultValues = parameterDefaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object[] parameters);
|
||||||
|
|
||||||
|
private delegate object MethodExecutor(object target, object[] parameters);
|
||||||
|
|
||||||
|
private delegate void VoidMethodExecutor(object target, object[] parameters);
|
||||||
|
|
||||||
|
public MethodInfo MethodInfo { get; }
|
||||||
|
|
||||||
|
public ParameterInfo[] MethodParameters { get; }
|
||||||
|
|
||||||
|
public TypeInfo TargetTypeInfo { get; }
|
||||||
|
|
||||||
|
public Type AsyncResultType { get; }
|
||||||
|
|
||||||
|
// This field is made internal set because it is set in unit tests.
|
||||||
|
public Type MethodReturnType { get; internal set; }
|
||||||
|
|
||||||
|
public bool IsMethodAsync { get; }
|
||||||
|
|
||||||
|
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo)
|
||||||
|
{
|
||||||
|
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues)
|
||||||
|
{
|
||||||
|
if (parameterDefaultValues == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parameterDefaultValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the configured method on <paramref name="target"/>. This can be used whether or not
|
||||||
|
/// the configured method is asynchronous.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than
|
||||||
|
/// ExecuteAsync if you know at compile time what the return type is, because then you can directly
|
||||||
|
/// "await" that value (via a cast), and then the generated code will be able to reference the
|
||||||
|
/// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated
|
||||||
|
/// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at
|
||||||
|
/// compile time what type it would be.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="target">The object whose method is to be executed.</param>
|
||||||
|
/// <param name="parameters">Parameters to pass to the method.</param>
|
||||||
|
/// <returns>The method return value.</returns>
|
||||||
|
public object Execute(object target, object[] parameters)
|
||||||
|
{
|
||||||
|
return _executor(target, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the configured method on <paramref name="target"/>. This can only be used if the configured
|
||||||
|
/// method is asynchronous.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync,
|
||||||
|
/// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
|
||||||
|
/// as compared with using Execute and then using "await" on the result value typecasted to the known
|
||||||
|
/// awaitable type. The possible extra heap allocations are for:
|
||||||
|
///
|
||||||
|
/// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
|
||||||
|
/// it's a reference type, and you normally create a new instance per call).
|
||||||
|
/// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
|
||||||
|
/// of it, and if it is, it will have to be boxed so the calling code can reference it as an object).
|
||||||
|
/// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling
|
||||||
|
/// code doesn't know what type it's going to be).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="target">The object whose method is to be executed.</param>
|
||||||
|
/// <param name="parameters">Parameters to pass to the method.</param>
|
||||||
|
/// <returns>An object that you can "await" to get the method return value.</returns>
|
||||||
|
public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object[] parameters)
|
||||||
|
{
|
||||||
|
return _executorAsync(target, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetDefaultValueForParameter(int index)
|
||||||
|
{
|
||||||
|
if (_parameterDefaultValues == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0 || index > MethodParameters.Length - 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _parameterDefaultValues[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo)
|
||||||
|
{
|
||||||
|
// Parameters to executor
|
||||||
|
var targetParameter = Expression.Parameter(typeof(object), "target");
|
||||||
|
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
|
||||||
|
|
||||||
|
// Build parameter list
|
||||||
|
var parameters = new List<Expression>();
|
||||||
|
var paramInfos = methodInfo.GetParameters();
|
||||||
|
for (int i = 0; i < paramInfos.Length; i++)
|
||||||
|
{
|
||||||
|
var paramInfo = paramInfos[i];
|
||||||
|
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
|
||||||
|
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
|
||||||
|
|
||||||
|
// valueCast is "(Ti) parameters[i]"
|
||||||
|
parameters.Add(valueCast);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call method
|
||||||
|
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType());
|
||||||
|
var methodCall = Expression.Call(instanceCast, methodInfo, parameters);
|
||||||
|
|
||||||
|
// methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)"
|
||||||
|
// Create function
|
||||||
|
if (methodCall.Type == typeof(void))
|
||||||
|
{
|
||||||
|
var lambda = Expression.Lambda<VoidMethodExecutor>(methodCall, targetParameter, parametersParameter);
|
||||||
|
var voidExecutor = lambda.Compile();
|
||||||
|
return WrapVoidMethod(voidExecutor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// must coerce methodCall to match ActionExecutor signature
|
||||||
|
var castMethodCall = Expression.Convert(methodCall, typeof(object));
|
||||||
|
var lambda = Expression.Lambda<MethodExecutor>(castMethodCall, targetParameter, parametersParameter);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor)
|
||||||
|
{
|
||||||
|
return delegate (object target, object[] parameters)
|
||||||
|
{
|
||||||
|
executor(target, parameters);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodExecutorAsync GetExecutorAsync(
|
||||||
|
MethodInfo methodInfo,
|
||||||
|
TypeInfo targetTypeInfo,
|
||||||
|
CoercedAwaitableInfo coercedAwaitableInfo)
|
||||||
|
{
|
||||||
|
// Parameters to executor
|
||||||
|
var targetParameter = Expression.Parameter(typeof(object), "target");
|
||||||
|
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
|
||||||
|
|
||||||
|
// Build parameter list
|
||||||
|
var parameters = new List<Expression>();
|
||||||
|
var paramInfos = methodInfo.GetParameters();
|
||||||
|
for (int i = 0; i < paramInfos.Length; i++)
|
||||||
|
{
|
||||||
|
var paramInfo = paramInfos[i];
|
||||||
|
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
|
||||||
|
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
|
||||||
|
|
||||||
|
// valueCast is "(Ti) parameters[i]"
|
||||||
|
parameters.Add(valueCast);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call method
|
||||||
|
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType());
|
||||||
|
var methodCall = Expression.Call(instanceCast, methodInfo, parameters);
|
||||||
|
|
||||||
|
// Using the method return value, construct an ObjectMethodExecutorAwaitable based on
|
||||||
|
// the info we have about its implementation of the awaitable pattern. Note that all
|
||||||
|
// the funcs/actions we construct here are precompiled, so that only one instance of
|
||||||
|
// each is preserved throughout the lifetime of the ObjectMethodExecutor.
|
||||||
|
|
||||||
|
// var getAwaiterFunc = (object awaitable) =>
|
||||||
|
// (object)((CustomAwaitableType)awaitable).GetAwaiter();
|
||||||
|
var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable");
|
||||||
|
var awaitableInfo = coercedAwaitableInfo.AwaitableInfo;
|
||||||
|
var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType;
|
||||||
|
var getAwaiterFunc = Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.Convert(
|
||||||
|
Expression.Call(
|
||||||
|
Expression.Convert(customAwaitableParam, postCoercionMethodReturnType),
|
||||||
|
awaitableInfo.GetAwaiterMethod),
|
||||||
|
typeof(object)),
|
||||||
|
customAwaitableParam).Compile();
|
||||||
|
|
||||||
|
// var isCompletedFunc = (object awaiter) =>
|
||||||
|
// ((CustomAwaiterType)awaiter).IsCompleted;
|
||||||
|
var isCompletedParam = Expression.Parameter(typeof(object), "awaiter");
|
||||||
|
var isCompletedFunc = Expression.Lambda<Func<object, bool>>(
|
||||||
|
Expression.MakeMemberAccess(
|
||||||
|
Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType),
|
||||||
|
awaitableInfo.AwaiterIsCompletedProperty),
|
||||||
|
isCompletedParam).Compile();
|
||||||
|
|
||||||
|
var getResultParam = Expression.Parameter(typeof(object), "awaiter");
|
||||||
|
Func<object, object> getResultFunc;
|
||||||
|
if (awaitableInfo.ResultType == typeof(void))
|
||||||
|
{
|
||||||
|
// var getResultFunc = (object awaiter) =>
|
||||||
|
// {
|
||||||
|
// ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions
|
||||||
|
// return (object)null;
|
||||||
|
// };
|
||||||
|
getResultFunc = Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.Block(
|
||||||
|
Expression.Call(
|
||||||
|
Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
|
||||||
|
awaitableInfo.AwaiterGetResultMethod),
|
||||||
|
Expression.Constant(null)
|
||||||
|
),
|
||||||
|
getResultParam).Compile();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// var getResultFunc = (object awaiter) =>
|
||||||
|
// (object)((CustomAwaiterType)awaiter).GetResult();
|
||||||
|
getResultFunc = Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.Convert(
|
||||||
|
Expression.Call(
|
||||||
|
Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
|
||||||
|
awaitableInfo.AwaiterGetResultMethod),
|
||||||
|
typeof(object)),
|
||||||
|
getResultParam).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// var onCompletedFunc = (object awaiter, Action continuation) => {
|
||||||
|
// ((CustomAwaiterType)awaiter).OnCompleted(continuation);
|
||||||
|
// };
|
||||||
|
var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
|
||||||
|
var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
|
||||||
|
var onCompletedFunc = Expression.Lambda<Action<object, Action>>(
|
||||||
|
Expression.Call(
|
||||||
|
Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType),
|
||||||
|
awaitableInfo.AwaiterOnCompletedMethod,
|
||||||
|
onCompletedParam2),
|
||||||
|
onCompletedParam1,
|
||||||
|
onCompletedParam2).Compile();
|
||||||
|
|
||||||
|
Action<object, Action> unsafeOnCompletedFunc = null;
|
||||||
|
if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null)
|
||||||
|
{
|
||||||
|
// var unsafeOnCompletedFunc = (object awaiter, Action continuation) => {
|
||||||
|
// ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation);
|
||||||
|
// };
|
||||||
|
var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
|
||||||
|
var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
|
||||||
|
unsafeOnCompletedFunc = Expression.Lambda<Action<object, Action>>(
|
||||||
|
Expression.Call(
|
||||||
|
Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType),
|
||||||
|
awaitableInfo.AwaiterUnsafeOnCompletedMethod,
|
||||||
|
unsafeOnCompletedParam2),
|
||||||
|
unsafeOnCompletedParam1,
|
||||||
|
unsafeOnCompletedParam2).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we need to pass the method call result through a coercer function to get an
|
||||||
|
// awaitable, then do so.
|
||||||
|
var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion
|
||||||
|
? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall)
|
||||||
|
: (Expression)methodCall;
|
||||||
|
|
||||||
|
// return new ObjectMethodExecutorAwaitable(
|
||||||
|
// (object)coercedMethodCall,
|
||||||
|
// getAwaiterFunc,
|
||||||
|
// isCompletedFunc,
|
||||||
|
// getResultFunc,
|
||||||
|
// onCompletedFunc,
|
||||||
|
// unsafeOnCompletedFunc);
|
||||||
|
var returnValueExpression = Expression.New(
|
||||||
|
_objectMethodExecutorAwaitableConstructor,
|
||||||
|
Expression.Convert(coercedMethodCall, typeof(object)),
|
||||||
|
Expression.Constant(getAwaiterFunc),
|
||||||
|
Expression.Constant(isCompletedFunc),
|
||||||
|
Expression.Constant(getResultFunc),
|
||||||
|
Expression.Constant(onCompletedFunc),
|
||||||
|
Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>)));
|
||||||
|
|
||||||
|
var lambda = Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter);
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
// 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.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync"/> can
|
||||||
|
/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
|
||||||
|
/// application-defined custom awaitable.
|
||||||
|
/// </summary>
|
||||||
|
internal struct ObjectMethodExecutorAwaitable
|
||||||
|
{
|
||||||
|
private readonly object _customAwaitable;
|
||||||
|
private readonly Func<object, object> _getAwaiterMethod;
|
||||||
|
private readonly Func<object, bool> _isCompletedMethod;
|
||||||
|
private readonly Func<object, object> _getResultMethod;
|
||||||
|
private readonly Action<object, Action> _onCompletedMethod;
|
||||||
|
private readonly Action<object, Action> _unsafeOnCompletedMethod;
|
||||||
|
|
||||||
|
// Perf note: since we're requiring the customAwaitable to be supplied here as an object,
|
||||||
|
// this will trigger a further allocation if it was a value type (i.e., to box it). We can't
|
||||||
|
// fix this by making the customAwaitable type generic, because the calling code typically
|
||||||
|
// does not know the type of the awaitable/awaiter at compile-time anyway.
|
||||||
|
//
|
||||||
|
// However, we could fix it by not passing the customAwaitable here at all, and instead
|
||||||
|
// passing a func that maps directly from the target object (e.g., controller instance),
|
||||||
|
// target method (e.g., action method info), and params array to the custom awaiter in the
|
||||||
|
// GetAwaiter() method below. In effect, by delaying the actual method call until the
|
||||||
|
// upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance.
|
||||||
|
// This optimization is not currently implemented because:
|
||||||
|
// [1] It would make no difference when the awaitable was an object type, which is
|
||||||
|
// by far the most common scenario (e.g., System.Task<T>).
|
||||||
|
// [2] It would be complex - we'd need some kind of object pool to track all the parameter
|
||||||
|
// arrays until we needed to use them in GetAwaiter().
|
||||||
|
// We can reconsider this in the future if there's a need to optimize for ValueTask<T>
|
||||||
|
// or other value-typed awaitables.
|
||||||
|
|
||||||
|
public ObjectMethodExecutorAwaitable(
|
||||||
|
object customAwaitable,
|
||||||
|
Func<object, object> getAwaiterMethod,
|
||||||
|
Func<object, bool> isCompletedMethod,
|
||||||
|
Func<object, object> getResultMethod,
|
||||||
|
Action<object, Action> onCompletedMethod,
|
||||||
|
Action<object, Action> unsafeOnCompletedMethod)
|
||||||
|
{
|
||||||
|
_customAwaitable = customAwaitable;
|
||||||
|
_getAwaiterMethod = getAwaiterMethod;
|
||||||
|
_isCompletedMethod = isCompletedMethod;
|
||||||
|
_getResultMethod = getResultMethod;
|
||||||
|
_onCompletedMethod = onCompletedMethod;
|
||||||
|
_unsafeOnCompletedMethod = unsafeOnCompletedMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Awaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
var customAwaiter = _getAwaiterMethod(_customAwaitable);
|
||||||
|
return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Awaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
private readonly object _customAwaiter;
|
||||||
|
private readonly Func<object, bool> _isCompletedMethod;
|
||||||
|
private readonly Func<object, object> _getResultMethod;
|
||||||
|
private readonly Action<object, Action> _onCompletedMethod;
|
||||||
|
private readonly Action<object, Action> _unsafeOnCompletedMethod;
|
||||||
|
|
||||||
|
public Awaiter(
|
||||||
|
object customAwaiter,
|
||||||
|
Func<object, bool> isCompletedMethod,
|
||||||
|
Func<object, object> getResultMethod,
|
||||||
|
Action<object, Action> onCompletedMethod,
|
||||||
|
Action<object, Action> unsafeOnCompletedMethod)
|
||||||
|
{
|
||||||
|
_customAwaiter = customAwaiter;
|
||||||
|
_isCompletedMethod = isCompletedMethod;
|
||||||
|
_getResultMethod = getResultMethod;
|
||||||
|
_onCompletedMethod = onCompletedMethod;
|
||||||
|
_unsafeOnCompletedMethod = unsafeOnCompletedMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => _isCompletedMethod(_customAwaiter);
|
||||||
|
|
||||||
|
public object GetResult() => _getResultMethod(_customAwaiter);
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
_onCompletedMethod(_customAwaiter, continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
// If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted.
|
||||||
|
// If not, fall back on using its OnCompleted.
|
||||||
|
//
|
||||||
|
// Why this is safe:
|
||||||
|
// - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it
|
||||||
|
// needs the execution context to be preserved (which it signals by calling OnCompleted), or
|
||||||
|
// that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not*
|
||||||
|
// to preserve and restore the context, so we prefer that where possible.
|
||||||
|
// - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted,
|
||||||
|
// there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen
|
||||||
|
// if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to
|
||||||
|
// pass the call on to the underlying awaitable's OnCompleted method.
|
||||||
|
|
||||||
|
var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod;
|
||||||
|
underlyingMethodToUse(_customAwaiter, continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying
|
||||||
|
/// an <see cref="Expression"/> for mapping instances of that type to a C# awaitable.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The main design goal here is to avoid taking a compile-time dependency on
|
||||||
|
/// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references
|
||||||
|
/// to FSharp types have to be constructed dynamically at runtime.
|
||||||
|
/// </remarks>
|
||||||
|
internal static class ObjectMethodExecutorFSharpSupport
|
||||||
|
{
|
||||||
|
private static object _fsharpValuesCacheLock = new object();
|
||||||
|
private static Assembly _fsharpCoreAssembly;
|
||||||
|
private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod;
|
||||||
|
private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty;
|
||||||
|
private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty;
|
||||||
|
|
||||||
|
public static bool TryBuildCoercerFromFSharpAsyncToAwaitable(
|
||||||
|
Type possibleFSharpAsyncType,
|
||||||
|
out Expression coerceToAwaitableExpression,
|
||||||
|
out Type awaitableType)
|
||||||
|
{
|
||||||
|
var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType
|
||||||
|
? possibleFSharpAsyncType.GetGenericTypeDefinition()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType))
|
||||||
|
{
|
||||||
|
coerceToAwaitableExpression = null;
|
||||||
|
awaitableType = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single();
|
||||||
|
awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType);
|
||||||
|
|
||||||
|
// coerceToAwaitableExpression = (object fsharpAsync) =>
|
||||||
|
// {
|
||||||
|
// return (object)FSharpAsync.StartAsTask<TResult>(
|
||||||
|
// (Microsoft.FSharp.Control.FSharpAsync<TResult>)fsharpAsync,
|
||||||
|
// FSharpOption<TaskCreationOptions>.None,
|
||||||
|
// FSharpOption<CancellationToken>.None);
|
||||||
|
// };
|
||||||
|
var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod
|
||||||
|
.MakeGenericMethod(awaiterResultType);
|
||||||
|
var coerceToAwaitableParam = Expression.Parameter(typeof(object));
|
||||||
|
coerceToAwaitableExpression = Expression.Lambda(
|
||||||
|
Expression.Convert(
|
||||||
|
Expression.Call(
|
||||||
|
startAsTaskClosedMethod,
|
||||||
|
Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType),
|
||||||
|
Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty),
|
||||||
|
Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)),
|
||||||
|
typeof(object)),
|
||||||
|
coerceToAwaitableParam);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType)
|
||||||
|
{
|
||||||
|
var typeFullName = possibleFSharpAsyncGenericType?.FullName;
|
||||||
|
if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_fsharpValuesCacheLock)
|
||||||
|
{
|
||||||
|
if (_fsharpCoreAssembly != null)
|
||||||
|
{
|
||||||
|
// Since we've already found the real FSharpAsync.Core assembly, we just have
|
||||||
|
// to check that the supplied FSharpAsync`1 type is the one from that assembly.
|
||||||
|
return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We'll keep trying to find the FSharp types/values each time any type called
|
||||||
|
// FSharpAsync`1 is supplied.
|
||||||
|
return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType)
|
||||||
|
{
|
||||||
|
var assembly = possibleFSharpAsyncGenericType.Assembly;
|
||||||
|
var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1");
|
||||||
|
var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync");
|
||||||
|
|
||||||
|
if (fsharpOptionType == null || fsharpAsyncType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a reference to FSharpOption<TaskCreationOptions>.None
|
||||||
|
var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType
|
||||||
|
.MakeGenericType(typeof(TaskCreationOptions));
|
||||||
|
_fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType
|
||||||
|
.GetTypeInfo()
|
||||||
|
.GetRuntimeProperty("None");
|
||||||
|
|
||||||
|
// Get a reference to FSharpOption<CancellationToken>.None
|
||||||
|
var fsharpOptionOfCancellationTokenType = fsharpOptionType
|
||||||
|
.MakeGenericType(typeof(CancellationToken));
|
||||||
|
_fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType
|
||||||
|
.GetTypeInfo()
|
||||||
|
.GetRuntimeProperty("None");
|
||||||
|
|
||||||
|
// Get a reference to FSharpAsync.StartAsTask<>
|
||||||
|
var fsharpAsyncMethods = fsharpAsyncType
|
||||||
|
.GetRuntimeMethods()
|
||||||
|
.Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal));
|
||||||
|
foreach (var candidateMethodInfo in fsharpAsyncMethods)
|
||||||
|
{
|
||||||
|
var parameters = candidateMethodInfo.GetParameters();
|
||||||
|
if (parameters.Length == 3
|
||||||
|
&& TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType)
|
||||||
|
&& parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType
|
||||||
|
&& parameters[2].ParameterType == fsharpOptionOfCancellationTokenType)
|
||||||
|
{
|
||||||
|
// This really does look like the correct method (and hence assembly).
|
||||||
|
_fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo;
|
||||||
|
_fsharpCoreAssembly = assembly;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _fsharpCoreAssembly != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TypesHaveSameIdentity(Type type1, Type type2)
|
||||||
|
{
|
||||||
|
return type1.Assembly == type2.Assembly
|
||||||
|
&& string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal)
|
||||||
|
&& string.Equals(type1.Name, type2.Name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal static class ProcessExtensions
|
||||||
|
{
|
||||||
|
private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
public static void KillTree(this Process process)
|
||||||
|
{
|
||||||
|
process.KillTree(_defaultTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void KillTree(this Process process, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
string stdout;
|
||||||
|
if (_isWindows)
|
||||||
|
{
|
||||||
|
RunProcessAndWaitForExit(
|
||||||
|
"taskkill",
|
||||||
|
$"/T /F /PID {process.Id}",
|
||||||
|
timeout,
|
||||||
|
out stdout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var children = new HashSet<int>();
|
||||||
|
GetAllChildIdsUnix(process.Id, children, timeout);
|
||||||
|
foreach (var childId in children)
|
||||||
|
{
|
||||||
|
KillProcessUnix(childId, timeout);
|
||||||
|
}
|
||||||
|
KillProcessUnix(process.Id, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetAllChildIdsUnix(int parentId, ISet<int> children, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
string stdout;
|
||||||
|
var exitCode = RunProcessAndWaitForExit(
|
||||||
|
"pgrep",
|
||||||
|
$"-P {parentId}",
|
||||||
|
timeout,
|
||||||
|
out stdout);
|
||||||
|
|
||||||
|
if (exitCode == 0 && !string.IsNullOrEmpty(stdout))
|
||||||
|
{
|
||||||
|
using (var reader = new StringReader(stdout))
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var text = reader.ReadLine();
|
||||||
|
if (text == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id;
|
||||||
|
if (int.TryParse(text, out id))
|
||||||
|
{
|
||||||
|
children.Add(id);
|
||||||
|
// Recursively get the children
|
||||||
|
GetAllChildIdsUnix(id, children, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void KillProcessUnix(int processId, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
string stdout;
|
||||||
|
RunProcessAndWaitForExit(
|
||||||
|
"kill",
|
||||||
|
$"-TERM {processId}",
|
||||||
|
timeout,
|
||||||
|
out stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout)
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = arguments,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var process = Process.Start(startInfo);
|
||||||
|
|
||||||
|
stdout = null;
|
||||||
|
if (process.WaitForExit((int)timeout.TotalMilliseconds))
|
||||||
|
{
|
||||||
|
stdout = process.StandardOutput.ReadToEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.ExitCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal class PropertyActivator<TContext>
|
||||||
|
{
|
||||||
|
private readonly Func<TContext, object> _valueAccessor;
|
||||||
|
private readonly Action<object, object> _fastPropertySetter;
|
||||||
|
|
||||||
|
public PropertyActivator(
|
||||||
|
PropertyInfo propertyInfo,
|
||||||
|
Func<TContext, object> valueAccessor)
|
||||||
|
{
|
||||||
|
if (propertyInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(propertyInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueAccessor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(valueAccessor));
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyInfo = propertyInfo;
|
||||||
|
_valueAccessor = valueAccessor;
|
||||||
|
_fastPropertySetter = PropertyHelper.MakeFastPropertySetter(propertyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyInfo PropertyInfo { get; private set; }
|
||||||
|
|
||||||
|
public object Activate(object instance, TContext context)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = _valueAccessor(context);
|
||||||
|
_fastPropertySetter(instance, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyActivator<TContext>[] GetPropertiesToActivate(
|
||||||
|
Type type,
|
||||||
|
Type activateAttributeType,
|
||||||
|
Func<PropertyInfo, PropertyActivator<TContext>> createActivateInfo)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activateAttributeType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(activateAttributeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createActivateInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(createActivateInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetPropertiesToActivate(type, activateAttributeType, createActivateInfo, includeNonPublic: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyActivator<TContext>[] GetPropertiesToActivate(
|
||||||
|
Type type,
|
||||||
|
Type activateAttributeType,
|
||||||
|
Func<PropertyInfo, PropertyActivator<TContext>> createActivateInfo,
|
||||||
|
bool includeNonPublic)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activateAttributeType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(activateAttributeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createActivateInfo == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(createActivateInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = type.GetRuntimeProperties()
|
||||||
|
.Where((property) =>
|
||||||
|
{
|
||||||
|
return
|
||||||
|
property.IsDefined(activateAttributeType) &&
|
||||||
|
property.GetIndexParameters().Length == 0 &&
|
||||||
|
property.SetMethod != null &&
|
||||||
|
!property.SetMethod.IsStatic;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!includeNonPublic)
|
||||||
|
{
|
||||||
|
properties = properties.Where(property => property.SetMethod.IsPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties.Select(createActivateInfo).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,526 @@
|
||||||
|
// 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.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal class PropertyHelper
|
||||||
|
{
|
||||||
|
// Delegate type for a by-ref property getter
|
||||||
|
private delegate TValue ByRefFunc<TDeclaringType, TValue>(ref TDeclaringType arg);
|
||||||
|
|
||||||
|
private static readonly MethodInfo CallPropertyGetterOpenGenericMethod =
|
||||||
|
typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter));
|
||||||
|
|
||||||
|
private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod =
|
||||||
|
typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference));
|
||||||
|
|
||||||
|
private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod =
|
||||||
|
typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter));
|
||||||
|
|
||||||
|
private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod =
|
||||||
|
typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference));
|
||||||
|
|
||||||
|
private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
|
||||||
|
typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter));
|
||||||
|
|
||||||
|
// Using an array rather than IEnumerable, as target will be called on the hot path numerous times.
|
||||||
|
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> PropertiesCache =
|
||||||
|
new ConcurrentDictionary<Type, PropertyHelper[]>();
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> VisiblePropertiesCache =
|
||||||
|
new ConcurrentDictionary<Type, PropertyHelper[]>();
|
||||||
|
|
||||||
|
private Action<object, object> _valueSetter;
|
||||||
|
private Func<object, object> _valueGetter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a fast <see cref="PropertyHelper"/>.
|
||||||
|
/// This constructor does not cache the helper. For caching, use <see cref="GetProperties(Type)"/>.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyHelper(PropertyInfo property)
|
||||||
|
{
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(property));
|
||||||
|
}
|
||||||
|
|
||||||
|
Property = property;
|
||||||
|
Name = property.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the backing <see cref="PropertyInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyInfo Property { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets (or sets in derived types) the property name.
|
||||||
|
/// </summary>
|
||||||
|
public virtual string Name { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property value getter.
|
||||||
|
/// </summary>
|
||||||
|
public Func<object, object> ValueGetter
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_valueGetter == null)
|
||||||
|
{
|
||||||
|
_valueGetter = MakeFastPropertyGetter(Property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _valueGetter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the property value setter.
|
||||||
|
/// </summary>
|
||||||
|
public Action<object, object> ValueSetter
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_valueSetter == null)
|
||||||
|
{
|
||||||
|
_valueSetter = MakeFastPropertySetter(Property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _valueSetter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the property value for the specified <paramref name="instance"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The object whose property value will be returned.</param>
|
||||||
|
/// <returns>The property value.</returns>
|
||||||
|
public object GetValue(object instance)
|
||||||
|
{
|
||||||
|
return ValueGetter(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the property value for the specified <paramref name="instance" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The object whose property value will be set.</param>
|
||||||
|
/// <param name="value">The property value.</param>
|
||||||
|
public void SetValue(object instance, object value)
|
||||||
|
{
|
||||||
|
ValueSetter(instance, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and caches fast property helpers that expose getters for every public get property on the
|
||||||
|
/// underlying type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeInfo">The type info to extract property accessors for.</param>
|
||||||
|
/// <returns>A cached array of all public properties of the specified type.
|
||||||
|
/// </returns>
|
||||||
|
public static PropertyHelper[] GetProperties(TypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
return GetProperties(typeInfo.AsType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and caches fast property helpers that expose getters for every public get property on the
|
||||||
|
/// specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type to extract property accessors for.</param>
|
||||||
|
/// <returns>A cached array of all public properties of the specified type.
|
||||||
|
/// </returns>
|
||||||
|
public static PropertyHelper[] GetProperties(Type type)
|
||||||
|
{
|
||||||
|
return GetProperties(type, CreateInstance, PropertiesCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Creates and caches fast property helpers that expose getters for every non-hidden get property
|
||||||
|
/// on the specified type.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
|
||||||
|
/// hidden by definitions using the <c>new</c> keyword.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeInfo">The type info to extract property accessors for.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A cached array of all public properties of the specified type.
|
||||||
|
/// </returns>
|
||||||
|
public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Creates and caches fast property helpers that expose getters for every non-hidden get property
|
||||||
|
/// on the specified type.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
|
||||||
|
/// hidden by definitions using the <c>new</c> keyword.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type to extract property accessors for.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A cached array of all public properties of the specified type.
|
||||||
|
/// </returns>
|
||||||
|
public static PropertyHelper[] GetVisibleProperties(Type type)
|
||||||
|
{
|
||||||
|
return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a single fast property getter. The result is not cached.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
|
||||||
|
/// <returns>a fast getter.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||||
|
/// same speed.
|
||||||
|
/// </remarks>
|
||||||
|
public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
Debug.Assert(propertyInfo != null);
|
||||||
|
|
||||||
|
return MakeFastPropertyGetter(
|
||||||
|
propertyInfo,
|
||||||
|
CallPropertyGetterOpenGenericMethod,
|
||||||
|
CallPropertyGetterByReferenceOpenGenericMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a single fast property getter which is safe for a null input object. The result is not cached.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
|
||||||
|
/// <returns>a fast getter.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||||
|
/// same speed.
|
||||||
|
/// </remarks>
|
||||||
|
public static Func<object, object> MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
Debug.Assert(propertyInfo != null);
|
||||||
|
|
||||||
|
return MakeFastPropertyGetter(
|
||||||
|
propertyInfo,
|
||||||
|
CallNullSafePropertyGetterOpenGenericMethod,
|
||||||
|
CallNullSafePropertyGetterByReferenceOpenGenericMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Func<object, object> MakeFastPropertyGetter(
|
||||||
|
PropertyInfo propertyInfo,
|
||||||
|
MethodInfo propertyGetterWrapperMethod,
|
||||||
|
MethodInfo propertyGetterByRefWrapperMethod)
|
||||||
|
{
|
||||||
|
Debug.Assert(propertyInfo != null);
|
||||||
|
|
||||||
|
// Must be a generic method with a Func<,> parameter
|
||||||
|
Debug.Assert(propertyGetterWrapperMethod != null);
|
||||||
|
Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition);
|
||||||
|
Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2);
|
||||||
|
|
||||||
|
// Must be a generic method with a ByRefFunc<,> parameter
|
||||||
|
Debug.Assert(propertyGetterByRefWrapperMethod != null);
|
||||||
|
Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition);
|
||||||
|
Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2);
|
||||||
|
|
||||||
|
var getMethod = propertyInfo.GetMethod;
|
||||||
|
Debug.Assert(getMethod != null);
|
||||||
|
Debug.Assert(!getMethod.IsStatic);
|
||||||
|
Debug.Assert(getMethod.GetParameters().Length == 0);
|
||||||
|
|
||||||
|
// Instance methods in the CLR can be turned into static methods where the first parameter
|
||||||
|
// is open over "target". This parameter is always passed by reference, so we have a code
|
||||||
|
// path for value types and a code path for reference types.
|
||||||
|
if (getMethod.DeclaringType.GetTypeInfo().IsValueType)
|
||||||
|
{
|
||||||
|
// Create a delegate (ref TDeclaringType) -> TValue
|
||||||
|
return MakeFastPropertyGetter(
|
||||||
|
typeof(ByRefFunc<,>),
|
||||||
|
getMethod,
|
||||||
|
propertyGetterByRefWrapperMethod);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create a delegate TDeclaringType -> TValue
|
||||||
|
return MakeFastPropertyGetter(
|
||||||
|
typeof(Func<,>),
|
||||||
|
getMethod,
|
||||||
|
propertyGetterWrapperMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Func<object, object> MakeFastPropertyGetter(
|
||||||
|
Type openGenericDelegateType,
|
||||||
|
MethodInfo propertyGetMethod,
|
||||||
|
MethodInfo openGenericWrapperMethod)
|
||||||
|
{
|
||||||
|
var typeInput = propertyGetMethod.DeclaringType;
|
||||||
|
var typeOutput = propertyGetMethod.ReturnType;
|
||||||
|
|
||||||
|
var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput);
|
||||||
|
var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType);
|
||||||
|
|
||||||
|
var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput);
|
||||||
|
var accessorDelegate = wrapperDelegateMethod.CreateDelegate(
|
||||||
|
typeof(Func<object, object>),
|
||||||
|
propertyGetterDelegate);
|
||||||
|
|
||||||
|
return (Func<object, object>)accessorDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a single fast property setter for reference types. The result is not cached.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
|
||||||
|
/// <returns>a fast getter.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||||
|
/// same speed. This only works for reference types.
|
||||||
|
/// </remarks>
|
||||||
|
public static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
Debug.Assert(propertyInfo != null);
|
||||||
|
Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType);
|
||||||
|
|
||||||
|
var setMethod = propertyInfo.SetMethod;
|
||||||
|
Debug.Assert(setMethod != null);
|
||||||
|
Debug.Assert(!setMethod.IsStatic);
|
||||||
|
Debug.Assert(setMethod.ReturnType == typeof(void));
|
||||||
|
var parameters = setMethod.GetParameters();
|
||||||
|
Debug.Assert(parameters.Length == 1);
|
||||||
|
|
||||||
|
// Instance methods in the CLR can be turned into static methods where the first parameter
|
||||||
|
// is open over "target". This parameter is always passed by reference, so we have a code
|
||||||
|
// path for value types and a code path for reference types.
|
||||||
|
var typeInput = setMethod.DeclaringType;
|
||||||
|
var parameterType = parameters[0].ParameterType;
|
||||||
|
|
||||||
|
// Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
|
||||||
|
var propertySetterAsAction =
|
||||||
|
setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
|
||||||
|
var callPropertySetterClosedGenericMethod =
|
||||||
|
CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
|
||||||
|
var callPropertySetterDelegate =
|
||||||
|
callPropertySetterClosedGenericMethod.CreateDelegate(
|
||||||
|
typeof(Action<object, object>), propertySetterAsAction);
|
||||||
|
|
||||||
|
return (Action<object, object>)callPropertySetterDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given an object, adds each instance property with a public get method as a key and its
|
||||||
|
/// associated value to a dictionary.
|
||||||
|
///
|
||||||
|
/// If the object is already an <see cref="IDictionary{String, Object}"/> instance, then a copy
|
||||||
|
/// is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The implementation of PropertyHelper will cache the property accessors per-type. This is
|
||||||
|
/// faster when the same type is used multiple times with ObjectToDictionary.
|
||||||
|
/// </remarks>
|
||||||
|
public static IDictionary<string, object> ObjectToDictionary(object value)
|
||||||
|
{
|
||||||
|
var dictionary = value as IDictionary<string, object>;
|
||||||
|
if (dictionary != null)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
foreach (var helper in GetProperties(value.GetType()))
|
||||||
|
{
|
||||||
|
dictionary[helper.Name] = helper.GetValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PropertyHelper CreateInstance(PropertyInfo property)
|
||||||
|
{
|
||||||
|
return new PropertyHelper(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called via reflection
|
||||||
|
private static object CallPropertyGetter<TDeclaringType, TValue>(
|
||||||
|
Func<TDeclaringType, TValue> getter,
|
||||||
|
object target)
|
||||||
|
{
|
||||||
|
return getter((TDeclaringType)target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called via reflection
|
||||||
|
private static object CallPropertyGetterByReference<TDeclaringType, TValue>(
|
||||||
|
ByRefFunc<TDeclaringType, TValue> getter,
|
||||||
|
object target)
|
||||||
|
{
|
||||||
|
var unboxed = (TDeclaringType)target;
|
||||||
|
return getter(ref unboxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called via reflection
|
||||||
|
private static object CallNullSafePropertyGetter<TDeclaringType, TValue>(
|
||||||
|
Func<TDeclaringType, TValue> getter,
|
||||||
|
object target)
|
||||||
|
{
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getter((TDeclaringType)target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called via reflection
|
||||||
|
private static object CallNullSafePropertyGetterByReference<TDeclaringType, TValue>(
|
||||||
|
ByRefFunc<TDeclaringType, TValue> getter,
|
||||||
|
object target)
|
||||||
|
{
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unboxed = (TDeclaringType)target;
|
||||||
|
return getter(ref unboxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CallPropertySetter<TDeclaringType, TValue>(
|
||||||
|
Action<TDeclaringType, TValue> setter,
|
||||||
|
object target,
|
||||||
|
object value)
|
||||||
|
{
|
||||||
|
setter((TDeclaringType)target, (TValue)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static PropertyHelper[] GetVisibleProperties(
|
||||||
|
Type type,
|
||||||
|
Func<PropertyInfo, PropertyHelper> createPropertyHelper,
|
||||||
|
ConcurrentDictionary<Type, PropertyHelper[]> allPropertiesCache,
|
||||||
|
ConcurrentDictionary<Type, PropertyHelper[]> visiblePropertiesCache)
|
||||||
|
{
|
||||||
|
PropertyHelper[] result;
|
||||||
|
if (visiblePropertiesCache.TryGetValue(type, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The simple and common case, this is normal POCO object - no need to allocate.
|
||||||
|
var allPropertiesDefinedOnType = true;
|
||||||
|
var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache);
|
||||||
|
foreach (var propertyHelper in allProperties)
|
||||||
|
{
|
||||||
|
if (propertyHelper.Property.DeclaringType != type)
|
||||||
|
{
|
||||||
|
allPropertiesDefinedOnType = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allPropertiesDefinedOnType)
|
||||||
|
{
|
||||||
|
result = allProperties;
|
||||||
|
visiblePropertiesCache.TryAdd(type, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's some inherited properties here, so we need to check for hiding via 'new'.
|
||||||
|
var filteredProperties = new List<PropertyHelper>(allProperties.Length);
|
||||||
|
foreach (var propertyHelper in allProperties)
|
||||||
|
{
|
||||||
|
var declaringType = propertyHelper.Property.DeclaringType;
|
||||||
|
if (declaringType == type)
|
||||||
|
{
|
||||||
|
filteredProperties.Add(propertyHelper);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this property was declared on a base type then look for the definition closest to the
|
||||||
|
// the type to see if we should include it.
|
||||||
|
var ignoreProperty = false;
|
||||||
|
|
||||||
|
// Walk up the hierarchy until we find the type that actually declares this
|
||||||
|
// PropertyInfo.
|
||||||
|
var currentTypeInfo = type.GetTypeInfo();
|
||||||
|
var declaringTypeInfo = declaringType.GetTypeInfo();
|
||||||
|
while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo)
|
||||||
|
{
|
||||||
|
// We've found a 'more proximal' public definition
|
||||||
|
var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name);
|
||||||
|
if (declaredProperty != null)
|
||||||
|
{
|
||||||
|
ignoreProperty = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreProperty)
|
||||||
|
{
|
||||||
|
filteredProperties.Add(propertyHelper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = filteredProperties.ToArray();
|
||||||
|
visiblePropertiesCache.TryAdd(type, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static PropertyHelper[] GetProperties(
|
||||||
|
Type type,
|
||||||
|
Func<PropertyInfo, PropertyHelper> createPropertyHelper,
|
||||||
|
ConcurrentDictionary<Type, PropertyHelper[]> cache)
|
||||||
|
{
|
||||||
|
// Unwrap nullable types. This means Nullable<T>.Value and Nullable<T>.HasValue will not be
|
||||||
|
// part of the sequence of properties returned by this method.
|
||||||
|
type = Nullable.GetUnderlyingType(type) ?? type;
|
||||||
|
|
||||||
|
PropertyHelper[] helpers;
|
||||||
|
if (!cache.TryGetValue(type, out helpers))
|
||||||
|
{
|
||||||
|
// We avoid loading indexed properties using the Where statement.
|
||||||
|
var properties = type.GetRuntimeProperties().Where(IsInterestingProperty);
|
||||||
|
|
||||||
|
var typeInfo = type.GetTypeInfo();
|
||||||
|
if (typeInfo.IsInterface)
|
||||||
|
{
|
||||||
|
// Reflection does not return information about inherited properties on the interface itself.
|
||||||
|
properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany(
|
||||||
|
interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty)));
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers = properties.Select(p => createPropertyHelper(p)).ToArray();
|
||||||
|
cache.TryAdd(type, helpers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indexed properties are not useful (or valid) for grabbing properties off an object.
|
||||||
|
private static bool IsInterestingProperty(PropertyInfo property)
|
||||||
|
{
|
||||||
|
// For improving application startup time, do not use GetIndexParameters() api early in this check as it
|
||||||
|
// creates a copy of parameter array and also we would like to check for the presence of a get method
|
||||||
|
// and short circuit asap.
|
||||||
|
return property.GetMethod != null &&
|
||||||
|
property.GetMethod.IsPublic &&
|
||||||
|
!property.GetMethod.IsStatic &&
|
||||||
|
property.GetMethod.GetParameters().Length == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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.Extensions.RazorViews
|
||||||
|
{
|
||||||
|
internal class AttributeValue
|
||||||
|
{
|
||||||
|
public AttributeValue(string prefix, object value, bool literal)
|
||||||
|
{
|
||||||
|
Prefix = prefix;
|
||||||
|
Value = value;
|
||||||
|
Literal = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Prefix { get; }
|
||||||
|
|
||||||
|
public object Value { get; }
|
||||||
|
|
||||||
|
public bool Literal { get; }
|
||||||
|
|
||||||
|
public static AttributeValue FromTuple(Tuple<string, object, bool> value)
|
||||||
|
{
|
||||||
|
return new AttributeValue(value.Item1, value.Item2, value.Item3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttributeValue FromTuple(Tuple<string, string, bool> value)
|
||||||
|
{
|
||||||
|
return new AttributeValue(value.Item1, value.Item2, value.Item3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator AttributeValue(Tuple<string, object, bool> value)
|
||||||
|
{
|
||||||
|
return FromTuple(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,279 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.RazorViews
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Infrastructure
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class BaseView
|
||||||
|
{
|
||||||
|
private static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
|
||||||
|
private readonly Stack<TextWriter> _textWriterStack = new Stack<TextWriter>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request context
|
||||||
|
/// </summary>
|
||||||
|
protected HttpContext Context { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request
|
||||||
|
/// </summary>
|
||||||
|
protected HttpRequest Request { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The response
|
||||||
|
/// </summary>
|
||||||
|
protected HttpResponse Response { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The output stream
|
||||||
|
/// </summary>
|
||||||
|
protected TextWriter Output { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Html encoder used to encode content.
|
||||||
|
/// </summary>
|
||||||
|
protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Url encoder used to encode content.
|
||||||
|
/// </summary>
|
||||||
|
protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JavaScript encoder used to encode content.
|
||||||
|
/// </summary>
|
||||||
|
protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute an individual request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public async Task ExecuteAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
Request = Context.Request;
|
||||||
|
Response = Context.Response;
|
||||||
|
Output = new StreamWriter(Response.Body, UTF8NoBOM, 4096, leaveOpen: true);
|
||||||
|
await ExecuteAsync();
|
||||||
|
Output.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute an individual request
|
||||||
|
/// </summary>
|
||||||
|
public abstract Task ExecuteAsync();
|
||||||
|
|
||||||
|
protected virtual void PushWriter(TextWriter writer)
|
||||||
|
{
|
||||||
|
if (writer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_textWriterStack.Push(Output);
|
||||||
|
Output = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual TextWriter PopWriter()
|
||||||
|
{
|
||||||
|
Output = _textWriterStack.Pop();
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write the given value without HTML encoding directly to <see cref="Output"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The <see cref="object"/> to write.</param>
|
||||||
|
protected void WriteLiteral(object value)
|
||||||
|
{
|
||||||
|
WriteLiteral(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write the given value without HTML encoding directly to <see cref="Output"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The <see cref="string"/> to write.</param>
|
||||||
|
protected void WriteLiteral(string value)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
Output.Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> AttributeValues { get; set; }
|
||||||
|
|
||||||
|
protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno)
|
||||||
|
{
|
||||||
|
if (AttributeValues == null)
|
||||||
|
{
|
||||||
|
AttributeValues = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeValues.Add(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AttributeEnding { get; set; }
|
||||||
|
|
||||||
|
protected void BeginWriteAttribute(string name, string begining, int startPosition, string ending, int endPosition, int thingy)
|
||||||
|
{
|
||||||
|
Debug.Assert(string.IsNullOrEmpty(AttributeEnding));
|
||||||
|
|
||||||
|
Output.Write(begining);
|
||||||
|
AttributeEnding = ending;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void EndWriteAttribute()
|
||||||
|
{
|
||||||
|
Debug.Assert(!string.IsNullOrEmpty(AttributeEnding));
|
||||||
|
|
||||||
|
var attributes = string.Join(" ", AttributeValues);
|
||||||
|
Output.Write(attributes);
|
||||||
|
AttributeValues = null;
|
||||||
|
|
||||||
|
Output.Write(AttributeEnding);
|
||||||
|
AttributeEnding = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given attribute to the given writer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the attribute to write</param>
|
||||||
|
/// <param name="leader">The value of the prefix</param>
|
||||||
|
/// <param name="trailer">The value of the suffix</param>
|
||||||
|
/// <param name="values">The <see cref="AttributeValue"/>s to write.</param>
|
||||||
|
protected void WriteAttribute(
|
||||||
|
string name,
|
||||||
|
string leader,
|
||||||
|
string trailer,
|
||||||
|
params AttributeValue[] values)
|
||||||
|
{
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leader == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(leader));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(trailer));
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLiteral(leader);
|
||||||
|
foreach (var value in values)
|
||||||
|
{
|
||||||
|
WriteLiteral(value.Prefix);
|
||||||
|
|
||||||
|
// The special cases here are that the value we're writing might already be a string, or that the
|
||||||
|
// value might be a bool. If the value is the bool 'true' we want to write the attribute name
|
||||||
|
// instead of the string 'true'. If the value is the bool 'false' we don't want to write anything.
|
||||||
|
// Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself.
|
||||||
|
string stringValue;
|
||||||
|
if (value.Value is bool)
|
||||||
|
{
|
||||||
|
if ((bool)value.Value)
|
||||||
|
{
|
||||||
|
stringValue = name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringValue = value.Value as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the WriteTo(string) overload when possible
|
||||||
|
if (value.Literal && stringValue != null)
|
||||||
|
{
|
||||||
|
WriteLiteral(stringValue);
|
||||||
|
}
|
||||||
|
else if (value.Literal)
|
||||||
|
{
|
||||||
|
WriteLiteral(value.Value);
|
||||||
|
}
|
||||||
|
else if (stringValue != null)
|
||||||
|
{
|
||||||
|
Write(stringValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(value.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteLiteral(trailer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The <see cref="HelperResult"/> to invoke</param>
|
||||||
|
protected void Write(HelperResult result)
|
||||||
|
{
|
||||||
|
Write(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified <paramref name="value"/> to <see cref="Output"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The <see cref="object"/> to write.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked for <see cref="HelperResult"/> types.
|
||||||
|
/// For all other types, the encoded result of <see cref="object.ToString"/> is written to
|
||||||
|
/// <see cref="Output"/>.
|
||||||
|
/// </remarks>
|
||||||
|
protected void Write(object value)
|
||||||
|
{
|
||||||
|
if (value is HelperResult helperResult)
|
||||||
|
{
|
||||||
|
helperResult.WriteTo(Output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified <paramref name="value"/> with HTML encoding to <see cref="Output"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The <see cref="string"/> to write.</param>
|
||||||
|
protected void Write(string value)
|
||||||
|
{
|
||||||
|
WriteLiteral(HtmlEncoder.Encode(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string HtmlEncodeAndReplaceLineBreaks(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on line breaks before passing it through the encoder.
|
||||||
|
return string.Join("<br />" + Environment.NewLine,
|
||||||
|
input.Split(new[] { "\r\n" }, StringSplitOptions.None)
|
||||||
|
.SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None))
|
||||||
|
.Select(HtmlEncoder.Encode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.RazorViews
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a deferred write operation in a <see cref="BaseView"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal class HelperResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="HelperResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">The delegate to invoke when <see cref="WriteTo(TextWriter)"/> is called.</param>
|
||||||
|
public HelperResult(Action<TextWriter> action)
|
||||||
|
{
|
||||||
|
WriteAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<TextWriter> WriteAction { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method invoked to produce content from the <see cref="HelperResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
|
||||||
|
public void WriteTo(TextWriter writer)
|
||||||
|
{
|
||||||
|
WriteAction(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper code used when implementing authentication middleware
|
||||||
|
/// </summary>
|
||||||
|
internal static class SecurityHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add all ClaimsIdentities from an additional ClaimPrincipal to the ClaimsPrincipal
|
||||||
|
/// Merges a new claims principal, placing all new identities first, and eliminating
|
||||||
|
/// any empty unauthenticated identities from context.User
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="existingPrincipal">The <see cref="ClaimsPrincipal"/> containing existing <see cref="ClaimsIdentity"/>.</param>
|
||||||
|
/// <param name="additionalPrincipal">The <see cref="ClaimsPrincipal"/> containing <see cref="ClaimsIdentity"/> to be added.</param>
|
||||||
|
public static ClaimsPrincipal MergeUserPrincipal(ClaimsPrincipal existingPrincipal, ClaimsPrincipal additionalPrincipal)
|
||||||
|
{
|
||||||
|
var newPrincipal = new ClaimsPrincipal();
|
||||||
|
|
||||||
|
// New principal identities go first
|
||||||
|
if (additionalPrincipal != null)
|
||||||
|
{
|
||||||
|
newPrincipal.AddIdentities(additionalPrincipal.Identities);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add any existing non empty or authenticated identities
|
||||||
|
if (existingPrincipal != null)
|
||||||
|
{
|
||||||
|
newPrincipal.AddIdentities(existingPrincipal.Identities.Where(i => i.IsAuthenticated || i.Claims.Any()));
|
||||||
|
}
|
||||||
|
return newPrincipal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains details for individual exception messages.
|
||||||
|
/// </summary>
|
||||||
|
internal class ExceptionDetails
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An individual exception
|
||||||
|
/// </summary>
|
||||||
|
public Exception Error { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The generated stack frames
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<StackFrameSourceCodeInfo> StackFrames { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the summary message.
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class ExceptionDetailsProvider
|
||||||
|
{
|
||||||
|
private readonly IFileProvider _fileProvider;
|
||||||
|
private readonly int _sourceCodeLineCount;
|
||||||
|
|
||||||
|
public ExceptionDetailsProvider(IFileProvider fileProvider, int sourceCodeLineCount)
|
||||||
|
{
|
||||||
|
_fileProvider = fileProvider;
|
||||||
|
_sourceCodeLineCount = sourceCodeLineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ExceptionDetails> GetDetails(Exception exception)
|
||||||
|
{
|
||||||
|
var exceptions = FlattenAndReverseExceptionTree(exception);
|
||||||
|
|
||||||
|
foreach (var ex in exceptions)
|
||||||
|
{
|
||||||
|
yield return new ExceptionDetails
|
||||||
|
{
|
||||||
|
Error = ex,
|
||||||
|
StackFrames = StackTraceHelper.GetFrames(ex)
|
||||||
|
.Select(frame => GetStackFrameSourceCodeInfo(
|
||||||
|
frame.MethodDisplayInfo.ToString(),
|
||||||
|
frame.FilePath,
|
||||||
|
frame.LineNumber))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception ex)
|
||||||
|
{
|
||||||
|
// ReflectionTypeLoadException is special because the details are in
|
||||||
|
// the LoaderExceptions property
|
||||||
|
var typeLoadException = ex as ReflectionTypeLoadException;
|
||||||
|
if (typeLoadException != null)
|
||||||
|
{
|
||||||
|
var typeLoadExceptions = new List<Exception>();
|
||||||
|
foreach (var loadException in typeLoadException.LoaderExceptions)
|
||||||
|
{
|
||||||
|
typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException));
|
||||||
|
}
|
||||||
|
|
||||||
|
typeLoadExceptions.Add(ex);
|
||||||
|
return typeLoadExceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = new List<Exception>();
|
||||||
|
if (ex is AggregateException aggregateException)
|
||||||
|
{
|
||||||
|
list.Add(ex);
|
||||||
|
foreach (var innerException in aggregateException.Flatten().InnerExceptions)
|
||||||
|
{
|
||||||
|
list.Add(innerException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (ex != null)
|
||||||
|
{
|
||||||
|
list.Add(ex);
|
||||||
|
ex = ex.InnerException;
|
||||||
|
}
|
||||||
|
list.Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it internal to enable unit testing
|
||||||
|
internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber)
|
||||||
|
{
|
||||||
|
var stackFrame = new StackFrameSourceCodeInfo
|
||||||
|
{
|
||||||
|
Function = method,
|
||||||
|
File = filePath,
|
||||||
|
Line = lineNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(stackFrame.File))
|
||||||
|
{
|
||||||
|
return stackFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> lines = null;
|
||||||
|
if (File.Exists(stackFrame.File))
|
||||||
|
{
|
||||||
|
lines = File.ReadLines(stackFrame.File);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Handle relative paths and embedded files
|
||||||
|
var fileInfo = _fileProvider.GetFileInfo(stackFrame.File);
|
||||||
|
if (fileInfo.Exists)
|
||||||
|
{
|
||||||
|
// ReadLines doesn't accept a stream. Use ReadLines as its more efficient
|
||||||
|
// relative to reading lines via stream reader
|
||||||
|
if (!string.IsNullOrEmpty(fileInfo.PhysicalPath))
|
||||||
|
{
|
||||||
|
lines = File.ReadLines(fileInfo.PhysicalPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines = ReadLines(fileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines != null)
|
||||||
|
{
|
||||||
|
ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stackFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it internal to enable unit testing
|
||||||
|
internal void ReadFrameContent(
|
||||||
|
StackFrameSourceCodeInfo frame,
|
||||||
|
IEnumerable<string> allLines,
|
||||||
|
int errorStartLineNumberInFile,
|
||||||
|
int errorEndLineNumberInFile)
|
||||||
|
{
|
||||||
|
// Get the line boundaries in the file to be read and read all these lines at once into an array.
|
||||||
|
var preErrorLineNumberInFile = Math.Max(errorStartLineNumberInFile - _sourceCodeLineCount, 1);
|
||||||
|
var postErrorLineNumberInFile = errorEndLineNumberInFile + _sourceCodeLineCount;
|
||||||
|
var codeBlock = allLines
|
||||||
|
.Skip(preErrorLineNumberInFile - 1)
|
||||||
|
.Take(postErrorLineNumberInFile - preErrorLineNumberInFile + 1)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var numOfErrorLines = (errorEndLineNumberInFile - errorStartLineNumberInFile) + 1;
|
||||||
|
var errorStartLineNumberInArray = errorStartLineNumberInFile - preErrorLineNumberInFile;
|
||||||
|
|
||||||
|
frame.PreContextLine = preErrorLineNumberInFile;
|
||||||
|
frame.PreContextCode = codeBlock.Take(errorStartLineNumberInArray).ToArray();
|
||||||
|
frame.ContextCode = codeBlock
|
||||||
|
.Skip(errorStartLineNumberInArray)
|
||||||
|
.Take(numOfErrorLines)
|
||||||
|
.ToArray();
|
||||||
|
frame.PostContextCode = codeBlock
|
||||||
|
.Skip(errorStartLineNumberInArray + numOfErrorLines)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> ReadLines(IFileInfo fileInfo)
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
yield return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class MethodDisplayInfo
|
||||||
|
{
|
||||||
|
public string DeclaringTypeName { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string GenericArguments { get; set; }
|
||||||
|
|
||||||
|
public string SubMethod { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ParameterDisplayInfo> Parameters { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(DeclaringTypeName))
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.Append(DeclaringTypeName)
|
||||||
|
.Append(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(Name);
|
||||||
|
builder.Append(GenericArguments);
|
||||||
|
|
||||||
|
builder.Append("(");
|
||||||
|
builder.Append(string.Join(", ", Parameters.Select(p => p.ToString())));
|
||||||
|
builder.Append(")");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(SubMethod))
|
||||||
|
{
|
||||||
|
builder.Append("+");
|
||||||
|
builder.Append(SubMethod);
|
||||||
|
builder.Append("()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class ParameterDisplayInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
public string Prefix { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(Prefix))
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.Append(Prefix)
|
||||||
|
.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(Type);
|
||||||
|
builder.Append(" ");
|
||||||
|
builder.Append(Name);
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
using System.Reflection.Metadata.Ecma335;
|
||||||
|
using System.Reflection.PortableExecutable;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class PortablePdbReader : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, MetadataReaderProvider> _cache =
|
||||||
|
new Dictionary<string, MetadataReaderProvider>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public void PopulateStackFrame(StackFrameInfo frameInfo, MethodBase method, int IlOffset)
|
||||||
|
{
|
||||||
|
if (method.Module.Assembly.IsDynamic)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
|
||||||
|
|
||||||
|
if (metadataReader == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodToken = MetadataTokens.Handle(method.MetadataToken);
|
||||||
|
|
||||||
|
Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition);
|
||||||
|
|
||||||
|
var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle();
|
||||||
|
|
||||||
|
if (!handle.IsNil)
|
||||||
|
{
|
||||||
|
var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle);
|
||||||
|
var sequencePoints = methodDebugInfo.GetSequencePoints();
|
||||||
|
SequencePoint? bestPointSoFar = null;
|
||||||
|
|
||||||
|
foreach (var point in sequencePoints)
|
||||||
|
{
|
||||||
|
if (point.Offset > IlOffset)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.StartLine != SequencePoint.HiddenLine)
|
||||||
|
{
|
||||||
|
bestPointSoFar = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestPointSoFar.HasValue)
|
||||||
|
{
|
||||||
|
frameInfo.LineNumber = bestPointSoFar.Value.StartLine;
|
||||||
|
frameInfo.FilePath = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetadataReader GetMetadataReader(string assemblyPath)
|
||||||
|
{
|
||||||
|
MetadataReaderProvider provider = null;
|
||||||
|
if (!_cache.TryGetValue(assemblyPath, out provider))
|
||||||
|
{
|
||||||
|
var pdbPath = GetPdbPath(assemblyPath);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath))
|
||||||
|
{
|
||||||
|
var pdbStream = File.OpenRead(pdbPath);
|
||||||
|
provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache[assemblyPath] = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider?.GetMetadataReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetPdbPath(string assemblyPath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(assemblyPath))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(assemblyPath))
|
||||||
|
{
|
||||||
|
var peStream = File.OpenRead(assemblyPath);
|
||||||
|
|
||||||
|
using (var peReader = new PEReader(peStream))
|
||||||
|
{
|
||||||
|
foreach (var entry in peReader.ReadDebugDirectory())
|
||||||
|
{
|
||||||
|
if (entry.Type == DebugDirectoryEntryType.CodeView)
|
||||||
|
{
|
||||||
|
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
|
||||||
|
var peDirectory = Path.GetDirectoryName(assemblyPath);
|
||||||
|
return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsPortable(string pdbPath)
|
||||||
|
{
|
||||||
|
using (var pdbStream = File.OpenRead(pdbPath))
|
||||||
|
{
|
||||||
|
return pdbStream.ReadByte() == 'B' &&
|
||||||
|
pdbStream.ReadByte() == 'S' &&
|
||||||
|
pdbStream.ReadByte() == 'J' &&
|
||||||
|
pdbStream.ReadByte() == 'B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var entry in _cache)
|
||||||
|
{
|
||||||
|
entry.Value?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class StackFrameInfo
|
||||||
|
{
|
||||||
|
public int LineNumber { get; set; }
|
||||||
|
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
|
||||||
|
public StackFrame StackFrame { get; set; }
|
||||||
|
|
||||||
|
public MethodDisplayInfo MethodDisplayInfo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the source code where the exception occurred.
|
||||||
|
/// </summary>
|
||||||
|
internal class StackFrameSourceCodeInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Function containing instruction
|
||||||
|
/// </summary>
|
||||||
|
public string Function { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// File containing the instruction
|
||||||
|
/// </summary>
|
||||||
|
public string File { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The line number of the instruction
|
||||||
|
/// </summary>
|
||||||
|
public int Line { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The line preceding the frame line
|
||||||
|
/// </summary>
|
||||||
|
public int PreContextLine { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lines of code before the actual error line(s).
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> PreContextCode { get; set; } = Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Line(s) of code responsible for the error.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> ContextCode { get; set; } = Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lines of code after the actual error line(s).
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> PostContextCode { get; set; } = Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specific error details for this stack frame.
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorDetails { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,261 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.StackTrace.Sources
|
||||||
|
{
|
||||||
|
internal class StackTraceHelper
|
||||||
|
{
|
||||||
|
public static IList<StackFrameInfo> GetFrames(Exception exception)
|
||||||
|
{
|
||||||
|
var frames = new List<StackFrameInfo>();
|
||||||
|
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var portablePdbReader = new PortablePdbReader())
|
||||||
|
{
|
||||||
|
var needFileInfo = true;
|
||||||
|
var stackTrace = new System.Diagnostics.StackTrace(exception, needFileInfo);
|
||||||
|
var stackFrames = stackTrace.GetFrames();
|
||||||
|
|
||||||
|
if (stackFrames == null)
|
||||||
|
{
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < stackFrames.Length; i++)
|
||||||
|
{
|
||||||
|
var frame = stackFrames[i];
|
||||||
|
var method = frame.GetMethod();
|
||||||
|
|
||||||
|
// Always show last stackFrame
|
||||||
|
if (!ShowInStackTrace(method) && i < stackFrames.Length - 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stackFrame = new StackFrameInfo
|
||||||
|
{
|
||||||
|
StackFrame = frame,
|
||||||
|
FilePath = frame.GetFileName(),
|
||||||
|
LineNumber = frame.GetFileLineNumber(),
|
||||||
|
MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(stackFrame.FilePath))
|
||||||
|
{
|
||||||
|
// .NET Framework and older versions of mono don't support portable PDBs
|
||||||
|
// so we read it manually to get file name and line information
|
||||||
|
portablePdbReader.PopulateStackFrame(stackFrame, method, frame.GetILOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
frames.Add(stackFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MethodDisplayInfo GetMethodDisplayString(MethodBase method)
|
||||||
|
{
|
||||||
|
// Special case: no method available
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodDisplayInfo = new MethodDisplayInfo();
|
||||||
|
|
||||||
|
// Type name
|
||||||
|
var type = method.DeclaringType;
|
||||||
|
|
||||||
|
var methodName = method.Name;
|
||||||
|
|
||||||
|
if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) &&
|
||||||
|
(typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type)))
|
||||||
|
{
|
||||||
|
// Convert StateMachine methods to correct overload +MoveNext()
|
||||||
|
if (TryResolveStateMachineMethod(ref method, out type))
|
||||||
|
{
|
||||||
|
methodDisplayInfo.SubMethod = methodName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ResolveStateMachineMethod may have set declaringType to null
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method name
|
||||||
|
methodDisplayInfo.Name = method.Name;
|
||||||
|
if (method.IsGenericMethod)
|
||||||
|
{
|
||||||
|
var genericArguments = string.Join(", ", method.GetGenericArguments()
|
||||||
|
.Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
|
||||||
|
methodDisplayInfo.GenericArguments += "<" + genericArguments + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method parameters
|
||||||
|
methodDisplayInfo.Parameters = method.GetParameters().Select(parameter =>
|
||||||
|
{
|
||||||
|
var parameterType = parameter.ParameterType;
|
||||||
|
|
||||||
|
var prefix = string.Empty;
|
||||||
|
if (parameter.IsOut)
|
||||||
|
{
|
||||||
|
prefix = "out";
|
||||||
|
}
|
||||||
|
else if (parameterType != null && parameterType.IsByRef)
|
||||||
|
{
|
||||||
|
prefix = "ref";
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameterTypeString = "?";
|
||||||
|
if (parameterType != null)
|
||||||
|
{
|
||||||
|
if (parameterType.IsByRef)
|
||||||
|
{
|
||||||
|
parameterType = parameterType.GetElementType();
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ParameterDisplayInfo
|
||||||
|
{
|
||||||
|
Prefix = prefix,
|
||||||
|
Name = parameter.Name,
|
||||||
|
Type = parameterTypeString,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return methodDisplayInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ShowInStackTrace(MethodBase method)
|
||||||
|
{
|
||||||
|
Debug.Assert(method != null);
|
||||||
|
|
||||||
|
// Don't show any methods marked with the StackTraceHiddenAttribute
|
||||||
|
// https://github.com/dotnet/coreclr/pull/14652
|
||||||
|
if (HasStackTraceHiddenAttribute(method))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var type = method.DeclaringType;
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasStackTraceHiddenAttribute(type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallbacks for runtime pre-StackTraceHiddenAttribute
|
||||||
|
if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (type == typeof(TaskAwaiter) ||
|
||||||
|
type == typeof(TaskAwaiter<>) ||
|
||||||
|
type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
|
||||||
|
type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
|
||||||
|
{
|
||||||
|
switch (method.Name)
|
||||||
|
{
|
||||||
|
case "HandleNonSuccessAndDebuggerNotification":
|
||||||
|
case "ThrowForNonSuccess":
|
||||||
|
case "ValidateEnd":
|
||||||
|
case "GetResult":
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
|
||||||
|
{
|
||||||
|
Debug.Assert(method != null);
|
||||||
|
Debug.Assert(method.DeclaringType != null);
|
||||||
|
|
||||||
|
declaringType = method.DeclaringType;
|
||||||
|
|
||||||
|
var parentType = declaringType.DeclaringType;
|
||||||
|
if (parentType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||||
|
if (methods == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var candidateMethod in methods)
|
||||||
|
{
|
||||||
|
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
||||||
|
if (attributes == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var asma in attributes)
|
||||||
|
{
|
||||||
|
if (asma.StateMachineType == declaringType)
|
||||||
|
{
|
||||||
|
method = candidateMethod;
|
||||||
|
declaringType = candidateMethod.DeclaringType;
|
||||||
|
// Mark the iterator as changed; so it gets the + annotation of the original method
|
||||||
|
// async statemachines resolve directly to their builder methods so aren't marked as changed
|
||||||
|
return asma is IteratorStateMachineAttribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasStackTraceHiddenAttribute(MemberInfo memberInfo)
|
||||||
|
{
|
||||||
|
IList<CustomAttributeData> attributes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Accessing MembmerInfo.GetCustomAttributesData throws for some types (such as types in dynamically generated assemblies).
|
||||||
|
// We'll skip looking up StackTraceHiddenAttributes on such types.
|
||||||
|
attributes = memberInfo.GetCustomAttributesData();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < attributes.Count; i++)
|
||||||
|
{
|
||||||
|
if (attributes[i].AttributeType.Name == "StackTraceHiddenAttribute")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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.Globalization;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.WebEncoders.Sources
|
||||||
|
{
|
||||||
|
// TODO using a resx file. project.json, unfortunately, fails to embed resx files when there are also compile items
|
||||||
|
// in the contentFiles section. Revisit once we convert repos to MSBuild
|
||||||
|
internal static class EncoderResources
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid {0}, {1} or {2} length.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Malformed input: {0} is an invalid input length.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid {0}, {1} or {2} length.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatWebEncoders_InvalidCountOffsetOrLength(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, WebEncoders_InvalidCountOffsetOrLength, p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Malformed input: {0} is an invalid input length.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatWebEncoders_MalformedInput(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, WebEncoders_MalformedInput, p0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,388 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.Extensions.WebEncoders.Sources;
|
||||||
|
|
||||||
|
#if WebEncoders_In_WebUtilities
|
||||||
|
namespace Microsoft.AspNetCore.WebUtilities
|
||||||
|
#else
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains utility APIs to assist with common encoding and decoding operations.
|
||||||
|
/// </summary>
|
||||||
|
#if WebEncoders_In_WebUtilities
|
||||||
|
public
|
||||||
|
#else
|
||||||
|
internal
|
||||||
|
#endif
|
||||||
|
static class WebEncoders
|
||||||
|
{
|
||||||
|
private static readonly byte[] EmptyBytes = new byte[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a base64url-encoded string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The base64url-encoded input to decode.</param>
|
||||||
|
/// <returns>The base64url-decoded form of the input.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The input must not contain any whitespace or padding characters.
|
||||||
|
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||||
|
/// </remarks>
|
||||||
|
public static byte[] Base64UrlDecode(string input)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64UrlDecode(input, offset: 0, count: input.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a base64url-encoded substring of a given string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">A string containing the base64url-encoded input to decode.</param>
|
||||||
|
/// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
|
||||||
|
/// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
|
||||||
|
/// <returns>The base64url-decoded form of the input.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The input must not contain any whitespace or padding characters.
|
||||||
|
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||||
|
/// </remarks>
|
||||||
|
public static byte[] Base64UrlDecode(string input, int offset, int count)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||||
|
|
||||||
|
// Special-case empty input
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return EmptyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form.
|
||||||
|
var buffer = new char[GetArraySizeRequiredToDecode(count)];
|
||||||
|
|
||||||
|
return Base64UrlDecode(input, offset, buffer, bufferOffset: 0, count: count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a base64url-encoded <paramref name="input"/> into a <c>byte[]</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">A string containing the base64url-encoded input to decode.</param>
|
||||||
|
/// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
|
||||||
|
/// <param name="buffer">
|
||||||
|
/// Scratch buffer to hold the <see cref="char"/>s to decode. Array must be large enough to hold
|
||||||
|
/// <paramref name="bufferOffset"/> and <paramref name="count"/> characters as well as Base64 padding
|
||||||
|
/// characters. Content is not preserved.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="bufferOffset">
|
||||||
|
/// The offset into <paramref name="buffer"/> at which to begin writing the <see cref="char"/>s to decode.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
|
||||||
|
/// <returns>The base64url-decoded form of the <paramref name="input"/>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The input must not contain any whitespace or padding characters.
|
||||||
|
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||||
|
/// </remarks>
|
||||||
|
public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, int bufferOffset, int count)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
if (buffer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||||
|
if (bufferOffset < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(bufferOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return EmptyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumption: input is base64url encoded without padding and contains no whitespace.
|
||||||
|
|
||||||
|
var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||||
|
var arraySizeRequired = checked(count + paddingCharsToAdd);
|
||||||
|
Debug.Assert(arraySizeRequired % 4 == 0, "Invariant: Array length must be a multiple of 4.");
|
||||||
|
|
||||||
|
if (buffer.Length - bufferOffset < arraySizeRequired)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||||
|
nameof(count),
|
||||||
|
nameof(bufferOffset),
|
||||||
|
nameof(input)),
|
||||||
|
nameof(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'.
|
||||||
|
var i = bufferOffset;
|
||||||
|
for (var j = offset; i - bufferOffset < count; i++, j++)
|
||||||
|
{
|
||||||
|
var ch = input[j];
|
||||||
|
if (ch == '-')
|
||||||
|
{
|
||||||
|
buffer[i] = '+';
|
||||||
|
}
|
||||||
|
else if (ch == '_')
|
||||||
|
{
|
||||||
|
buffer[i] = '/';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[i] = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the padding characters back.
|
||||||
|
for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--)
|
||||||
|
{
|
||||||
|
buffer[i] = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode.
|
||||||
|
// If the caller provided invalid base64 chars, they'll be caught here.
|
||||||
|
return Convert.FromBase64CharArray(buffer, bufferOffset, arraySizeRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the minimum <c>char[]</c> size required for decoding of <paramref name="count"/> characters
|
||||||
|
/// with the <see cref="Base64UrlDecode(string, int, char[], int, int)"/> method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to decode.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The minimum <c>char[]</c> size required for decoding of <paramref name="count"/> characters.
|
||||||
|
/// </returns>
|
||||||
|
public static int GetArraySizeRequiredToDecode(int count)
|
||||||
|
{
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||||
|
|
||||||
|
return checked(count + numPaddingCharsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The binary input to encode.</param>
|
||||||
|
/// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
|
||||||
|
public static string Base64UrlEncode(byte[] input)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64UrlEncode(input, offset: 0, count: input.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The binary input to encode.</param>
|
||||||
|
/// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
|
||||||
|
/// <param name="count">The number of bytes from <paramref name="input"/> to encode.</param>
|
||||||
|
/// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
|
||||||
|
public static string Base64UrlEncode(byte[] input, int offset, int count)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||||
|
|
||||||
|
// Special-case empty input
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new char[GetArraySizeRequiredToEncode(count)];
|
||||||
|
var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count);
|
||||||
|
|
||||||
|
return new String(buffer, startIndex: 0, length: numBase64Chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The binary input to encode.</param>
|
||||||
|
/// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
|
||||||
|
/// <param name="output">
|
||||||
|
/// Buffer to receive the base64url-encoded form of <paramref name="input"/>. Array must be large enough to
|
||||||
|
/// hold <paramref name="outputOffset"/> characters and the full base64-encoded form of
|
||||||
|
/// <paramref name="input"/>, including padding characters.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="outputOffset">
|
||||||
|
/// The offset into <paramref name="output"/> at which to begin writing the base64url-encoded form of
|
||||||
|
/// <paramref name="input"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="count">The number of <c>byte</c>s from <paramref name="input"/> to encode.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The number of characters written to <paramref name="output"/>, less any padding characters.
|
||||||
|
/// </returns>
|
||||||
|
public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(input));
|
||||||
|
}
|
||||||
|
if (output == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||||
|
if (outputOffset < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(outputOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
var arraySizeRequired = GetArraySizeRequiredToEncode(count);
|
||||||
|
if (output.Length - outputOffset < arraySizeRequired)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||||
|
nameof(count),
|
||||||
|
nameof(outputOffset),
|
||||||
|
nameof(output)),
|
||||||
|
nameof(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special-case empty input.
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use base64url encoding with no padding characters. See RFC 4648, Sec. 5.
|
||||||
|
|
||||||
|
// Start with default Base64 encoding.
|
||||||
|
var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset);
|
||||||
|
|
||||||
|
// Fix up '+' -> '-' and '/' -> '_'. Drop padding characters.
|
||||||
|
for (var i = outputOffset; i - outputOffset < numBase64Chars; i++)
|
||||||
|
{
|
||||||
|
var ch = output[i];
|
||||||
|
if (ch == '+')
|
||||||
|
{
|
||||||
|
output[i] = '-';
|
||||||
|
}
|
||||||
|
else if (ch == '/')
|
||||||
|
{
|
||||||
|
output[i] = '_';
|
||||||
|
}
|
||||||
|
else if (ch == '=')
|
||||||
|
{
|
||||||
|
// We've reached a padding character; truncate the remainder.
|
||||||
|
return i - outputOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numBase64Chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
|
||||||
|
/// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of characters to encode.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The minimum output <c>char[]</c> size required for encoding <paramref name="count"/> <see cref="byte"/>s.
|
||||||
|
/// </returns>
|
||||||
|
public static int GetArraySizeRequiredToEncode(int count)
|
||||||
|
{
|
||||||
|
var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
|
||||||
|
return checked(numWholeOrPartialInputBlocks * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetNumBase64PaddingCharsInString(string str)
|
||||||
|
{
|
||||||
|
// Assumption: input contains a well-formed base64 string with no whitespace.
|
||||||
|
|
||||||
|
// base64 guaranteed have 0 - 2 padding characters.
|
||||||
|
if (str[str.Length - 1] == '=')
|
||||||
|
{
|
||||||
|
if (str[str.Length - 2] == '=')
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
|
||||||
|
{
|
||||||
|
switch (inputLength % 4)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 2:
|
||||||
|
return 2;
|
||||||
|
case 3:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
throw new FormatException(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
EncoderResources.WebEncoders_MalformedInput,
|
||||||
|
inputLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateParameters(int bufferLength, string inputName, int offset, int count)
|
||||||
|
{
|
||||||
|
if (offset < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||||
|
}
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
}
|
||||||
|
if (bufferLength - offset < count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||||
|
nameof(count),
|
||||||
|
nameof(offset),
|
||||||
|
inputName),
|
||||||
|
nameof(count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
// 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.Collections.ObjectModel;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class ClosedGenericMatcherTest
|
||||||
|
{
|
||||||
|
// queryType, interfaceType, expectedResult
|
||||||
|
public static TheoryData<Type, Type, Type> ExtractGenericInterfaceDataSet
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<Type, Type, Type>
|
||||||
|
{
|
||||||
|
// Closed generic types that match given open generic type.
|
||||||
|
{
|
||||||
|
typeof(IEnumerable<BaseClass>),
|
||||||
|
typeof(IEnumerable<>),
|
||||||
|
typeof(IEnumerable<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IReadOnlyList<int>),
|
||||||
|
typeof(IReadOnlyList<>),
|
||||||
|
typeof(IReadOnlyList<int>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(KeyValuePair<string, object>),
|
||||||
|
typeof(KeyValuePair<,>),
|
||||||
|
typeof(KeyValuePair<string, object>)
|
||||||
|
},
|
||||||
|
// Closed generic interfaces that implement sub-interface of given open generic type.
|
||||||
|
{
|
||||||
|
typeof(ICollection<BaseClass>),
|
||||||
|
typeof(IEnumerable<>),
|
||||||
|
typeof(IEnumerable<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IReadOnlyList<int>),
|
||||||
|
typeof(IEnumerable<>),
|
||||||
|
typeof(IEnumerable<int>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IDictionary<string, object>),
|
||||||
|
typeof(IEnumerable<>),
|
||||||
|
typeof(IEnumerable<KeyValuePair<string, object>>)
|
||||||
|
},
|
||||||
|
// Class that implements closed generic based on given open generic interface.
|
||||||
|
{
|
||||||
|
typeof(BaseClass),
|
||||||
|
typeof(IDictionary<,>),
|
||||||
|
typeof(IDictionary<string, object>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(BaseClass),
|
||||||
|
typeof(IEquatable<>),
|
||||||
|
typeof(IEquatable<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(BaseClass),
|
||||||
|
typeof(ICollection<>),
|
||||||
|
typeof(ICollection<KeyValuePair<string, object>>)
|
||||||
|
},
|
||||||
|
// Derived class that implements closed generic based on given open generic interface.
|
||||||
|
{
|
||||||
|
typeof(DerivedClass),
|
||||||
|
typeof(IDictionary<,>),
|
||||||
|
typeof(IDictionary<string, object>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClass),
|
||||||
|
typeof(IEquatable<>),
|
||||||
|
typeof(IEquatable<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClass),
|
||||||
|
typeof(ICollection<>),
|
||||||
|
typeof(ICollection<KeyValuePair<string, object>>)
|
||||||
|
},
|
||||||
|
// Derived class that also implements another interface.
|
||||||
|
{
|
||||||
|
typeof(DerivedClassWithComparable),
|
||||||
|
typeof(IDictionary<,>),
|
||||||
|
typeof(IDictionary<string, object>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClassWithComparable),
|
||||||
|
typeof(IEquatable<>),
|
||||||
|
typeof(IEquatable<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClassWithComparable),
|
||||||
|
typeof(ICollection<>),
|
||||||
|
typeof(ICollection<KeyValuePair<string, object>>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClassWithComparable),
|
||||||
|
typeof(IComparable<>),
|
||||||
|
typeof(IComparable<DerivedClassWithComparable>)
|
||||||
|
},
|
||||||
|
// Derived class using system implementation.
|
||||||
|
{
|
||||||
|
typeof(DerivedClassFromSystemImplementation),
|
||||||
|
typeof(ICollection<>),
|
||||||
|
typeof(ICollection<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClassFromSystemImplementation),
|
||||||
|
typeof(IReadOnlyList<>),
|
||||||
|
typeof(IReadOnlyList<BaseClass>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(DerivedClassFromSystemImplementation),
|
||||||
|
typeof(IEnumerable<>),
|
||||||
|
typeof(IEnumerable<BaseClass>)
|
||||||
|
},
|
||||||
|
// Not given an open generic type.
|
||||||
|
{
|
||||||
|
typeof(IEnumerable<BaseClass>),
|
||||||
|
typeof(IEnumerable<BaseClass>),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IEnumerable<BaseClass>),
|
||||||
|
typeof(IEnumerable),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IReadOnlyList<int>),
|
||||||
|
typeof(BaseClass),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(KeyValuePair<,>),
|
||||||
|
typeof(KeyValuePair<string, object>),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
// Not a match.
|
||||||
|
{
|
||||||
|
typeof(IEnumerable<BaseClass>),
|
||||||
|
typeof(IReadOnlyList<>),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IList<int>),
|
||||||
|
typeof(IReadOnlyList<>),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(IDictionary<string, object>),
|
||||||
|
typeof(KeyValuePair<,>),
|
||||||
|
null
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ExtractGenericInterfaceDataSet))]
|
||||||
|
public void ExtractGenericInterface_ReturnsExpectedType(
|
||||||
|
Type queryType,
|
||||||
|
Type interfaceType,
|
||||||
|
Type expectedResult)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IEnumerable<int> is preferred because it is defined on the more-derived type.
|
||||||
|
[Fact]
|
||||||
|
public void ExtractGenericInterface_MultipleDefinitionsInherited()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(TwoIEnumerableImplementationsInherited);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
Assert.Equal(typeof(IEnumerable<int>), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IEnumerable<int> is preferred because we sort by Ordinal on the full name.
|
||||||
|
[Fact]
|
||||||
|
public void ExtractGenericInterface_MultipleDefinitionsOnSameType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(TwoIEnumerableImplementationsOnSameClass);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>));
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
Assert.Equal(typeof(IEnumerable<int>), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TwoIEnumerableImplementationsOnSameClass : IEnumerable<string>, IEnumerable<int>
|
||||||
|
{
|
||||||
|
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<string> IEnumerable<string>.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TwoIEnumerableImplementationsInherited : List<int>, IEnumerable<string>
|
||||||
|
{
|
||||||
|
IEnumerator<string> IEnumerable<string>.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BaseClass : IDictionary<string, object>, IEquatable<BaseClass>
|
||||||
|
{
|
||||||
|
object IDictionary<string, object>.this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ICollection<KeyValuePair<string, object>>.Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ICollection<string> IDictionary<string, object>.Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ICollection<object> IDictionary<string, object>.Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(BaseClass other)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDictionary<string, object>.Add(string key, object value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.Clear()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDictionary<string, object>.ContainsKey(string key)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDictionary<string, object>.Remove(string key)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDictionary<string, object>.TryGetValue(string key, out object value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedClass : BaseClass
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedClassWithComparable : DerivedClass, IComparable<DerivedClassWithComparable>
|
||||||
|
{
|
||||||
|
public int CompareTo(DerivedClassWithComparable other)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedClassFromSystemImplementation : Collection<BaseClass>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
// 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 Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class CopyOnWriteDictionaryHolderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "test-key", "test-value" },
|
||||||
|
{ "key2", "key2-value" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
Assert.Equal("key2-value", holder["key2"]);
|
||||||
|
Assert.Equal(2, holder.Count);
|
||||||
|
Assert.Equal(new string[] { "test-key", "key2" }, holder.Keys.ToArray());
|
||||||
|
Assert.Equal(new object[] { "test-value", "key2-value" }, holder.Values.ToArray());
|
||||||
|
Assert.True(holder.ContainsKey("test-key"));
|
||||||
|
|
||||||
|
object value;
|
||||||
|
Assert.False(holder.TryGetValue("different-key", out value));
|
||||||
|
|
||||||
|
Assert.False(holder.HasBeenCopied);
|
||||||
|
Assert.Same(source, holder.ReadDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "key1", "value1" },
|
||||||
|
{ "key2", "value2" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
holder["key2"] = "value3";
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("value2", source["key2"]);
|
||||||
|
Assert.Equal(2, holder.Count);
|
||||||
|
Assert.Equal("value1", holder["key1"]);
|
||||||
|
Assert.Equal("value3", holder["key2"]);
|
||||||
|
|
||||||
|
Assert.True(holder.HasBeenCopied);
|
||||||
|
Assert.NotSame(source, holder.ReadDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceValueIsAdded()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "key1", "value1" },
|
||||||
|
{ "key2", "value2" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var holder = new CopyOnWriteDictionaryHolder<string, object>(source);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
holder.Add("key3", "value3");
|
||||||
|
holder.Remove("key1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, source.Count);
|
||||||
|
Assert.Equal("value1", source["key1"]);
|
||||||
|
Assert.Equal(2, holder.Count);
|
||||||
|
Assert.Equal("value2", holder["KeY2"]);
|
||||||
|
Assert.Equal("value3", holder["key3"]);
|
||||||
|
|
||||||
|
Assert.True(holder.HasBeenCopied);
|
||||||
|
Assert.NotSame(source, holder.ReadDictionary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
// 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 Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class CopyOnWriteDictionaryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var values = new List<object>();
|
||||||
|
var enumerator = Mock.Of<IEnumerator<KeyValuePair<string, object>>>();
|
||||||
|
var sourceDictionary = new Mock<IDictionary<string, object>>(MockBehavior.Strict);
|
||||||
|
sourceDictionary
|
||||||
|
.SetupGet(d => d.Count)
|
||||||
|
.Returns(100)
|
||||||
|
.Verifiable();
|
||||||
|
sourceDictionary
|
||||||
|
.SetupGet(d => d.Values)
|
||||||
|
.Returns(values)
|
||||||
|
.Verifiable();
|
||||||
|
sourceDictionary
|
||||||
|
.Setup(d => d.ContainsKey("test-key"))
|
||||||
|
.Returns(value: true)
|
||||||
|
.Verifiable();
|
||||||
|
sourceDictionary
|
||||||
|
.Setup(d => d.GetEnumerator())
|
||||||
|
.Returns(enumerator)
|
||||||
|
.Verifiable();
|
||||||
|
sourceDictionary
|
||||||
|
.Setup(d => d["key2"])
|
||||||
|
.Returns("key2-value")
|
||||||
|
.Verifiable();
|
||||||
|
object value;
|
||||||
|
sourceDictionary.Setup(d => d.TryGetValue("different-key", out value))
|
||||||
|
.Returns(false)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(sourceDictionary.Object,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
Assert.Equal("key2-value", copyOnWriteDictionary["key2"]);
|
||||||
|
Assert.Equal(100, copyOnWriteDictionary.Count);
|
||||||
|
Assert.Same(values, copyOnWriteDictionary.Values);
|
||||||
|
Assert.True(copyOnWriteDictionary.ContainsKey("test-key"));
|
||||||
|
Assert.Same(enumerator, copyOnWriteDictionary.GetEnumerator());
|
||||||
|
Assert.False(copyOnWriteDictionary.TryGetValue("different-key", out value));
|
||||||
|
sourceDictionary.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var values = new List<object>();
|
||||||
|
var sourceDictionary = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "key1", "value1" },
|
||||||
|
{ "key2", "value2" }
|
||||||
|
};
|
||||||
|
var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(
|
||||||
|
sourceDictionary,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
copyOnWriteDictionary["key2"] = "value3";
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("value2", sourceDictionary["key2"]);
|
||||||
|
Assert.Equal(2, copyOnWriteDictionary.Count);
|
||||||
|
Assert.Equal("value1", copyOnWriteDictionary["key1"]);
|
||||||
|
Assert.Equal("value3", copyOnWriteDictionary["key2"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceDictionaryIsModified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var values = new List<object>();
|
||||||
|
var sourceDictionary = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "key1", "value1" },
|
||||||
|
{ "key2", "value2" }
|
||||||
|
};
|
||||||
|
var copyOnWriteDictionary = new CopyOnWriteDictionary<string, object>(
|
||||||
|
sourceDictionary,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
copyOnWriteDictionary.Add("key3", "value3");
|
||||||
|
copyOnWriteDictionary.Remove("key1");
|
||||||
|
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, sourceDictionary.Count);
|
||||||
|
Assert.Equal("value1", sourceDictionary["key1"]);
|
||||||
|
Assert.Equal(2, copyOnWriteDictionary.Count);
|
||||||
|
Assert.Equal("value2", copyOnWriteDictionary["KeY2"]);
|
||||||
|
Assert.Equal("value3", copyOnWriteDictionary["key3"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyActivator\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)PropertyHelper\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)StackTrace\StackFrame\**\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="FSharp.Core" />
|
||||||
|
<Reference Include="System.Reflection.Metadata" />
|
||||||
|
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\testassets\ThrowingLibrary\ThrowingLibrary.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,634 @@
|
||||||
|
// 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.FSharp.Control;
|
||||||
|
using Microsoft.FSharp.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class ObjectMethodExecutorTest
|
||||||
|
{
|
||||||
|
private TestObject _targetObject = new TestObject();
|
||||||
|
private TypeInfo targetTypeInfo = typeof(TestObject).GetTypeInfo();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExecuteValueMethod()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethod");
|
||||||
|
var result = executor.Execute(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { 10, 20 });
|
||||||
|
Assert.False(executor.IsMethodAsync);
|
||||||
|
Assert.Equal(30, (int)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExecuteVoidValueMethod()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("VoidValueMethod");
|
||||||
|
var result = executor.Execute(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { 10 });
|
||||||
|
Assert.False(executor.IsMethodAsync);
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExecuteValueMethodWithReturnType()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodWithReturnType");
|
||||||
|
var result = executor.Execute(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { 10 });
|
||||||
|
var resultObject = Assert.IsType<TestObject>(result);
|
||||||
|
Assert.False(executor.IsMethodAsync);
|
||||||
|
Assert.Equal("Hello", resultObject.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExecuteValueMethodUpdateValue()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodUpdateValue");
|
||||||
|
var parameter = new TestObject();
|
||||||
|
var result = executor.Execute(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { parameter });
|
||||||
|
var resultObject = Assert.IsType<TestObject>(result);
|
||||||
|
Assert.False(executor.IsMethodAsync);
|
||||||
|
Assert.Equal("HelloWorld", resultObject.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExecuteValueMethodWithReturnTypeThrowsException()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsException");
|
||||||
|
var parameter = new TestObject();
|
||||||
|
Assert.False(executor.IsMethodAsync);
|
||||||
|
Assert.Throws<NotImplementedException>(
|
||||||
|
() => executor.Execute(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { parameter }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteValueMethodAsync()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodAsync");
|
||||||
|
var result = await executor.ExecuteAsync(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { 10, 20 });
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Equal(30, (int)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteValueMethodWithReturnTypeAsync()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodWithReturnTypeAsync");
|
||||||
|
var result = await executor.ExecuteAsync(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { 10 });
|
||||||
|
var resultObject = Assert.IsType<TestObject>(result);
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Equal("Hello", resultObject.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteValueMethodUpdateValueAsync()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodUpdateValueAsync");
|
||||||
|
var parameter = new TestObject();
|
||||||
|
var result = await executor.ExecuteAsync(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { parameter });
|
||||||
|
var resultObject = Assert.IsType<TestObject>(result);
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Equal("HelloWorld", resultObject.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteValueMethodWithReturnTypeThrowsExceptionAsync()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsExceptionAsync");
|
||||||
|
var parameter = new TestObject();
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
await Assert.ThrowsAsync<NotImplementedException>(
|
||||||
|
async () => await executor.ExecuteAsync(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { parameter }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteValueMethodWithReturnVoidThrowsExceptionAsync()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("ValueMethodWithReturnVoidThrowsExceptionAsync");
|
||||||
|
var parameter = new TestObject();
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
await Assert.ThrowsAsync<NotImplementedException>(
|
||||||
|
async () => await executor.ExecuteAsync(
|
||||||
|
_targetObject,
|
||||||
|
new object[] { parameter }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDefaultValueForParameters_ReturnsSuppliedValues()
|
||||||
|
{
|
||||||
|
var suppliedDefaultValues = new object[] { 123, "test value" };
|
||||||
|
var executor = GetExecutorForMethod("MethodWithMultipleParameters", suppliedDefaultValues);
|
||||||
|
Assert.Equal(suppliedDefaultValues[0], executor.GetDefaultValueForParameter(0));
|
||||||
|
Assert.Equal(suppliedDefaultValues[1], executor.GetDefaultValueForParameter(1));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => executor.GetDefaultValueForParameter(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDefaultValueForParameters_ThrowsIfNoneWereSupplied()
|
||||||
|
{
|
||||||
|
var executor = GetExecutorForMethod("MethodWithMultipleParameters");
|
||||||
|
Assert.Throws<InvalidOperationException>(() => executor.GetDefaultValueForParameter(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await (TestAwaitable<TestObject>)executor.Execute(_targetObject, new object[] { "Hello", 123 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(TestObject), executor.AsyncResultType);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal("Hello 123", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await (TestAwaitable<int>)executor.Execute(_targetObject, new object[] { 123, 456 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(int), executor.AsyncResultType);
|
||||||
|
Assert.Equal(579, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { "Hello", 123 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(TestObject), executor.AsyncResultType);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.IsType<TestObject>(result);
|
||||||
|
Assert.Equal("Hello 123", ((TestObject)result).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { 123, 456 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(int), executor.AsyncResultType);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.IsType<int>(result);
|
||||||
|
Assert.Equal(579, (int)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("VoidValueMethodAsync");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(void), executor.AsyncResultType);
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[0]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("Used UnsafeOnCompleted", (string)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[0]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("Used OnCompleted", (string)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("ValueTaskOfValueType");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await (ValueTask<int>)executor.Execute(_targetObject, new object[] { 123 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(int), executor.AsyncResultType);
|
||||||
|
Assert.Equal(123, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("ValueTaskOfReferenceType");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await (ValueTask<string>)executor.Execute(_targetObject, new object[] { "test result" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("test result", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("ValueTaskOfValueType");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(int), executor.AsyncResultType);
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(123, (int)result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("ValueTaskOfReferenceType");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("test result", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("FSharpAsyncMethod");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var fsharpAsync = (FSharpAsync<string>)executor.Execute(_targetObject, new object[] { "test result" });
|
||||||
|
var result = await FSharpAsync.StartAsTask(fsharpAsync,
|
||||||
|
FSharpOption<TaskCreationOptions>.None,
|
||||||
|
FSharpOption<CancellationToken>.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("test result", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("FSharpAsyncFailureMethod");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var fsharpAsync = (FSharpAsync<string>)executor.Execute(_targetObject, new object[] { "test result" });
|
||||||
|
var resultTask = FSharpAsync.StartAsTask(fsharpAsync,
|
||||||
|
FSharpOption<TaskCreationOptions>.None,
|
||||||
|
FSharpOption<CancellationToken>.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<AggregateException>(async () => await resultTask);
|
||||||
|
Assert.IsType<InvalidOperationException>(exception.InnerException);
|
||||||
|
Assert.Equal("Test exception", exception.InnerException.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("FSharpAsyncMethod");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
Assert.Equal("test result", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var executor = GetExecutorForMethod("FSharpAsyncFailureMethod");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var resultTask = executor.ExecuteAsync(_targetObject, new object[] { "test result" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(executor.IsMethodAsync);
|
||||||
|
Assert.Same(typeof(string), executor.AsyncResultType);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<AggregateException>(async () => await resultTask);
|
||||||
|
Assert.IsType<InvalidOperationException>(exception.InnerException);
|
||||||
|
Assert.Equal("Test exception", exception.InnerException.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectMethodExecutor GetExecutorForMethod(string methodName)
|
||||||
|
{
|
||||||
|
var method = typeof(TestObject).GetMethod(methodName);
|
||||||
|
return ObjectMethodExecutor.Create(method, targetTypeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectMethodExecutor GetExecutorForMethod(string methodName, object[] parameterDefaultValues)
|
||||||
|
{
|
||||||
|
var method = typeof(TestObject).GetMethod(methodName);
|
||||||
|
return ObjectMethodExecutor.Create(method, targetTypeInfo, parameterDefaultValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestObject
|
||||||
|
{
|
||||||
|
public string value;
|
||||||
|
public int ValueMethod(int i, int j)
|
||||||
|
{
|
||||||
|
return i + j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VoidValueMethod(int i)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestObject ValueMethodWithReturnType(int i)
|
||||||
|
{
|
||||||
|
return new TestObject() { value = "Hello" }; ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestObject ValueMethodWithReturnTypeThrowsException(TestObject i)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Not Implemented Exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestObject ValueMethodUpdateValue(TestObject parameter)
|
||||||
|
{
|
||||||
|
parameter.value = "HelloWorld";
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<int> ValueMethodAsync(int i, int j)
|
||||||
|
{
|
||||||
|
return Task.FromResult<int>(i + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task VoidValueMethodAsync(int i)
|
||||||
|
{
|
||||||
|
await ValueMethodAsync(3, 4);
|
||||||
|
}
|
||||||
|
public Task<TestObject> ValueMethodWithReturnTypeAsync(int i)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TestObject>(new TestObject() { value = "Hello" });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ValueMethodWithReturnVoidThrowsExceptionAsync(TestObject i)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException("Not Implemented Exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TestObject> ValueMethodWithReturnTypeThrowsExceptionAsync(TestObject i)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException("Not Implemented Exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<TestObject> ValueMethodUpdateValueAsync(TestObject parameter)
|
||||||
|
{
|
||||||
|
parameter.value = "HelloWorld";
|
||||||
|
return Task.FromResult<TestObject>(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaitable<TestObject> CustomAwaitableOfReferenceTypeAsync(
|
||||||
|
string input1,
|
||||||
|
int input2)
|
||||||
|
{
|
||||||
|
return new TestAwaitable<TestObject>(new TestObject
|
||||||
|
{
|
||||||
|
value = $"{input1} {input2}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaitable<int> CustomAwaitableOfValueTypeAsync(
|
||||||
|
int input1,
|
||||||
|
int input2)
|
||||||
|
{
|
||||||
|
return new TestAwaitable<int>(input1 + input2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaitableWithICriticalNotifyCompletion CustomAwaitableWithICriticalNotifyCompletion()
|
||||||
|
{
|
||||||
|
return new TestAwaitableWithICriticalNotifyCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaitableWithoutICriticalNotifyCompletion CustomAwaitableWithoutICriticalNotifyCompletion()
|
||||||
|
{
|
||||||
|
return new TestAwaitableWithoutICriticalNotifyCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<int> ValueTaskOfValueType(int result)
|
||||||
|
{
|
||||||
|
return new ValueTask<int>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<string> ValueTaskOfReferenceType(string result)
|
||||||
|
{
|
||||||
|
return new ValueTask<string>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MethodWithMultipleParameters(int valueTypeParam, string referenceTypeParam)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FSharpAsync<string> FSharpAsyncMethod(string parameter)
|
||||||
|
{
|
||||||
|
return FSharpAsync.AwaitTask(Task.FromResult(parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FSharpAsync<string> FSharpAsyncFailureMethod(string parameter)
|
||||||
|
{
|
||||||
|
return FSharpAsync.AwaitTask(
|
||||||
|
Task.FromException<string>(new InvalidOperationException("Test exception")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAwaitable<T>
|
||||||
|
{
|
||||||
|
private T _result;
|
||||||
|
private bool _isCompleted;
|
||||||
|
private List<Action> _onCompletedCallbacks = new List<Action>();
|
||||||
|
|
||||||
|
public TestAwaitable(T result)
|
||||||
|
{
|
||||||
|
_result = result;
|
||||||
|
|
||||||
|
// Simulate a brief delay before completion
|
||||||
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
SetCompleted();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetCompleted()
|
||||||
|
{
|
||||||
|
_isCompleted = true;
|
||||||
|
|
||||||
|
foreach (var callback in _onCompletedCallbacks)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return new TestAwaiter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TestAwaiter : INotifyCompletion
|
||||||
|
{
|
||||||
|
private TestAwaitable<T> _owner;
|
||||||
|
|
||||||
|
public TestAwaiter(TestAwaitable<T> owner) : this()
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => _owner._isCompleted;
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
if (_owner._isCompleted)
|
||||||
|
{
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_owner._onCompletedCallbacks.Add(continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetResult()
|
||||||
|
{
|
||||||
|
return _owner._result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAwaitableWithICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
public TestAwaiterWithICriticalNotifyCompletion GetAwaiter()
|
||||||
|
=> new TestAwaiterWithICriticalNotifyCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAwaitableWithoutICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
public TestAwaiterWithoutICriticalNotifyCompletion GetAwaiter()
|
||||||
|
=> new TestAwaiterWithoutICriticalNotifyCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAwaiterWithICriticalNotifyCompletion
|
||||||
|
: CompletionTrackingAwaiterBase, ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestAwaiterWithoutICriticalNotifyCompletion
|
||||||
|
: CompletionTrackingAwaiterBase, INotifyCompletion
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompletionTrackingAwaiterBase
|
||||||
|
{
|
||||||
|
private string _result;
|
||||||
|
|
||||||
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
|
public string GetResult() => _result;
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
_result = "Used OnCompleted";
|
||||||
|
IsCompleted = true;
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
_result = "Used UnsafeOnCompleted";
|
||||||
|
IsCompleted = true;
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
// 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.Reflection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class PropertyActivatorTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Activate_InvokesValueAccessorWithExpectedValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClass();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var property = typeInfo.GetDeclaredProperty("IntProperty");
|
||||||
|
var invokedWith = -1;
|
||||||
|
var activator = new PropertyActivator<int>(
|
||||||
|
property,
|
||||||
|
valueAccessor: (val) =>
|
||||||
|
{
|
||||||
|
invokedWith = val;
|
||||||
|
return val;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
activator.Activate(instance, 123);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(123, invokedWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Activate_SetsPropertyValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClass();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var property = typeInfo.GetDeclaredProperty("IntProperty");
|
||||||
|
var activator = new PropertyActivator<int>(property, valueAccessor: (val) => val + 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
activator.Activate(instance, 123);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(124, instance.IntProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPropertiesToActivate_RestrictsActivatableProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClass();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var expectedPropertyInfo = typeInfo.GetDeclaredProperty("ActivatableProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
|
||||||
|
type: typeof(TestClass),
|
||||||
|
activateAttributeType: typeof(TestActivateAttribute),
|
||||||
|
createActivateInfo:
|
||||||
|
(propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val + 1));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
propertiesToActivate,
|
||||||
|
(activator) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(expectedPropertyInfo, activator.PropertyInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPropertiesToActivate_CanCreateCustomPropertyActivators()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClass();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var expectedPropertyInfo = typeInfo.GetDeclaredProperty("IntProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
|
||||||
|
type: typeof(TestClass),
|
||||||
|
activateAttributeType: typeof(TestActivateAttribute),
|
||||||
|
createActivateInfo:
|
||||||
|
(propertyInfo) => new PropertyActivator<int>(expectedPropertyInfo, valueAccessor: (val) => val + 1));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
propertiesToActivate,
|
||||||
|
(activator) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(expectedPropertyInfo, activator.PropertyInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPropertiesToActivate_ExcludesNonPublic()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClassWithPropertyVisiblity();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var expectedPropertyInfo = typeInfo.GetDeclaredProperty("Public");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
|
||||||
|
typeof(TestClassWithPropertyVisiblity),
|
||||||
|
typeof(TestActivateAttribute),
|
||||||
|
(propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Single(propertiesToActivate);
|
||||||
|
Assert.Single(propertiesToActivate, p => p.PropertyInfo == expectedPropertyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPropertiesToActivate_IncludesNonPublic()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new TestClassWithPropertyVisiblity();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var propertiesToActivate = PropertyActivator<int>.GetPropertiesToActivate(
|
||||||
|
typeof(TestClassWithPropertyVisiblity),
|
||||||
|
typeof(TestActivateAttribute),
|
||||||
|
(propertyInfo) => new PropertyActivator<int>(propertyInfo, valueAccessor: (val) => val),
|
||||||
|
includeNonPublic: true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(5, propertiesToActivate.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestClass
|
||||||
|
{
|
||||||
|
public int IntProperty { get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
public int ActivatableProperty { get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
public int NoSetterActivatableProperty { get; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
public int this[int something] // Not activatable
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
public static int StaticActivatablProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestClassWithPropertyVisiblity
|
||||||
|
{
|
||||||
|
[TestActivate]
|
||||||
|
public int Public { get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
protected int Protected { get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
internal int Internal { get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
protected internal int ProtectedInternal {get; set; }
|
||||||
|
|
||||||
|
[TestActivate]
|
||||||
|
private int Private { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
private class TestActivateAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ActivationInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,831 @@
|
||||||
|
// 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.Reflection;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class PropertyHelperTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsNameCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { foo = "bar" };
|
||||||
|
var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = new PropertyHelper(property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("foo", property.Name);
|
||||||
|
Assert.Equal("foo", helper.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsValueCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { bar = "baz" };
|
||||||
|
var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = new PropertyHelper(property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("bar", helper.Name);
|
||||||
|
Assert.Equal("baz", helper.GetValue(anonymous));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsGetterDelegate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { bar = "baz" };
|
||||||
|
var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = new PropertyHelper(property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(helper.ValueGetter);
|
||||||
|
Assert.Equal("baz", helper.ValueGetter(anonymous));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetValue_SetsPropertyValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "new value";
|
||||||
|
var instance = new BaseClass { PropA = "old value" };
|
||||||
|
var helper = PropertyHelper.GetProperties(
|
||||||
|
instance.GetType()).First(prop => prop.Name == "PropA");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
helper.SetValue(instance, expected);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, instance.PropA);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsSetterDelegate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "new value";
|
||||||
|
var instance = new BaseClass { PropA = "old value" };
|
||||||
|
var helper = PropertyHelper.GetProperties(
|
||||||
|
instance.GetType()).First(prop => prop.Name == "PropA");
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
Assert.NotNull(helper.ValueSetter);
|
||||||
|
helper.ValueSetter(instance, expected);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, instance.PropA);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsValueCorrectly_ForValueTypes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { foo = 32 };
|
||||||
|
var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = new PropertyHelper(property);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("foo", helper.Name);
|
||||||
|
Assert.Equal(32, helper.GetValue(anonymous));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ReturnsCachedPropertyHelper()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { foo = "bar" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers1 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo());
|
||||||
|
var helpers2 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Single(helpers1);
|
||||||
|
Assert.Same(helpers1, helpers2);
|
||||||
|
Assert.Same(helpers1[0], helpers2[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_DoesNotChangeUnderscores()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new { bar_baz2 = "foo" };
|
||||||
|
|
||||||
|
// Act + Assert
|
||||||
|
var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
|
||||||
|
Assert.Equal("bar_baz2", helper.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_DoesNotFindPrivateProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new PrivateProperties();
|
||||||
|
|
||||||
|
// Act + Assert
|
||||||
|
var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
|
||||||
|
Assert.Equal("Prop1", helper.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_DoesNotFindStaticProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new Static();
|
||||||
|
|
||||||
|
// Act + Assert
|
||||||
|
var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
|
||||||
|
Assert.Equal("Prop5", helper.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_DoesNotFindSetOnlyProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new SetOnly();
|
||||||
|
|
||||||
|
// Act + Assert
|
||||||
|
var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()));
|
||||||
|
Assert.Equal("Prop6", helper.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(typeof(int?))]
|
||||||
|
[InlineData(typeof(DayOfWeek?))]
|
||||||
|
public void PropertyHelper_WorksForNullablePrimitiveAndEnumTypes(Type nullableType)
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var properties = PropertyHelper.GetProperties(nullableType);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_UnwrapsNullableTypes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var myType = typeof(MyStruct?);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var properties = PropertyHelper.GetProperties(myType);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var property = Assert.Single(properties);
|
||||||
|
Assert.Equal("Foo", property.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_WorksForStruct()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anonymous = new MyProperties();
|
||||||
|
|
||||||
|
anonymous.IntProp = 3;
|
||||||
|
anonymous.StringProp = "Five";
|
||||||
|
|
||||||
|
// Act + Assert
|
||||||
|
var helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "IntProp"));
|
||||||
|
var helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "StringProp"));
|
||||||
|
Assert.Equal(3, helper1.GetValue(anonymous));
|
||||||
|
Assert.Equal("Five", helper2.GetValue(anonymous));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ForDerivedClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(helpers);
|
||||||
|
Assert.Equal(2, helpers.Length);
|
||||||
|
|
||||||
|
var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
|
||||||
|
var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
|
||||||
|
|
||||||
|
Assert.Equal("propAValue", propAHelper.GetValue(derived));
|
||||||
|
Assert.Equal("propBValue", propBHelper.GetValue(derived));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ForDerivedClass_WithNew()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var derived = new DerivedClassWithNew { PropA = "propAValue" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(helpers);
|
||||||
|
Assert.Equal(2, helpers.Length);
|
||||||
|
|
||||||
|
var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
|
||||||
|
var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
|
||||||
|
|
||||||
|
Assert.Equal("propAValue", propAHelper.GetValue(derived));
|
||||||
|
Assert.Equal("Newed", propBHelper.GetValue(derived));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ForDerived_WithVirtual()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(helpers);
|
||||||
|
Assert.Equal(2, helpers.Length);
|
||||||
|
|
||||||
|
var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
|
||||||
|
var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));
|
||||||
|
|
||||||
|
Assert.Equal("OverridenpropAValue", propAHelper.GetValue(derived));
|
||||||
|
Assert.Equal("propBValue", propBHelper.GetValue(derived));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ForInterface_ReturnsExpectedProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedNames = new[] { "Count", "IsReadOnly" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers = PropertyHelper.GetProperties(typeof(ICollection<string>));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal),
|
||||||
|
helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); },
|
||||||
|
helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PropertyHelper_ForDerivedInterface_ReturnsAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedNames = new[] { "Count", "IsReadOnly", "Keys", "Values" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helpers = PropertyHelper.GetProperties(typeof(IDictionary<string, string>));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal),
|
||||||
|
helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); },
|
||||||
|
helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); },
|
||||||
|
helper => { Assert.Equal(expectedNames[2], helper.Name, StringComparer.Ordinal); },
|
||||||
|
helper => { Assert.Equal(expectedNames[3], helper.Name, StringComparer.Ordinal); });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetProperties_ExcludesIndexersAndPropertiesWithoutPublicGetters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(DerivedClassWithNonReadableProperties);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetProperties(type).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(3, result.Length);
|
||||||
|
Assert.Equal("Visible", result[0].Name);
|
||||||
|
Assert.Equal("PropA", result[1].Name);
|
||||||
|
Assert.Equal("PropB", result[2].Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_NoHiddenProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(string);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var property = Assert.Single(result);
|
||||||
|
Assert.Equal("Length", property.Name);
|
||||||
|
Assert.Equal(typeof(int), property.Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_HiddenProperty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(DerivedHiddenProperty);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, result.Length);
|
||||||
|
Assert.Equal("Id", result[0].Name);
|
||||||
|
Assert.Equal(typeof(string), result[0].Property.PropertyType);
|
||||||
|
Assert.Equal("Name", result[1].Name);
|
||||||
|
Assert.Equal(typeof(string), result[1].Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_HiddenProperty_TwoLevels()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(DerivedHiddenProperty2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, result.Length);
|
||||||
|
Assert.Equal("Id", result[0].Name);
|
||||||
|
Assert.Equal(typeof(Guid), result[0].Property.PropertyType);
|
||||||
|
Assert.Equal("Name", result[1].Name);
|
||||||
|
Assert.Equal(typeof(string), result[1].Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_NoHiddenPropertyWithTypeInfoInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(string);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var property = Assert.Single(result);
|
||||||
|
Assert.Equal("Length", property.Name);
|
||||||
|
Assert.Equal(typeof(int), property.Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_HiddenPropertyWithTypeInfoInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(DerivedHiddenProperty);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, result.Length);
|
||||||
|
Assert.Equal("Id", result[0].Name);
|
||||||
|
Assert.Equal(typeof(string), result[0].Property.PropertyType);
|
||||||
|
Assert.Equal("Name", result[1].Name);
|
||||||
|
Assert.Equal(typeof(string), result[1].Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetVisibleProperties_HiddenProperty_TwoLevelsWithTypeInfoInput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(DerivedHiddenProperty2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, result.Length);
|
||||||
|
Assert.Equal("Id", result[0].Name);
|
||||||
|
Assert.Equal(typeof(Guid), result[0].Property.PropertyType);
|
||||||
|
Assert.Equal("Name", result[1].Name);
|
||||||
|
Assert.Equal(typeof(string), result[1].Property.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeFastPropertySetter_SetsPropertyValues_ForPublicAndNobPublicProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new BaseClass();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var publicProperty = typeInfo.GetDeclaredProperty("PropA");
|
||||||
|
var protectedProperty = typeInfo.GetDeclaredProperty("PropProtected");
|
||||||
|
var publicPropertySetter = PropertyHelper.MakeFastPropertySetter(publicProperty);
|
||||||
|
var protectedPropertySetter = PropertyHelper.MakeFastPropertySetter(protectedProperty);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
publicPropertySetter(instance, "TestPublic");
|
||||||
|
protectedPropertySetter(instance, "TestProtected");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("TestPublic", instance.PropA);
|
||||||
|
Assert.Equal("TestProtected", instance.GetPropProtected());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeFastPropertySetter_SetsPropertyValues_ForOverridenProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new DerivedClassWithOverride();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var property = typeInfo.GetDeclaredProperty("PropA");
|
||||||
|
var propertySetter = PropertyHelper.MakeFastPropertySetter(property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
propertySetter(instance, "Test value");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("OverridenTest value", instance.PropA);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeFastPropertySetter_SetsPropertyValues_ForNewedProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var instance = new DerivedClassWithNew();
|
||||||
|
var typeInfo = instance.GetType().GetTypeInfo();
|
||||||
|
var property = typeInfo.GetDeclaredProperty("PropB");
|
||||||
|
var propertySetter = PropertyHelper.MakeFastPropertySetter(property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
propertySetter(instance, "Test value");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("NewedTest value", instance.PropB);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeFastPropertyGetter_ReferenceType_ForNullObject_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(BaseClass))
|
||||||
|
.Single(p => p.Name == nameof(BaseClass.PropA));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<NullReferenceException>(() => accessor(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeFastPropertyGetter_ValueType_ForNullObject_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(MyProperties))
|
||||||
|
.Single(p => p.Name == nameof(MyProperties.StringProp));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<NullReferenceException>(() => accessor(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeNullSafeFastPropertyGetter_ReferenceType_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(BaseClass))
|
||||||
|
.Single(p => p.Name == nameof(BaseClass.PropA));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = accessor(new BaseClass() { PropA = "Hi" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Hi", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeNullSafeFastPropertyGetter_ValueType_Success()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(MyProperties))
|
||||||
|
.Single(p => p.Name == nameof(MyProperties.StringProp));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = accessor(new MyProperties() { StringProp = "Hi" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Hi", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeNullSafeFastPropertyGetter_ReferenceType_ForNullObject_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(BaseClass))
|
||||||
|
.Single(p => p.Name == nameof(BaseClass.PropA));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = accessor(null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeNullSafeFastPropertyGetter_ValueType_ForNullObject_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var property = PropertyHelper
|
||||||
|
.GetProperties(typeof(MyProperties))
|
||||||
|
.Single(p => p.Name == nameof(MyProperties.StringProp));
|
||||||
|
|
||||||
|
var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var value = accessor(null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData<object, KeyValuePair<string, object>> IgnoreCaseTestData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<object, KeyValuePair<string, object>>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
selected = true,
|
||||||
|
SeLeCtEd = false
|
||||||
|
},
|
||||||
|
new KeyValuePair<string, object>("selected", false)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
SeLeCtEd = false,
|
||||||
|
selected = true
|
||||||
|
},
|
||||||
|
new KeyValuePair<string, object>("SeLeCtEd", true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
SelECTeD = false,
|
||||||
|
SeLECTED = true
|
||||||
|
},
|
||||||
|
new KeyValuePair<string, object>("SelECTeD", true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(IgnoreCaseTestData))]
|
||||||
|
public void ObjectToDictionary_IgnoresPropertyCase(object testObject,
|
||||||
|
KeyValuePair<string, object> expectedEntry)
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = PropertyHelper.ObjectToDictionary(testObject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var entry = Assert.Single(result);
|
||||||
|
Assert.Equal(expectedEntry, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_WithNullObject_ReturnsEmptyDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(0, dictValues.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_WithPlainObjectType_ReturnsEmptyDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = new object();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(0, dictValues.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_WithPrimitiveType_LooksUpPublicProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = "test";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(1, dictValues.Count);
|
||||||
|
Assert.Equal(4, dictValues["Length"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_WithAnonymousType_LooksUpProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = new { test = "value", other = 1 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(2, dictValues.Count);
|
||||||
|
Assert.Equal("value", dictValues["test"]);
|
||||||
|
Assert.Equal(1, dictValues["other"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_ReturnsCaseInsensitiveDictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = new { TEST = "value", oThEr = 1 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(2, dictValues.Count);
|
||||||
|
Assert.Equal("value", dictValues["test"]);
|
||||||
|
Assert.Equal(1, dictValues["other"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ObjectToDictionary_ReturnsInheritedProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = new ThreeDPoint() { X = 5, Y = 10, Z = 17 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dictValues = PropertyHelper.ObjectToDictionary(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(dictValues);
|
||||||
|
Assert.Equal(3, dictValues.Count);
|
||||||
|
Assert.Equal(5, dictValues["X"]);
|
||||||
|
Assert.Equal(10, dictValues["Y"]);
|
||||||
|
Assert.Equal(17, dictValues["Z"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Point
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ThreeDPoint : Point
|
||||||
|
{
|
||||||
|
public int Z { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Static
|
||||||
|
{
|
||||||
|
public static int Prop2 { get; set; }
|
||||||
|
public int Prop5 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct MyProperties
|
||||||
|
{
|
||||||
|
public int IntProp { get; set; }
|
||||||
|
public string StringProp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SetOnly
|
||||||
|
{
|
||||||
|
public int Prop2 { set { } }
|
||||||
|
public int Prop6 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PrivateProperties
|
||||||
|
{
|
||||||
|
public int Prop1 { get; set; }
|
||||||
|
protected int Prop2 { get; set; }
|
||||||
|
private int Prop3 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BaseClass
|
||||||
|
{
|
||||||
|
public string PropA { get; set; }
|
||||||
|
|
||||||
|
protected string PropProtected { get; set; }
|
||||||
|
|
||||||
|
public string GetPropProtected()
|
||||||
|
{
|
||||||
|
return PropProtected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DerivedClass : BaseClass
|
||||||
|
{
|
||||||
|
public string PropB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BaseClassWithVirtual
|
||||||
|
{
|
||||||
|
public virtual string PropA { get; set; }
|
||||||
|
public string PropB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DerivedClassWithNew : BaseClassWithVirtual
|
||||||
|
{
|
||||||
|
private string _value = "Newed";
|
||||||
|
|
||||||
|
public new string PropB
|
||||||
|
{
|
||||||
|
get { return _value; }
|
||||||
|
set { _value = "Newed" + value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DerivedClassWithOverride : BaseClassWithVirtual
|
||||||
|
{
|
||||||
|
private string _value = "Overriden";
|
||||||
|
|
||||||
|
public override string PropA
|
||||||
|
{
|
||||||
|
get { return _value; }
|
||||||
|
set { _value = "Overriden" + value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedClassWithNonReadableProperties : BaseClassWithVirtual
|
||||||
|
{
|
||||||
|
public string this[int index]
|
||||||
|
{
|
||||||
|
get { return string.Empty; }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Visible { get; set; }
|
||||||
|
|
||||||
|
private string NotVisible { get; set; }
|
||||||
|
|
||||||
|
public string NotVisible2 { private get; set; }
|
||||||
|
|
||||||
|
public string NotVisible3
|
||||||
|
{
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string NotVisible4 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct MyStruct
|
||||||
|
{
|
||||||
|
public string Foo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BaseHiddenProperty
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedHiddenProperty : BaseHiddenProperty
|
||||||
|
{
|
||||||
|
public new string Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedHiddenProperty2 : DerivedHiddenProperty
|
||||||
|
{
|
||||||
|
public new Guid Id { get; set; }
|
||||||
|
|
||||||
|
public new string Name { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class SecurityHelperTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AddingToAnonymousIdentityDoesNotKeepAnonymousIdentity()
|
||||||
|
{
|
||||||
|
var user = SecurityHelper.MergeUserPrincipal(new ClaimsPrincipal(), new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), new string[0]));
|
||||||
|
|
||||||
|
Assert.NotNull(user);
|
||||||
|
Assert.Equal("Alpha", user.Identity.AuthenticationType);
|
||||||
|
Assert.Equal("Test1", user.Identity.Name);
|
||||||
|
Assert.IsAssignableFrom<ClaimsPrincipal>(user);
|
||||||
|
Assert.IsAssignableFrom<ClaimsIdentity>(user.Identity);
|
||||||
|
Assert.Single(user.Identities);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddingExistingIdentityChangesDefaultButPreservesPrior()
|
||||||
|
{
|
||||||
|
ClaimsPrincipal user = new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), null);
|
||||||
|
|
||||||
|
Assert.Equal("Alpha", user.Identity.AuthenticationType);
|
||||||
|
Assert.Equal("Test1", user.Identity.Name);
|
||||||
|
|
||||||
|
user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test2", "Beta"), new string[0]));
|
||||||
|
|
||||||
|
Assert.Equal("Beta", user.Identity.AuthenticationType);
|
||||||
|
Assert.Equal("Test2", user.Identity.Name);
|
||||||
|
|
||||||
|
user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0]));
|
||||||
|
|
||||||
|
Assert.Equal("Gamma", user.Identity.AuthenticationType);
|
||||||
|
Assert.Equal("Test3", user.Identity.Name);
|
||||||
|
|
||||||
|
Assert.Equal(3, user.Identities.Count());
|
||||||
|
Assert.Equal("Test3", user.Identities.Skip(0).First().Name);
|
||||||
|
Assert.Equal("Test2", user.Identities.Skip(1).First().Name);
|
||||||
|
Assert.Equal("Test1", user.Identities.Skip(2).First().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddingPreservesNewIdentitiesAndDropsEmpty()
|
||||||
|
{
|
||||||
|
var existingPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
|
||||||
|
var identityNoAuthTypeWithClaim = new ClaimsIdentity();
|
||||||
|
identityNoAuthTypeWithClaim.AddClaim(new Claim("identityNoAuthTypeWithClaim", "yes"));
|
||||||
|
existingPrincipal.AddIdentity(identityNoAuthTypeWithClaim);
|
||||||
|
var identityEmptyWithAuthType = new ClaimsIdentity("empty");
|
||||||
|
existingPrincipal.AddIdentity(identityEmptyWithAuthType);
|
||||||
|
|
||||||
|
Assert.False(existingPrincipal.Identity.IsAuthenticated);
|
||||||
|
|
||||||
|
var newPrincipal = new ClaimsPrincipal();
|
||||||
|
var newEmptyIdentity = new ClaimsIdentity();
|
||||||
|
var identityTwo = new ClaimsIdentity("yep");
|
||||||
|
newPrincipal.AddIdentity(newEmptyIdentity);
|
||||||
|
newPrincipal.AddIdentity(identityTwo);
|
||||||
|
|
||||||
|
var user = SecurityHelper.MergeUserPrincipal(existingPrincipal, newPrincipal);
|
||||||
|
|
||||||
|
// Preserve newPrincipal order
|
||||||
|
Assert.False(user.Identity.IsAuthenticated);
|
||||||
|
Assert.Null(user.Identity.Name);
|
||||||
|
|
||||||
|
Assert.Equal(4, user.Identities.Count());
|
||||||
|
Assert.Equal(newEmptyIdentity, user.Identities.Skip(0).First());
|
||||||
|
Assert.Equal(identityTwo, user.Identities.Skip(1).First());
|
||||||
|
Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First());
|
||||||
|
Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First());
|
||||||
|
|
||||||
|
// This merge should drop newEmptyIdentity since its empty
|
||||||
|
user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0]));
|
||||||
|
|
||||||
|
Assert.Equal("Gamma", user.Identity.AuthenticationType);
|
||||||
|
Assert.Equal("Test3", user.Identity.Name);
|
||||||
|
|
||||||
|
Assert.Equal(4, user.Identities.Count());
|
||||||
|
Assert.Equal("Test3", user.Identities.Skip(0).First().Name);
|
||||||
|
Assert.Equal(identityTwo, user.Identities.Skip(1).First());
|
||||||
|
Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First());
|
||||||
|
Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,345 @@
|
||||||
|
// 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.Linq.Expressions;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.StackTrace.Sources;
|
||||||
|
using ThrowingLibrary;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class StackTraceHelperTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_IncludesLineNumbersForFiles()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Throwing an exception in the current assembly always seems to populate the full stack
|
||||||
|
// trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used
|
||||||
|
// on desktop.
|
||||||
|
Thrower.Throw();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(stackFrames,
|
||||||
|
frame =>
|
||||||
|
{
|
||||||
|
Assert.Contains("Thrower.cs", frame.FilePath);
|
||||||
|
Assert.Equal(17, frame.LineNumber);
|
||||||
|
},
|
||||||
|
frame =>
|
||||||
|
{
|
||||||
|
Assert.Contains("StackTraceHelperTest.cs", frame.FilePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => GenericMethod<string>(null));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod<T>(T val)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => MethodWithOutParameter(out var value));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter<TVal>(string a, out TVal value)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = 0;
|
||||||
|
var exception = Record.Exception(() => MethodWithRefParameter(ref value));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = 0;
|
||||||
|
var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter<TVal>(ref TVal value)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var value = 0;
|
||||||
|
var exception = Record.Exception(() => MethodWithNullableParameter(value));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable<int> value)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => new GenericClass<int>().Throw(0));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.Throw(T parameter)", methods[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_ProducesReadableOutput()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedCallStack = new List<string>()
|
||||||
|
{
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()",
|
||||||
|
"string.Join(string separator, IEnumerable<string> values)",
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.GenericMethod<V>(ref V value)",
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)",
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync<TValue>(TValue value)",
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)",
|
||||||
|
"Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()",
|
||||||
|
};
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Method("test");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedCallStack, methodNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||||
|
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = (Action)Expression.Lambda(
|
||||||
|
Expression.Throw(
|
||||||
|
Expression.New(typeof(Exception)))).Compile();
|
||||||
|
var exception = Record.Exception(action);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var frames = StackTraceHelper.GetFrames(exception).ToArray();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var frame = frames[0];
|
||||||
|
Assert.Null(frame.FilePath);
|
||||||
|
Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
async Task<string> MethodAsync(int value)
|
||||||
|
{
|
||||||
|
await Task.Delay(0);
|
||||||
|
return GenericClass<byte>.GenericMethod(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
async Task<string> MethodAsync<TValue>(TValue value)
|
||||||
|
{
|
||||||
|
return await MethodAsync(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
string Method(string value)
|
||||||
|
{
|
||||||
|
return MethodAsync(value).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
static IEnumerable<string> Iterator()
|
||||||
|
{
|
||||||
|
yield return "Success";
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void MethodWithOutParameter(out int value) => throw new Exception();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void MethodWithGenericOutParameter<TVal>(string a, out TVal value) => throw new Exception();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void MethodWithRefParameter(ref int value) => throw new Exception();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void MethodWithGenericRefParameter<TVal>(ref TVal value) => throw new Exception();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void MethodWithNullableParameter(int? value) => throw new Exception();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic();
|
||||||
|
|
||||||
|
class GenericClass<T>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
public static string GenericMethod<V>(ref V value)
|
||||||
|
{
|
||||||
|
var returnVal = "";
|
||||||
|
for (var i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
returnVal += string.Join(", ", Iterator());
|
||||||
|
}
|
||||||
|
return returnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
public void Throw(T parameter) => throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
private void GenericMethod<T>(T val) where T : class => throw new Exception();
|
||||||
|
|
||||||
|
private class StackTraceHiddenAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[StackTraceHidden]
|
||||||
|
private class TypeWithStackTraceHiddenAttribute
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
public void Throw() => ThrowCore();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
public static void ThrowStatic() => ThrowCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypeWithMethodWithStackTraceHiddenAttribute
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
[StackTraceHidden]
|
||||||
|
public void MethodWithStackTraceHiddenAttribute()
|
||||||
|
{
|
||||||
|
ThrowCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
public void Throw() => MethodWithStackTraceHiddenAttribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||||
|
private static void ThrowCore() => throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
public class WebEncodersTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", 1, 0)]
|
||||||
|
[InlineData("", 0, 1)]
|
||||||
|
[InlineData("0123456789", 9, 2)]
|
||||||
|
[InlineData("0123456789", Int32.MaxValue, 2)]
|
||||||
|
[InlineData("0123456789", 9, -1)]
|
||||||
|
public void Base64UrlDecode_BadOffsets(string input, int offset, int count)
|
||||||
|
{
|
||||||
|
// Act & assert
|
||||||
|
Assert.ThrowsAny<ArgumentException>(() =>
|
||||||
|
{
|
||||||
|
var retVal = WebEncoders.Base64UrlDecode(input, offset, count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("x")]
|
||||||
|
[InlineData("(x)")]
|
||||||
|
public void Base64UrlDecode_MalformedInput(string input)
|
||||||
|
{
|
||||||
|
// Act & assert
|
||||||
|
Assert.Throws<FormatException>(() =>
|
||||||
|
{
|
||||||
|
var retVal = WebEncoders.Base64UrlDecode(input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", "")]
|
||||||
|
[InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")]
|
||||||
|
[InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")]
|
||||||
|
[InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")]
|
||||||
|
public void Base64UrlEncode_And_Decode(string base64Input, string expectedBase64Url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
byte[] input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray();
|
||||||
|
|
||||||
|
// Act & assert - 1
|
||||||
|
string actualBase64Url = WebEncoders.Base64UrlEncode(input, 3, input.Length - 5); // also helps test offsets
|
||||||
|
Assert.Equal(expectedBase64Url, actualBase64Url);
|
||||||
|
|
||||||
|
// Act & assert - 2
|
||||||
|
// Verify that values round-trip
|
||||||
|
byte[] roundTripped = WebEncoders.Base64UrlDecode("xx" + actualBase64Url + "yyy", 2, actualBase64Url.Length); // also helps test offsets
|
||||||
|
string roundTrippedAsBase64 = Convert.ToBase64String(roundTripped);
|
||||||
|
Assert.Equal(roundTrippedAsBase64, base64Input);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", "")]
|
||||||
|
[InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")]
|
||||||
|
[InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")]
|
||||||
|
[InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")]
|
||||||
|
public void Base64UrlEncode_And_Decode_WithBufferOffsets(string base64Input, string expectedBase64Url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray();
|
||||||
|
var buffer = new char[30];
|
||||||
|
var output = new char[30];
|
||||||
|
for (var i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
buffer[i] = '^';
|
||||||
|
output[i] = '^';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act 1
|
||||||
|
var numEncodedChars =
|
||||||
|
WebEncoders.Base64UrlEncode(input, offset: 3, output: output, outputOffset: 4, count: input.Length - 5);
|
||||||
|
|
||||||
|
// Assert 1
|
||||||
|
var encodedString = new string(output, startIndex: 4, length: numEncodedChars);
|
||||||
|
Assert.Equal(expectedBase64Url, encodedString);
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
var roundTripInput = new string(output);
|
||||||
|
var roundTripped =
|
||||||
|
WebEncoders.Base64UrlDecode(roundTripInput, offset: 4, buffer: buffer, bufferOffset: 5, count: numEncodedChars);
|
||||||
|
|
||||||
|
// Assert 2, verify that values round-trip
|
||||||
|
var roundTrippedAsBase64 = Convert.ToBase64String(roundTripped);
|
||||||
|
Assert.Equal(roundTrippedAsBase64, base64Input);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 1, 0)]
|
||||||
|
[InlineData(0, 0, 1)]
|
||||||
|
[InlineData(10, 9, 2)]
|
||||||
|
[InlineData(10, Int32.MaxValue, 2)]
|
||||||
|
[InlineData(10, 9, -1)]
|
||||||
|
public void Base64UrlEncode_BadOffsets(int inputLength, int offset, int count)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
byte[] input = new byte[inputLength];
|
||||||
|
|
||||||
|
// Act & assert
|
||||||
|
Assert.ThrowsAny<ArgumentException>(() =>
|
||||||
|
{
|
||||||
|
var retVal = WebEncoders.Base64UrlEncode(input, offset, count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace ThrowingLibrary
|
||||||
|
{
|
||||||
|
// Throwing an exception in the current assembly always seems to populate the full stack
|
||||||
|
// trace regardless of symbol type. This type exists to simulate an exception thrown
|
||||||
|
// across assemblies which is the typical use case for StackTraceHelper.
|
||||||
|
public static class Thrower
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void Throw()
|
||||||
|
{
|
||||||
|
throw new DivideByZeroException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -17,5 +17,6 @@
|
||||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
<SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@
|
||||||
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
|
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
|
||||||
<MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreToolsPackageVersion>
|
<MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.1</MicrosoftEntityFrameworkCoreToolsPackageVersion>
|
||||||
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.1</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.1</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.1</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.1</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.1</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
|
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.1</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
|
||||||
|
|
@ -78,10 +77,7 @@
|
||||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.1</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.1</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.1</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -14,6 +14,11 @@
|
||||||
<Compile Include="..\Common\DuplexPipe.cs" Link="DuplexPipe.cs" />
|
<Compile Include="..\Common\DuplexPipe.cs" Link="DuplexPipe.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)SecurityHelper\**\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)WebEncoders\**\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.Http.Connections.Common\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.Http.Connections.Common\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -24,8 +29,6 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(MicrosoftAspNetCoreWebSocketsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(MicrosoftAspNetCoreWebSocketsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" PrivateAssets="All" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.Extensions.ValueStopwatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" PrivateAssets="All" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@
|
||||||
<RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
|
<RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(SharedSourceRoot)ClosedGenericMatcher\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ObjectMethodExecutor\*.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Protocols.Json\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Protocols.Json\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
|
||||||
|
|
@ -15,8 +20,6 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftAspNetCoreAuthorizationPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="$(MicrosoftAspNetCoreAuthorizationPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ObjectMethodExecutor.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="System.Reflection.Emit" Version="$(SystemReflectionEmitPackageVersion)" />
|
<PackageReference Include="System.Reflection.Emit" Version="$(SystemReflectionEmitPackageVersion)" />
|
||||||
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,6 @@
|
||||||
<RepositoryUrl>https://github.com/aspnet/AspNetCore</RepositoryUrl>
|
<RepositoryUrl>https://github.com/aspnet/AspNetCore</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<SharedSourceRoot>$(MSBuildThisFileDirectory)..\Shared\</SharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
||||||
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
|
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
|
||||||
<MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>
|
<MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>
|
||||||
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.1</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
|
||||||
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreCookiePolicyPackageVersion>
|
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.2-rtm-30913</MicrosoftAspNetCoreCookiePolicyPackageVersion>
|
||||||
<MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.1</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>
|
<MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.1</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpsPolicyPackageVersion>2.1.1</MicrosoftAspNetCoreHttpsPolicyPackageVersion>
|
<MicrosoftAspNetCoreHttpsPolicyPackageVersion>2.1.1</MicrosoftAspNetCoreHttpsPolicyPackageVersion>
|
||||||
|
|
@ -35,7 +34,6 @@
|
||||||
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
|
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
|
||||||
<MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreToolsPackageVersion>
|
<MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.2-rtm-30913</MicrosoftEntityFrameworkCoreToolsPackageVersion>
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsProcessSourcesPackageVersion>2.1.1</MicrosoftExtensionsProcessSourcesPackageVersion>
|
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.5</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.5</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNETSdkRazorPackageVersion>2.1.2-rtm-30913</MicrosoftNETSdkRazorPackageVersion>
|
<MicrosoftNETSdkRazorPackageVersion>2.1.2-rtm-30913</MicrosoftNETSdkRazorPackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,15 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="template-baselines.json" />
|
<EmbeddedResource Include="template-baselines.json" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)Process\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
|
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
|
||||||
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" PrivateAssets="All" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
||||||
<PackageReference Include="Selenium.Firefox.WebDriver" Version="$(SeleniumFirefoxWebDriverPackageVersion)" />
|
<PackageReference Include="Selenium.Firefox.WebDriver" Version="$(SeleniumFirefoxWebDriverPackageVersion)" />
|
||||||
<PackageReference Include="Selenium.Support" Version="$(SeleniumSupportPackageVersion)" NoWarn="NU1701" />
|
<PackageReference Include="Selenium.Support" Version="$(SeleniumSupportPackageVersion)" NoWarn="NU1701" />
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<BaseIntermediateOutputPath>$(RepositoryRoot)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
|
<BaseIntermediateOutputPath>$(RepositoryRoot)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
|
||||||
<BaseOutputPath>$(RepositoryRoot)bin\$(MSBuildProjectName)\</BaseOutputPath>
|
<BaseOutputPath>$(RepositoryRoot)bin\$(MSBuildProjectName)\</BaseOutputPath>
|
||||||
|
<ToolSharedSourceRoot>$(MSBuildThisFileDirectory)Shared\</ToolSharedSourceRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" />
|
<Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// 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.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
@ -0,0 +1,300 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
|
{
|
||||||
|
public class CertificateManagerTests
|
||||||
|
{
|
||||||
|
public CertificateManagerTests(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
Output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const string TestCertificateSubject = "CN=aspnet.test";
|
||||||
|
|
||||||
|
public ITestOutputHelper Output { get; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(EnsureCertificateResult.Succeeded, result);
|
||||||
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
|
||||||
|
Assert.NotNull(exportedCertificate);
|
||||||
|
Assert.False(exportedCertificate.HasPrivateKey);
|
||||||
|
|
||||||
|
var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false);
|
||||||
|
var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject);
|
||||||
|
Assert.True(httpsCertificate.HasPrivateKey);
|
||||||
|
Assert.Equal(TestCertificateSubject, httpsCertificate.Subject);
|
||||||
|
Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer);
|
||||||
|
Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName);
|
||||||
|
Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value);
|
||||||
|
|
||||||
|
Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore);
|
||||||
|
Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter);
|
||||||
|
Assert.Contains(
|
||||||
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509BasicConstraintsExtension basicConstraints &&
|
||||||
|
basicConstraints.Critical == true &&
|
||||||
|
basicConstraints.CertificateAuthority == false &&
|
||||||
|
basicConstraints.HasPathLengthConstraint == false &&
|
||||||
|
basicConstraints.PathLengthConstraint == 0);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509KeyUsageExtension keyUsage &&
|
||||||
|
keyUsage.Critical == true &&
|
||||||
|
keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage &&
|
||||||
|
enhancedKeyUsage.Critical == true &&
|
||||||
|
enhancedKeyUsage.EnhancedKeyUsages.OfType<Oid>().Single() is Oid keyUsage &&
|
||||||
|
keyUsage.Value == "1.3.6.1.5.5.7.3.1");
|
||||||
|
|
||||||
|
// Subject alternative name
|
||||||
|
Assert.Contains(
|
||||||
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e.Critical == true &&
|
||||||
|
e.Oid.Value == "2.5.29.17");
|
||||||
|
|
||||||
|
// ASP.NET HTTPS Development certificate extension
|
||||||
|
Assert.Contains(
|
||||||
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e.Critical == false &&
|
||||||
|
e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" &&
|
||||||
|
Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate");
|
||||||
|
|
||||||
|
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Output.WriteLine(e.Message);
|
||||||
|
ListCertificates(Output);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ListCertificates(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||||
|
{
|
||||||
|
store.Open(OpenFlags.ReadOnly);
|
||||||
|
var certificates = store.Certificates;
|
||||||
|
foreach (var certificate in certificates)
|
||||||
|
{
|
||||||
|
Output.WriteLine($"Certificate: '{Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}'.");
|
||||||
|
certificate.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
|
||||||
|
var certificatePassword = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
var httpsCertificate = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result);
|
||||||
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
|
||||||
|
Assert.NotNull(exportedCertificate);
|
||||||
|
Assert.True(exportedCertificate.HasPrivateKey);
|
||||||
|
|
||||||
|
|
||||||
|
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "Requires user interaction")]
|
||||||
|
public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows()
|
||||||
|
{
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
var trustFailed = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "Requires user interaction")]
|
||||||
|
public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates()
|
||||||
|
{
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
manager.CleanupHttpsCertificates(TestCertificateSubject);
|
||||||
|
|
||||||
|
Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(EnsureCertificateResult.Succeeded, result);
|
||||||
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
|
||||||
|
Assert.NotNull(exportedCertificate);
|
||||||
|
Assert.False(exportedCertificate.HasPrivateKey);
|
||||||
|
|
||||||
|
var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false);
|
||||||
|
var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject);
|
||||||
|
Assert.True(identityCertificate.HasPrivateKey);
|
||||||
|
Assert.Equal(TestCertificateSubject, identityCertificate.Subject);
|
||||||
|
Assert.Equal(TestCertificateSubject, identityCertificate.Issuer);
|
||||||
|
Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName);
|
||||||
|
Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value);
|
||||||
|
|
||||||
|
Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore);
|
||||||
|
Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter);
|
||||||
|
Assert.Contains(
|
||||||
|
identityCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509BasicConstraintsExtension basicConstraints &&
|
||||||
|
basicConstraints.Critical == true &&
|
||||||
|
basicConstraints.CertificateAuthority == false &&
|
||||||
|
basicConstraints.HasPathLengthConstraint == false &&
|
||||||
|
basicConstraints.PathLengthConstraint == 0);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
identityCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509KeyUsageExtension keyUsage &&
|
||||||
|
keyUsage.Critical == true &&
|
||||||
|
keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature);
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
identityCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage &&
|
||||||
|
enhancedKeyUsage.Critical == true &&
|
||||||
|
enhancedKeyUsage.EnhancedKeyUsages.OfType<Oid>().Single() is Oid keyUsage &&
|
||||||
|
keyUsage.Value == "1.3.6.1.5.5.7.3.1");
|
||||||
|
|
||||||
|
// ASP.NET Core Identity Json Web Token signing development certificate
|
||||||
|
Assert.Contains(
|
||||||
|
identityCertificate.Extensions.OfType<X509Extension>(),
|
||||||
|
e => e.Critical == false &&
|
||||||
|
e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" &&
|
||||||
|
Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate");
|
||||||
|
|
||||||
|
Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
|
||||||
|
var certificatePassword = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var manager = new CertificateManager();
|
||||||
|
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result);
|
||||||
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
|
||||||
|
Assert.NotNull(exportedCertificate);
|
||||||
|
Assert.True(exportedCertificate.HasPrivateKey);
|
||||||
|
|
||||||
|
Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue