#96 Add a session Id
This commit is contained in:
parent
7c60cd5fa6
commit
432de7a158
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:2481/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"Hosting:Environment": "Development"
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
"commandName": "web",
|
||||
"environmentVariables": {
|
||||
"Hosting:Environment": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
|
@ -15,11 +16,14 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
public class DistributedSession : ISession
|
||||
{
|
||||
private const byte SerializationRevision = 1;
|
||||
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 _sessionId;
|
||||
private readonly string _sessionKey;
|
||||
private readonly TimeSpan _idleTimeout;
|
||||
private readonly Func<bool> _tryEstablishSession;
|
||||
private readonly IDictionary<EncodedKey, byte[]> _store;
|
||||
|
|
@ -27,10 +31,12 @@ namespace Microsoft.AspNetCore.Session
|
|||
private bool _isModified;
|
||||
private bool _loaded;
|
||||
private bool _isNewSessionKey;
|
||||
private string _sessionId;
|
||||
private byte[] _sessionIdBytes;
|
||||
|
||||
public DistributedSession(
|
||||
IDistributedCache cache,
|
||||
string sessionId,
|
||||
string sessionKey,
|
||||
TimeSpan idleTimeout,
|
||||
Func<bool> tryEstablishSession,
|
||||
ILoggerFactory loggerFactory,
|
||||
|
|
@ -41,9 +47,9 @@ namespace Microsoft.AspNetCore.Session
|
|||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(sessionId))
|
||||
if (string.IsNullOrEmpty(sessionKey))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionId));
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionKey));
|
||||
}
|
||||
|
||||
if (tryEstablishSession == null)
|
||||
|
|
@ -57,7 +63,7 @@ namespace Microsoft.AspNetCore.Session
|
|||
}
|
||||
|
||||
_cache = cache;
|
||||
_sessionId = sessionId;
|
||||
_sessionKey = sessionKey;
|
||||
_idleTimeout = idleTimeout;
|
||||
_tryEstablishSession = tryEstablishSession;
|
||||
_store = new Dictionary<EncodedKey, byte[]>();
|
||||
|
|
@ -65,6 +71,33 @@ namespace Microsoft.AspNetCore.Session
|
|||
_isNewSessionKey = isNewSessionKey;
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
Load(); // TODO: Silent failure
|
||||
if (_sessionId == null)
|
||||
{
|
||||
_sessionId = new Guid(IdBytes).ToString();
|
||||
}
|
||||
return _sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] IdBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
Load(); // TODO: Silent failure
|
||||
if (_sessionIdBytes == null)
|
||||
{
|
||||
_sessionIdBytes = new byte[IdByteCount];
|
||||
CryptoRandom.GetBytes(_sessionIdBytes);
|
||||
}
|
||||
return _sessionIdBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Keys
|
||||
{
|
||||
get
|
||||
|
|
@ -122,7 +155,16 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
if (!_loaded)
|
||||
{
|
||||
LoadAsync().GetAwaiter().GetResult();
|
||||
var data = _cache.Get(_sessionKey);
|
||||
if (data != null)
|
||||
{
|
||||
Deserialize(new MemoryStream(data));
|
||||
}
|
||||
else if (!_isNewSessionKey)
|
||||
{
|
||||
_logger.AccessingExpiredSession(_sessionKey);
|
||||
}
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,14 +174,14 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
if (!_loaded)
|
||||
{
|
||||
var data = await _cache.GetAsync(_sessionId);
|
||||
var data = await _cache.GetAsync(_sessionKey);
|
||||
if (data != null)
|
||||
{
|
||||
Deserialize(new MemoryStream(data));
|
||||
}
|
||||
else if (!_isNewSessionKey)
|
||||
{
|
||||
_logger.AccessingExpiredSession(_sessionId);
|
||||
_logger.AccessingExpiredSession(_sessionKey);
|
||||
}
|
||||
_loaded = true;
|
||||
}
|
||||
|
|
@ -149,29 +191,35 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
if (_isModified)
|
||||
{
|
||||
var data = await _cache.GetAsync(_sessionId);
|
||||
var data = await _cache.GetAsync(_sessionKey);
|
||||
if (_logger.IsEnabled(LogLevel.Information) && data == null)
|
||||
{
|
||||
_logger.SessionStarted(_sessionId);
|
||||
_logger.SessionStarted(_sessionKey, Id);
|
||||
}
|
||||
_isModified = false;
|
||||
|
||||
var stream = new MemoryStream();
|
||||
Serialize(stream);
|
||||
await _cache.SetAsync(
|
||||
_sessionId,
|
||||
_sessionKey,
|
||||
stream.ToArray(),
|
||||
new DistributedCacheEntryOptions().SetSlidingExpiration(_idleTimeout));
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.SessionStored(_sessionKey, Id, _store.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _cache.RefreshAsync(_sessionId);
|
||||
await _cache.RefreshAsync(_sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 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[]
|
||||
|
|
@ -181,6 +229,7 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
output.WriteByte(SerializationRevision);
|
||||
SerializeNumAs3Bytes(output, _store.Count);
|
||||
output.Write(IdBytes, 0, IdByteCount);
|
||||
|
||||
foreach (var entry in _store)
|
||||
{
|
||||
|
|
@ -203,6 +252,8 @@ namespace Microsoft.AspNetCore.Session
|
|||
}
|
||||
|
||||
int expectedEntries = DeserializeNumFrom3Bytes(content);
|
||||
_sessionIdBytes = ReadBytes(content, IdByteCount);
|
||||
|
||||
for (int i = 0; i < expectedEntries; i++)
|
||||
{
|
||||
int keyLength = DeserializeNumFrom2Bytes(content);
|
||||
|
|
@ -210,6 +261,12 @@ namespace Microsoft.AspNetCore.Session
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ namespace Microsoft.AspNetCore.Session
|
|||
}
|
||||
}
|
||||
|
||||
public ISession Create(string sessionId, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
|
||||
public ISession Create(string sessionKey, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sessionId))
|
||||
if (string.IsNullOrEmpty(sessionKey))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionId));
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(sessionKey));
|
||||
}
|
||||
|
||||
if (tryEstablishSession == null)
|
||||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Session
|
|||
throw new ArgumentNullException(nameof(tryEstablishSession));
|
||||
}
|
||||
|
||||
return new DistributedSession(_cache, sessionId, idleTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
|
||||
return new DistributedSession(_cache, sessionKey, idleTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,6 @@ namespace Microsoft.AspNetCore.Session
|
|||
{
|
||||
bool IsAvailable { get; }
|
||||
|
||||
ISession Create(string sessionId, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
|
||||
ISession Create(string sessionKey, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,9 @@ namespace Microsoft.Extensions.Logging
|
|||
{
|
||||
private static Action<ILogger, Exception> _errorClosingTheSession;
|
||||
private static Action<ILogger, string, Exception> _accessingExpiredSession;
|
||||
private static Action<ILogger, string, Exception> _sessionStarted;
|
||||
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;
|
||||
|
||||
static LoggingExtensions()
|
||||
{
|
||||
|
|
@ -20,11 +22,19 @@ namespace Microsoft.Extensions.Logging
|
|||
_accessingExpiredSession = LoggerMessage.Define<string>(
|
||||
eventId: 2,
|
||||
logLevel: LogLevel.Warning,
|
||||
formatString: "Accessing expired session {SessionId}");
|
||||
_sessionStarted = LoggerMessage.Define<string>(
|
||||
formatString: "Accessing expired session; Key:{sessionKey}");
|
||||
_sessionStarted = LoggerMessage.Define<string, string>(
|
||||
eventId: 3,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Session {SessionId} started");
|
||||
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}");
|
||||
}
|
||||
|
||||
public static void ErrorClosingTheSession(this ILogger logger, Exception exception)
|
||||
|
|
@ -32,14 +42,24 @@ namespace Microsoft.Extensions.Logging
|
|||
_errorClosingTheSession(logger, exception);
|
||||
}
|
||||
|
||||
public static void AccessingExpiredSession(this ILogger logger, string sessionId)
|
||||
public static void AccessingExpiredSession(this ILogger logger, string sessionKey)
|
||||
{
|
||||
_accessingExpiredSession(logger, sessionId, null);
|
||||
_accessingExpiredSession(logger, sessionKey, null);
|
||||
}
|
||||
|
||||
public static void SessionStarted(this ILogger logger, string sessionId)
|
||||
public static void SessionStarted(this ILogger logger, string sessionKey, string sessionId)
|
||||
{
|
||||
_sessionStarted(logger, sessionId, null);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,9 +262,11 @@ namespace Microsoft.AspNetCore.Session
|
|||
|
||||
var sessionLogMessages = sink.Writes.OnlyMessagesFromSource<DistributedSession>().ToArray();
|
||||
|
||||
Assert.Single(sessionLogMessages);
|
||||
Assert.Equal(2, sessionLogMessages.Length);
|
||||
Assert.Contains("started", sessionLogMessages[0].State.ToString());
|
||||
Assert.Equal(LogLevel.Information, sessionLogMessages[0].LogLevel);
|
||||
Assert.Contains("stored", sessionLogMessages[1].State.ToString());
|
||||
Assert.Equal(LogLevel.Debug, sessionLogMessages[1].LogLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,11 +317,13 @@ namespace Microsoft.AspNetCore.Session
|
|||
|
||||
var sessionLogMessages = sink.Writes.OnlyMessagesFromSource<DistributedSession>().ToArray();
|
||||
|
||||
Assert.Equal(2, sessionLogMessages.Length);
|
||||
Assert.Equal(3, sessionLogMessages.Length);
|
||||
Assert.Contains("started", sessionLogMessages[0].State.ToString());
|
||||
Assert.Contains("expired", sessionLogMessages[1].State.ToString());
|
||||
Assert.Contains("stored", sessionLogMessages[1].State.ToString());
|
||||
Assert.Contains("expired", sessionLogMessages[2].State.ToString());
|
||||
Assert.Equal(LogLevel.Information, sessionLogMessages[0].LogLevel);
|
||||
Assert.Equal(LogLevel.Warning, sessionLogMessages[1].LogLevel);
|
||||
Assert.Equal(LogLevel.Debug, sessionLogMessages[1].LogLevel);
|
||||
Assert.Equal(LogLevel.Warning, sessionLogMessages[2].LogLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue