/* * Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Sessions.Cache.Client * File: RemoteSession.cs * * RemoteSession.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger * VNLib collection of libraries and utilities. * * VNLib.Plugins.Sessions.Cache.Client 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.Sessions.Cache.Client 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.Collections.Generic; using VNLib.Utils.Extensions; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Extensions; namespace VNLib.Plugins.Sessions.Cache.Client { /// /// Base class for cacheable lazy initialized session entires /// that exist in a remote caching server /// public abstract class RemoteSession : SessionBase, IRemoteSession { protected const string CREATED_TIME_ENTRY = "__.i.ctime"; /// /// The session data store /// protected readonly IDictionary DataStore; /// /// The reason that the session was destroyed if an error occured /// protected Exception? ErrorCause { get; set; } /// /// Initialzies a new session /// /// The id of the current session /// The initial data /// A flag that determines if the session is considered new protected RemoteSession(string sessionId, IDictionary initialData, bool isNew) { SessionID = sessionId; DataStore = initialData; IsNew = isNew; } /// public override string SessionID { get; } /// public override DateTimeOffset Created { get { //Deserialze the base32 ms long unixMs = this.GetValueType(CREATED_TIME_ENTRY); //set created time from ms return DateTimeOffset.FromUnixTimeMilliseconds(unixMs); } set => this.SetValueType(CREATED_TIME_ENTRY, value.ToUnixTimeMilliseconds()); } /// protected override string IndexerGet(string key) { //Get the value at the key or an empty string as a default return DataStore.GetValueOrDefault(key) ?? string.Empty; } /// protected override void IndexerSet(string key, string value) { //If the value is null, remove the key from the store if (value == null) { //Set modified flag IsModified |= DataStore.Remove(key); } else { //Store the value at the specified key DataStore[key] = value; IsModified = true; } } /// public virtual SessionStatus GetStatus() { SessionStatus status = SessionStatus.None; status |= Flags.IsSet(INVALID_MSK) ? SessionStatus.Delete : SessionStatus.None; status |= Flags.IsSet(REGEN_ID_MSK) ? SessionStatus.RegenId : SessionStatus.None; status |= Flags.IsSet(MODIFIED_MSK) ? SessionStatus.UpdateOnly: SessionStatus.None; status |= Flags.IsSet(DETACHED_MSK) ? SessionStatus.Detach: SessionStatus.None; return status; } /// public virtual IDictionary GetSessionData() => DataStore; /// public virtual void Destroy(Exception? cause) { //Set invalid status ErrorCause = cause; Flags.Set(INVALID_MSK); //Clear all session data DataStore.Clear(); } /// public virtual bool IsValid(out Exception? cause) { /* * Were reusing the invalid mask assuming that when a session is invalidated * it will be deleted and destroyed */ cause = ErrorCause; return !Flags.IsSet(INVALID_MSK); } /// public virtual void SessionUpdateComplete() { //Reset flags Flags.ClearAll(); } } }