Merge remote-tracking branch 'Session/rybrande/release21ToSrc' into rybrande/Mondo2.1

This commit is contained in:
Ryan Brandenburg 2018-11-21 10:05:41 -08:00
commit e6b3a5b79b
36 changed files with 3874 additions and 0 deletions

33
src/Session/.gitignore vendored Normal file
View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
{
"Default": {
"rules": [
"DefaultCompositeRule"
]
}
}

12
src/Session/README.md Normal file
View File

@ -0,0 +1,12 @@
Session
================
AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/yyivj6uwu3uj2x40/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/Session/branch/dev)
Travis: [![Travis](https://travis-ci.org/aspnet/Session.svg?branch=dev)](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.

74
src/Session/Session.sln Normal file
View File

@ -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

BIN
src/Session/build/Key.snk Normal file

Binary file not shown.

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"
}
}
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -0,0 +1,7 @@
<Project>
<Import Project="..\Directory.Build.props" />
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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();
}
}

View 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;
}
}
}

View File

@ -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>

View File

@ -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 = "/";
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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": []
}
]
}

View File

@ -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>

View File

@ -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

12
src/Session/version.props Normal file
View File

@ -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>