diff options
Diffstat (limited to 'libs/VNLib.Plugins.Sessions.VNCache/src/WebSessionStore.cs')
-rw-r--r-- | libs/VNLib.Plugins.Sessions.VNCache/src/WebSessionStore.cs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/libs/VNLib.Plugins.Sessions.VNCache/src/WebSessionStore.cs b/libs/VNLib.Plugins.Sessions.VNCache/src/WebSessionStore.cs new file mode 100644 index 0000000..6560f57 --- /dev/null +++ b/libs/VNLib.Plugins.Sessions.VNCache/src/WebSessionStore.cs @@ -0,0 +1,150 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.Sessions.VNCache +* File: WebSessionStore.cs +* +* WebSessionStore.cs is part of VNLib.Plugins.Essentials.Sessions.VNCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.Sessions.VNCache is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.Sessions.VNCache is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +using VNLib.Net.Http; +using VNLib.Hashing; +using VNLib.Utils.Logging; +using VNLib.Data.Caching; +using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Sessions.Cache.Client; +using VNLib.Plugins.Extensions.VNCache; +using VNLib.Plugins.Essentials.Sessions; + +namespace VNLib.Plugins.Sessions.VNCache +{ + [ConfigurationName(WebSessionProviderEntry.WEB_SESSION_CONFIG)] + internal sealed class WebSessionStore : SessionStore<WebSession> + { + private ILogProvider? baseLog; + + protected override ISessionIdFactory IdFactory { get; } + protected override IRemoteCacheStore Cache { get; } + protected override ISessionFactory<WebSession> SessionFactory { get; } + protected override ILogProvider Log => baseLog!; + + public WebSessionStore(PluginBase pbase, IConfigScope config) + { + //Get id factory + IdFactory = pbase.GetOrCreateSingleton<WebSessionIdFactory>(); + + //Session factory + SessionFactory = new WebSessionFactory(); + + /* + * Init prefixed cache, a prefix key is required from + * the config + */ + + string cachePrefix = config["cache_prefix"].GetString() + ?? throw new KeyNotFoundException($"Missing required element 'cache_prefix' for config '{WebSessionProviderEntry.WEB_SESSION_CONFIG}'"); + + //Create a simple prefix cache provider + IGlobalCacheProvider cache = pbase.GetOrCreateSingleton<VnGlobalCache>() + .GetPrefixedCache(cachePrefix, HashAlg.SHA256); + + //Create cache store from global cache + Cache = new GlobalCacheStore(cache); + + //Default log to plugin log + baseLog = pbase.Log; + } + + public void InitLog(ILogProvider log) + { + baseLog = log; + } + + /// <summary> + /// A value that indicates if the remote cache client is connected + /// </summary> + public bool IsConnected => Cache.IsConnected; + + public override ValueTask ReleaseSessionAsync(WebSession session, IHttpEvent entity) + { + //Get status flags first + SessionStatus status = session.GetStatus(); + + //If status is delete, we need to invalidate the session, and copy its security information + if(status.HasFlag(SessionStatus.Delete)) + { + //Run delete/cleanup + Task delete = DeleteSessionAsync(session); + + //Regenid and create new session + string newId = IdFactory.RegenerateId(entity); + + //Get new session empty session for the connection + WebSession newSession = SessionFactory.GetNewSession(entity, newId, null); + + //Reset security information for new session + newSession.InitNewSession(entity.Server); + + IDictionary<string, string> data = newSession.GetSessionData(); + + //commit session to cache + Task add = Cache.AddOrUpdateObjectAsync(newId, null, data); + + /* + * Call complete on session for good practice, this SHOULD be + * called after the update has been awaited though. + * + * We also do not need to use the mutal exclusion mechanism because + * no other connections should have this session's id yet. + */ + newSession.SessionUpdateComplete(); + + //Await the invalidation async + return new(AwaitInvalidate(delete, add)); + } + else + { + return base.ReleaseSessionAsync(session, entity); + } + } + + private static async Task AwaitInvalidate(Task delete, Task addNew) + { + try + { + await Task.WhenAll(delete, addNew); + } + catch (OperationCanceledException) + { + throw; + } + catch (SessionException) + { + throw; + } + catch(Exception ex) + { + throw new SessionException("An exception occured during session invalidation", ex); + } + } + } +} |