Merge remote-tracking branch 'origin/release/2.1' into rybrande/Mondo2.2
This commit is contained in:
commit
bfffb3c31e
|
|
@ -62,22 +62,10 @@
|
|||
path = modules/Security
|
||||
url = https://github.com/aspnet/Security.git
|
||||
branch = release/2.2
|
||||
[submodule "modules/ServerTests"]
|
||||
path = modules/ServerTests
|
||||
url = https://github.com/aspnet/ServerTests.git
|
||||
branch = release/2.2
|
||||
[submodule "modules/Session"]
|
||||
path = modules/Session
|
||||
url = https://github.com/aspnet/Session.git
|
||||
branch = release/2.2
|
||||
[submodule "modules/SignalR"]
|
||||
path = modules/SignalR
|
||||
url = https://github.com/aspnet/SignalR.git
|
||||
branch = release/2.2
|
||||
[submodule "modules/StaticFiles"]
|
||||
path = modules/StaticFiles
|
||||
url = https://github.com/aspnet/StaticFiles.git
|
||||
branch = release/2.2
|
||||
[submodule "src/IISIntegration/test/gtest/googletest"]
|
||||
path = src/IISIntegration/test/gtest/googletest
|
||||
url = https://github.com/google/googletest
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
<RepositoryBuildOrder Include="BasicMiddleware" Order="9" />
|
||||
<RepositoryBuildOrder Include="Antiforgery" Order="10" />
|
||||
<RepositoryBuildOrder Include="IISIntegration" Order="10" RootPath="$(RepositoryRoot)src\IISIntegration\" />
|
||||
<RepositoryBuildOrder Include="StaticFiles" Order="11" />
|
||||
<RepositoryBuildOrder Include="ResponseCaching" Order="11" />
|
||||
<RepositoryBuildOrder Include="Session" Order="11" />
|
||||
<RepositoryBuildOrder Include="ServerTests" Order="11" />
|
||||
<RepositoryBuildOrder Include="CORS" Order="12" />
|
||||
<RepositoryBuildOrder Include="StaticFiles" Order="11" RootPath="$(RepositoryRoot)src\StaticFiles\" />
|
||||
<RepositoryBuildOrder Include="Routing" Order="12" />
|
||||
<RepositoryBuildOrder Include="ResponseCaching" Order="11" />
|
||||
<RepositoryBuildOrder Include="Session" Order="11" RootPath="$(RepositoryRoot)src\Session\" />
|
||||
<RepositoryBuildOrder Include="ServerTests" Order="11" RootPath="$(RepositoryRoot)src\ServerTests\" />
|
||||
<RepositoryBuildOrder Include="Diagnostics" Order="12" />
|
||||
<RepositoryBuildOrder Include="Localization" Order="13" />
|
||||
<RepositoryBuildOrder Include="Security" Order="13" />
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
<!-- Test-only repos -->
|
||||
<Repository Include="AuthSamples" RootPath="$(RepositoryRoot)src\AuthSamples\" PatchPolicy="AlwaysUpdateAndCascadeVersions" />
|
||||
<Repository Include="MusicStore" RootPath="$(RepositoryRoot)src\MusicStore\" PatchPolicy="AlwaysUpdateAndCascadeVersions" />
|
||||
<Repository Include="ServerTests" PatchPolicy="AlwaysUpdateAndCascadeVersions" />
|
||||
<Repository Include="ServerTests" RootPath="$(RepositoryRoot)src\ServerTests\" PatchPolicy="AlwaysUpdateAndCascadeVersions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -65,8 +65,8 @@
|
|||
<ShippedRepository Include="ResponseCaching" />
|
||||
<ShippedRepository Include="Routing" />
|
||||
<ShippedRepository Include="Security" />
|
||||
<ShippedRepository Include="Session" />
|
||||
<ShippedRepository Include="Session" RootPath="$(RepositoryRoot)src\Session\" />
|
||||
<ShippedRepository Include="SignalR" />
|
||||
<ShippedRepository Include="StaticFiles" />
|
||||
<ShippedRepository Include="StaticFiles" RootPath="$(RepositoryRoot)src\StaticFiles\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 04c6c6bab868ca11fc6521d5621b986f5b7352ee
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 774079d60d29762ef7c8bba3f0fa06e73cb323f2
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit a5fcd9dd8724eb71f0642340f68aeb85b0473374
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
[Oo]bj/
|
||||
[Bb]in/
|
||||
TestResults/
|
||||
.nuget/
|
||||
*.sln.ide/
|
||||
_ReSharper.*/
|
||||
packages/
|
||||
artifacts/
|
||||
PublishProfiles/
|
||||
*.user
|
||||
*.suo
|
||||
*.cache
|
||||
*.docstates
|
||||
_ReSharper.*
|
||||
nuget.exe
|
||||
*net45.csproj
|
||||
*net451.csproj
|
||||
*k10.csproj
|
||||
*.psess
|
||||
*.vsp
|
||||
*.pidb
|
||||
*.userprefs
|
||||
*DS_Store
|
||||
*.ncrunchsolution
|
||||
*.*sdf
|
||||
*.ipch
|
||||
project.lock.json
|
||||
/.vs/
|
||||
.testPublish/
|
||||
.build/
|
||||
global.json
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project>
|
||||
<Import
|
||||
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"
|
||||
Condition=" '$(CI)' != 'true' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))' != '' " />
|
||||
|
||||
<Import Project="version.props" />
|
||||
<Import Project="build\dependencies.props" />
|
||||
<Import Project="build\sources.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Product>Microsoft ASP.NET Core</Product>
|
||||
<RepositoryUrl>https://github.com/aspnet/servertests</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
|
||||
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Server Tests
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/aspnet/ServerTests/branches)
|
||||
[](https://ci.appveyor.com/project/aspnetci/ServerTests/branch/dev)
|
||||
|
||||
This repo hosts [HttpSysServer](https://github.com/aspnet/HttpSysServer) and [Kestrel](https://github.com/aspnet/KestrelHttpServer) tests.
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2036
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{49AB8AAA-8160-48DF-A18B-78F51E54E02A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
NuGet.config = NuGet.config
|
||||
build\repo.props = build\repo.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{FA91F388-F4AF-4850-9D68-D4D128E6B1A6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerComparison.FunctionalTests", "test\ServerComparison.FunctionalTests\ServerComparison.FunctionalTests.csproj", "{A319ACCE-060B-4385-9534-9F2202F6180E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerComparison.TestSites", "test\ServerComparison.TestSites\ServerComparison.TestSites.csproj", "{030225D8-4EE8-47E5-B692-2A96B3B51A38}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{55694E45-5EDE-46F8-80AA-797DE5F8C5C3}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
build\dependencies.props = build\dependencies.props
|
||||
build\repo.props = build\repo.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E} = {FA91F388-F4AF-4850-9D68-D4D128E6B1A6}
|
||||
{030225D8-4EE8-47E5-B692-2A96B3B51A38} = {FA91F388-F4AF-4850-9D68-D4D128E6B1A6}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8A313020-8407-494F-81D7-7631580C5FCC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These package versions may be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Auto">
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.3-rtm-15802</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreAspNetCoreModulePackageVersion>2.1.1</MicrosoftAspNetCoreAspNetCoreModulePackageVersion>
|
||||
<MicrosoftAspNetCoreAspNetCoreModuleV1PackageVersion>2.1.1</MicrosoftAspNetCoreAspNetCoreModuleV1PackageVersion>
|
||||
<MicrosoftAspNetCoreResponseCompressionPackageVersion>2.1.1</MicrosoftAspNetCoreResponseCompressionPackageVersion>
|
||||
<MicrosoftAspNetCoreServerHttpSysPackageVersion>2.1.1</MicrosoftAspNetCoreServerHttpSysPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.1</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISPackageVersion>2.1.1</MicrosoftAspNetCoreServerIISPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>0.5.1</MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.2</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.1</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.1</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNetHttpHeadersPackageVersion>2.1.1</MicrosoftNetHttpHeadersPackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<SerilogExtensionsLoggingPackageVersion>1.4.0</SerilogExtensionsLoggingPackageVersion>
|
||||
<SerilogSinksFilePackageVersion>3.2.0</SerilogSinksFilePackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This may import a generated file which may override the variables above. -->
|
||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
||||
|
||||
<!-- These are package versions that should not be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Pinned" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ExcludeFromTest Include="$(RepositoryRoot)test\ServerComparison.TestSites\ServerComparison.TestSites.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||
<LineupPackageVersion>2.1.0-rc1-*</LineupPackageVersion>
|
||||
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project>
|
||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||
|
||||
<PropertyGroup Label="RestoreSources">
|
||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||
$(RestoreSources);
|
||||
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
||||
</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
|
||||
$(RestoreSources);
|
||||
https://api.nuget.org/v3/index.json;
|
||||
</RestoreSources>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
curl -sSL http://nginx.org/download/nginx-1.8.0.tar.gz | tar zxfv - -C /tmp && cd /tmp/nginx-1.8.0/
|
||||
./configure --prefix=$HOME/nginxinstall --with-http_ssl_module
|
||||
make
|
||||
make install
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<DeveloperBuildTestTfms>netcoreapp2.1;netcoreapp2.0</DeveloperBuildTestTfms>
|
||||
<StandardTestTfms>$(DeveloperBuildTestTfms)</StandardTestTfms>
|
||||
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(StandardTestTfms);net461</StandardTestTfms>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// 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;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace ServerComparison.FunctionalTests
|
||||
{
|
||||
public class HelloWorldTests : LoggedTest
|
||||
{
|
||||
public HelloWorldTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task HelloWorld_WebListener(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return HelloWorld(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
public Task HelloWorld_IISExpress(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return HelloWorld(ServerType.IISExpress, runtimeFlavor, RuntimeArchitecture.x64, applicationType, hostingModel: hostingModel, additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
public Task HelloWorld_Kestrel_Clr(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return HelloWorld(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task HelloWorld_Kestrel(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return HelloWorld(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task HelloWorld_Nginx(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return HelloWorld(ServerType.Nginx, runtimeFlavor, RuntimeArchitecture.x64, applicationType);
|
||||
}
|
||||
|
||||
|
||||
private async Task HelloWorld(ServerType serverType,
|
||||
RuntimeFlavor runtimeFlavor,
|
||||
RuntimeArchitecture architecture,
|
||||
ApplicationType applicationType,
|
||||
[CallerMemberName] string testName = null,
|
||||
HostingModel hostingModel = HostingModel.OutOfProcess,
|
||||
string additionalPublishParameters = "")
|
||||
{
|
||||
testName = $"{testName}_{serverType}_{runtimeFlavor}_{architecture}_{applicationType}";
|
||||
using (StartLog(out var loggerFactory, testName))
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger("HelloWorld");
|
||||
|
||||
var deploymentParameters = new DeploymentParameters(Helpers.GetApplicationPath(applicationType), serverType, runtimeFlavor, architecture)
|
||||
{
|
||||
EnvironmentName = "HelloWorld", // Will pick the Start class named 'StartupHelloWorld',
|
||||
ServerConfigTemplateContent = Helpers.GetConfigContent(serverType, "Http.config", "nginx.conf"),
|
||||
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||
TargetFramework = Helpers.GetTargetFramework(runtimeFlavor),
|
||||
ApplicationType = applicationType,
|
||||
HostingModel = hostingModel,
|
||||
AdditionalPublishParameters = additionalPublishParameters
|
||||
};
|
||||
|
||||
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||
{
|
||||
var deploymentResult = await deployer.DeployAsync();
|
||||
|
||||
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||
var response = await RetryHelper.RetryRequest(() =>
|
||||
{
|
||||
return deploymentResult.HttpClient.GetAsync(string.Empty);
|
||||
}, logger, deploymentResult.HostShutdownToken);
|
||||
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Hello World", responseText);
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 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 Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
|
||||
namespace ServerComparison.FunctionalTests
|
||||
{
|
||||
public class Helpers
|
||||
{
|
||||
public static string GetApplicationPath(ApplicationType applicationType)
|
||||
{
|
||||
var applicationBasePath = AppContext.BaseDirectory;
|
||||
|
||||
var directoryInfo = new DirectoryInfo(applicationBasePath);
|
||||
do
|
||||
{
|
||||
var solutionFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "ServerTests.sln"));
|
||||
if (solutionFileInfo.Exists)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(directoryInfo.FullName, "test", "ServerComparison.TestSites"));
|
||||
}
|
||||
|
||||
directoryInfo = directoryInfo.Parent;
|
||||
}
|
||||
while (directoryInfo.Parent != null);
|
||||
|
||||
throw new Exception($"Solution root could not be found using {applicationBasePath}");
|
||||
}
|
||||
|
||||
public static string GetConfigContent(ServerType serverType, string iisConfig, string nginxConfig)
|
||||
{
|
||||
var applicationBasePath = AppContext.BaseDirectory;
|
||||
|
||||
string content = null;
|
||||
if (serverType == ServerType.IISExpress)
|
||||
{
|
||||
content = File.ReadAllText(Path.Combine(applicationBasePath, iisConfig));
|
||||
}
|
||||
else if (serverType == ServerType.Nginx)
|
||||
{
|
||||
content = File.ReadAllText(Path.Combine(applicationBasePath, nginxConfig));
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public static string GetTargetFramework(RuntimeFlavor runtimeFlavor)
|
||||
{
|
||||
if (runtimeFlavor == RuntimeFlavor.Clr)
|
||||
{
|
||||
return "net461";
|
||||
}
|
||||
else if (runtimeFlavor == RuntimeFlavor.CoreClr)
|
||||
{
|
||||
#if NETCOREAPP2_0
|
||||
return "netcoreapp2.0";
|
||||
#elif NETCOREAPP2_1 || NET461
|
||||
return "netcoreapp2.1";
|
||||
#else
|
||||
#error Target frameworks need to be updated.
|
||||
#endif
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unknown RuntimeFlavor '{runtimeFlavor}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,36 @@
|
|||
error_log [errorlog];
|
||||
user [user];
|
||||
worker_processes 4;
|
||||
pid [pidFile];
|
||||
|
||||
events {
|
||||
worker_connections 768;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 10;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
default_type application/octet-stream;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
access_log [accesslog];
|
||||
|
||||
gzip off;
|
||||
|
||||
server {
|
||||
listen [listenPort];
|
||||
location / {
|
||||
proxy_pass [redirectUri];
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,119 @@
|
|||
// 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.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace ServerComparison.FunctionalTests
|
||||
{
|
||||
public class NtlmAuthenticationTests : LoggedTest
|
||||
{
|
||||
public NtlmAuthenticationTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.Clr, "net461", RuntimeArchitecture.x64, ApplicationType.Portable, HostingModel.OutOfProcess, "V2", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(ServerType.WebListener, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.WebListener, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.WebListener, RuntimeFlavor.CoreClr, "netcoreapp2.0", RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
[InlineData(ServerType.WebListener, RuntimeFlavor.CoreClr, "netcoreapp2.1", RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public async Task NtlmAuthentication(ServerType serverType,
|
||||
RuntimeFlavor runtimeFlavor,
|
||||
string targetFramework,
|
||||
RuntimeArchitecture architecture,
|
||||
ApplicationType applicationType,
|
||||
HostingModel hostingModel = HostingModel.OutOfProcess,
|
||||
string additionalPublishParameters = "")
|
||||
{
|
||||
var testName = $"NtlmAuthentication_{serverType}_{runtimeFlavor}_{architecture}_{applicationType}";
|
||||
using (StartLog(out var loggerFactory, testName))
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger("NtlmAuthenticationTest");
|
||||
|
||||
var deploymentParameters = new DeploymentParameters(Helpers.GetApplicationPath(applicationType), serverType, runtimeFlavor, architecture)
|
||||
{
|
||||
EnvironmentName = "NtlmAuthentication", // Will pick the Start class named 'StartupNtlmAuthentication'
|
||||
ServerConfigTemplateContent = Helpers.GetConfigContent(serverType, "NtlmAuthentication.config", nginxConfig: null),
|
||||
SiteName = "NtlmAuthenticationTestSite", // This is configured in the NtlmAuthentication.config
|
||||
TargetFramework = targetFramework,
|
||||
ApplicationType = applicationType,
|
||||
HostingModel = hostingModel,
|
||||
AdditionalPublishParameters = additionalPublishParameters
|
||||
};
|
||||
|
||||
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||
{
|
||||
var deploymentResult = await deployer.DeployAsync();
|
||||
var httpClient = deploymentResult.HttpClient;
|
||||
|
||||
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||
var response = await RetryHelper.RetryRequest(() =>
|
||||
{
|
||||
return httpClient.GetAsync(string.Empty);
|
||||
}, logger, deploymentResult.HostShutdownToken);
|
||||
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Hello World", responseText);
|
||||
|
||||
logger.LogInformation("Testing /Anonymous");
|
||||
response = await httpClient.GetAsync("/Anonymous");
|
||||
responseText = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("Anonymous?True", responseText);
|
||||
|
||||
logger.LogInformation("Testing /Restricted");
|
||||
response = await httpClient.GetAsync("/Restricted");
|
||||
responseText = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
|
||||
Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
|
||||
|
||||
logger.LogInformation("Testing /Forbidden");
|
||||
response = await httpClient.GetAsync("/Forbidden");
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
|
||||
logger.LogInformation("Enabling Default Credentials");
|
||||
|
||||
// Change the http client to one that uses default credentials
|
||||
httpClient = deploymentResult.CreateHttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
|
||||
|
||||
logger.LogInformation("Testing /Restricted");
|
||||
response = await httpClient.GetAsync("/Restricted");
|
||||
responseText = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("Authenticated", responseText);
|
||||
|
||||
logger.LogInformation("Testing /Forbidden");
|
||||
response = await httpClient.GetAsync("/Forbidden");
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
// 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.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace ServerComparison.FunctionalTests
|
||||
{
|
||||
public class ResponseCompressionTests : LoggedTest
|
||||
{
|
||||
// NGinx's default min size is 20 bytes
|
||||
private static readonly string HelloWorldBody = "Hello World;" + new string('a', 20);
|
||||
|
||||
public ResponseCompressionTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
// IIS Express
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
public Task ResponseCompression_IISExpress_NoCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseCompression(ServerType.IISExpress,
|
||||
runtimeFlavor,
|
||||
RuntimeArchitecture.x64,
|
||||
CheckNoCompressionAsync,
|
||||
applicationType,
|
||||
hostCompression: false,
|
||||
hostingModel: hostingModel,
|
||||
additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
public Task ResponseCompression_IISExpress_HostCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseCompression(ServerType.IISExpress,
|
||||
runtimeFlavor,
|
||||
RuntimeArchitecture.x64,
|
||||
CheckHostCompressionAsync,
|
||||
applicationType,
|
||||
hostCompression: true,
|
||||
hostingModel: hostingModel,
|
||||
additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
[ConditionalTheory(Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
public Task ResponseCompression_IISExpress_AppCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseCompression(ServerType.IISExpress,
|
||||
runtimeFlavor,
|
||||
RuntimeArchitecture.x64,
|
||||
CheckAppCompressionAsync,
|
||||
applicationType,
|
||||
hostCompression: true,
|
||||
hostingModel: hostingModel,
|
||||
additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
public Task ResponseCompression_IISExpress_AppAndHostCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseCompression(ServerType.IISExpress,
|
||||
runtimeFlavor,
|
||||
RuntimeArchitecture.x64,
|
||||
CheckAppCompressionAsync,
|
||||
applicationType,
|
||||
hostCompression: true,
|
||||
hostingModel: hostingModel,
|
||||
additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
// WebListener
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_WebListener_NoCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckNoCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
// WebListener doesn't support HostCompression
|
||||
// "The archive entry was compressed using an unsupported compression method."
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_WebListener_AppCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckAppCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_WebListener_AppAndHostCompression(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckAppCompressionAsync, applicationType, hostCompression: true);
|
||||
}
|
||||
|
||||
// Kestrel
|
||||
[Theory]
|
||||
[InlineData(ServerType.Kestrel, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Kestrel, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Kestrel_NoCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckNoCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ServerType.Kestrel, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Kestrel, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Kestrel_AppCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckAppCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
// Nginx
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Nginx_NoCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckNoCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Nginx_HostCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckHostCompressionAsync, applicationType, hostCompression: true);
|
||||
}
|
||||
|
||||
[ConditionalTheory(Skip = "No pass-through compression https://github.com/aspnet/BasicMiddleware/issues/123")]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Nginx_AppCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckHostCompressionAsync, applicationType, hostCompression: false);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Portable)]
|
||||
[InlineData(ServerType.Nginx, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, ApplicationType.Standalone)]
|
||||
public Task ResponseCompression_Nginx_AppAndHostCompression(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseCompression(serverType, runtimeFlavor, architecture, CheckAppCompressionAsync, applicationType, hostCompression: true);
|
||||
}
|
||||
|
||||
private async Task ResponseCompression(ServerType serverType,
|
||||
RuntimeFlavor runtimeFlavor,
|
||||
RuntimeArchitecture architecture,
|
||||
Func<HttpClient, ILogger, Task> scenario,
|
||||
ApplicationType applicationType,
|
||||
bool hostCompression,
|
||||
[CallerMemberName] string testName = null,
|
||||
HostingModel hostingModel = HostingModel.OutOfProcess,
|
||||
string additionalPublishParameters = "")
|
||||
{
|
||||
testName = $"{testName}_{serverType}_{runtimeFlavor}_{architecture}_{applicationType}";
|
||||
using (StartLog(out var loggerFactory, testName))
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger("ResponseCompression");
|
||||
|
||||
var deploymentParameters = new DeploymentParameters(Helpers.GetApplicationPath(applicationType), serverType, runtimeFlavor, architecture)
|
||||
{
|
||||
EnvironmentName = "ResponseCompression",
|
||||
ServerConfigTemplateContent = Helpers.GetConfigContent(serverType,
|
||||
hostCompression ? "http.config" : "NoCompression.config",
|
||||
hostCompression ? "nginx.conf" : "NoCompression.conf"),
|
||||
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||
TargetFramework = Helpers.GetTargetFramework(runtimeFlavor),
|
||||
ApplicationType = applicationType,
|
||||
HostingModel = hostingModel,
|
||||
AdditionalPublishParameters = additionalPublishParameters
|
||||
};
|
||||
|
||||
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||
{
|
||||
var deploymentResult = await deployer.DeployAsync();
|
||||
var httpClientHandler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.None };
|
||||
Assert.True(httpClientHandler.SupportsAutomaticDecompression);
|
||||
var httpClient = deploymentResult.CreateHttpClient(httpClientHandler);
|
||||
|
||||
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||
var response = await RetryHelper.RetryRequest(() =>
|
||||
{
|
||||
return httpClient.GetAsync(string.Empty);
|
||||
}, logger, deploymentResult.HostShutdownToken);
|
||||
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Running", responseText);
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
|
||||
await scenario(httpClient, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckNoCompressionAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing /NoAppCompression");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "NoAppCompression");
|
||||
request.Headers.AcceptEncoding.ParseAdd("gzip,deflate");
|
||||
var response = await client.SendAsync(request);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal(HelloWorldBody, responseText);
|
||||
Assert.Equal(HelloWorldBody.Length.ToString(), GetContentLength(response));
|
||||
Assert.Equal(0, response.Content.Headers.ContentEncoding.Count);
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static Task CheckHostCompressionAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
return CheckCompressionAsync(client, "NoAppCompression", logger);
|
||||
}
|
||||
|
||||
private static Task CheckAppCompressionAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
return CheckCompressionAsync(client, "AppCompression", logger);
|
||||
}
|
||||
|
||||
private static async Task CheckCompressionAsync(HttpClient client, string url, ILogger logger)
|
||||
{
|
||||
// Manage the compression manually because HttpClient removes the Content-Encoding header when decompressing.
|
||||
logger.LogInformation($"Testing /{url}");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.AcceptEncoding.ParseAdd("gzip,deflate");
|
||||
var response = await client.SendAsync(request);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
responseText = await ReadCompressedAsStringAsync(response.Content);
|
||||
Assert.Equal(HelloWorldBody, responseText);
|
||||
Assert.Equal(1, response.Content.Headers.ContentEncoding.Count);
|
||||
Assert.Equal("gzip", response.Content.Headers.ContentEncoding.First());
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetContentLength(HttpResponseMessage response)
|
||||
{
|
||||
// Don't use response.Content.Headers.ContentLength, it will dynamically calculate the value if it can.
|
||||
return response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out var values) ? values.FirstOrDefault() : null;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadCompressedAsStringAsync(HttpContent content)
|
||||
{
|
||||
using (var stream = await content.ReadAsStreamAsync())
|
||||
using (var compressStream = new GZipStream(stream, CompressionMode.Decompress))
|
||||
using (var reader = new StreamReader(compressStream))
|
||||
{
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
// 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.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace ServerComparison.FunctionalTests
|
||||
{
|
||||
public class ResponseTests : LoggedTest
|
||||
{
|
||||
public ResponseTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
// IIS Express
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable, HostingModel.OutOfProcess, "", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
public Task ResponseFormats_IISExpress_ContentLength(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseFormats(ServerType.IISExpress, runtimeFlavor, RuntimeArchitecture.x64, CheckContentLengthAsync, applicationType, hostingModel: hostingModel, additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable, HostingModel.OutOfProcess, "", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
public Task ResponseFormats_IISExpress_Chunked(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseFormats(ServerType.IISExpress, runtimeFlavor, RuntimeArchitecture.x64, CheckChunkedAsync, applicationType, hostingModel: hostingModel, additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable, HostingModel.OutOfProcess, "", Skip = "Websdk issue with full framework publish. See https://github.com/aspnet/websdk/pull/322")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V1")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone, HostingModel.OutOfProcess, "/p:ANCMVersion=V2")]
|
||||
public Task ResponseFormats_IIS_ManuallyChunk(RuntimeFlavor runtimeFlavor, ApplicationType applicationType, HostingModel hostingModel, string additionalPublishParameters)
|
||||
{
|
||||
return ResponseFormats(ServerType.IISExpress, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAsync, applicationType, hostingModel: hostingModel, additionalPublishParameters: additionalPublishParameters);
|
||||
}
|
||||
|
||||
// Weblistener
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_WebListener_ContentLength(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckContentLengthAsync, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_WebListener_Chunked(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
// IIS will remove the "Connection: close" header https://github.com/aspnet/IISIntegration/issues/7
|
||||
public Task ResponseFormats_WebListener_Http10ConnectionClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckHttp10ConnectionCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)] // https://github.com/aspnet/WebListener/issues/259
|
||||
// IIS will remove the "Connection: close" header https://github.com/aspnet/IISIntegration/issues/7
|
||||
public Task ResponseFormats_WebListener_Http11ConnectionClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckHttp11ConnectionCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_WebListener_ManuallyChunk(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InlineData(RuntimeFlavor.Clr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_WebListener_ManuallyChunkAndClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.WebListener, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAndCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
// Kestrel
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_ContentLength(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckContentLengthAsync, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_Http10ConnectionClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckHttp10ConnectionCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_Http11ConnectionClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckHttp11ConnectionCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_Chunked(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_ManuallyChunk(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Kestrel_ManuallyChunkAndClose(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Kestrel, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAndCloseAsync, applicationType);
|
||||
}
|
||||
|
||||
// Nginx
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Nginx_ContentLength( RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Nginx, runtimeFlavor, RuntimeArchitecture.x64, CheckContentLengthAsync, applicationType);
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Nginx_Chunked(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Nginx, runtimeFlavor, RuntimeArchitecture.x64, CheckChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[ConditionalTheory]
|
||||
[OSSkipCondition(OperatingSystems.Windows)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Portable)]
|
||||
[InlineData(RuntimeFlavor.CoreClr, ApplicationType.Standalone)]
|
||||
public Task ResponseFormats_Nginx_ManuallyChunk(RuntimeFlavor runtimeFlavor, ApplicationType applicationType)
|
||||
{
|
||||
return ResponseFormats(ServerType.Nginx, runtimeFlavor, RuntimeArchitecture.x64, CheckManuallyChunkedAsync, applicationType);
|
||||
}
|
||||
|
||||
private async Task ResponseFormats(ServerType serverType,
|
||||
RuntimeFlavor runtimeFlavor,
|
||||
RuntimeArchitecture architecture,
|
||||
Func<HttpClient, ILogger, Task> scenario,
|
||||
ApplicationType applicationType,
|
||||
[CallerMemberName] string testName = null,
|
||||
HostingModel hostingModel = HostingModel.OutOfProcess,
|
||||
string additionalPublishParameters = "")
|
||||
{
|
||||
testName = $"{testName}_{serverType}_{runtimeFlavor}_{architecture}_{applicationType}";
|
||||
using (StartLog(out var loggerFactory, testName))
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger("ResponseFormats");
|
||||
|
||||
var deploymentParameters = new DeploymentParameters(Helpers.GetApplicationPath(applicationType), serverType, runtimeFlavor, architecture)
|
||||
{
|
||||
EnvironmentName = "Responses",
|
||||
ServerConfigTemplateContent = Helpers.GetConfigContent(serverType, "Http.config", "nginx.conf"),
|
||||
SiteName = "HttpTestSite", // This is configured in the Http.config
|
||||
TargetFramework = Helpers.GetTargetFramework(runtimeFlavor),
|
||||
ApplicationType = applicationType,
|
||||
HostingModel = hostingModel,
|
||||
AdditionalPublishParameters = additionalPublishParameters
|
||||
};
|
||||
|
||||
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
|
||||
{
|
||||
var deploymentResult = await deployer.DeployAsync();
|
||||
|
||||
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
|
||||
var response = await RetryHelper.RetryRequest(() =>
|
||||
{
|
||||
return deploymentResult.HttpClient.GetAsync(string.Empty);
|
||||
}, logger, deploymentResult.HostShutdownToken);
|
||||
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Running", responseText);
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
|
||||
await scenario(deploymentResult.HttpClient, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckContentLengthAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing ContentLength");
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "contentlength")
|
||||
{
|
||||
Version = new Version(1, 1)
|
||||
};
|
||||
|
||||
var response = await client.SendAsync(requestMessage);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Content Length", responseText);
|
||||
Assert.Null(response.Headers.TransferEncodingChunked);
|
||||
Assert.Null(response.Headers.ConnectionClose);
|
||||
Assert.Equal("14", GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckHttp11ConnectionCloseAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing Http11ConnectionClose");
|
||||
var response = await client.GetAsync("connectionclose");
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Connnection Close", responseText);
|
||||
Assert.True(response.Headers.ConnectionClose, "/connectionclose, closed?");
|
||||
Assert.True(response.Headers.TransferEncodingChunked);
|
||||
Assert.Null(GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckHttp10ConnectionCloseAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing Http10ConnectionClose");
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "connectionclose")
|
||||
{
|
||||
Version = new Version(1, 0)
|
||||
};
|
||||
|
||||
var response = await client.SendAsync(requestMessage);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Connnection Close", responseText);
|
||||
Assert.True(response.Headers.ConnectionClose, "/connectionclose, closed?");
|
||||
Assert.Null(response.Headers.TransferEncodingChunked);
|
||||
Assert.Null(GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckChunkedAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing Chunked");
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "chunked")
|
||||
{
|
||||
Version = new Version(1, 1)
|
||||
};
|
||||
|
||||
var response = await client.SendAsync(requestMessage);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Chunked", responseText);
|
||||
Assert.True(response.Headers.TransferEncodingChunked, "/chunked, chunked?");
|
||||
Assert.Null(response.Headers.ConnectionClose);
|
||||
Assert.Null(GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckManuallyChunkedAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing ManuallyChunked");
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "manuallychunked")
|
||||
{
|
||||
Version = new Version(1, 1)
|
||||
};
|
||||
|
||||
var response = await client.SendAsync(requestMessage);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Manually Chunked", responseText);
|
||||
Assert.True(response.Headers.TransferEncodingChunked, "/manuallychunked, chunked?");
|
||||
Assert.Null(response.Headers.ConnectionClose);
|
||||
Assert.Null(GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckManuallyChunkedAndCloseAsync(HttpClient client, ILogger logger)
|
||||
{
|
||||
logger.LogInformation("Testing ManuallyChunkedAndClose");
|
||||
var response = await client.GetAsync("manuallychunkedandclose");
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Assert.Equal("Manually Chunked and Close", responseText);
|
||||
Assert.True(response.Headers.TransferEncodingChunked, "/manuallychunkedandclose, chunked?");
|
||||
Assert.True(response.Headers.ConnectionClose, "/manuallychunkedandclose, closed?");
|
||||
Assert.Null(GetContentLength(response));
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
logger.LogWarning(response.ToString());
|
||||
logger.LogWarning(responseText);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetContentLength(HttpResponseMessage response)
|
||||
{
|
||||
// Don't use response.Content.Headers.ContentLength, it will dynamically calculate the value if it can.
|
||||
IEnumerable<string> values;
|
||||
return response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out values) ? values.FirstOrDefault() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Use 2.1 to run the tests. The tests will select their own TFMs for the test application when publishing -->
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="*.config;*.conf" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="$(SerilogSinksFilePackageVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
error_log [errorlog];
|
||||
worker_processes 4;
|
||||
pid [pidFile];
|
||||
|
||||
events {
|
||||
worker_connections 768;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 10;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
default_type application/octet-stream;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
access_log [accesslog];
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain;
|
||||
gzip_disable "msie6";
|
||||
|
||||
server {
|
||||
listen [listenPort];
|
||||
location / {
|
||||
proxy_pass [redirectUri];
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 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 Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.HttpSys;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.UseConfiguration(config)
|
||||
.ConfigureLogging((_, factory) =>
|
||||
{
|
||||
factory.AddConsole();
|
||||
factory.AddFilter("Console", level => level >= LogLevel.Warning);
|
||||
})
|
||||
.UseStartup("ServerComparison.TestSites");
|
||||
|
||||
// Switch between Kestrel, IIS, and HttpSys for different tests. Default to Kestrel for normal app execution.
|
||||
if (string.Equals(builder.GetSetting("server"), "Microsoft.AspNetCore.Server.HttpSys", StringComparison.Ordinal))
|
||||
{
|
||||
if (string.Equals(builder.GetSetting("environment") ??
|
||||
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
|
||||
"NtlmAuthentication", System.StringComparison.Ordinal))
|
||||
{
|
||||
// Set up NTLM authentication for HttpSys as follows.
|
||||
// For IIS and IISExpress use inetmgr to setup NTLM authentication on the application or
|
||||
// modify the applicationHost.config to enable NTLM.
|
||||
builder.UseHttpSys(options =>
|
||||
{
|
||||
options.Authentication.AllowAnonymous = true;
|
||||
options.Authentication.Schemes =
|
||||
AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.UseHttpSys();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check that we are not using IIS inproc before we add Kestrel.
|
||||
builder.UseKestrel();
|
||||
}
|
||||
|
||||
builder.UseIISIntegration();
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:39982/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNET_ENVIRONMENT": "HelloWorld"
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
"commandName": "web",
|
||||
"environmentVariables": {
|
||||
"ASPNET_ENVIRONMENT": "HelloWorld"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="">$(StandardTestTfms)</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x86;win7-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT' AND ('$(ANCMVersion)' == 'V2' Or '$(ANCMVersion)' == '')">
|
||||
<PackageReference Include="Microsoft.AspNetCore.AspNetCoreModule" Version="$(MicrosoftAspNetCoreAspNetCoreModulePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT' AND ('$(ANCMVersion)' == 'V1')">
|
||||
<PackageReference Include="Microsoft.AspNetCore.AspNetCoreModuleV1" Version="$(MicrosoftAspNetCoreAspNetCoreModuleV1PackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="$(MicrosoftAspNetCoreResponseCompressionPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.HttpSys" Version="$(MicrosoftAspNetCoreServerHttpSysPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public class StartupHelloWorld
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.Run(ctx =>
|
||||
{
|
||||
return ctx.Response.WriteAsync("Hello World");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public class StartupNtlmAuthentication
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await next();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (context.Response.HasStarted)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
context.Response.Clear();
|
||||
context.Response.StatusCode = 500;
|
||||
await context.Response.WriteAsync(ex.ToString());
|
||||
}
|
||||
});
|
||||
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
if (context.Request.Path.Equals("/Anonymous"))
|
||||
{
|
||||
return context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated);
|
||||
}
|
||||
|
||||
if (context.Request.Path.Equals("/Restricted"))
|
||||
{
|
||||
if (context.User.Identity.IsAuthenticated)
|
||||
{
|
||||
return context.Response.WriteAsync("Authenticated");
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.ChallengeAsync("Windows");
|
||||
}
|
||||
}
|
||||
|
||||
if (context.Request.Path.Equals("/Forbidden"))
|
||||
{
|
||||
return context.ForbidAsync("Windows");
|
||||
}
|
||||
|
||||
return context.Response.WriteAsync("Hello World");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public class StartupResponseCompression
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddResponseCompression();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
|
||||
{
|
||||
// NGinx's default min size is 20 bytes
|
||||
var helloWorldBody = "Hello World;" + new string('a', 20);
|
||||
|
||||
app.Map("/NoAppCompression", subApp =>
|
||||
{
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Response.ContentType = "text/plain";
|
||||
context.Response.ContentLength = helloWorldBody.Length;
|
||||
return context.Response.WriteAsync(helloWorldBody);
|
||||
});
|
||||
});
|
||||
|
||||
app.Map("/AppCompression", subApp =>
|
||||
{
|
||||
subApp.UseResponseCompression();
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Response.ContentType = "text/plain";
|
||||
context.Response.ContentLength = helloWorldBody.Length;
|
||||
return context.Response.WriteAsync(helloWorldBody);
|
||||
});
|
||||
});
|
||||
/* If we implement DisableResponseBuffering on IISMiddleware
|
||||
app.Map("/NoBuffer", subApp =>
|
||||
{
|
||||
subApp.UseResponseCompression();
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Features.Get<IHttpBufferingFeature>().DisableResponseBuffering();
|
||||
context.Response.ContentType = "text/plain";
|
||||
context.Response.ContentLength = helloWorldBody.Length;
|
||||
return context.Response.WriteAsync(helloWorldBody);
|
||||
});
|
||||
});
|
||||
*/
|
||||
app.Run(context =>
|
||||
{
|
||||
context.Response.ContentType = "text/plain";
|
||||
string body;
|
||||
if (context.Request.Path.Value == "/")
|
||||
{
|
||||
body = "Running";
|
||||
}
|
||||
else
|
||||
{
|
||||
body = "Not Implemented: " + context.Request.Path;
|
||||
}
|
||||
|
||||
context.Response.ContentLength = body.Length;
|
||||
return context.Response.WriteAsync(body);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace ServerComparison.TestSites
|
||||
{
|
||||
public class StartupResponses
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.Map("/contentlength", subApp =>
|
||||
{
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Response.ContentLength = 14;
|
||||
return context.Response.WriteAsync("Content Length");
|
||||
});
|
||||
});
|
||||
|
||||
app.Map("/connectionclose", subApp =>
|
||||
{
|
||||
subApp.Run(async context =>
|
||||
{
|
||||
context.Response.Headers[HeaderNames.Connection] = "close";
|
||||
await context.Response.WriteAsync("Connnection Close");
|
||||
await context.Response.Body.FlushAsync(); // Bypass IIS write-behind buffering
|
||||
});
|
||||
});
|
||||
|
||||
app.Map("/chunked", subApp =>
|
||||
{
|
||||
subApp.Run(async context =>
|
||||
{
|
||||
await context.Response.WriteAsync("Chunked");
|
||||
await context.Response.Body.FlushAsync(); // Bypass IIS write-behind buffering
|
||||
});
|
||||
});
|
||||
|
||||
app.Map("/manuallychunked", subApp =>
|
||||
{
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Response.Headers[HeaderNames.TransferEncoding] = "chunked";
|
||||
return context.Response.WriteAsync("10\r\nManually Chunked\r\n0\r\n\r\n");
|
||||
});
|
||||
});
|
||||
|
||||
app.Map("/manuallychunkedandclose", subApp =>
|
||||
{
|
||||
subApp.Run(context =>
|
||||
{
|
||||
context.Response.Headers[HeaderNames.Connection] = "close";
|
||||
context.Response.Headers[HeaderNames.TransferEncoding] = "chunked";
|
||||
return context.Response.WriteAsync("1A\r\nManually Chunked and Close\r\n0\r\n\r\n");
|
||||
});
|
||||
});
|
||||
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Running");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
|
||||
</handlers>
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true"/>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>2.1.1</VersionPrefix>
|
||||
<VersionSuffix>rtm</VersionSuffix>
|
||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
|
||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
|
||||
<BuildNumber Condition="'$(BuildNumber)' == ''">t000</BuildNumber>
|
||||
<FeatureBranchVersionPrefix Condition="'$(FeatureBranchVersionPrefix)' == ''">a-</FeatureBranchVersionPrefix>
|
||||
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(FeatureBranchVersionSuffix)' != ''">$(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))</VersionSuffix>
|
||||
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
[Oo]bj/
|
||||
[Bb]in/
|
||||
TestResults/
|
||||
.nuget/
|
||||
*.sln.ide/
|
||||
_ReSharper.*/
|
||||
packages/
|
||||
artifacts/
|
||||
PublishProfiles/
|
||||
*.user
|
||||
*.suo
|
||||
*.cache
|
||||
*.docstates
|
||||
_ReSharper.*
|
||||
nuget.exe
|
||||
*net45.csproj
|
||||
*net451.csproj
|
||||
*k10.csproj
|
||||
*.psess
|
||||
*.vsp
|
||||
*.pidb
|
||||
*.userprefs
|
||||
*DS_Store
|
||||
*.ncrunchsolution
|
||||
*.*sdf
|
||||
*.ipch
|
||||
.vs/
|
||||
.vscode/
|
||||
project.lock.json
|
||||
.build/
|
||||
.testPublish/
|
||||
launchSettings.json
|
||||
global.json
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<Project>
|
||||
<Import
|
||||
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"
|
||||
Condition=" '$(CI)' != 'true' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))' != '' " />
|
||||
|
||||
<Import Project="version.props" />
|
||||
<Import Project="build\dependencies.props" />
|
||||
<Import Project="build\sources.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Product>Microsoft ASP.NET Core</Product>
|
||||
<RepositoryUrl>https://github.com/aspnet/Session</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
|
||||
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Session
|
||||
================
|
||||
|
||||
AppVeyor: [](https://ci.appveyor.com/project/aspnetci/Session/branch/dev)
|
||||
|
||||
Travis: [](https://travis-ci.org/aspnet/Session)
|
||||
|
||||
Contains libraries for session state middleware for ASP.NET Core.
|
||||
|
||||
For ASP.NET 4.x session state, please go to https://github.com/aspnet/AspNetSessionState.
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26621.2
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E9D63F97-6078-42AD-BFD3-F956BF921BB5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
test\Directory.Build.props = test\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A189F10C-3A9C-4F81-83D0-32E5FE50DAD8}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\Directory.Build.props = src\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Session", "src\Microsoft.AspNetCore.Session\Microsoft.AspNetCore.Session.csproj", "{71802736-F640-4733-9671-02D267EDD76A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Session.Tests", "test\Microsoft.AspNetCore.Session.Tests\Microsoft.AspNetCore.Session.Tests.csproj", "{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{94E80ED2-9F27-40AC-A9EF-C707BDFAA3BE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionSample", "samples\SessionSample\SessionSample.csproj", "{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{3B45F658-5BF1-4E07-BE9C-6F5110AC2277}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.appveyor.yml = .appveyor.yml
|
||||
.gitattributes = .gitattributes
|
||||
.gitignore = .gitignore
|
||||
.travis.yml = .travis.yml
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
NuGet.config = NuGet.config
|
||||
NuGetPackageVerifier.json = NuGetPackageVerifier.json
|
||||
README.md = README.md
|
||||
version.xml = version.xml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{4F21221F-2813-41B7-AAFC-E03FD52971CC}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
build\common.props = build\common.props
|
||||
build\dependencies.props = build\dependencies.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{71802736-F640-4733-9671-02D267EDD76A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{71802736-F640-4733-9671-02D267EDD76A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{71802736-F640-4733-9671-02D267EDD76A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{71802736-F640-4733-9671-02D267EDD76A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{71802736-F640-4733-9671-02D267EDD76A} = {A189F10C-3A9C-4F81-83D0-32E5FE50DAD8}
|
||||
{8C131A0A-BC1A-4CF3-8B77-8813FBFE5639} = {E9D63F97-6078-42AD-BFD3-F956BF921BB5}
|
||||
{FE0B9969-3BDE-4A7D-BE1B-47EAE8DBF365} = {94E80ED2-9F27-40AC-A9EF-C707BDFAA3BE}
|
||||
{4F21221F-2813-41B7-AAFC-E03FD52971CC} = {3B45F658-5BF1-4E07-BE9C-6F5110AC2277}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6AE224B9-B604-4E47-9617-9D114DAE9BE5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
|
|
@ -0,0 +1,36 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These package versions may be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Auto">
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.3-rtm-15802</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This may import a generated file which may override the variables above. -->
|
||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
||||
|
||||
<!-- These are package versions that should not be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Pinned">
|
||||
<MicrosoftAspNetCoreDataProtectionPackageVersion>2.1.1</MicrosoftAspNetCoreDataProtectionPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.1</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.2</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.1</MicrosoftAspNetCoreTestHostPackageVersion>
|
||||
<MicrosoftExtensionsCachingAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsCachingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||
<MicrosoftExtensionsCachingRedisPackageVersion>2.1.1</MicrosoftExtensionsCachingRedisPackageVersion>
|
||||
<MicrosoftExtensionsCachingSqlServerPackageVersion>2.1.1</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||
<LineupPackageVersion>2.1.0-rc1-*</LineupPackageVersion>
|
||||
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project>
|
||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||
|
||||
<PropertyGroup Label="RestoreSources">
|
||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||
$(RestoreSources);
|
||||
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
||||
</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
|
||||
$(RestoreSources);
|
||||
https://api.nuget.org/v3/index.json;
|
||||
</RestoreSources>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:2481/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"SessionSample": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Session\Microsoft.AspNetCore.Session.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="$(MicrosoftExtensionsCachingRedisPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// 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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SessionSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Adds a default in-memory implementation of IDistributedCache
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
// Uncomment the following line to use the Microsoft SQL Server implementation of IDistributedCache.
|
||||
// Note that this would require setting up the session state database.
|
||||
//services.AddSqlServerCache(o =>
|
||||
//{
|
||||
// o.ConnectionString = "Server=.;Database=ASPNET5SessionState;Trusted_Connection=True;";
|
||||
// o.SchemaName = "dbo";
|
||||
// o.TableName = "Sessions";
|
||||
//});
|
||||
|
||||
// Uncomment the following line to use the Redis implementation of IDistributedCache.
|
||||
// This will override any previously registered IDistributedCache service.
|
||||
//services.AddDistributedRedisCache(o =>
|
||||
//{
|
||||
// o.Configuration = "localhost";
|
||||
// o.InstanceName = "SampleInstance";
|
||||
//});
|
||||
|
||||
services.AddSession(o =>
|
||||
{
|
||||
o.IdleTimeout = TimeSpan.FromSeconds(10);
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseSession();
|
||||
|
||||
app.Map("/session", subApp =>
|
||||
{
|
||||
subApp.Run(async context =>
|
||||
{
|
||||
int visits = 0;
|
||||
visits = context.Session.GetInt32("visits") ?? 0;
|
||||
context.Session.SetInt32("visits", ++visits);
|
||||
await context.Response.WriteAsync("Counting: You have visited our page this many times: " + visits);
|
||||
});
|
||||
});
|
||||
|
||||
app.Run(async context =>
|
||||
{
|
||||
int visits = 0;
|
||||
visits = context.Session.GetInt32("visits") ?? 0;
|
||||
await context.Response.WriteAsync("<html><body>");
|
||||
if (visits == 0)
|
||||
{
|
||||
await context.Response.WriteAsync("Your session has not been established.<br>");
|
||||
await context.Response.WriteAsync(DateTime.Now + "<br>");
|
||||
await context.Response.WriteAsync("<a href=\"/session\">Establish session</a>.<br>");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Session.SetInt32("visits", ++visits);
|
||||
await context.Response.WriteAsync("Your session was located, you've visited the site this many times: " + visits);
|
||||
}
|
||||
await context.Response.WriteAsync("</body></html>");
|
||||
});
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.ConfigureLogging(factory => factory.AddConsole())
|
||||
.UseKestrel()
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
internal static class CookieProtection
|
||||
{
|
||||
internal static string Protect(IDataProtector protector, string data)
|
||||
{
|
||||
if (protector == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(protector));
|
||||
}
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var userData = Encoding.UTF8.GetBytes(data);
|
||||
|
||||
var protectedData = protector.Protect(userData);
|
||||
return Convert.ToBase64String(protectedData).TrimEnd('=');
|
||||
}
|
||||
|
||||
internal static string Unprotect(IDataProtector protector, string protectedText, ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(protectedText))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var protectedData = Convert.FromBase64String(Pad(protectedText));
|
||||
if (protectedData == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var userData = protector.Unprotect(protectedData);
|
||||
if (userData == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(userData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the exception, but do not leak other information
|
||||
logger.ErrorUnprotectingSessionCookie(ex);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Pad(string text)
|
||||
{
|
||||
var padding = 3 - ((text.Length + 3) % 4);
|
||||
if (padding == 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
return text + new string('=', padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,425 @@
|
|||
// 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.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
public class DistributedSession : ISession
|
||||
{
|
||||
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
||||
private const int IdByteCount = 16;
|
||||
|
||||
private const byte SerializationRevision = 2;
|
||||
private const int KeyLengthLimit = ushort.MaxValue;
|
||||
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly string _sessionKey;
|
||||
private readonly TimeSpan _idleTimeout;
|
||||
private readonly TimeSpan _ioTimeout;
|
||||
private readonly Func<bool> _tryEstablishSession;
|
||||
private readonly ILogger _logger;
|
||||
private IDictionary<EncodedKey, byte[]> _store;
|
||||
private bool _isModified;
|
||||
private bool _loaded;
|
||||
private bool _isAvailable;
|
||||
private bool _isNewSessionKey;
|
||||
private string _sessionId;
|
||||
private byte[] _sessionIdBytes;
|
||||
|
||||
public DistributedSession(
|
||||
IDistributedCache cache,
|
||||
string sessionKey,
|
||||
TimeSpan idleTimeout,
|
||||
TimeSpan ioTimeout,
|
||||
Func<bool> tryEstablishSession,
|
||||
ILoggerFactory loggerFactory,
|
||||
bool isNewSessionKey)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(sessionKey))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionKey));
|
||||
}
|
||||
|
||||
if (tryEstablishSession == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tryEstablishSession));
|
||||
}
|
||||
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_cache = cache;
|
||||
_sessionKey = sessionKey;
|
||||
_idleTimeout = idleTimeout;
|
||||
_ioTimeout = ioTimeout;
|
||||
_tryEstablishSession = tryEstablishSession;
|
||||
_store = new Dictionary<EncodedKey, byte[]>();
|
||||
_logger = loggerFactory.CreateLogger<DistributedSession>();
|
||||
_isNewSessionKey = isNewSessionKey;
|
||||
}
|
||||
|
||||
public bool IsAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
Load();
|
||||
return _isAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
Load();
|
||||
if (_sessionId == null)
|
||||
{
|
||||
_sessionId = new Guid(IdBytes).ToString();
|
||||
}
|
||||
return _sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] IdBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsAvailable && _sessionIdBytes == null)
|
||||
{
|
||||
_sessionIdBytes = new byte[IdByteCount];
|
||||
CryptoRandom.GetBytes(_sessionIdBytes);
|
||||
}
|
||||
return _sessionIdBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
Load();
|
||||
return _store.Keys.Select(key => key.KeyString);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out byte[] value)
|
||||
{
|
||||
Load();
|
||||
return _store.TryGetValue(new EncodedKey(key), out value);
|
||||
}
|
||||
|
||||
public void Set(string key, byte[] value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (IsAvailable)
|
||||
{
|
||||
var encodedKey = new EncodedKey(key);
|
||||
if (encodedKey.KeyBytes.Length > KeyLengthLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(key),
|
||||
Resources.FormatException_KeyLengthIsExceeded(KeyLengthLimit));
|
||||
}
|
||||
|
||||
if (!_tryEstablishSession())
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Exception_InvalidSessionEstablishment);
|
||||
}
|
||||
_isModified = true;
|
||||
byte[] copy = new byte[value.Length];
|
||||
Buffer.BlockCopy(src: value, srcOffset: 0, dst: copy, dstOffset: 0, count: value.Length);
|
||||
_store[encodedKey] = copy;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
Load();
|
||||
_isModified |= _store.Remove(new EncodedKey(key));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Load();
|
||||
_isModified |= _store.Count > 0;
|
||||
_store.Clear();
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
if (!_loaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _cache.Get(_sessionKey);
|
||||
if (data != null)
|
||||
{
|
||||
Deserialize(new MemoryStream(data));
|
||||
}
|
||||
else if (!_isNewSessionKey)
|
||||
{
|
||||
_logger.AccessingExpiredSession(_sessionKey);
|
||||
}
|
||||
_isAvailable = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.SessionCacheReadException(_sessionKey, exception);
|
||||
_isAvailable = false;
|
||||
_sessionId = string.Empty;
|
||||
_sessionIdBytes = null;
|
||||
_store = new NoOpSessionStore();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will throw if called directly and a failure occurs. The user is expected to handle the failures.
|
||||
public async Task LoadAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_loaded)
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(_ioTimeout))
|
||||
{
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken);
|
||||
try
|
||||
{
|
||||
cts.Token.ThrowIfCancellationRequested();
|
||||
var data = await _cache.GetAsync(_sessionKey, cts.Token);
|
||||
if (data != null)
|
||||
{
|
||||
Deserialize(new MemoryStream(data));
|
||||
}
|
||||
else if (!_isNewSessionKey)
|
||||
{
|
||||
_logger.AccessingExpiredSession(_sessionKey);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException oex)
|
||||
{
|
||||
if (timeout.Token.IsCancellationRequested)
|
||||
{
|
||||
_logger.SessionLoadingTimeout();
|
||||
throw new OperationCanceledException("Timed out loading the session.", oex, timeout.Token);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
_isAvailable = true;
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CommitAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(_ioTimeout))
|
||||
{
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken);
|
||||
if (_isModified)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
// This operation is only so we can log if the session already existed.
|
||||
// Log and ignore failures.
|
||||
try
|
||||
{
|
||||
cts.Token.ThrowIfCancellationRequested();
|
||||
var data = await _cache.GetAsync(_sessionKey, cts.Token);
|
||||
if (data == null)
|
||||
{
|
||||
_logger.SessionStarted(_sessionKey, Id);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.SessionCacheReadException(_sessionKey, exception);
|
||||
}
|
||||
}
|
||||
|
||||
var stream = new MemoryStream();
|
||||
Serialize(stream);
|
||||
|
||||
try
|
||||
{
|
||||
cts.Token.ThrowIfCancellationRequested();
|
||||
await _cache.SetAsync(
|
||||
_sessionKey,
|
||||
stream.ToArray(),
|
||||
new DistributedCacheEntryOptions().SetSlidingExpiration(_idleTimeout),
|
||||
cts.Token);
|
||||
_isModified = false;
|
||||
_logger.SessionStored(_sessionKey, Id, _store.Count);
|
||||
}
|
||||
catch (OperationCanceledException oex)
|
||||
{
|
||||
if (timeout.Token.IsCancellationRequested)
|
||||
{
|
||||
_logger.SessionCommitTimeout();
|
||||
throw new OperationCanceledException("Timed out committing the session.", oex, timeout.Token);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await _cache.RefreshAsync(_sessionKey, cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException oex)
|
||||
{
|
||||
if (timeout.Token.IsCancellationRequested)
|
||||
{
|
||||
_logger.SessionRefreshTimeout();
|
||||
throw new OperationCanceledException("Timed out refreshing the session.", oex, timeout.Token);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format:
|
||||
// Serialization revision: 1 byte, range 0-255
|
||||
// Entry count: 3 bytes, range 0-16,777,215
|
||||
// SessionId: IdByteCount bytes (16)
|
||||
// foreach entry:
|
||||
// key name byte length: 2 bytes, range 0-65,535
|
||||
// UTF-8 encoded key name byte[]
|
||||
// data byte length: 4 bytes, range 0-2,147,483,647
|
||||
// data byte[]
|
||||
private void Serialize(Stream output)
|
||||
{
|
||||
output.WriteByte(SerializationRevision);
|
||||
SerializeNumAs3Bytes(output, _store.Count);
|
||||
output.Write(IdBytes, 0, IdByteCount);
|
||||
|
||||
foreach (var entry in _store)
|
||||
{
|
||||
var keyBytes = entry.Key.KeyBytes;
|
||||
SerializeNumAs2Bytes(output, keyBytes.Length);
|
||||
output.Write(keyBytes, 0, keyBytes.Length);
|
||||
SerializeNumAs4Bytes(output, entry.Value.Length);
|
||||
output.Write(entry.Value, 0, entry.Value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private void Deserialize(Stream content)
|
||||
{
|
||||
if (content == null || content.ReadByte() != SerializationRevision)
|
||||
{
|
||||
// Replace the un-readable format.
|
||||
_isModified = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int expectedEntries = DeserializeNumFrom3Bytes(content);
|
||||
_sessionIdBytes = ReadBytes(content, IdByteCount);
|
||||
|
||||
for (int i = 0; i < expectedEntries; i++)
|
||||
{
|
||||
int keyLength = DeserializeNumFrom2Bytes(content);
|
||||
var key = new EncodedKey(ReadBytes(content, keyLength));
|
||||
int dataLength = DeserializeNumFrom4Bytes(content);
|
||||
_store[key] = ReadBytes(content, dataLength);
|
||||
}
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_sessionId = new Guid(_sessionIdBytes).ToString();
|
||||
_logger.SessionLoaded(_sessionKey, _sessionId, expectedEntries);
|
||||
}
|
||||
}
|
||||
|
||||
private void SerializeNumAs2Bytes(Stream output, int num)
|
||||
{
|
||||
if (num < 0 || ushort.MaxValue < num)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(num), Resources.Exception_InvalidToSerializeIn2Bytes);
|
||||
}
|
||||
output.WriteByte((byte)(num >> 8));
|
||||
output.WriteByte((byte)(0xFF & num));
|
||||
}
|
||||
|
||||
private int DeserializeNumFrom2Bytes(Stream content)
|
||||
{
|
||||
return content.ReadByte() << 8 | content.ReadByte();
|
||||
}
|
||||
|
||||
private void SerializeNumAs3Bytes(Stream output, int num)
|
||||
{
|
||||
if (num < 0 || 0xFFFFFF < num)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(num), Resources.Exception_InvalidToSerializeIn3Bytes);
|
||||
}
|
||||
output.WriteByte((byte)(num >> 16));
|
||||
output.WriteByte((byte)(0xFF & (num >> 8)));
|
||||
output.WriteByte((byte)(0xFF & num));
|
||||
}
|
||||
|
||||
private int DeserializeNumFrom3Bytes(Stream content)
|
||||
{
|
||||
return content.ReadByte() << 16 | content.ReadByte() << 8 | content.ReadByte();
|
||||
}
|
||||
|
||||
private void SerializeNumAs4Bytes(Stream output, int num)
|
||||
{
|
||||
if (num < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(num), Resources.Exception_NumberShouldNotBeNegative);
|
||||
}
|
||||
output.WriteByte((byte)(num >> 24));
|
||||
output.WriteByte((byte)(0xFF & (num >> 16)));
|
||||
output.WriteByte((byte)(0xFF & (num >> 8)));
|
||||
output.WriteByte((byte)(0xFF & num));
|
||||
}
|
||||
|
||||
private int DeserializeNumFrom4Bytes(Stream content)
|
||||
{
|
||||
return content.ReadByte() << 24 | content.ReadByte() << 16 | content.ReadByte() << 8 | content.ReadByte();
|
||||
}
|
||||
|
||||
private byte[] ReadBytes(Stream stream, int count)
|
||||
{
|
||||
var output = new byte[count];
|
||||
int total = 0;
|
||||
while (total < count)
|
||||
{
|
||||
var read = stream.Read(output, total, count - total);
|
||||
if (read == 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
total += read;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 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 Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
public class DistributedSessionStore : ISessionStore
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public DistributedSessionStore(IDistributedCache cache, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_cache = cache;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
public ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sessionKey))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionKey));
|
||||
}
|
||||
|
||||
if (tryEstablishSession == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tryEstablishSession));
|
||||
}
|
||||
|
||||
return new DistributedSession(_cache, sessionKey, idleTimeout, ioTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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.AspNetCore.Session
|
||||
{
|
||||
// Keys are stored in their utf-8 encoded state.
|
||||
// This saves us from de-serializing and re-serializing every key on every request.
|
||||
internal class EncodedKey
|
||||
{
|
||||
private string _keyString;
|
||||
private int? _hashCode;
|
||||
|
||||
internal EncodedKey(string key)
|
||||
{
|
||||
_keyString = key;
|
||||
KeyBytes = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
|
||||
public EncodedKey(byte[] key)
|
||||
{
|
||||
KeyBytes = key;
|
||||
}
|
||||
|
||||
internal string KeyString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_keyString == null)
|
||||
{
|
||||
_keyString = Encoding.UTF8.GetString(KeyBytes, 0, KeyBytes.Length);
|
||||
}
|
||||
return _keyString;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] KeyBytes { get; private set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var otherKey = obj as EncodedKey;
|
||||
if (otherKey == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (KeyBytes.Length != otherKey.KeyBytes.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_hashCode.HasValue && otherKey._hashCode.HasValue
|
||||
&& _hashCode.Value != otherKey._hashCode.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < KeyBytes.Length; i++)
|
||||
{
|
||||
if (KeyBytes[i] != otherKey.KeyBytes[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (!_hashCode.HasValue)
|
||||
{
|
||||
_hashCode = SipHash.GetHashCode(KeyBytes);
|
||||
}
|
||||
return _hashCode.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return KeyString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
public interface ISessionStore
|
||||
{
|
||||
ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
namespace Microsoft.Extensions.Logging
|
||||
{
|
||||
internal static class LoggingExtensions
|
||||
{
|
||||
private static Action<ILogger, Exception> _errorClosingTheSession;
|
||||
private static Action<ILogger, string, Exception> _accessingExpiredSession;
|
||||
private static Action<ILogger, string, string, Exception> _sessionStarted;
|
||||
private static Action<ILogger, string, string, int, Exception> _sessionLoaded;
|
||||
private static Action<ILogger, string, string, int, Exception> _sessionStored;
|
||||
private static Action<ILogger, string, Exception> _sessionCacheReadException;
|
||||
private static Action<ILogger, Exception> _errorUnprotectingCookie;
|
||||
private static Action<ILogger, Exception> _sessionLoadingTimeout;
|
||||
private static Action<ILogger, Exception> _sessionCommitTimeout;
|
||||
private static Action<ILogger, Exception> _sessionCommitCanceled;
|
||||
private static Action<ILogger, Exception> _sessionRefreshTimeout;
|
||||
private static Action<ILogger, Exception> _sessionRefreshCanceled;
|
||||
|
||||
static LoggingExtensions()
|
||||
{
|
||||
_errorClosingTheSession = LoggerMessage.Define(
|
||||
eventId: 1,
|
||||
logLevel: LogLevel.Error,
|
||||
formatString: "Error closing the session.");
|
||||
_accessingExpiredSession = LoggerMessage.Define<string>(
|
||||
eventId: 2,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Accessing expired session, Key:{sessionKey}");
|
||||
_sessionStarted = LoggerMessage.Define<string, string>(
|
||||
eventId: 3,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Session started; Key:{sessionKey}, Id:{sessionId}");
|
||||
_sessionLoaded = LoggerMessage.Define<string, string, int>(
|
||||
eventId: 4,
|
||||
logLevel: LogLevel.Debug,
|
||||
formatString: "Session loaded; Key:{sessionKey}, Id:{sessionId}, Count:{count}");
|
||||
_sessionStored = LoggerMessage.Define<string, string, int>(
|
||||
eventId: 5,
|
||||
logLevel: LogLevel.Debug,
|
||||
formatString: "Session stored; Key:{sessionKey}, Id:{sessionId}, Count:{count}");
|
||||
_sessionCacheReadException = LoggerMessage.Define<string>(
|
||||
eventId: 6,
|
||||
logLevel: LogLevel.Error,
|
||||
formatString: "Session cache read exception, Key:{sessionKey}");
|
||||
_errorUnprotectingCookie = LoggerMessage.Define(
|
||||
eventId: 7,
|
||||
logLevel: LogLevel.Warning,
|
||||
formatString: "Error unprotecting the session cookie.");
|
||||
_sessionLoadingTimeout = LoggerMessage.Define(
|
||||
eventId: 8,
|
||||
logLevel: LogLevel.Warning,
|
||||
formatString: "Loading the session timed out.");
|
||||
_sessionCommitTimeout = LoggerMessage.Define(
|
||||
eventId: 9,
|
||||
logLevel: LogLevel.Warning,
|
||||
formatString: "Committing the session timed out.");
|
||||
_sessionCommitCanceled = LoggerMessage.Define(
|
||||
eventId: 10,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Committing the session was canceled.");
|
||||
_sessionRefreshTimeout = LoggerMessage.Define(
|
||||
eventId: 11,
|
||||
logLevel: LogLevel.Warning,
|
||||
formatString: "Refreshing the session timed out.");
|
||||
_sessionRefreshCanceled = LoggerMessage.Define(
|
||||
eventId: 12,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Refreshing the session was canceled.");
|
||||
}
|
||||
|
||||
public static void ErrorClosingTheSession(this ILogger logger, Exception exception)
|
||||
{
|
||||
_errorClosingTheSession(logger, exception);
|
||||
}
|
||||
|
||||
public static void AccessingExpiredSession(this ILogger logger, string sessionKey)
|
||||
{
|
||||
_accessingExpiredSession(logger, sessionKey, null);
|
||||
}
|
||||
|
||||
public static void SessionStarted(this ILogger logger, string sessionKey, string sessionId)
|
||||
{
|
||||
_sessionStarted(logger, sessionKey, sessionId, null);
|
||||
}
|
||||
|
||||
public static void SessionLoaded(this ILogger logger, string sessionKey, string sessionId, int count)
|
||||
{
|
||||
_sessionLoaded(logger, sessionKey, sessionId, count, null);
|
||||
}
|
||||
|
||||
public static void SessionStored(this ILogger logger, string sessionKey, string sessionId, int count)
|
||||
{
|
||||
_sessionStored(logger, sessionKey, sessionId, count, null);
|
||||
}
|
||||
|
||||
public static void SessionCacheReadException(this ILogger logger, string sessionKey, Exception exception)
|
||||
{
|
||||
_sessionCacheReadException(logger, sessionKey, exception);
|
||||
}
|
||||
|
||||
public static void ErrorUnprotectingSessionCookie(this ILogger logger, Exception exception)
|
||||
{
|
||||
_errorUnprotectingCookie(logger, exception);
|
||||
}
|
||||
|
||||
public static void SessionLoadingTimeout(this ILogger logger)
|
||||
{
|
||||
_sessionLoadingTimeout(logger, null);
|
||||
}
|
||||
|
||||
public static void SessionCommitTimeout(this ILogger logger)
|
||||
{
|
||||
_sessionCommitTimeout(logger, null);
|
||||
}
|
||||
|
||||
public static void SessionCommitCanceled(this ILogger logger)
|
||||
{
|
||||
_sessionCommitCanceled(logger, null);
|
||||
}
|
||||
|
||||
public static void SessionRefreshTimeout(this ILogger logger)
|
||||
{
|
||||
_sessionRefreshTimeout(logger, null);
|
||||
}
|
||||
|
||||
public static void SessionRefreshCanceled(this ILogger logger)
|
||||
{
|
||||
_sessionRefreshCanceled(logger, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>ASP.NET Core session state middleware.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;session;sessionstate</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="$(MicrosoftAspNetCoreDataProtectionPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
internal class NoOpSessionStore : IDictionary<EncodedKey, byte[]>
|
||||
{
|
||||
public byte[] this[EncodedKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int Count { get; } = 0;
|
||||
|
||||
public bool IsReadOnly { get; } = false;
|
||||
|
||||
public ICollection<EncodedKey> Keys { get; } = new EncodedKey[0];
|
||||
|
||||
public ICollection<byte[]> Values { get; } = new byte[0][];
|
||||
|
||||
public void Add(KeyValuePair<EncodedKey, byte[]> item) { }
|
||||
|
||||
public void Add(EncodedKey key, byte[] value) { }
|
||||
|
||||
public void Clear() { }
|
||||
|
||||
public bool Contains(KeyValuePair<EncodedKey, byte[]> item) => false;
|
||||
|
||||
public bool ContainsKey(EncodedKey key) => false;
|
||||
|
||||
public void CopyTo(KeyValuePair<EncodedKey, byte[]>[] array, int arrayIndex) { }
|
||||
|
||||
public IEnumerator<KeyValuePair<EncodedKey, byte[]>> GetEnumerator() => Enumerable.Empty<KeyValuePair<EncodedKey, byte[]>>().GetEnumerator();
|
||||
|
||||
public bool Remove(KeyValuePair<EncodedKey, byte[]> item) => false;
|
||||
|
||||
public bool Remove(EncodedKey key) => false;
|
||||
|
||||
public bool TryGetValue(EncodedKey key, out byte[] value)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
126
src/Session/src/Microsoft.AspNetCore.Session/Properties/Resources.Designer.cs
generated
Normal file
126
src/Session/src/Microsoft.AspNetCore.Session/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNetCore.Session.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The key cannot be longer than '{0}' when encoded with UTF-8.
|
||||
/// </summary>
|
||||
internal static string Exception_KeyLengthIsExceeded
|
||||
{
|
||||
get { return GetString("Exception_KeyLengthIsExceeded"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key cannot be longer than '{0}' when encoded with UTF-8.
|
||||
/// </summary>
|
||||
internal static string FormatException_KeyLengthIsExceeded(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Exception_KeyLengthIsExceeded"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The session cannot be established after the response has started.
|
||||
/// </summary>
|
||||
internal static string Exception_InvalidSessionEstablishment
|
||||
{
|
||||
get { return GetString("Exception_InvalidSessionEstablishment"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The session cannot be established after the response has started.
|
||||
/// </summary>
|
||||
internal static string FormatException_InvalidSessionEstablishment()
|
||||
{
|
||||
return GetString("Exception_InvalidSessionEstablishment");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be serialized in two bytes.
|
||||
/// </summary>
|
||||
internal static string Exception_InvalidToSerializeIn2Bytes
|
||||
{
|
||||
get { return GetString("Exception_InvalidToSerializeIn2Bytes"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be serialized in two bytes.
|
||||
/// </summary>
|
||||
internal static string FormatException_InvalidToSerializeIn2Bytes()
|
||||
{
|
||||
return GetString("Exception_InvalidToSerializeIn2Bytes");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be serialized in three bytes.
|
||||
/// </summary>
|
||||
internal static string Exception_InvalidToSerializeIn3Bytes
|
||||
{
|
||||
get { return GetString("Exception_InvalidToSerializeIn3Bytes"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be serialized in three bytes.
|
||||
/// </summary>
|
||||
internal static string FormatException_InvalidToSerializeIn3Bytes()
|
||||
{
|
||||
return GetString("Exception_InvalidToSerializeIn3Bytes");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be negative.
|
||||
/// </summary>
|
||||
internal static string Exception_NumberShouldNotBeNegative
|
||||
{
|
||||
get { return GetString("Exception_NumberShouldNotBeNegative"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value cannot be negative.
|
||||
/// </summary>
|
||||
internal static string FormatException_NumberShouldNotBeNegative()
|
||||
{
|
||||
return GetString("Exception_NumberShouldNotBeNegative");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Argument cannot be null or empty string.
|
||||
/// </summary>
|
||||
internal static string ArgumentCannotBeNullOrEmpty
|
||||
{
|
||||
get { return GetString("ArgumentCannotBeNullOrEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Argument cannot be null or empty string.
|
||||
/// </summary>
|
||||
internal static string FormatArgumentCannotBeNullOrEmpty()
|
||||
{
|
||||
return GetString("ArgumentCannotBeNullOrEmpty");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Exception_KeyLengthIsExceeded" xml:space="preserve">
|
||||
<value>The key cannot be longer than '{0}' when encoded with UTF-8.</value>
|
||||
</data>
|
||||
<data name="Exception_InvalidSessionEstablishment" xml:space="preserve">
|
||||
<value>The session cannot be established after the response has started.</value>
|
||||
</data>
|
||||
<data name="Exception_InvalidToSerializeIn2Bytes" xml:space="preserve">
|
||||
<value>The value cannot be serialized in two bytes.</value>
|
||||
</data>
|
||||
<data name="Exception_InvalidToSerializeIn3Bytes" xml:space="preserve">
|
||||
<value>The value cannot be serialized in three bytes.</value>
|
||||
</data>
|
||||
<data name="Exception_NumberShouldNotBeNegative" xml:space="preserve">
|
||||
<value>The value cannot be negative.</value>
|
||||
</data>
|
||||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Argument cannot be null or empty string.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Session
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents defaults for the Session.
|
||||
/// </summary>
|
||||
public static class SessionDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent the default cookie name, which is ".AspNetCore.Session".
|
||||
/// </summary>
|
||||
public static readonly string CookieName = ".AspNetCore.Session";
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default path used to create the cookie, which is "/".
|
||||
/// </summary>
|
||||
public static readonly string CookiePath = "/";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
public class SessionFeature : ISessionFeature
|
||||
{
|
||||
public ISession Session { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
// 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.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Session
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables the session state for the application.
|
||||
/// </summary>
|
||||
public class SessionMiddleware
|
||||
{
|
||||
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
||||
private const int SessionKeyLength = 36; // "382c74c3-721d-4f34-80e5-57657b6cbc27"
|
||||
private static readonly Func<bool> ReturnTrue = () => true;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly SessionOptions _options;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISessionStore _sessionStore;
|
||||
private readonly IDataProtector _dataProtector;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SessionMiddleware"/>.
|
||||
/// </summary>
|
||||
/// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> representing the factory that used to create logger instances.</param>
|
||||
/// <param name="dataProtectionProvider">The <see cref="IDataProtectionProvider"/> used to protect and verify the cookie.</param>
|
||||
/// <param name="sessionStore">The <see cref="ISessionStore"/> representing the session store.</param>
|
||||
/// <param name="options">The session configuration options.</param>
|
||||
public SessionMiddleware(
|
||||
RequestDelegate next,
|
||||
ILoggerFactory loggerFactory,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ISessionStore sessionStore,
|
||||
IOptions<SessionOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (dataProtectionProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dataProtectionProvider));
|
||||
}
|
||||
|
||||
if (sessionStore == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sessionStore));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_logger = loggerFactory.CreateLogger<SessionMiddleware>();
|
||||
_dataProtector = dataProtectionProvider.CreateProtector(nameof(SessionMiddleware));
|
||||
_options = options.Value;
|
||||
_sessionStore = sessionStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the logic of the middleware.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="HttpContext"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> that completes when the middleware has completed processing.</returns>
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var isNewSessionKey = false;
|
||||
Func<bool> tryEstablishSession = ReturnTrue;
|
||||
var cookieValue = context.Request.Cookies[_options.Cookie.Name];
|
||||
var sessionKey = CookieProtection.Unprotect(_dataProtector, cookieValue, _logger);
|
||||
if (string.IsNullOrWhiteSpace(sessionKey) || sessionKey.Length != SessionKeyLength)
|
||||
{
|
||||
// No valid cookie, new session.
|
||||
var guidBytes = new byte[16];
|
||||
CryptoRandom.GetBytes(guidBytes);
|
||||
sessionKey = new Guid(guidBytes).ToString();
|
||||
cookieValue = CookieProtection.Protect(_dataProtector, sessionKey);
|
||||
var establisher = new SessionEstablisher(context, cookieValue, _options);
|
||||
tryEstablishSession = establisher.TryEstablishSession;
|
||||
isNewSessionKey = true;
|
||||
}
|
||||
|
||||
var feature = new SessionFeature();
|
||||
feature.Session = _sessionStore.Create(sessionKey, _options.IdleTimeout, _options.IOTimeout, tryEstablishSession, isNewSessionKey);
|
||||
context.Features.Set<ISessionFeature>(feature);
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Features.Set<ISessionFeature>(null);
|
||||
|
||||
if (feature.Session != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await feature.Session.CommitAsync(context.RequestAborted);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.SessionCommitCanceled();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorClosingTheSession(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SessionEstablisher
|
||||
{
|
||||
private readonly HttpContext _context;
|
||||
private readonly string _cookieValue;
|
||||
private readonly SessionOptions _options;
|
||||
private bool _shouldEstablishSession;
|
||||
|
||||
public SessionEstablisher(HttpContext context, string cookieValue, SessionOptions options)
|
||||
{
|
||||
_context = context;
|
||||
_cookieValue = cookieValue;
|
||||
_options = options;
|
||||
context.Response.OnStarting(OnStartingCallback, state: this);
|
||||
}
|
||||
|
||||
private static Task OnStartingCallback(object state)
|
||||
{
|
||||
var establisher = (SessionEstablisher)state;
|
||||
if (establisher._shouldEstablishSession)
|
||||
{
|
||||
establisher.SetCookie();
|
||||
}
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
private void SetCookie()
|
||||
{
|
||||
var cookieOptions = _options.Cookie.Build(_context);
|
||||
|
||||
_context.Response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions);
|
||||
|
||||
_context.Response.Headers["Cache-Control"] = "no-cache";
|
||||
_context.Response.Headers["Pragma"] = "no-cache";
|
||||
_context.Response.Headers["Expires"] = "-1";
|
||||
}
|
||||
|
||||
// Returns true if the session has already been established, or if it still can be because the response has not been sent.
|
||||
internal bool TryEstablishSession()
|
||||
{
|
||||
return (_shouldEstablishSession |= !_context.Response.HasStarted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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 Microsoft.AspNetCore.Session;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for adding the <see cref="SessionMiddleware"/> to an application.
|
||||
/// </summary>
|
||||
public static class SessionMiddlewareExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="SessionMiddleware"/> to automatically enable session state for the application.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseSession(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<SessionMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="SessionMiddleware"/> to automatically enable session state for the application.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="options">The <see cref="SessionOptions"/>.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseSession(this IApplicationBuilder app, SessionOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<SessionMiddleware>(Options.Create(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Session;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the session state options for the application.
|
||||
/// </summary>
|
||||
public class SessionOptions
|
||||
{
|
||||
private CookieBuilder _cookieBuilder = new SessionCookieBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// Determines the settings used to create the cookie.
|
||||
/// <para>
|
||||
/// <see cref="CookieBuilder.Name"/> defaults to <see cref="SessionDefaults.CookieName"/>.
|
||||
/// <see cref="CookieBuilder.Path"/> defaults to <see cref="SessionDefaults.CookiePath"/>.
|
||||
/// <see cref="CookieBuilder.SameSite"/> defaults to <see cref="SameSiteMode.Lax"/>.
|
||||
/// <see cref="CookieBuilder.HttpOnly"/> defaults to <c>true</c>
|
||||
/// <see cref="CookieBuilder.IsEssential"/> defaults to <c>false</c>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public CookieBuilder Cookie
|
||||
{
|
||||
get => _cookieBuilder;
|
||||
set => _cookieBuilder = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The IdleTimeout indicates how long the session can be idle before its contents are abandoned. Each session access
|
||||
/// resets the timeout. Note this only applies to the content of the session, not the cookie.
|
||||
/// </summary>
|
||||
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(20);
|
||||
|
||||
/// <summary>
|
||||
/// The maximim amount of time allowed to load a session from the store or to commit it back to the store.
|
||||
/// Note this may only apply to asynchronous operations. This timeout can be disabled using <see cref="Timeout.InfiniteTimeSpan"/>.
|
||||
/// </summary>
|
||||
public TimeSpan IOTimeout { get; set; } = TimeSpan.FromMinutes(1);
|
||||
|
||||
#region Obsolete API
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This property is obsolete and will be removed in a future version. The recommended alternative is <seealso cref="CookieBuilder.Name"/> on <see cref="Cookie"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Determines the cookie name used to persist the session ID.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Name) + ".")]
|
||||
public string CookieName { get => Cookie.Name; set => Cookie.Name = value; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This property is obsolete and will be removed in a future version. The recommended alternative is <seealso cref="CookieBuilder.Domain"/> on <see cref="Cookie"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Determines the domain used to create the cookie. Is not provided by default.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Domain) + ".")]
|
||||
public string CookieDomain { get => Cookie.Domain; set => Cookie.Domain = value; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This property is obsolete and will be removed in a future version. The recommended alternative is <seealso cref="CookieBuilder.Path"/> on <see cref="Cookie"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Determines the path used to create the cookie.
|
||||
/// Defaults to <see cref="SessionDefaults.CookiePath"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Path) + ".")]
|
||||
public string CookiePath { get => Cookie.Path; set => Cookie.Path = value; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This property is obsolete and will be removed in a future version. The recommended alternative is <seealso cref="CookieBuilder.HttpOnly"/> on <see cref="Cookie"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Determines if the browser should allow the cookie to be accessed by client-side JavaScript. The
|
||||
/// default is true, which means the cookie will only be passed to HTTP requests and is not made available
|
||||
/// to script on the page.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.HttpOnly) + ".")]
|
||||
public bool CookieHttpOnly { get => Cookie.HttpOnly; set => Cookie.HttpOnly = value; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This property is obsolete and will be removed in a future version. The recommended alternative is <seealso cref="CookieBuilder.SecurePolicy"/> on <see cref="Cookie"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Determines if the cookie should only be transmitted on HTTPS requests.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.SecurePolicy) + ".")]
|
||||
public CookieSecurePolicy CookieSecure { get => Cookie.SecurePolicy; set => Cookie.SecurePolicy = value; }
|
||||
#endregion
|
||||
|
||||
private class SessionCookieBuilder : CookieBuilder
|
||||
{
|
||||
public SessionCookieBuilder()
|
||||
{
|
||||
Name = SessionDefaults.CookieName;
|
||||
Path = SessionDefaults.CookiePath;
|
||||
SecurePolicy = CookieSecurePolicy.None;
|
||||
SameSite = SameSiteMode.Lax;
|
||||
HttpOnly = true;
|
||||
// Session is considered non-essential as it's designed for ephemeral data.
|
||||
IsEssential = false;
|
||||
}
|
||||
|
||||
public override TimeSpan? Expiration
|
||||
{
|
||||
get => null;
|
||||
set => throw new InvalidOperationException(nameof(Expiration) + " cannot be set for the cookie defined by " + nameof(SessionOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Session;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for adding session services to the DI container.
|
||||
/// </summary>
|
||||
public static class SessionServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds services required for application session state.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
|
||||
public static IServiceCollection AddSession(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.TryAddTransient<ISessionStore, DistributedSessionStore>();
|
||||
services.AddDataProtection();
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds services required for application session state.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
|
||||
/// <param name="configure">The session options to configure the middleware with.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
|
||||
public static IServiceCollection AddSession(this IServiceCollection services, Action<SessionOptions> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
services.Configure(configure);
|
||||
services.AddSession();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
// 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.Session
|
||||
{
|
||||
// A byte[] equality comparer based on the SipHash-2-4 algorithm. Key differences:
|
||||
// (a) we output 32-bit hashes instead of 64-bit hashes, and
|
||||
// (b) we don't care about endianness since hashes are used only in hash tables
|
||||
// and aren't returned to user code.
|
||||
//
|
||||
// Derived from the implementation in SignalR and modified to address byte[] instead of string. This derived version is not for cryptographic use, just hash codes.
|
||||
// https://github.com/aspnet/SignalR-Server/blob/75f74169c81a51780f195d06b798302b2d76dbde/src/Microsoft.AspNetCore.SignalR.Server/Infrastructure/SipHashBasedStringEqualityComparer.cs
|
||||
// Derivative work of https://github.com/tanglebones/ch-siphash.
|
||||
internal static class SipHash
|
||||
{
|
||||
internal static int GetHashCode(byte[] bytes)
|
||||
{
|
||||
// Assume SipHash-2-4 is a strong PRF, therefore truncation to 32 bits is acceptable.
|
||||
return (int)SipHash_2_4_UlongCast_ForcedInline(bytes);
|
||||
}
|
||||
|
||||
private static ulong SipHash_2_4_UlongCast_ForcedInline(byte[] bytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ulong v0 = 0x736f6d6570736575;
|
||||
ulong v1 = 0x646f72616e646f6d;
|
||||
ulong v2 = 0x6c7967656e657261;
|
||||
ulong v3 = 0x7465646279746573;
|
||||
|
||||
uint inlen = (uint)bytes.Length;
|
||||
fixed (byte* finb = bytes)
|
||||
{
|
||||
var b = ((ulong)inlen) << 56;
|
||||
|
||||
if (inlen > 0)
|
||||
{
|
||||
var inb = finb;
|
||||
var left = inlen & 7;
|
||||
var end = inb + inlen - left;
|
||||
var linb = (ulong*)finb;
|
||||
var lend = (ulong*)end;
|
||||
for (; linb < lend; ++linb)
|
||||
{
|
||||
v3 ^= *linb;
|
||||
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
|
||||
v0 ^= *linb;
|
||||
}
|
||||
for (var i = 0; i < left; ++i)
|
||||
{
|
||||
b |= ((ulong)end[i]) << (8 * i);
|
||||
}
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 ^= b;
|
||||
v2 ^= 0xff;
|
||||
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
v0 += v1;
|
||||
v1 = (v1 << 13) | (v1 >> (64 - 13));
|
||||
v1 ^= v0;
|
||||
v0 = (v0 << 32) | (v0 >> (64 - 32));
|
||||
|
||||
v2 += v3;
|
||||
v3 = (v3 << 16) | (v3 >> (64 - 16));
|
||||
v3 ^= v2;
|
||||
|
||||
v0 += v3;
|
||||
v3 = (v3 << 21) | (v3 >> (64 - 21));
|
||||
v3 ^= v0;
|
||||
|
||||
v2 += v1;
|
||||
v1 = (v1 << 17) | (v1 >> (64 - 17));
|
||||
v1 ^= v2;
|
||||
v2 = (v2 << 32) | (v2 >> (64 - 32));
|
||||
}
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,687 @@
|
|||
{
|
||||
"AssemblyIdentity": "Microsoft.AspNetCore.Session, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||
"Types": [
|
||||
{
|
||||
"Name": "Microsoft.Extensions.DependencyInjection.SessionServiceCollectionExtensions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"Abstract": true,
|
||||
"Static": true,
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "AddSession",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "services",
|
||||
"Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "AddSession",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "services",
|
||||
"Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
|
||||
},
|
||||
{
|
||||
"Name": "configure",
|
||||
"Type": "System.Action<Microsoft.AspNetCore.Builder.SessionOptions>"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Builder.SessionMiddlewareExtensions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"Abstract": true,
|
||||
"Static": true,
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseSession",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "app",
|
||||
"Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseSession",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "app",
|
||||
"Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
|
||||
},
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Builder.SessionOptions"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Builder.SessionOptions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Cookie",
|
||||
"Parameters": [],
|
||||
"ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_Cookie",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "Microsoft.AspNetCore.Http.CookieBuilder"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_IdleTimeout",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.TimeSpan",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_IdleTimeout",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.TimeSpan"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_IOTimeout",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.TimeSpan",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_IOTimeout",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.TimeSpan"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CookieName",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CookieName",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CookieDomain",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CookieDomain",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CookiePath",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CookiePath",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CookieHttpOnly",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CookieHttpOnly",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CookieSecure",
|
||||
"Parameters": [],
|
||||
"ReturnType": "Microsoft.AspNetCore.Http.CookieSecurePolicy",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CookieSecure",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "Microsoft.AspNetCore.Http.CookieSecurePolicy"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.DistributedSession",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.AspNetCore.Http.ISession"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_IsAvailable",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Id",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Keys",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Collections.Generic.IEnumerable<System.String>",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "TryGetValue",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "key",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Byte[]",
|
||||
"Direction": "Out"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Set",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "key",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Byte[]"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Remove",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "key",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Clear",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Void",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "LoadAsync",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "cancellationToken",
|
||||
"Type": "System.Threading.CancellationToken",
|
||||
"DefaultValue": "default(System.Threading.CancellationToken)"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Threading.Tasks.Task",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "CommitAsync",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "cancellationToken",
|
||||
"Type": "System.Threading.CancellationToken",
|
||||
"DefaultValue": "default(System.Threading.CancellationToken)"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Threading.Tasks.Task",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "cache",
|
||||
"Type": "Microsoft.Extensions.Caching.Distributed.IDistributedCache"
|
||||
},
|
||||
{
|
||||
"Name": "sessionKey",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "idleTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "ioTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "tryEstablishSession",
|
||||
"Type": "System.Func<System.Boolean>"
|
||||
},
|
||||
{
|
||||
"Name": "loggerFactory",
|
||||
"Type": "Microsoft.Extensions.Logging.ILoggerFactory"
|
||||
},
|
||||
{
|
||||
"Name": "isNewSessionKey",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.DistributedSessionStore",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.AspNetCore.Session.ISessionStore"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Create",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "sessionKey",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "idleTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "ioTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "tryEstablishSession",
|
||||
"Type": "System.Func<System.Boolean>"
|
||||
},
|
||||
{
|
||||
"Name": "isNewSessionKey",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Session.ISessionStore",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "cache",
|
||||
"Type": "Microsoft.Extensions.Caching.Distributed.IDistributedCache"
|
||||
},
|
||||
{
|
||||
"Name": "loggerFactory",
|
||||
"Type": "Microsoft.Extensions.Logging.ILoggerFactory"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.ISessionStore",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Interface",
|
||||
"Abstract": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Create",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "sessionKey",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "idleTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "ioTimeout",
|
||||
"Type": "System.TimeSpan"
|
||||
},
|
||||
{
|
||||
"Name": "tryEstablishSession",
|
||||
"Type": "System.Func<System.Boolean>"
|
||||
},
|
||||
{
|
||||
"Name": "isNewSessionKey",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Http.ISession",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.SessionDefaults",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"Abstract": true,
|
||||
"Static": true,
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "CookieName",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Static": true,
|
||||
"ReadOnly": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "CookiePath",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.String",
|
||||
"Static": true,
|
||||
"ReadOnly": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.SessionFeature",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.AspNetCore.Http.Features.ISessionFeature"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_Session",
|
||||
"Parameters": [],
|
||||
"ReturnType": "Microsoft.AspNetCore.Http.ISession",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.Features.ISessionFeature",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_Session",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "Microsoft.AspNetCore.Http.ISession"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Http.Features.ISessionFeature",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Session.SessionMiddleware",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "Invoke",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "context",
|
||||
"Type": "Microsoft.AspNetCore.Http.HttpContext"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Threading.Tasks.Task",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "next",
|
||||
"Type": "Microsoft.AspNetCore.Http.RequestDelegate"
|
||||
},
|
||||
{
|
||||
"Name": "loggerFactory",
|
||||
"Type": "Microsoft.Extensions.Logging.ILoggerFactory"
|
||||
},
|
||||
{
|
||||
"Name": "dataProtectionProvider",
|
||||
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider"
|
||||
},
|
||||
{
|
||||
"Name": "sessionStore",
|
||||
"Type": "Microsoft.AspNetCore.Session.ISessionStore"
|
||||
},
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.SessionOptions>"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<DeveloperBuildTestTfms>netcoreapp2.1</DeveloperBuildTestTfms>
|
||||
<StandardTestTfms>$(DeveloperBuildTestTfms)</StandardTestTfms>
|
||||
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' ">netcoreapp2.1;netcoreapp2.0</StandardTestTfms>
|
||||
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(StandardTestTfms);net461</StandardTestTfms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Session\Microsoft.AspNetCore.Session.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,12 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>2.1.1</VersionPrefix>
|
||||
<VersionSuffix>rtm</VersionSuffix>
|
||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
|
||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
|
||||
<BuildNumber Condition="'$(BuildNumber)' == ''">t000</BuildNumber>
|
||||
<FeatureBranchVersionPrefix Condition="'$(FeatureBranchVersionPrefix)' == ''">a-</FeatureBranchVersionPrefix>
|
||||
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(FeatureBranchVersionSuffix)' != ''">$(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))</VersionSuffix>
|
||||
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
[Oo]bj/
|
||||
[Bb]in/
|
||||
TestResults/
|
||||
.nuget/
|
||||
_ReSharper.*/
|
||||
packages/
|
||||
artifacts/
|
||||
PublishProfiles/
|
||||
*.user
|
||||
*.suo
|
||||
*.cache
|
||||
*.docstates
|
||||
_ReSharper.*
|
||||
nuget.exe
|
||||
*net45.csproj
|
||||
*net451.csproj
|
||||
*k10.csproj
|
||||
*.psess
|
||||
*.vsp
|
||||
*.pidb
|
||||
*.userprefs
|
||||
*DS_Store
|
||||
*.ncrunchsolution
|
||||
*.*sdf
|
||||
*.ipch
|
||||
*.sln.ide
|
||||
project.lock.json
|
||||
.build/
|
||||
.testPublish/
|
||||
/.vs/
|
||||
global.json
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<Project>
|
||||
<Import
|
||||
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"
|
||||
Condition=" '$(CI)' != 'true' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))' != '' " />
|
||||
|
||||
<Import Project="version.props" />
|
||||
<Import Project="build\dependencies.props" />
|
||||
<Import Project="build\sources.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Product>Microsoft ASP.NET Core</Product>
|
||||
<RepositoryUrl>https://github.com/aspnet/StaticFiles</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
|
||||
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"adx-nonshipping": {
|
||||
"rules": [],
|
||||
"packages": {
|
||||
"Microsoft.AspNetCore.RangeHelper.Sources": {}
|
||||
}
|
||||
},
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
StaticFiles
|
||||
===========
|
||||
|
||||
AppVeyor: [](https://ci.appveyor.com/project/aspnetci/StaticFiles/branch/dev)
|
||||
|
||||
Travis: [](https://travis-ci.org/aspnet/StaticFiles)
|
||||
|
||||
This repo contains middleware for handling requests for file system resources including files and directories.
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.9
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{40EE0889-960E-41B4-A3D3-9CE963EB0797}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\Directory.Build.props = src\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8B21A3A9-9CA6-4857-A6E0-1A3203404B60}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.StaticFiles", "src\Microsoft.AspNetCore.StaticFiles\Microsoft.AspNetCore.StaticFiles.csproj", "{8D7BC5A4-F19C-4184-8338-A6B42997218C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StaticFileSample", "samples\StaticFileSample\StaticFileSample.csproj", "{092141D9-305A-4FC5-AE74-CB23982CA8D4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EF02AFE8-7C15-4DDB-8B2C-58A676112A98}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
test\Directory.Build.props = test\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.StaticFiles.Tests", "test\Microsoft.AspNetCore.StaticFiles.Tests\Microsoft.AspNetCore.StaticFiles.Tests.csproj", "{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.StaticFiles.FunctionalTests", "test\Microsoft.AspNetCore.StaticFiles.FunctionalTests\Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj", "{FDF0539C-1F62-4B78-91B1-C687886931CA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.RangeHelper.Sources.Test", "test\Microsoft.AspNetCore.RangeHelper.Sources.Test\Microsoft.AspNetCore.RangeHelper.Sources.Test.csproj", "{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{360DC2F8-EEB4-4C69-9784-C686EAD78279}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.AspNetCore.RangeHelper.Sources", "Microsoft.AspNetCore.RangeHelper.Sources", "{DB6A1D14-B8A2-488F-9C4B-422FD45C8853}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
shared\Microsoft.AspNetCore.RangeHelper.Sources\RangeHelper.cs = shared\Microsoft.AspNetCore.RangeHelper.Sources\RangeHelper.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{8D7BC5A4-F19C-4184-8338-A6B42997218C} = {40EE0889-960E-41B4-A3D3-9CE963EB0797}
|
||||
{092141D9-305A-4FC5-AE74-CB23982CA8D4} = {8B21A3A9-9CA6-4857-A6E0-1A3203404B60}
|
||||
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5} = {EF02AFE8-7C15-4DDB-8B2C-58A676112A98}
|
||||
{FDF0539C-1F62-4B78-91B1-C687886931CA} = {EF02AFE8-7C15-4DDB-8B2C-58A676112A98}
|
||||
{D3D752C4-4CDF-4F18-AC7F-48CB980A69DA} = {EF02AFE8-7C15-4DDB-8B2C-58A676112A98}
|
||||
{DB6A1D14-B8A2-488F-9C4B-422FD45C8853} = {360DC2F8-EEB4-4C69-9784-C686EAD78279}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
|
|
@ -0,0 +1,39 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These package versions may be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Auto">
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.3-rtm-15802</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This may import a generated file which may override the variables above. -->
|
||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
||||
|
||||
<!-- These are package versions that should not be overridden or updated by automation. -->
|
||||
<PropertyGroup Label="Package Versions: Pinned">
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.1</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.1.1</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpPackageVersion>2.1.1</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreServerHttpSysPackageVersion>2.1.1</MicrosoftAspNetCoreServerHttpSysPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.1</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>0.5.1</MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.2</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.1</MicrosoftAspNetCoreTestHostPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||
<LineupPackageVersion>2.1.0-rc1-*</LineupPackageVersion>
|
||||
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
|
||||
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project>
|
||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||
|
||||
<PropertyGroup Label="RestoreSources">
|
||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||
$(RestoreSources);
|
||||
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
|
||||
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
||||
</RestoreSources>
|
||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
|
||||
$(RestoreSources);
|
||||
https://api.nuget.org/v3/index.json;
|
||||
</RestoreSources>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:35192/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"StaticFileSample": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:5000/",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace StaticFilesSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddDirectoryBrowser();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment host)
|
||||
{
|
||||
Console.WriteLine("webroot: " + host.WebRootPath);
|
||||
|
||||
app.UseFileServer(new FileServerOptions
|
||||
{
|
||||
EnableDirectoryBrowsing = true
|
||||
});
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.ConfigureLogging(factory =>
|
||||
{
|
||||
factory.AddFilter("Console", level => level >= LogLevel.Debug);
|
||||
factory.AddConsole();
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseKestrel()
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.StaticFiles\Microsoft.AspNetCore.StaticFiles.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
A static HTML file.
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a parser for the Range Header in an <see cref="HttpContext.Request"/>.
|
||||
/// </summary>
|
||||
internal static class RangeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the normalized form of the requested range if the Range Header in the <see cref="HttpContext.Request"/> is valid.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="HttpContext"/> associated with the request.</param>
|
||||
/// <param name="requestHeaders">The <see cref="RequestHeaders"/> associated with the given <paramref name="context"/>.</param>
|
||||
/// <param name="length">The total length of the file representation requested.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||
/// <returns>A boolean value which represents if the <paramref name="requestHeaders"/> contain a single valid
|
||||
/// range request. A <see cref="RangeItemHeaderValue"/> which represents the normalized form of the
|
||||
/// range parsed from the <paramref name="requestHeaders"/> or <c>null</c> if it cannot be normalized.</returns>
|
||||
/// <remark>If the Range header exists but cannot be parsed correctly, or if the provided length is 0, then the range request cannot be satisfied (status 416).
|
||||
/// This results in (<c>true</c>,<c>null</c>) return values.</remark>
|
||||
public static (bool isRangeRequest, RangeItemHeaderValue range) ParseRange(
|
||||
HttpContext context,
|
||||
RequestHeaders requestHeaders,
|
||||
long length,
|
||||
ILogger logger)
|
||||
{
|
||||
var rawRangeHeader = context.Request.Headers[HeaderNames.Range];
|
||||
if (StringValues.IsNullOrEmpty(rawRangeHeader))
|
||||
{
|
||||
logger.LogTrace("Range header's value is empty.");
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
// Perf: Check for a single entry before parsing it
|
||||
if (rawRangeHeader.Count > 1 || rawRangeHeader[0].IndexOf(',') >= 0)
|
||||
{
|
||||
logger.LogDebug("Multiple ranges are not supported.");
|
||||
|
||||
// The spec allows for multiple ranges but we choose not to support them because the client may request
|
||||
// very strange ranges (e.g. each byte separately, overlapping ranges, etc.) that could negatively
|
||||
// impact the server. Ignore the header and serve the response normally.
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var rangeHeader = requestHeaders.Range;
|
||||
if (rangeHeader == null)
|
||||
{
|
||||
logger.LogDebug("Range header's value is invalid.");
|
||||
// Invalid
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
// Already verified above
|
||||
Debug.Assert(rangeHeader.Ranges.Count == 1);
|
||||
|
||||
var ranges = rangeHeader.Ranges;
|
||||
if (ranges == null)
|
||||
{
|
||||
logger.LogDebug("Range header's value is invalid.");
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
if (ranges.Count == 0)
|
||||
{
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
// Normalize the ranges
|
||||
var range = NormalizeRange(ranges.SingleOrDefault(), length);
|
||||
|
||||
// Return the single range
|
||||
return (true, range);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static RangeItemHeaderValue NormalizeRange(RangeItemHeaderValue range, long length)
|
||||
{
|
||||
var start = range.From;
|
||||
var end = range.To;
|
||||
|
||||
// X-[Y]
|
||||
if (start.HasValue)
|
||||
{
|
||||
if (start.Value >= length)
|
||||
{
|
||||
// Not satisfiable, skip/discard.
|
||||
return null;
|
||||
}
|
||||
if (!end.HasValue || end.Value >= length)
|
||||
{
|
||||
end = length - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// suffix range "-X" e.g. the last X bytes, resolve
|
||||
if (end.Value == 0)
|
||||
{
|
||||
// Not satisfiable, skip/discard.
|
||||
return null;
|
||||
}
|
||||
|
||||
var bytes = Math.Min(end.Value, length);
|
||||
start = length - bytes;
|
||||
end = start + bytes - 1;
|
||||
}
|
||||
|
||||
return new RangeItemHeaderValue(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.StaticFiles
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string ServerCapabilitiesKey = "server.Capabilities";
|
||||
internal const string SendFileVersionKey = "sendfile.Version";
|
||||
internal const string SendFileVersion = "1.0";
|
||||
|
||||
internal const int Status200Ok = 200;
|
||||
internal const int Status206PartialContent = 206;
|
||||
internal const int Status304NotModified = 304;
|
||||
internal const int Status412PreconditionFailed = 412;
|
||||
internal const int Status416RangeNotSatisfiable = 416;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Dictionary>
|
||||
<Words>
|
||||
<Recognized>
|
||||
<Word>Owin</Word>
|
||||
</Recognized>
|
||||
</Words>
|
||||
</Dictionary>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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 Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the DefaultFilesMiddleware
|
||||
/// </summary>
|
||||
public static class DefaultFilesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables default file mapping on the current path
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<DefaultFilesMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables default file mapping for the given request path
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="requestPath">The relative request path.</param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app, string requestPath)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseDefaultFiles(new DefaultFilesOptions
|
||||
{
|
||||
RequestPath = new PathString(requestPath)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables default file mapping with the given options
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDefaultFiles(this IApplicationBuilder app, DefaultFilesOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<DefaultFilesMiddleware>(Options.Create(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.StaticFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// This examines a directory path and determines if there is a default file present.
|
||||
/// If so the file name is appended to the path and execution continues.
|
||||
/// Note we don't just serve the file because it may require interpretation.
|
||||
/// </summary>
|
||||
public class DefaultFilesMiddleware
|
||||
{
|
||||
private readonly DefaultFilesOptions _options;
|
||||
private readonly PathString _matchUrl;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IFileProvider _fileProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the DefaultFilesMiddleware.
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the pipeline.</param>
|
||||
/// <param name="hostingEnv">The <see cref="IHostingEnvironment"/> used by this middleware.</param>
|
||||
/// <param name="options">The configuration options for this middleware.</param>
|
||||
public DefaultFilesMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions<DefaultFilesOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (hostingEnv == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostingEnv));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_options = options.Value;
|
||||
_fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv);
|
||||
_matchUrl = _options.RequestPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This examines the request to see if it matches a configured directory, and if there are any files with the
|
||||
/// configured default names in that directory. If so this will append the corresponding file name to the request
|
||||
/// path for a later middleware to handle.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
PathString subpath;
|
||||
if (Helpers.IsGetOrHeadMethod(context.Request.Method)
|
||||
&& Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out subpath))
|
||||
{
|
||||
var dirContents = _fileProvider.GetDirectoryContents(subpath.Value);
|
||||
if (dirContents.Exists)
|
||||
{
|
||||
// Check if any of our default files exist.
|
||||
for (int matchIndex = 0; matchIndex < _options.DefaultFileNames.Count; matchIndex++)
|
||||
{
|
||||
string defaultFile = _options.DefaultFileNames[matchIndex];
|
||||
var file = _fileProvider.GetFileInfo(subpath + defaultFile);
|
||||
// TryMatchPath will make sure subpath always ends with a "/" by adding it if needed.
|
||||
if (file.Exists)
|
||||
{
|
||||
// If the path matches a directory but does not end in a slash, redirect to add the slash.
|
||||
// This prevents relative links from breaking.
|
||||
if (!Helpers.PathEndsInSlash(context.Request.Path))
|
||||
{
|
||||
context.Response.StatusCode = 301;
|
||||
context.Response.Headers[HeaderNames.Location] = context.Request.PathBase + context.Request.Path + "/" + context.Request.QueryString;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Match found, re-write the url. A later middleware will actually serve the file.
|
||||
context.Request.Path = new PathString(context.Request.Path.Value + defaultFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// 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 Microsoft.AspNetCore.StaticFiles.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for selecting default file names.
|
||||
/// </summary>
|
||||
public class DefaultFilesOptions : SharedOptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for the DefaultFilesMiddleware.
|
||||
/// </summary>
|
||||
public DefaultFilesOptions()
|
||||
: this(new SharedOptions())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the DefaultFilesMiddleware.
|
||||
/// </summary>
|
||||
/// <param name="sharedOptions"></param>
|
||||
public DefaultFilesOptions(SharedOptions sharedOptions)
|
||||
: base(sharedOptions)
|
||||
{
|
||||
// Prioritized list
|
||||
DefaultFileNames = new List<string>()
|
||||
{
|
||||
"default.htm",
|
||||
"default.html",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An ordered list of file names to select by default. List length and ordering may affect performance.
|
||||
/// </summary>
|
||||
public IList<string> DefaultFileNames { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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 Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the DirectoryBrowserMiddleware
|
||||
/// </summary>
|
||||
public static class DirectoryBrowserExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enable directory browsing on the current path
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<DirectoryBrowserMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables directory browsing for the given request path
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="requestPath">The relative request path.</param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, string requestPath)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseDirectoryBrowser(new DirectoryBrowserOptions
|
||||
{
|
||||
RequestPath = new PathString(requestPath)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable directory browsing with the given options
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseDirectoryBrowser(this IApplicationBuilder app, DirectoryBrowserOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<DirectoryBrowserMiddleware>(Options.Create(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.StaticFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables directory browsing
|
||||
/// </summary>
|
||||
public class DirectoryBrowserMiddleware
|
||||
{
|
||||
private readonly DirectoryBrowserOptions _options;
|
||||
private readonly PathString _matchUrl;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IDirectoryFormatter _formatter;
|
||||
private readonly IFileProvider _fileProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the SendFileMiddleware. Using <see cref="HtmlEncoder.Default"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the pipeline.</param>
|
||||
/// <param name="hostingEnv">The <see cref="IHostingEnvironment"/> used by this middleware.</param>
|
||||
/// <param name="options">The configuration for this middleware.</param>
|
||||
public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions<DirectoryBrowserOptions> options)
|
||||
: this(next, hostingEnv, HtmlEncoder.Default, options)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the SendFileMiddleware.
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the pipeline.</param>
|
||||
/// <param name="hostingEnv">The <see cref="IHostingEnvironment"/> used by this middleware.</param>
|
||||
/// <param name="encoder">The <see cref="HtmlEncoder"/> used by the default <see cref="HtmlDirectoryFormatter"/>.</param>
|
||||
/// <param name="options">The configuration for this middleware.</param>
|
||||
public DirectoryBrowserMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, HtmlEncoder encoder, IOptions<DirectoryBrowserOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (hostingEnv == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostingEnv));
|
||||
}
|
||||
|
||||
if (encoder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(encoder));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_options = options.Value;
|
||||
_fileProvider = _options.FileProvider ?? Helpers.ResolveFileProvider(hostingEnv);
|
||||
_formatter = options.Value.Formatter ?? new HtmlDirectoryFormatter(encoder);
|
||||
_matchUrl = _options.RequestPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Examines the request to see if it matches a configured directory. If so, a view of the directory contents is returned.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
// Check if the URL matches any expected paths
|
||||
PathString subpath;
|
||||
IDirectoryContents contents;
|
||||
if (Helpers.IsGetOrHeadMethod(context.Request.Method)
|
||||
&& Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out subpath)
|
||||
&& TryGetDirectoryInfo(subpath, out contents))
|
||||
{
|
||||
// If the path matches a directory but does not end in a slash, redirect to add the slash.
|
||||
// This prevents relative links from breaking.
|
||||
if (!Helpers.PathEndsInSlash(context.Request.Path))
|
||||
{
|
||||
context.Response.StatusCode = 301;
|
||||
context.Response.Headers[HeaderNames.Location] = context.Request.PathBase + context.Request.Path + "/" + context.Request.QueryString;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return _formatter.GenerateContentAsync(context, contents);
|
||||
}
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
|
||||
private bool TryGetDirectoryInfo(PathString subpath, out IDirectoryContents contents)
|
||||
{
|
||||
contents = _fileProvider.GetDirectoryContents(subpath.Value);
|
||||
return contents.Exists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Directory browsing options
|
||||
/// </summary>
|
||||
public class DirectoryBrowserOptions : SharedOptionsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Enabled directory browsing for all request paths
|
||||
/// </summary>
|
||||
public DirectoryBrowserOptions()
|
||||
: this(new SharedOptions())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enabled directory browsing all request paths
|
||||
/// </summary>
|
||||
/// <param name="sharedOptions"></param>
|
||||
public DirectoryBrowserOptions(SharedOptions sharedOptions)
|
||||
: base(sharedOptions)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The component that generates the view.
|
||||
/// </summary>
|
||||
public IDirectoryFormatter Formatter { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for adding directory browser services.
|
||||
/// </summary>
|
||||
public static class DirectoryBrowserServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds directory browser middleware services.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
|
||||
public static IServiceCollection AddDirectoryBrowser(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.AddWebEncoders();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,459 @@
|
|||
// 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.AspNetCore.StaticFiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a mapping between file extensions and MIME types.
|
||||
/// </summary>
|
||||
public class FileExtensionContentTypeProvider : IContentTypeProvider
|
||||
{
|
||||
#region Extension mapping table
|
||||
/// <summary>
|
||||
/// Creates a new provider with a set of default mappings.
|
||||
/// </summary>
|
||||
public FileExtensionContentTypeProvider()
|
||||
: this(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ ".323", "text/h323" },
|
||||
{ ".3g2", "video/3gpp2" },
|
||||
{ ".3gp2", "video/3gpp2" },
|
||||
{ ".3gp", "video/3gpp" },
|
||||
{ ".3gpp", "video/3gpp" },
|
||||
{ ".aac", "audio/aac" },
|
||||
{ ".aaf", "application/octet-stream" },
|
||||
{ ".aca", "application/octet-stream" },
|
||||
{ ".accdb", "application/msaccess" },
|
||||
{ ".accde", "application/msaccess" },
|
||||
{ ".accdt", "application/msaccess" },
|
||||
{ ".acx", "application/internet-property-stream" },
|
||||
{ ".adt", "audio/vnd.dlna.adts" },
|
||||
{ ".adts", "audio/vnd.dlna.adts" },
|
||||
{ ".afm", "application/octet-stream" },
|
||||
{ ".ai", "application/postscript" },
|
||||
{ ".aif", "audio/x-aiff" },
|
||||
{ ".aifc", "audio/aiff" },
|
||||
{ ".aiff", "audio/aiff" },
|
||||
{ ".appcache", "text/cache-manifest" },
|
||||
{ ".application", "application/x-ms-application" },
|
||||
{ ".art", "image/x-jg" },
|
||||
{ ".asd", "application/octet-stream" },
|
||||
{ ".asf", "video/x-ms-asf" },
|
||||
{ ".asi", "application/octet-stream" },
|
||||
{ ".asm", "text/plain" },
|
||||
{ ".asr", "video/x-ms-asf" },
|
||||
{ ".asx", "video/x-ms-asf" },
|
||||
{ ".atom", "application/atom+xml" },
|
||||
{ ".au", "audio/basic" },
|
||||
{ ".avi", "video/x-msvideo" },
|
||||
{ ".axs", "application/olescript" },
|
||||
{ ".bas", "text/plain" },
|
||||
{ ".bcpio", "application/x-bcpio" },
|
||||
{ ".bin", "application/octet-stream" },
|
||||
{ ".bmp", "image/bmp" },
|
||||
{ ".c", "text/plain" },
|
||||
{ ".cab", "application/vnd.ms-cab-compressed" },
|
||||
{ ".calx", "application/vnd.ms-office.calx" },
|
||||
{ ".cat", "application/vnd.ms-pki.seccat" },
|
||||
{ ".cdf", "application/x-cdf" },
|
||||
{ ".chm", "application/octet-stream" },
|
||||
{ ".class", "application/x-java-applet" },
|
||||
{ ".clp", "application/x-msclip" },
|
||||
{ ".cmx", "image/x-cmx" },
|
||||
{ ".cnf", "text/plain" },
|
||||
{ ".cod", "image/cis-cod" },
|
||||
{ ".cpio", "application/x-cpio" },
|
||||
{ ".cpp", "text/plain" },
|
||||
{ ".crd", "application/x-mscardfile" },
|
||||
{ ".crl", "application/pkix-crl" },
|
||||
{ ".crt", "application/x-x509-ca-cert" },
|
||||
{ ".csh", "application/x-csh" },
|
||||
{ ".css", "text/css" },
|
||||
{ ".csv", "application/octet-stream" },
|
||||
{ ".cur", "application/octet-stream" },
|
||||
{ ".dcr", "application/x-director" },
|
||||
{ ".deploy", "application/octet-stream" },
|
||||
{ ".der", "application/x-x509-ca-cert" },
|
||||
{ ".dib", "image/bmp" },
|
||||
{ ".dir", "application/x-director" },
|
||||
{ ".disco", "text/xml" },
|
||||
{ ".dlm", "text/dlm" },
|
||||
{ ".doc", "application/msword" },
|
||||
{ ".docm", "application/vnd.ms-word.document.macroEnabled.12" },
|
||||
{ ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
|
||||
{ ".dot", "application/msword" },
|
||||
{ ".dotm", "application/vnd.ms-word.template.macroEnabled.12" },
|
||||
{ ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" },
|
||||
{ ".dsp", "application/octet-stream" },
|
||||
{ ".dtd", "text/xml" },
|
||||
{ ".dvi", "application/x-dvi" },
|
||||
{ ".dvr-ms", "video/x-ms-dvr" },
|
||||
{ ".dwf", "drawing/x-dwf" },
|
||||
{ ".dwp", "application/octet-stream" },
|
||||
{ ".dxr", "application/x-director" },
|
||||
{ ".eml", "message/rfc822" },
|
||||
{ ".emz", "application/octet-stream" },
|
||||
{ ".eot", "application/vnd.ms-fontobject" },
|
||||
{ ".eps", "application/postscript" },
|
||||
{ ".etx", "text/x-setext" },
|
||||
{ ".evy", "application/envoy" },
|
||||
{ ".fdf", "application/vnd.fdf" },
|
||||
{ ".fif", "application/fractals" },
|
||||
{ ".fla", "application/octet-stream" },
|
||||
{ ".flr", "x-world/x-vrml" },
|
||||
{ ".flv", "video/x-flv" },
|
||||
{ ".gif", "image/gif" },
|
||||
{ ".gtar", "application/x-gtar" },
|
||||
{ ".gz", "application/x-gzip" },
|
||||
{ ".h", "text/plain" },
|
||||
{ ".hdf", "application/x-hdf" },
|
||||
{ ".hdml", "text/x-hdml" },
|
||||
{ ".hhc", "application/x-oleobject" },
|
||||
{ ".hhk", "application/octet-stream" },
|
||||
{ ".hhp", "application/octet-stream" },
|
||||
{ ".hlp", "application/winhlp" },
|
||||
{ ".hqx", "application/mac-binhex40" },
|
||||
{ ".hta", "application/hta" },
|
||||
{ ".htc", "text/x-component" },
|
||||
{ ".htm", "text/html" },
|
||||
{ ".html", "text/html" },
|
||||
{ ".htt", "text/webviewhtml" },
|
||||
{ ".hxt", "text/html" },
|
||||
{ ".ical", "text/calendar" },
|
||||
{ ".icalendar", "text/calendar" },
|
||||
{ ".ico", "image/x-icon" },
|
||||
{ ".ics", "text/calendar" },
|
||||
{ ".ief", "image/ief" },
|
||||
{ ".ifb", "text/calendar" },
|
||||
{ ".iii", "application/x-iphone" },
|
||||
{ ".inf", "application/octet-stream" },
|
||||
{ ".ins", "application/x-internet-signup" },
|
||||
{ ".isp", "application/x-internet-signup" },
|
||||
{ ".IVF", "video/x-ivf" },
|
||||
{ ".jar", "application/java-archive" },
|
||||
{ ".java", "application/octet-stream" },
|
||||
{ ".jck", "application/liquidmotion" },
|
||||
{ ".jcz", "application/liquidmotion" },
|
||||
{ ".jfif", "image/pjpeg" },
|
||||
{ ".jpb", "application/octet-stream" },
|
||||
{ ".jpe", "image/jpeg" },
|
||||
{ ".jpeg", "image/jpeg" },
|
||||
{ ".jpg", "image/jpeg" },
|
||||
{ ".js", "application/javascript" },
|
||||
{ ".json", "application/json" },
|
||||
{ ".jsx", "text/jscript" },
|
||||
{ ".latex", "application/x-latex" },
|
||||
{ ".lit", "application/x-ms-reader" },
|
||||
{ ".lpk", "application/octet-stream" },
|
||||
{ ".lsf", "video/x-la-asf" },
|
||||
{ ".lsx", "video/x-la-asf" },
|
||||
{ ".lzh", "application/octet-stream" },
|
||||
{ ".m13", "application/x-msmediaview" },
|
||||
{ ".m14", "application/x-msmediaview" },
|
||||
{ ".m1v", "video/mpeg" },
|
||||
{ ".m2ts", "video/vnd.dlna.mpeg-tts" },
|
||||
{ ".m3u", "audio/x-mpegurl" },
|
||||
{ ".m4a", "audio/mp4" },
|
||||
{ ".m4v", "video/mp4" },
|
||||
{ ".man", "application/x-troff-man" },
|
||||
{ ".manifest", "application/x-ms-manifest" },
|
||||
{ ".map", "text/plain" },
|
||||
{ ".markdown", "text/markdown" },
|
||||
{ ".md", "text/markdown" },
|
||||
{ ".mdb", "application/x-msaccess" },
|
||||
{ ".mdp", "application/octet-stream" },
|
||||
{ ".me", "application/x-troff-me" },
|
||||
{ ".mht", "message/rfc822" },
|
||||
{ ".mhtml", "message/rfc822" },
|
||||
{ ".mid", "audio/mid" },
|
||||
{ ".midi", "audio/mid" },
|
||||
{ ".mix", "application/octet-stream" },
|
||||
{ ".mmf", "application/x-smaf" },
|
||||
{ ".mno", "text/xml" },
|
||||
{ ".mny", "application/x-msmoney" },
|
||||
{ ".mov", "video/quicktime" },
|
||||
{ ".movie", "video/x-sgi-movie" },
|
||||
{ ".mp2", "video/mpeg" },
|
||||
{ ".mp3", "audio/mpeg" },
|
||||
{ ".mp4", "video/mp4" },
|
||||
{ ".mp4v", "video/mp4" },
|
||||
{ ".mpa", "video/mpeg" },
|
||||
{ ".mpe", "video/mpeg" },
|
||||
{ ".mpeg", "video/mpeg" },
|
||||
{ ".mpg", "video/mpeg" },
|
||||
{ ".mpp", "application/vnd.ms-project" },
|
||||
{ ".mpv2", "video/mpeg" },
|
||||
{ ".ms", "application/x-troff-ms" },
|
||||
{ ".msi", "application/octet-stream" },
|
||||
{ ".mso", "application/octet-stream" },
|
||||
{ ".mvb", "application/x-msmediaview" },
|
||||
{ ".mvc", "application/x-miva-compiled" },
|
||||
{ ".nc", "application/x-netcdf" },
|
||||
{ ".nsc", "video/x-ms-asf" },
|
||||
{ ".nws", "message/rfc822" },
|
||||
{ ".ocx", "application/octet-stream" },
|
||||
{ ".oda", "application/oda" },
|
||||
{ ".odc", "text/x-ms-odc" },
|
||||
{ ".ods", "application/oleobject" },
|
||||
{ ".oga", "audio/ogg" },
|
||||
{ ".ogg", "video/ogg" },
|
||||
{ ".ogv", "video/ogg" },
|
||||
{ ".ogx", "application/ogg" },
|
||||
{ ".one", "application/onenote" },
|
||||
{ ".onea", "application/onenote" },
|
||||
{ ".onetoc", "application/onenote" },
|
||||
{ ".onetoc2", "application/onenote" },
|
||||
{ ".onetmp", "application/onenote" },
|
||||
{ ".onepkg", "application/onenote" },
|
||||
{ ".osdx", "application/opensearchdescription+xml" },
|
||||
{ ".otf", "font/otf" },
|
||||
{ ".p10", "application/pkcs10" },
|
||||
{ ".p12", "application/x-pkcs12" },
|
||||
{ ".p7b", "application/x-pkcs7-certificates" },
|
||||
{ ".p7c", "application/pkcs7-mime" },
|
||||
{ ".p7m", "application/pkcs7-mime" },
|
||||
{ ".p7r", "application/x-pkcs7-certreqresp" },
|
||||
{ ".p7s", "application/pkcs7-signature" },
|
||||
{ ".pbm", "image/x-portable-bitmap" },
|
||||
{ ".pcx", "application/octet-stream" },
|
||||
{ ".pcz", "application/octet-stream" },
|
||||
{ ".pdf", "application/pdf" },
|
||||
{ ".pfb", "application/octet-stream" },
|
||||
{ ".pfm", "application/octet-stream" },
|
||||
{ ".pfx", "application/x-pkcs12" },
|
||||
{ ".pgm", "image/x-portable-graymap" },
|
||||
{ ".pko", "application/vnd.ms-pki.pko" },
|
||||
{ ".pma", "application/x-perfmon" },
|
||||
{ ".pmc", "application/x-perfmon" },
|
||||
{ ".pml", "application/x-perfmon" },
|
||||
{ ".pmr", "application/x-perfmon" },
|
||||
{ ".pmw", "application/x-perfmon" },
|
||||
{ ".png", "image/png" },
|
||||
{ ".pnm", "image/x-portable-anymap" },
|
||||
{ ".pnz", "image/png" },
|
||||
{ ".pot", "application/vnd.ms-powerpoint" },
|
||||
{ ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" },
|
||||
{ ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" },
|
||||
{ ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" },
|
||||
{ ".ppm", "image/x-portable-pixmap" },
|
||||
{ ".pps", "application/vnd.ms-powerpoint" },
|
||||
{ ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" },
|
||||
{ ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" },
|
||||
{ ".ppt", "application/vnd.ms-powerpoint" },
|
||||
{ ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" },
|
||||
{ ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
|
||||
{ ".prf", "application/pics-rules" },
|
||||
{ ".prm", "application/octet-stream" },
|
||||
{ ".prx", "application/octet-stream" },
|
||||
{ ".ps", "application/postscript" },
|
||||
{ ".psd", "application/octet-stream" },
|
||||
{ ".psm", "application/octet-stream" },
|
||||
{ ".psp", "application/octet-stream" },
|
||||
{ ".pub", "application/x-mspublisher" },
|
||||
{ ".qt", "video/quicktime" },
|
||||
{ ".qtl", "application/x-quicktimeplayer" },
|
||||
{ ".qxd", "application/octet-stream" },
|
||||
{ ".ra", "audio/x-pn-realaudio" },
|
||||
{ ".ram", "audio/x-pn-realaudio" },
|
||||
{ ".rar", "application/octet-stream" },
|
||||
{ ".ras", "image/x-cmu-raster" },
|
||||
{ ".rf", "image/vnd.rn-realflash" },
|
||||
{ ".rgb", "image/x-rgb" },
|
||||
{ ".rm", "application/vnd.rn-realmedia" },
|
||||
{ ".rmi", "audio/mid" },
|
||||
{ ".roff", "application/x-troff" },
|
||||
{ ".rpm", "audio/x-pn-realaudio-plugin" },
|
||||
{ ".rtf", "application/rtf" },
|
||||
{ ".rtx", "text/richtext" },
|
||||
{ ".scd", "application/x-msschedule" },
|
||||
{ ".sct", "text/scriptlet" },
|
||||
{ ".sea", "application/octet-stream" },
|
||||
{ ".setpay", "application/set-payment-initiation" },
|
||||
{ ".setreg", "application/set-registration-initiation" },
|
||||
{ ".sgml", "text/sgml" },
|
||||
{ ".sh", "application/x-sh" },
|
||||
{ ".shar", "application/x-shar" },
|
||||
{ ".sit", "application/x-stuffit" },
|
||||
{ ".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12" },
|
||||
{ ".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" },
|
||||
{ ".smd", "audio/x-smd" },
|
||||
{ ".smi", "application/octet-stream" },
|
||||
{ ".smx", "audio/x-smd" },
|
||||
{ ".smz", "audio/x-smd" },
|
||||
{ ".snd", "audio/basic" },
|
||||
{ ".snp", "application/octet-stream" },
|
||||
{ ".spc", "application/x-pkcs7-certificates" },
|
||||
{ ".spl", "application/futuresplash" },
|
||||
{ ".spx", "audio/ogg" },
|
||||
{ ".src", "application/x-wais-source" },
|
||||
{ ".ssm", "application/streamingmedia" },
|
||||
{ ".sst", "application/vnd.ms-pki.certstore" },
|
||||
{ ".stl", "application/vnd.ms-pki.stl" },
|
||||
{ ".sv4cpio", "application/x-sv4cpio" },
|
||||
{ ".sv4crc", "application/x-sv4crc" },
|
||||
{ ".svg", "image/svg+xml" },
|
||||
{ ".svgz", "image/svg+xml" },
|
||||
{ ".swf", "application/x-shockwave-flash" },
|
||||
{ ".t", "application/x-troff" },
|
||||
{ ".tar", "application/x-tar" },
|
||||
{ ".tcl", "application/x-tcl" },
|
||||
{ ".tex", "application/x-tex" },
|
||||
{ ".texi", "application/x-texinfo" },
|
||||
{ ".texinfo", "application/x-texinfo" },
|
||||
{ ".tgz", "application/x-compressed" },
|
||||
{ ".thmx", "application/vnd.ms-officetheme" },
|
||||
{ ".thn", "application/octet-stream" },
|
||||
{ ".tif", "image/tiff" },
|
||||
{ ".tiff", "image/tiff" },
|
||||
{ ".toc", "application/octet-stream" },
|
||||
{ ".tr", "application/x-troff" },
|
||||
{ ".trm", "application/x-msterminal" },
|
||||
{ ".ts", "video/vnd.dlna.mpeg-tts" },
|
||||
{ ".tsv", "text/tab-separated-values" },
|
||||
{ ".ttc", "application/x-font-ttf" },
|
||||
{ ".ttf", "application/x-font-ttf" },
|
||||
{ ".tts", "video/vnd.dlna.mpeg-tts" },
|
||||
{ ".txt", "text/plain" },
|
||||
{ ".u32", "application/octet-stream" },
|
||||
{ ".uls", "text/iuls" },
|
||||
{ ".ustar", "application/x-ustar" },
|
||||
{ ".vbs", "text/vbscript" },
|
||||
{ ".vcf", "text/x-vcard" },
|
||||
{ ".vcs", "text/plain" },
|
||||
{ ".vdx", "application/vnd.ms-visio.viewer" },
|
||||
{ ".vml", "text/xml" },
|
||||
{ ".vsd", "application/vnd.visio" },
|
||||
{ ".vss", "application/vnd.visio" },
|
||||
{ ".vst", "application/vnd.visio" },
|
||||
{ ".vsto", "application/x-ms-vsto" },
|
||||
{ ".vsw", "application/vnd.visio" },
|
||||
{ ".vsx", "application/vnd.visio" },
|
||||
{ ".vtx", "application/vnd.visio" },
|
||||
{ ".wav", "audio/wav" },
|
||||
{ ".wax", "audio/x-ms-wax" },
|
||||
{ ".wbmp", "image/vnd.wap.wbmp" },
|
||||
{ ".wcm", "application/vnd.ms-works" },
|
||||
{ ".wdb", "application/vnd.ms-works" },
|
||||
{ ".webm", "video/webm" },
|
||||
{ ".webp", "image/webp" },
|
||||
{ ".wks", "application/vnd.ms-works" },
|
||||
{ ".wm", "video/x-ms-wm" },
|
||||
{ ".wma", "audio/x-ms-wma" },
|
||||
{ ".wmd", "application/x-ms-wmd" },
|
||||
{ ".wmf", "application/x-msmetafile" },
|
||||
{ ".wml", "text/vnd.wap.wml" },
|
||||
{ ".wmlc", "application/vnd.wap.wmlc" },
|
||||
{ ".wmls", "text/vnd.wap.wmlscript" },
|
||||
{ ".wmlsc", "application/vnd.wap.wmlscriptc" },
|
||||
{ ".wmp", "video/x-ms-wmp" },
|
||||
{ ".wmv", "video/x-ms-wmv" },
|
||||
{ ".wmx", "video/x-ms-wmx" },
|
||||
{ ".wmz", "application/x-ms-wmz" },
|
||||
{ ".woff", "application/font-woff" }, // https://www.w3.org/TR/WOFF/#appendix-b
|
||||
{ ".woff2", "font/woff2" }, // https://www.w3.org/TR/WOFF2/#IMT
|
||||
{ ".wps", "application/vnd.ms-works" },
|
||||
{ ".wri", "application/x-mswrite" },
|
||||
{ ".wrl", "x-world/x-vrml" },
|
||||
{ ".wrz", "x-world/x-vrml" },
|
||||
{ ".wsdl", "text/xml" },
|
||||
{ ".wtv", "video/x-ms-wtv" },
|
||||
{ ".wvx", "video/x-ms-wvx" },
|
||||
{ ".x", "application/directx" },
|
||||
{ ".xaf", "x-world/x-vrml" },
|
||||
{ ".xaml", "application/xaml+xml" },
|
||||
{ ".xap", "application/x-silverlight-app" },
|
||||
{ ".xbap", "application/x-ms-xbap" },
|
||||
{ ".xbm", "image/x-xbitmap" },
|
||||
{ ".xdr", "text/plain" },
|
||||
{ ".xht", "application/xhtml+xml" },
|
||||
{ ".xhtml", "application/xhtml+xml" },
|
||||
{ ".xla", "application/vnd.ms-excel" },
|
||||
{ ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" },
|
||||
{ ".xlc", "application/vnd.ms-excel" },
|
||||
{ ".xlm", "application/vnd.ms-excel" },
|
||||
{ ".xls", "application/vnd.ms-excel" },
|
||||
{ ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" },
|
||||
{ ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" },
|
||||
{ ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
|
||||
{ ".xlt", "application/vnd.ms-excel" },
|
||||
{ ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" },
|
||||
{ ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" },
|
||||
{ ".xlw", "application/vnd.ms-excel" },
|
||||
{ ".xml", "text/xml" },
|
||||
{ ".xof", "x-world/x-vrml" },
|
||||
{ ".xpm", "image/x-xpixmap" },
|
||||
{ ".xps", "application/vnd.ms-xpsdocument" },
|
||||
{ ".xsd", "text/xml" },
|
||||
{ ".xsf", "text/xml" },
|
||||
{ ".xsl", "text/xml" },
|
||||
{ ".xslt", "text/xml" },
|
||||
{ ".xsn", "application/octet-stream" },
|
||||
{ ".xtp", "application/octet-stream" },
|
||||
{ ".xwd", "image/x-xwindowdump" },
|
||||
{ ".z", "application/x-compress" },
|
||||
{ ".zip", "application/x-zip-compressed" },
|
||||
})
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a lookup engine using the provided mapping.
|
||||
/// It is recommended that the IDictionary instance use StringComparer.OrdinalIgnoreCase.
|
||||
/// </summary>
|
||||
/// <param name="mapping"></param>
|
||||
public FileExtensionContentTypeProvider(IDictionary<string, string> mapping)
|
||||
{
|
||||
if (mapping == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mapping));
|
||||
}
|
||||
Mappings = mapping;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cross reference table of file extensions and content-types.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Mappings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Given a file path, determine the MIME type
|
||||
/// </summary>
|
||||
/// <param name="subpath">A file path</param>
|
||||
/// <param name="contentType">The resulting MIME type</param>
|
||||
/// <returns>True if MIME type could be determined</returns>
|
||||
public bool TryGetContentType(string subpath, out string contentType)
|
||||
{
|
||||
string extension = GetExtension(subpath);
|
||||
if (extension == null)
|
||||
{
|
||||
contentType = null;
|
||||
return false;
|
||||
}
|
||||
return Mappings.TryGetValue(extension, out contentType);
|
||||
}
|
||||
|
||||
private static string GetExtension(string path)
|
||||
{
|
||||
// Don't use Path.GetExtension as that may throw an exception if there are
|
||||
// invalid characters in the path. Invalid characters should be handled
|
||||
// by the FileProviders
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = path.LastIndexOf('.');
|
||||
if (index < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return path.Substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue