/*
* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: SessionProvider
* File: SessionProviderEntry.cs
*
* SessionProviderEntry.cs is part of SessionProvider which is part of the larger
* VNLib collection of libraries and utilities.
*
* SessionProvider 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.
*
* SessionProvider 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.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.ComponentModel.Design;
using VNLib.Net.Http;
using VNLib.Utils;
using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Attributes;
using VNLib.Plugins.Extensions.Loading;
namespace VNLib.Plugins.Essentials.Sessions
{
///
/// The implementation type for dynamic loading of unified session providers
///
public sealed class SessionProviderEntry : PluginBase
{
///
public override string PluginName => "Essentials.Sessions";
private SessionProvider _provider = new();
/*
* Declare a service configuration method to
* expose the session provider, the service container
* will dispose the provider instance
*/
[ServiceConfigurator]
public void ConfigureServices(IServiceContainer services)
{
//publish the service
services.AddService(_provider);
}
protected override void OnLoad()
{
List providers = new();
try
{
Log.Verbose("Loading all specified session providers");
//Get all provider names
IEnumerable providerAssemblyNames = PluginConfig.GetProperty("provider_assemblies")
.EnumerateArray()
.Where(s => s.GetString() != null)
.Select(s => s.GetString()!);
foreach(string asm in providerAssemblyNames)
{
Log.Verbose("Loading {dll} session provider", asm);
//Attempt to load provider
AssemblyLoader prov = this.LoadAssembly(asm);
try
{
//Create localized log
ILogProvider scopded = Log.CreateScope(Path.GetFileName(asm));
RuntimeSessionProvider p = new(prov);
//Call load method
p.Load(this, scopded);
//Add to list
providers.Add(p);
}
catch
{
prov.Dispose();
throw;
}
}
if(providers.Count > 0)
{
//Create array for searching for providers
_provider = new(providers.ToArray());
Log.Information("Loaded {count} session providers", providers.Count);
}
else
{
Log.Information("No session providers loaded");
}
Log.Information("Plugin loaded");
}
catch
{
//Dispose providers
providers.ForEach(static s => s.Dispose());
throw;
}
}
protected override void OnUnLoad()
{
Log.Information("Plugin unloaded");
}
protected override void ProcessHostCommand(string cmd)
{
if (!this.IsDebug())
{
return;
}
}
/*
* When exposing the session provider as a service, it may be disposed by the
* service container if its delcared as disposable.
*/
private sealed class SessionProvider : VnDisposeable, ISessionProvider, IDisposable
{
//Default to an empty array for default support even if no runtime providers are loaded
private RuntimeSessionProvider[] ProviderArray = Array.Empty();
public SessionProvider(RuntimeSessionProvider[] loaded)
{
ProviderArray = loaded;
}
public SessionProvider()
{ }
ValueTask ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken)
{
//Loop through providers
for (int i = 0; i < ProviderArray.Length; i++)
{
//Check if provider can process the entity
if (ProviderArray[i].CanProcess(entity))
{
//Get session
return ProviderArray[i].GetSessionAsync(entity, cancellationToken);
}
}
//Return empty session
return new ValueTask(SessionHandle.Empty);
}
protected override void Free()
{
//Remove current providers so we can dispose them
RuntimeSessionProvider[] current = Interlocked.Exchange(ref ProviderArray, Array.Empty());
//Cleanup assemblies
current.TryForeach(static p => p.Dispose());
}
}
}
}