Got at least one data fetching method working; turns out, we can't use a patched LogicStack to get the data
This commit is contained in:
112
Vendor/EmbedIO-3.5.2/Sessions/ISession.cs
vendored
Normal file
112
Vendor/EmbedIO-3.5.2/Sessions/ISession.cs
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a session.
|
||||
/// </summary>
|
||||
public interface ISession
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique identifier for the session.
|
||||
/// </summary>
|
||||
/// <value>The unique identifier for this session.</value>
|
||||
/// <seealso cref="Session.IdComparison"/>
|
||||
/// <seealso cref="Session.IdComparer"/>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time interval, starting from <see cref="LastActivity"/>,
|
||||
/// after which the session expires.
|
||||
/// </summary>
|
||||
/// <value> The expiration time.</value>
|
||||
TimeSpan Duration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UTC date and time of last activity on the session.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The UTC date and time of last activity on the session.
|
||||
/// </value>
|
||||
DateTime LastActivity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of key/value pairs contained in a session.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The number of key/value pairs contained in the object that implements <see cref="ISession"/>.
|
||||
/// </value>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether a session is empty.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true"/> if the object that implements <see cref="ISession"/> is empty,
|
||||
/// i.e. contains no key / value pairs; otherwise, <see langword="false"/>.
|
||||
/// </value>
|
||||
bool IsEmpty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets or sets the value associated with the specified key.</para>
|
||||
/// <para>Note that a session does not store null values; therefore, setting this property to <see langword="null"/>
|
||||
/// has the same effect as removing <paramref name="key"/> from the dictionary.</para>
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The value associated with the specified key, if <paramref name="key"/>
|
||||
/// is found in the dictionary; otherwise, <see langword="null"/>.
|
||||
/// </value>
|
||||
/// <param name="key">The key of the value to get or set.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
object this[string key] { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes all keys and values from a session.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a session contains an element with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to locate in the object that implements <see cref="ISession"/>.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the object that implements <see cref="ISession"/> contains an element with the key;
|
||||
/// otherwise, <see langword="false"/> .
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
bool ContainsKey(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key whose value to get.</param>
|
||||
/// <param name="value">When this method returns, the value associated with the specified <paramref name="key"/>,
|
||||
/// if the key is found; otherwise, <see langword="null"/>. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true"/> if the object that implements <see cref="ISession"/>
|
||||
/// contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
bool TryGetValue(string key, out object value);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove and return the value that has the specified key from a session.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to remove and return.</param>
|
||||
/// <param name="value">When this method returns, the value removed from the object that implements <see cref="ISession"/>,
|
||||
/// if the key is found; otherwise, <see langword="null"/>. This parameter is passed uninitialized.</param>
|
||||
/// <returns><see langword="true"/> if the value was removed successfully; otherwise, <see langword="false"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
bool TryRemove(string key, out object value);
|
||||
|
||||
/// <summary>
|
||||
/// Takes and returns a snapshot of the contents of a session at the time of calling.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IReadOnlyList{T}">IReadOnlyList<KeyValuePair<string,object>></see> interface
|
||||
/// containing an immutable copy of the session data as it was at the time of calling this method.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The objects contained in the session data are copied by reference, not cloned; therefore
|
||||
/// you should be aware that their state may change even after the snapshot is taken.</para>
|
||||
/// </remarks>
|
||||
IReadOnlyList<KeyValuePair<string, object>> TakeSnapshot();
|
||||
}
|
||||
}
|
||||
46
Vendor/EmbedIO-3.5.2/Sessions/ISessionManager.cs
vendored
Normal file
46
Vendor/EmbedIO-3.5.2/Sessions/ISessionManager.cs
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a session manager, which is in charge of managing session objects
|
||||
/// and their association to HTTP contexts.
|
||||
/// </summary>
|
||||
public interface ISessionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Signals a session manager that the web server is starting.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token used to stop the web server.</param>
|
||||
void Start(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the session associated with an <see cref="IHttpContext"/>.
|
||||
/// If a session ID can be retrieved for the context and stored session data
|
||||
/// are available, the returned <see cref="ISession"/> will contain those data;
|
||||
/// otherwise, a new session is created and its ID is stored in the response
|
||||
/// to be retrieved by subsequent requests.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context.</param>
|
||||
/// <returns>An <see cref="ISession"/> interface.</returns>
|
||||
ISession Create(IHttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the session (if any) associated with the specified context
|
||||
/// and removes the session's ID from the context.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context.</param>
|
||||
/// <param name="id">The unique ID of the session.</param>
|
||||
/// <seealso cref="ISession.Id"/>
|
||||
void Delete(IHttpContext context, string id);
|
||||
|
||||
/// <summary>
|
||||
/// <para>Called by a session proxy when a session has been obtained
|
||||
/// for an <see cref="IHttpContext"/> and the context is closed,
|
||||
/// even if the session was subsequently deleted.</para>
|
||||
/// <para>This method can be used to save session data to a storage medium.</para>
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="IHttpContext"/> for which a session was obtained.</param>
|
||||
void OnContextClose(IHttpContext context);
|
||||
}
|
||||
}
|
||||
33
Vendor/EmbedIO-3.5.2/Sessions/ISessionProxy.cs
vendored
Normal file
33
Vendor/EmbedIO-3.5.2/Sessions/ISessionProxy.cs
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a session proxy, i.e. an object that provides
|
||||
/// the same interface as a session object, plus a basic interface
|
||||
/// to a session manager.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A session proxy can be used just as if it were a session object.
|
||||
/// A session is automatically created wherever its data are accessed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ISession" />
|
||||
public interface ISessionProxy : ISession
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a session exists for the current context.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true"/> if a session exists; otherwise, <see langword="false"/>.
|
||||
/// </value>
|
||||
bool Exists { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the session for the current context.
|
||||
/// </summary>
|
||||
void Delete();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the session for the current context and creates a new one.
|
||||
/// </summary>
|
||||
void Regenerate();
|
||||
}
|
||||
}
|
||||
65
Vendor/EmbedIO-3.5.2/Sessions/Internal/DummySessionProxy.cs
vendored
Normal file
65
Vendor/EmbedIO-3.5.2/Sessions/Internal/DummySessionProxy.cs
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EmbedIO.Sessions.Internal
|
||||
{
|
||||
internal sealed class DummySessionProxy : ISessionProxy
|
||||
{
|
||||
private DummySessionProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public static ISessionProxy Instance { get; } = new DummySessionProxy();
|
||||
|
||||
public bool Exists => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Id => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan Duration => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastActivity => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmpty => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object this[string key]
|
||||
{
|
||||
get => throw NoSessionManager();
|
||||
set => throw NoSessionManager();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Regenerate() => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Clear()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ContainsKey(string key) => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetValue(string key, out object value) => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryRemove(string key, out object value) => throw NoSessionManager();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyList<KeyValuePair<string, object>> TakeSnapshot() => throw NoSessionManager();
|
||||
|
||||
private InvalidOperationException NoSessionManager() => new InvalidOperationException("No session manager registered in the web server.");
|
||||
}
|
||||
}
|
||||
150
Vendor/EmbedIO-3.5.2/Sessions/LocalSessionManager.SessionImpl.cs
vendored
Normal file
150
Vendor/EmbedIO-3.5.2/Sessions/LocalSessionManager.SessionImpl.cs
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EmbedIO.Utilities;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
partial class LocalSessionManager
|
||||
{
|
||||
private class SessionImpl : ISession
|
||||
{
|
||||
private readonly Dictionary<string, object> _data = new Dictionary<string, object>(Session.KeyComparer);
|
||||
|
||||
private int _usageCount;
|
||||
|
||||
public SessionImpl(string id, TimeSpan duration)
|
||||
{
|
||||
Id = Validate.NotNullOrEmpty(nameof(id), id);
|
||||
Duration = duration;
|
||||
LastActivity = DateTime.UtcNow;
|
||||
_usageCount = 1;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public TimeSpan Duration { get; }
|
||||
|
||||
public DateTime LastActivity { get; private set; }
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.Count == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object? this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.TryGetValue(key, out var value) ? value : null;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
if (value == null)
|
||||
_data.Remove(key);
|
||||
else
|
||||
_data[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
_data.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRemove(string key, out object value)
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
if (!_data.TryGetValue(key, out value))
|
||||
return false;
|
||||
|
||||
_data.Remove(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<KeyValuePair<string, object>> TakeSnapshot()
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
return _data.TryGetValue(key, out value);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BeginUse()
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
_usageCount++;
|
||||
LastActivity = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EndUse(Action unregister)
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
--_usageCount;
|
||||
UnregisterIfNeededCore(unregister);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UnregisterIfNeeded(Action unregister)
|
||||
{
|
||||
lock (_data)
|
||||
{
|
||||
UnregisterIfNeededCore(unregister);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterIfNeededCore(Action unregister)
|
||||
{
|
||||
if (_usageCount < 1 && (IsEmpty || DateTime.UtcNow > LastActivity + Duration))
|
||||
unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
303
Vendor/EmbedIO-3.5.2/Sessions/LocalSessionManager.cs
vendored
Normal file
303
Vendor/EmbedIO-3.5.2/Sessions/LocalSessionManager.cs
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO.Utilities;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>A simple session manager to handle in-memory sessions.</para>
|
||||
/// <para>Not for intensive use or for distributed applications.</para>
|
||||
/// </summary>
|
||||
public partial class LocalSessionManager : ISessionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The default name for session cookies, i.e. <c>"__session"</c>.
|
||||
/// </summary>
|
||||
public const string DefaultCookieName = "__session";
|
||||
|
||||
/// <summary>
|
||||
/// The default path for session cookies, i.e. <c>"/"</c>.
|
||||
/// </summary>
|
||||
public const string DefaultCookiePath = "/";
|
||||
|
||||
/// <summary>
|
||||
/// The default HTTP-only flag for session cookies, i.e. <see langword="true"/>.
|
||||
/// </summary>
|
||||
public const bool DefaultCookieHttpOnly = true;
|
||||
|
||||
/// <summary>
|
||||
/// The default duration for session cookies, i.e. <see cref="TimeSpan.Zero"/>.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan DefaultCookieDuration = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The default duration for sessions, i.e. 30 minutes.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan DefaultSessionDuration = TimeSpan.FromMinutes(30);
|
||||
|
||||
/// <summary>
|
||||
/// The default interval between automatic purges of expired and empty sessions, i.e. 30 seconds.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan DefaultPurgeInterval = TimeSpan.FromSeconds(30);
|
||||
|
||||
private readonly ConcurrentDictionary<string, SessionImpl> _sessions =
|
||||
new ConcurrentDictionary<string, SessionImpl>(Session.KeyComparer);
|
||||
|
||||
private string _cookieName = DefaultCookieName;
|
||||
|
||||
private string _cookiePath = DefaultCookiePath;
|
||||
|
||||
private TimeSpan _cookieDuration = DefaultCookieDuration;
|
||||
|
||||
private bool _cookieHttpOnly = DefaultCookieHttpOnly;
|
||||
|
||||
private TimeSpan _sessionDuration = DefaultSessionDuration;
|
||||
|
||||
private TimeSpan _purgeInterval = DefaultPurgeInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalSessionManager"/> class
|
||||
/// with default values for all properties.
|
||||
/// </summary>
|
||||
/// <seealso cref="DefaultSessionDuration"/>
|
||||
/// <seealso cref="DefaultPurgeInterval"/>
|
||||
/// <seealso cref="DefaultCookieName"/>
|
||||
/// <seealso cref="DefaultCookiePath"/>
|
||||
/// <seealso cref="DefaultCookieDuration"/>
|
||||
/// <seealso cref="DefaultCookieHttpOnly"/>
|
||||
public LocalSessionManager()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of newly-created sessions.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <seealso cref="DefaultSessionDuration"/>
|
||||
public TimeSpan SessionDuration
|
||||
{
|
||||
get => _sessionDuration;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_sessionDuration = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between purges of expired sessions.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <seealso cref="DefaultPurgeInterval"/>
|
||||
public TimeSpan PurgeInterval
|
||||
{
|
||||
get => _purgeInterval;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_purgeInterval = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets or sets the name for session cookies.</para>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <exception cref="ArgumentNullException">This property is being set to <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">This property is being set and the provided value
|
||||
/// is not a valid URL path.</exception>
|
||||
/// <seealso cref="DefaultCookieName"/>
|
||||
public string CookieName
|
||||
{
|
||||
get => _cookieName;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_cookieName = Validate.Rfc2616Token(nameof(value), value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets or sets the path for session cookies.</para>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <exception cref="ArgumentNullException">This property is being set to <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">This property is being set and the provided value
|
||||
/// is not a valid URL path.</exception>
|
||||
/// <seealso cref="DefaultCookiePath"/>
|
||||
public string CookiePath
|
||||
{
|
||||
get => _cookiePath;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_cookiePath = Validate.UrlPath(nameof(value), value, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of session cookies.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <seealso cref="DefaultCookieDuration"/>
|
||||
public TimeSpan CookieDuration
|
||||
{
|
||||
get => _cookieDuration;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_cookieDuration = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether session cookies are hidden from Javascript code running on a user agent.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">This property is being set after calling
|
||||
/// the <see cref="Start"/> method.</exception>
|
||||
/// <seealso cref="DefaultCookieHttpOnly"/>
|
||||
public bool CookieHttpOnly
|
||||
{
|
||||
get => _cookieHttpOnly;
|
||||
set
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
_cookieHttpOnly = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ConfigurationLocked { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Start(CancellationToken cancellationToken)
|
||||
{
|
||||
ConfigurationLocked = true;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
PurgeExpiredAndEmptySessions();
|
||||
await Task.Delay(PurgeInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISession Create(IHttpContext context)
|
||||
{
|
||||
var id = context.Request.Cookies.FirstOrDefault(IsSessionCookie)?.Value.Trim();
|
||||
|
||||
SessionImpl session;
|
||||
lock (_sessions)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(id) && _sessions.TryGetValue(id!, out session))
|
||||
{
|
||||
session.BeginUse();
|
||||
}
|
||||
else
|
||||
{
|
||||
id = UniqueIdGenerator.GetNext();
|
||||
session = new SessionImpl(id, SessionDuration);
|
||||
_sessions.TryAdd(id, session);
|
||||
}
|
||||
}
|
||||
|
||||
context.Request.Cookies.Add(BuildSessionCookie(id));
|
||||
context.Response.Cookies.Add(BuildSessionCookie(id));
|
||||
return session;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Delete(IHttpContext context, string id)
|
||||
{
|
||||
lock (_sessions)
|
||||
{
|
||||
if (_sessions.TryGetValue(id, out var session))
|
||||
session.EndUse(() => _sessions.TryRemove(id, out _));
|
||||
}
|
||||
|
||||
context.Request.Cookies.Add(BuildSessionCookie(string.Empty));
|
||||
context.Response.Cookies.Add(BuildSessionCookie(string.Empty));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnContextClose(IHttpContext context)
|
||||
{
|
||||
if (!context.Session.Exists)
|
||||
return;
|
||||
|
||||
var id = context.Session.Id;
|
||||
lock (_sessions)
|
||||
{
|
||||
if (_sessions.TryGetValue(id, out var session))
|
||||
{
|
||||
session.EndUse(() => _sessions.TryRemove(id, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureConfigurationNotLocked()
|
||||
{
|
||||
if (ConfigurationLocked)
|
||||
throw new InvalidOperationException($"Cannot configure a {nameof(LocalSessionManager)} once it has been started.");
|
||||
}
|
||||
|
||||
private bool IsSessionCookie(Cookie cookie)
|
||||
=> cookie.Name.Equals(CookieName, StringComparison.OrdinalIgnoreCase)
|
||||
&& !cookie.Expired;
|
||||
|
||||
private Cookie BuildSessionCookie(string? id)
|
||||
{
|
||||
var cookie = new Cookie(CookieName, id, CookiePath)
|
||||
{
|
||||
HttpOnly = CookieHttpOnly,
|
||||
};
|
||||
|
||||
if (CookieDuration > TimeSpan.Zero)
|
||||
{
|
||||
cookie.Expires = DateTime.UtcNow.Add(CookieDuration);
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
private void PurgeExpiredAndEmptySessions()
|
||||
{
|
||||
string[] ids;
|
||||
lock (_sessions)
|
||||
{
|
||||
ids = _sessions.Keys.ToArray();
|
||||
}
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
lock (_sessions)
|
||||
{
|
||||
if (!_sessions.TryGetValue(id, out var session))
|
||||
return;
|
||||
|
||||
session.UnregisterIfNeeded(() => _sessions.TryRemove(id, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSessionId(IHttpContext context) => context.Request.Cookies.FirstOrDefault(IsSessionCookie)?.Value.Trim() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
35
Vendor/EmbedIO-3.5.2/Sessions/Session.cs
vendored
Normal file
35
Vendor/EmbedIO-3.5.2/Sessions/Session.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides useful constants related to session management.
|
||||
/// </summary>
|
||||
public static class Session
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The <seealso cref="StringComparison"/> used to disambiguate session IDs.</para>
|
||||
/// <para>Corresponds to <see cref="StringComparison.Ordinal"/>.</para>
|
||||
/// </summary>
|
||||
public const StringComparison IdComparison = StringComparison.Ordinal;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The <seealso cref="StringComparison"/> used to disambiguate session keys.</para>
|
||||
/// <para>Corresponds to <see cref="StringComparison.InvariantCulture"/>.</para>
|
||||
/// </summary>
|
||||
public const StringComparison KeyComparison = StringComparison.InvariantCulture;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The equality comparer used for session IDs.</para>
|
||||
/// <para>Corresponds to <see cref="StringComparer.Ordinal"/>.</para>
|
||||
/// </summary>
|
||||
public static readonly IEqualityComparer<string> IdComparer = StringComparer.Ordinal;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The equality comparer used for session keys.</para>
|
||||
/// <para>Corresponds to <see cref="StringComparer.InvariantCulture"/>.</para>
|
||||
/// </summary>
|
||||
public static readonly IEqualityComparer<string> KeyComparer = StringComparer.InvariantCulture;
|
||||
}
|
||||
}
|
||||
60
Vendor/EmbedIO-3.5.2/Sessions/SessionExtensions.cs
vendored
Normal file
60
Vendor/EmbedIO-3.5.2/Sessions/SessionExtensions.cs
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for types implementing <see cref="ISession"/>.
|
||||
/// </summary>
|
||||
public static class SessionExtensions
|
||||
{
|
||||
/// <summary>Gets the value associated with the specified key.</summary>
|
||||
/// <typeparam name="T">The desired type of the value.</typeparam>
|
||||
/// <param name="this">The <see cref="ISession"/> on which this method is called.</param>
|
||||
/// <param name="key">The key whose value to get from the session.</param>
|
||||
/// <param name="value">
|
||||
/// <para>When this method returns, the value associated with the specified key,
|
||||
/// if the key is found and the associated value is of type <typeparamref name="T"/>;
|
||||
/// otherwise, the default value for <typeparamref name="T"/>.</para>
|
||||
/// <para>This parameter is passed uninitialized.</para>
|
||||
/// </param>
|
||||
/// <returns><see langword="true"/> if the key is found and the associated value is of type <typeparamref name="T"/>;
|
||||
/// otherwise, <see langword="false"/>.</returns>
|
||||
/// <exception cref="NullReferenceException"><paramref name="this"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
|
||||
public static bool TryGetValue<T>(this ISession @this, string key, out T value)
|
||||
{
|
||||
if (@this.TryGetValue(key, out var foundValue) && foundValue is T typedValue)
|
||||
{
|
||||
value = typedValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning disable CS8653 // "default" can be null - We are returning false, so value is undefined
|
||||
value = default;
|
||||
#pragma warning restore CS8653
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Gets the value associated with the specified key.</summary>
|
||||
/// <typeparam name="T">The desired type of the value.</typeparam>
|
||||
/// <param name="this">The <see cref="ISession"/> on which this method is called.</param>
|
||||
/// <param name="key">The key whose value to get from the session.</param>
|
||||
/// <returns>The value associated with the specified key,
|
||||
/// if the key is found and the associated value is of type <typeparamref name="T"/>;
|
||||
/// otherwise, the default value for <typeparamref name="T"/>.</returns>
|
||||
public static T GetValue<T>(this ISession @this, string key)
|
||||
=> @this.TryGetValue(key, out var value) && value is T typedValue ? typedValue : default;
|
||||
|
||||
/// <summary>Gets the value associated with the specified key.</summary>
|
||||
/// <typeparam name="T">The desired type of the value.</typeparam>
|
||||
/// <param name="this">The <see cref="ISession"/> on which this method is called.</param>
|
||||
/// <param name="key">The key whose value to get from the session.</param>
|
||||
/// <param name="defaultValue">The default value to return if the key is not found
|
||||
/// or its associated value is not of type <typeparamref name="T"/>.</param>
|
||||
/// <returns>The value associated with the specified key,
|
||||
/// if the key is found and the associated value is of type <typeparamref name="T"/>;
|
||||
/// otherwise, <paramref name="defaultValue"/>.</returns>
|
||||
public static T GetOrDefault<T>(this ISession @this, string key, T defaultValue)
|
||||
=> @this.TryGetValue(key, out var value) && value is T typedValue ? typedValue : defaultValue;
|
||||
}
|
||||
}
|
||||
167
Vendor/EmbedIO-3.5.2/Sessions/SessionProxy.cs
vendored
Normal file
167
Vendor/EmbedIO-3.5.2/Sessions/SessionProxy.cs
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using EmbedIO.Sessions.Internal;
|
||||
|
||||
namespace EmbedIO.Sessions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the same interface as a session object,
|
||||
/// plus a basic interface to a session manager.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A session proxy can be used just as if it were a session object.
|
||||
/// A session is automatically created wherever its data are accessed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ISessionProxy" />
|
||||
public sealed class SessionProxy : ISessionProxy
|
||||
{
|
||||
private readonly IHttpContext _context;
|
||||
private readonly ISessionManager? _sessionManager;
|
||||
|
||||
private ISession? _session;
|
||||
private bool _onCloseRegistered;
|
||||
|
||||
internal SessionProxy(IHttpContext context, ISessionManager? sessionManager)
|
||||
{
|
||||
_context = context;
|
||||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a "dummy" <see cref="ISessionProxy"/> interface that will always behave as if no session manager has been defined.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The returned <see cref="ISessionProxy"/> interface is only useful
|
||||
/// to initialize a non-nullable property of type <see cref="ISessionProxy"/>.</para>
|
||||
/// </remarks>
|
||||
public static ISessionProxy None => DummySessionProxy.Instance;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Exists => _session != null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.Id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.Duration;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastActivity
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.LastActivity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count => _session?.Count ?? 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsEmpty => _session?.IsEmpty ?? true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session![key];
|
||||
}
|
||||
set
|
||||
{
|
||||
EnsureSessionExists();
|
||||
_session![key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete()
|
||||
{
|
||||
EnsureSessionExists();
|
||||
|
||||
if (_session == null)
|
||||
return;
|
||||
|
||||
_sessionManager!.Delete(_context, _session.Id);
|
||||
_session = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Regenerate()
|
||||
{
|
||||
if (_session != null)
|
||||
_sessionManager!.Delete(_context, _session.Id);
|
||||
|
||||
EnsureSessionManagerExists();
|
||||
_session = _sessionManager!.Create(_context);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Clear() => _session?.Clear();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryRemove(string key, out object value)
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.TryRemove(key, out value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyList<KeyValuePair<string, object>> TakeSnapshot()
|
||||
{
|
||||
EnsureSessionExists();
|
||||
return _session!.TakeSnapshot();
|
||||
}
|
||||
|
||||
private void EnsureSessionManagerExists()
|
||||
{
|
||||
if (_sessionManager == null)
|
||||
throw new InvalidOperationException("No session manager registered in the web server.");
|
||||
}
|
||||
|
||||
private void EnsureSessionExists()
|
||||
{
|
||||
if (_session != null)
|
||||
return;
|
||||
|
||||
EnsureSessionManagerExists();
|
||||
_session = _sessionManager!.Create(_context);
|
||||
|
||||
if (_onCloseRegistered)
|
||||
return;
|
||||
|
||||
_context.OnClose(_sessionManager.OnContextClose);
|
||||
_onCloseRegistered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user