diff options
author | vman <public@vaughnnugent.com> | 2022-12-28 14:19:32 -0500 |
---|---|---|
committer | vman <public@vaughnnugent.com> | 2022-12-28 14:19:32 -0500 |
commit | 9bb5ddd8f19c0ecabd7af4ee58d80c16826bc183 (patch) | |
tree | 7f8a3863d18f4673294ab11e427fe0b77fd70abf /Libs | |
parent | 1f2b3530ebeafa162fe4df41e691c33cb2ff0009 (diff) |
Cache provider abstractions, reduced deps
Diffstat (limited to 'Libs')
17 files changed, 201 insertions, 379 deletions
diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs index f4462a4..7e72714 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs @@ -27,12 +27,14 @@ using System.Text.Json; using VNLib.Net.Http; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; -using VNLib.Data.Caching.Extensions; +using VNLib.Data.Caching; +using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Oauth.Tokens; using VNLib.Plugins.Essentials.Oauth.Applications; using VNLib.Plugins.Essentials.Sessions.OAuth; using VNLib.Plugins.Essentials.Sessions.Runtime; using VNLib.Plugins.Essentials.Sessions.OAuth.Endpoints; +using VNLib.Plugins.Extensions.VNCache; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Routing; using VNLib.Plugins.Extensions.Loading.Sql; @@ -44,7 +46,6 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth public sealed class O2SessionProviderEntry : IRuntimeSessionProvider { - const string VNCACHE_CONFIG_KEY = "vncache"; const string OAUTH2_CONFIG_KEY = "oauth2"; private OAuth2SessionProvider? _sessions; @@ -63,14 +64,11 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) { - //Try get vncache config element - IReadOnlyDictionary<string, JsonElement> cacheConfig = plugin.GetConfig(VNCACHE_CONFIG_KEY); - - IReadOnlyDictionary<string, JsonElement> oauth2Config = plugin.GetConfig(OAUTH2_CONFIG_KEY); + IReadOnlyDictionary<string, JsonElement> oauth2Config = plugin.GetConfigForType<OAuth2SessionProvider>(); //Optional application jwt token Task<JsonDocument?> jwtTokenSecret = plugin.TryGetSecretAsync("application_token_key") - .ContinueWith(static t => t.Result == null ? null : t.Result.GetJsonDocument(), TaskScheduler.Default); + .ContinueWith(static t => t.Result?.GetJsonDocument(), TaskScheduler.Default); //Access token endpoint is optional if (oauth2Config.TryGetValue("token_path", out JsonElement el)) @@ -89,74 +87,38 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth plugin.Route<RevocationEndpoint>(); } - //Run - _ = plugin.DeferTask(() => CacheWokerDoWorkAsync(plugin, localized, cacheConfig, oauth2Config), 100); - - } + int cacheLimit = oauth2Config["cache_size"].GetInt32(); + int maxTokensPerApp = oauth2Config["max_tokens_per_app"].GetInt32(); + int sessionIdSize = (int)oauth2Config["access_token_size"].GetUInt32(); + TimeSpan tokenValidFor = oauth2Config["token_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); + TimeSpan cleanupInterval = oauth2Config["gc_interval_sec"].GetTimeSpan(TimeParseType.Seconds); + string sessionIdPrefix = oauth2Config["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'cache_prefix' in '{OAUTH2_CONFIG_KEY}' config"); - private async Task<IOAuth2TokenResult?> CreateTokenDelegateAsync(HttpEntity entity, UserApplication app, CancellationToken cancellation) - { - return await _sessions!.CreateAccessTokenAsync(entity, app, cancellation).ConfigureAwait(false); - } + //init the id provider + OAuth2SessionIdProvider idProv = new(sessionIdPrefix, maxTokensPerApp, sessionIdSize, tokenValidFor); - /* - * Starts and monitors the VNCache connection - */ + //Get shared global-cache + IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - private async Task CacheWokerDoWorkAsync(PluginBase plugin, ILogProvider localized, - IReadOnlyDictionary<string, JsonElement> cacheConfig, - IReadOnlyDictionary<string, JsonElement> oauth2Config) - { - //Init cache client - using VnCacheClient cache = new(plugin.IsDebug() ? localized : null, Utils.Memory.Memory.Shared); - - try - { - int cacheLimit = oauth2Config["cache_size"].GetInt32(); - int maxTokensPerApp = oauth2Config["max_tokens_per_app"].GetInt32(); - int sessionIdSize = (int)oauth2Config["access_token_size"].GetUInt32(); - TimeSpan tokenValidFor = oauth2Config["token_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); - TimeSpan cleanupInterval = oauth2Config["gc_interval_sec"].GetTimeSpan(TimeParseType.Seconds); - string sessionIdPrefix = oauth2Config["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'cache_prefix' in '{OAUTH2_CONFIG_KEY}' config"); - - //init the id provider - OAuth2SessionIdProvider idProv = new(sessionIdPrefix, maxTokensPerApp, sessionIdSize, tokenValidFor); - - //Try loading config - await cache.LoadConfigAsync(plugin, cacheConfig); + //Create cache store from global cache + GlobalCacheStore cacheStore = new(globalCache); - //Init session provider now that client is loaded - _sessions = new(cache.Resource!, cacheLimit, 100, idProv, plugin.GetContextOptions()); + //Init session provider now that client is loaded + _sessions = new(cacheStore, cacheLimit, 100, idProv, plugin.GetContextOptions()); - //Schedule cleanup interval with the plugin scheduler - plugin.ScheduleInterval(_sessions, cleanupInterval); + //Schedule cleanup interval with the plugin scheduler + plugin.ScheduleInterval(_sessions, cleanupInterval); - localized.Information("Session provider loaded"); + //Wait and cleanup expired sessions + _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - //Run and wait for exit - await cache.RunAsync(localized, plugin.UnloadToken); + localized.Information("Session provider loaded"); - } - catch (OperationCanceledException) - {} - catch (KeyNotFoundException e) - { - localized.Error("Missing required configuration variable for VnCache client: {0}", e.Message); - } - catch(FBMServerNegiationException fne) - { - localized.Error("Failed to negotiate connection with cache server {reason}", fne.Message); - } - catch (Exception ex) - { - localized.Error(ex, "Cache client error occured in session provider"); - } - finally - { - _sessions = null; - } + } - localized.Information("Cache client exited"); + private async Task<IOAuth2TokenResult?> CreateTokenDelegateAsync(HttpEntity entity, UserApplication app, CancellationToken cancellation) + { + return await _sessions!.CreateAccessTokenAsync(entity, app, cancellation).ConfigureAwait(false); } } }
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs index 539799c..e9a69cd 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs @@ -25,7 +25,6 @@ using System; using VNLib.Net.Http; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Sessions.Cache.Client.Exceptions; @@ -44,10 +43,10 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth /// Initalizes a new <see cref="OAuth2Session"/> /// </summary> /// <param name="sessionId">The session id (or token)</param> - /// <param name="client">The <see cref="FBMClient"/> used as the backing cache provider</param> + /// <param name="client">The <see cref="IRemoteCacheStore"/> used as the backing cache provider</param> /// <param name="backgroundTimeOut">The ammount of time to wait for a background operation (delete, update, get)</param> /// <param name="invalidCache">Called when the session has been marked as invalid and the close even hook is being executed</param> - public OAuth2Session(string sessionId, FBMClient client, TimeSpan backgroundTimeOut, Action<OAuth2Session> invalidCache) + public OAuth2Session(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Action<OAuth2Session> invalidCache) : base(sessionId, client, backgroundTimeOut) { InvalidateCache = invalidCache; diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs index 106029f..dca7909 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs @@ -30,14 +30,13 @@ using Microsoft.EntityFrameworkCore; using VNLib.Net.Http; using VNLib.Utils; using VNLib.Utils.Logging; -using VNLib.Data.Caching; using VNLib.Data.Caching.Exceptions; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Oauth; using VNLib.Plugins.Essentials.Oauth.Tokens; using VNLib.Plugins.Essentials.Oauth.Applications; using VNLib.Plugins.Extensions.Loading.Events; +using VNLib.Plugins.Extensions.Loading; namespace VNLib.Plugins.Essentials.Sessions.OAuth { @@ -45,7 +44,8 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth /// <summary> /// Provides OAuth2 session management /// </summary> - internal sealed class OAuth2SessionProvider : SessionCacheClient, ISessionProvider, ITokenManager, IIntervalScheduleable + [ConfigurationName("oauth2")] + internal sealed class OAuth2SessionProvider : SessionCacheClient, ITokenManager, IIntervalScheduleable { private static readonly SessionHandle NotFoundHandle = new(null, FileProcessArgs.NotFound, null); @@ -57,7 +57,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth private readonly TokenStore TokenStore; private readonly uint MaxConnections; - public OAuth2SessionProvider(FBMClient client, int maxCacheItems, uint maxConnections, IOauthSessionIdFactory idFactory, DbContextOptions dbCtx) + public OAuth2SessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxConnections, IOauthSessionIdFactory idFactory, DbContextOptions dbCtx) : base(client, maxCacheItems) { factory = idFactory; @@ -66,7 +66,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth } ///<inheritdoc/> - protected override RemoteSession SessionCtor(string sessionId) => new OAuth2Session(sessionId, Client, BackgroundTimeout, InvalidatateCache); + protected override RemoteSession SessionCtor(string sessionId) => new OAuth2Session(sessionId, Store, BackgroundTimeout, InvalidatateCache); private void InvalidatateCache(OAuth2Session session) { @@ -97,7 +97,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth if (WaitingConnections > MaxConnections) { //Set 503 for temporary unavail - entity.CloseResponse(System.Net.HttpStatusCode.ServiceUnavailable); + entity.CloseResponse(HttpStatusCode.ServiceUnavailable); return new SessionHandle(null, FileProcessArgs.VirtualSkip, null); } @@ -202,7 +202,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth try { //Remove tokens by thier object id from cache - await base.Client.DeleteObjectAsync(token.Id, cancellationToken); + await base.Store.DeleteObjectAsync(token.Id, cancellationToken); } //Ignore if the object has already been removed catch (ObjectNotFoundException) diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj index e9927b5..d6775c8 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj @@ -13,7 +13,8 @@ <Version>1.0.1.1</Version> <PackageProjectUrl>https://www.vaughnugent.com</PackageProjectUrl> <AnalysisLevel>latest-all</AnalysisLevel> - + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -34,11 +35,10 @@ <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> <ProjectReference Include="..\..\..\..\VNLib\Plugins\src\VNLib.Plugins.csproj" /> <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching.Extensions\VNLib.Data.Caching.Extensions.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\src\VNLib.Data.Caching.csproj" /> <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" /> <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" /> + <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.VNCache\VNLib.Plugins.Extensions.VNCache.csproj" /> <ProjectReference Include="..\..\..\Oauth\Libs\VNLib.Plugins.Essentials.Oauth\VNLib.Plugins.Essentials.Oauth.csproj" /> <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> <ProjectReference Include="..\VNLib.Plugins.Essentials.Sessions.Runtime\VNLib.Plugins.Essentials.Sessions.Runtime.csproj" /> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj index cf6d0c9..87c29e8 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj @@ -6,17 +6,22 @@ <Nullable>enable</Nullable> <Authors>Vaughn Nugent</Authors> <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <Version>1.0.1.1</Version> + <Version>1.0.1.2</Version> <SignAssembly>False</SignAssembly> <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - <GenerateDocumentationFile>True</GenerateDocumentationFile> <AnalysisLevel>latest-all</AnalysisLevel> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" /> + + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'" /> + <ItemGroup> <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching.Extensions\VNLib.Data.Caching.Extensions.csproj" /> <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> </ItemGroup> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs deleted file mode 100644 index 2f7bdf2..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs +++ /dev/null @@ -1,201 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Runtime -* File: VnCacheClient.cs -* -* VnCacheClient.cs is part of VNLib.Plugins.Essentials.Sessions.Runtime which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Runtime 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.Runtime 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.Text.Json; -using System.Net.Sockets; -using System.Net.WebSockets; -using System.Security.Cryptography; - -using VNLib.Utils.Memory; -using VNLib.Utils.Logging; -using VNLib.Utils.Resources; -using VNLib.Utils.Extensions; -using VNLib.Data.Caching.Extensions; -using VNLib.Net.Messaging.FBM.Client; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Hashing.IdentityUtility; - -namespace VNLib.Plugins.Essentials.Sessions.Runtime -{ - /// <summary> - /// A wrapper to simplify a cache client object - /// </summary> - public sealed class VnCacheClient : OpenResourceHandle<FBMClient?> - { - FBMClient? _client; - - /// <summary> - /// The wrapped client - /// </summary> - public override FBMClient? Resource => _client; - - - private TimeSpan RetryInterval; - - private readonly ILogProvider? DebugLog; - private readonly IUnmangedHeap? ClientHeap; - - /// <summary> - /// Initializes an emtpy client wrapper that still requires - /// configuration loading - /// </summary> - /// <param name="debugLog">An optional debugging log</param> - /// <param name="heap">An optional <see cref="IUnmangedHeap"/> for <see cref="FBMClient"/> buffers</param> - public VnCacheClient(ILogProvider? debugLog, IUnmangedHeap? heap = null) - { - DebugLog = debugLog; - //Default to 10 seconds - RetryInterval = TimeSpan.FromSeconds(10); - - ClientHeap = heap; - } - - ///<inheritdoc/> - protected override void Free() - { - _client?.Dispose(); - _client = null; - } - - /// <summary> - /// Loads required configuration variables from the config store and - /// intializes the interal client - /// </summary> - /// <param name="pbase"></param> - /// <param name="config">A dictionary of configuration varables</param> - /// <exception cref="KeyNotFoundException"></exception> - public async Task LoadConfigAsync(PluginBase pbase, IReadOnlyDictionary<string, JsonElement> config) - { - int maxMessageSize = config["max_message_size"].GetInt32(); - string? brokerAddress = config["broker_address"].GetString() ?? throw new KeyNotFoundException("Missing required configuration variable broker_address"); - - //Get keys async - Task<SecretResult?> clientPrivTask = pbase.TryGetSecretAsync("client_private_key"); - Task<SecretResult?> brokerPubTask = pbase.TryGetSecretAsync("broker_public_key"); - Task<SecretResult?> cachePubTask = pbase.TryGetSecretAsync("cache_public_key"); - - //Wait for all tasks to complete - _ = await Task.WhenAll(clientPrivTask, brokerPubTask, cachePubTask); - - using SecretResult clientPriv = await clientPrivTask ?? throw new KeyNotFoundException("Missing required secret client_private_key"); - using SecretResult brokerPub = await brokerPubTask ?? throw new KeyNotFoundException("Missing required secret broker_public_key"); - using SecretResult cachePub = await cachePubTask ?? throw new KeyNotFoundException("Missing required secret cache_public_key"); - - ReadOnlyJsonWebKey clientCert = clientPriv.GetJsonWebKey(); - ReadOnlyJsonWebKey brokerPubKey = brokerPub.GetJsonWebKey(); - ReadOnlyJsonWebKey cachePubKey = cachePub.GetJsonWebKey(); - - RetryInterval = config["retry_interval_sec"].GetTimeSpan(TimeParseType.Seconds); - - Uri brokerUri = new(brokerAddress); - - //Init the client with default settings - FBMClientConfig conf = FBMDataCacheExtensions.GetDefaultConfig(ClientHeap ?? Memory.Shared, maxMessageSize, DebugLog); - - _client = new(conf); - - //Add the configuration to the client - _client.GetCacheConfiguration() - .WithBroker(brokerUri) - .WithVerificationKey(cachePubKey) - .WithSigningCertificate(clientCert) - .WithBrokerVerificationKey(brokerPubKey) - .WithTls(brokerUri.Scheme == Uri.UriSchemeHttps); - } - - /// <summary> - /// Discovers nodes in the configured cluster and connects to a random node - /// </summary> - /// <param name="Log">A <see cref="ILogProvider"/> to write log events to</param> - /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A task that completes when the operation has been cancelled or an unrecoverable error occured</returns> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="OperationCanceledException"></exception> - public async Task RunAsync(ILogProvider Log, CancellationToken cancellationToken) - { - _ = Resource ?? throw new InvalidOperationException("Client configuration not loaded, cannot connect to cache servers"); - - while (true) - { - //Load the server list - ActiveServer[]? servers; - while (true) - { - try - { - Log.Debug("Discovering cluster nodes in broker"); - //Get server list - servers = await Resource.DiscoverCacheNodesAsync(cancellationToken); - break; - } - catch (HttpRequestException re) when (re.InnerException is SocketException) - { - Log.Warn("Broker server is unreachable"); - } - catch (Exception ex) - { - Log.Warn("Failed to get server list from broker, reason {r}", ex.Message); - } - - //Gen random ms delay - int randomMsDelay = RandomNumberGenerator.GetInt32(1000, 2000); - await Task.Delay(randomMsDelay, cancellationToken); - } - - if (servers?.Length == 0) - { - Log.Warn("No cluster nodes found, retrying"); - await Task.Delay(RetryInterval, cancellationToken); - continue; - } - - try - { - Log.Debug("Connecting to random cache server"); - - //Connect to a random server - ActiveServer selected = await Resource.ConnectToRandomCacheAsync(cancellationToken); - Log.Debug("Connected to cache server {s}", selected.ServerId); - - //Wait for disconnect - await Resource.WaitForExitAsync(cancellationToken); - - Log.Debug("Cache server disconnected"); - } - catch (WebSocketException wse) - { - Log.Warn("Failed to connect to cache server {reason}", wse.Message); - continue; - } - catch (HttpRequestException he) when (he.InnerException is SocketException) - { - Log.Debug("Failed to connect to random cache server server"); - //Continue next loop - continue; - } - } - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj index dc73ed8..9895eea 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj @@ -14,6 +14,8 @@ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <GenerateDocumentationFile>True</GenerateDocumentationFile> <AnalysisLevel>latest-all</AnalysisLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> @@ -23,9 +25,8 @@ <ItemGroup> <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching.Extensions\VNLib.Data.Caching.Extensions.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\src\VNLib.Data.Caching.csproj" /> <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" /> + <ProjectReference Include="..\..\..\Extensions\VNLib.Plugins.Extensions.VNCache\VNLib.Plugins.Extensions.VNCache.csproj" /> <ProjectReference Include="..\..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" /> <ProjectReference Include="..\VNLib.Plugins.Essentials.Sessions.Runtime\VNLib.Plugins.Essentials.Sessions.Runtime.csproj" /> <ProjectReference Include="..\VNLib.Plugins.Sessions.Cache.Client\VNLib.Plugins.Sessions.Cache.Client.csproj" /> diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs index cd5657d..87e08e7 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs @@ -23,8 +23,6 @@ */ using VNLib.Net.Http; -using VNLib.Data.Caching; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Extensions; using VNLib.Plugins.Sessions.Cache.Client; using static VNLib.Plugins.Essentials.Sessions.ISessionExtensions; @@ -38,7 +36,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache protected readonly Func<IHttpEvent, string, string> UpdateId; private string? _oldId; - public WebSession(string sessionId, FBMClient client, TimeSpan backgroundTimeOut, Func<IHttpEvent, string, string> UpdateId) + public WebSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Func<IHttpEvent, string, string> UpdateId) : base(sessionId, client, backgroundTimeOut) { this.UpdateId = UpdateId; diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs index 5747175..ebc3c3d 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs @@ -25,7 +25,7 @@ using System; using VNLib.Net.Http; -using VNLib.Net.Messaging.FBM.Client; +using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Sessions.Cache.Client; namespace VNLib.Plugins.Essentials.Sessions.VNCache @@ -33,6 +33,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache /// <summary> /// The implementation of a VNCache web based session /// </summary> + [ConfigurationName("web")] internal sealed class WebSessionProvider : SessionCacheClient, ISessionProvider { static readonly TimeSpan BackgroundUpdateTimeout = TimeSpan.FromSeconds(10); @@ -47,7 +48,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache /// <param name="maxCacheItems">The max number of items to store in cache</param> /// <param name="maxWaiting">The maxium number of waiting session events before 503s are sent</param> /// <param name="factory">The session-id factory</param> - public WebSessionProvider(FBMClient client, int maxCacheItems, uint maxWaiting, IWebSessionIdFactory factory) : base(client, maxCacheItems) + public WebSessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxWaiting, IWebSessionIdFactory factory) : base(client, maxCacheItems) { this.factory = factory; MaxConnections = maxWaiting; @@ -69,7 +70,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache return newid; } - protected override RemoteSession SessionCtor(string sessionId) => new WebSession(sessionId, Client, BackgroundUpdateTimeout, UpdateSessionId); + protected override RemoteSession SessionCtor(string sessionId) => new WebSession(sessionId, Store, BackgroundUpdateTimeout, UpdateSessionId); public async ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) { diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs index 8e25416..309db47 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs @@ -25,29 +25,26 @@ using System.Text.Json; using VNLib.Net.Http; -using VNLib.Utils.Memory; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; -using VNLib.Plugins.Extensions.Loading; +using VNLib.Data.Caching; +using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Data.Caching.Extensions; +using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Extensions.VNCache; namespace VNLib.Plugins.Essentials.Sessions.VNCache { public sealed class WebSessionProviderEntry : IRuntimeSessionProvider { - const string VNCACHE_CONFIG_KEY = "vncache"; const string WEB_SESSION_CONFIG = "web"; private WebSessionProvider? _sessions; - public bool CanProcess(IHttpEvent entity) - { - //Web sessions can always be provided so long as cache is loaded - return _sessions != null; - } + //Web sessions can always be provided so long as cache is loaded + bool IRuntimeSessionProvider.CanProcess(IHttpEvent entity) => _sessions != null; - public ValueTask<SessionHandle> GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) + ValueTask<SessionHandle> ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) { return _sessions!.GetSessionAsync(entity, cancellationToken); } @@ -55,77 +52,31 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) { //Try get vncache config element - IReadOnlyDictionary<string, JsonElement> cacheConfig = plugin.GetConfig(VNCACHE_CONFIG_KEY); - - IReadOnlyDictionary<string, JsonElement> webSessionConfig = plugin.GetConfig(WEB_SESSION_CONFIG); + IReadOnlyDictionary<string, JsonElement> webSessionConfig = plugin.GetConfigForType<WebSessionProvider>(); uint cookieSize = webSessionConfig["cookie_size"].GetUInt32(); string cookieName = webSessionConfig["cookie_name"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cookie_name' for config '{WEB_SESSION_CONFIG}'"); string cachePrefix = webSessionConfig["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cache_prefix' for config '{WEB_SESSION_CONFIG}'"); + int cacheLimit = (int)webSessionConfig["cache_size"].GetUInt32(); + uint maxConnections = webSessionConfig["max_waiting_connections"].GetUInt32(); TimeSpan validFor = webSessionConfig["valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); //Init id factory WebSessionIdFactoryImpl idFactory = new(cookieSize, cookieName, cachePrefix, validFor); - //Run client connection - _ = plugin.DeferTask(() => WokerDoWorkAsync(plugin, localized, idFactory, cacheConfig, webSessionConfig)); - } - - - /* - * Starts and monitors the VNCache connection - */ - - private async Task WokerDoWorkAsync( - PluginBase plugin, - ILogProvider localized, - WebSessionIdFactoryImpl idFactory, - IReadOnlyDictionary<string, JsonElement> cacheConfig, - IReadOnlyDictionary<string, JsonElement> webSessionConfig) - { - //Init cache client - using VnCacheClient cache = new(plugin.IsDebug() ? localized : null, Memory.Shared); - - try - { - int cacheLimit = (int)webSessionConfig["cache_size"].GetUInt32(); - uint maxConnections = webSessionConfig["max_waiting_connections"].GetUInt32(); - - //Try loading config - await cache.LoadConfigAsync(plugin, cacheConfig); - - //Init provider - _sessions = new(cache.Resource!, cacheLimit, maxConnections, idFactory); - - localized.Information("Session provider loaded"); + //Get shared global-cache + IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - //Listen for cache table events - _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken)); + //Create cache store from global cache + GlobalCacheStore cacheStore = new(globalCache); - //Run and wait for exit - await cache.RunAsync(localized, plugin.UnloadToken); + //Init provider + _sessions = new(cacheStore, cacheLimit, maxConnections, idFactory); - } - catch (OperationCanceledException) - { } - catch (KeyNotFoundException e) - { - localized.Error("Missing required configuration variable for VnCache client: {0}", e.Message); - } - catch (FBMServerNegiationException fne) - { - localized.Error("Failed to negotiate connection with cache server {reason}", fne.Message); - } - catch (Exception ex) - { - localized.Error(ex, "Cache client error occured in session provider"); - } - finally - { - _sessions = null; - } + //Load and run cached sessions on deferred task lib + _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - localized.Information("Cache client exited"); + localized.Information("Session provider loaded"); } } }
\ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs index f58129a..07ae04b 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs @@ -77,10 +77,10 @@ namespace VNLib.Plugins.Essentials.Sessions.Memory _ = plugin.DeferTask(() => _sessions.CleanupExiredAsync(localized, plugin.UnloadToken)); //Schedule garbage collector - _ = plugin.ScheduleInterval(this, TimeSpan.FromMinutes(1)); + plugin.ScheduleInterval(this, TimeSpan.FromMinutes(1)); //Call cleanup on exit - _ = plugin.UnloadToken.RegisterUnobserved(_sessions.Cleanup); + _ = plugin.RegisterForUnload(_sessions.Cleanup); } Task IIntervalScheduleable.OnIntervalAsync(ILogProvider log, CancellationToken cancellationToken) diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj index c4a65f5..78fe298 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj @@ -8,6 +8,8 @@ <Authors>Vaughn Nugent</Authors> <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <!-- Resolve nuget dll files and store them in the output dir --> diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs new file mode 100644 index 0000000..df3c564 --- /dev/null +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Sessions.Cache.Client +* File: GlobalCacheStore.cs +* +* GlobalCacheStore.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.Threading; +using System.Threading.Tasks; + +using VNLib.Data.Caching; + +namespace VNLib.Plugins.Sessions.Cache.Client +{ + /// <summary> + /// A wrapper class to provide a <see cref="IRemoteCacheStore"/> from + /// a <see cref="IGlobalCacheProvider"/> client instance + /// </summary> + public sealed class GlobalCacheStore : IRemoteCacheStore + { + private readonly IGlobalCacheProvider _cache; + + public GlobalCacheStore(IGlobalCacheProvider globalCache) + { + _cache = globalCache ?? throw new ArgumentNullException(nameof(globalCache)); + } + + ///<inheritdoc/> + public Task AddOrUpdateObjectAsync<T>(string objectId, string? newId, T obj, CancellationToken cancellationToken = default) + { + return _cache.AddOrUpdateAsync(objectId, newId, obj, cancellationToken); + } + + ///<inheritdoc/> + public Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default) + { + return _cache.DeleteAsync(objectId, cancellationToken); + } + + ///<inheritdoc/> + public Task<T?> GetObjectAsync<T>(string objectId, CancellationToken cancellationToken = default) + { + return _cache.GetAsync<T>(objectId, cancellationToken); + } + } +} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs new file mode 100644 index 0000000..2a8bd49 --- /dev/null +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Sessions.Cache.Client +* File: IRemoteCacheStore.cs +* +* IRemoteCacheStore.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.Threading; +using System.Threading.Tasks; + +namespace VNLib.Plugins.Sessions.Cache.Client +{ + /// <summary> + /// Represents an asynchronous interface to a remote cache store + /// </summary> + public interface IRemoteCacheStore + { + /// <summary> + /// Gets an object from the cache provider by key + /// </summary> + /// <typeparam name="T">The data type</typeparam> + /// <param name="objectId">The key/id of the object to recover</param> + /// <param name="cancellationToken">A token to cancel the operation</param> + /// <returns>A task that resolves the found object or null otherwise</returns> + Task<T?> GetObjectAsync<T>(string objectId, CancellationToken cancellationToken = default); + + Task AddOrUpdateObjectAsync<T>(string objectId, string? newId, T obj, CancellationToken cancellationToken = default); + + Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default); + } +} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs index 3b61f68..af2c969 100644 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs @@ -30,10 +30,8 @@ using System.Collections.Generic; using Microsoft.VisualStudio.Threading; using VNLib.Net.Http; -using VNLib.Data.Caching; using VNLib.Data.Caching.Exceptions; using VNLib.Utils.Extensions; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Extensions; @@ -47,7 +45,7 @@ namespace VNLib.Plugins.Sessions.Cache.Client { protected const string CREATED_TIME_ENTRY = "__.i.ctime"; - protected FBMClient Client { get; } + protected IRemoteCacheStore Client { get; } protected TimeSpan UpdateTimeout { get; } private readonly AsyncLazyInitializer Initializer; @@ -57,7 +55,7 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// </summary> protected Dictionary<string, string>? DataStore; - protected RemoteSession(string sessionId, FBMClient client, TimeSpan backgroundTimeOut) + protected RemoteSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut) { SessionID = sessionId; UpdateTimeout = backgroundTimeOut; diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs index 2e72391..800ad66 100644 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs @@ -32,7 +32,6 @@ using VNLib.Net.Http; using VNLib.Utils.Async; using VNLib.Utils.Logging; using VNLib.Utils.Memory.Caching; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Sessions; namespace VNLib.Plugins.Sessions.Cache.Client @@ -97,19 +96,19 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// <summary> /// The client used to communicate with the cache server /// </summary> - protected FBMClient Client { get; } + protected IRemoteCacheStore Store { get; } /// <summary> /// Initializes a new <see cref="SessionCacheClient"/> /// </summary> /// <param name="client"></param> /// <param name="maxCacheItems">The maximum number of sessions to keep in memory</param> - protected SessionCacheClient(FBMClient client, int maxCacheItems) + protected SessionCacheClient(IRemoteCacheStore client, int maxCacheItems) { MaxLoadedEntires = maxCacheItems; CacheLock = new(); CacheTable = new(maxCacheItems); - Client = client; + Store = client; } private ulong _waitingCount; @@ -202,12 +201,6 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// <returns></returns> public async Task CleanupExpiredSessionsAsync(ILogProvider log, CancellationToken token) { - //Close handler - void OnConnectionClosed(object? sender, EventArgs e) => CacheHardClear(); - - //Attach event - Client.ConnectionClosed += OnConnectionClosed; - while (true) { try @@ -229,9 +222,6 @@ namespace VNLib.Plugins.Sessions.Cache.Client log.Error(ex); } } - - //remove handler - Client.ConnectionClosed -= OnConnectionClosed; } ///<inheritdoc/> diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj index a75ebe3..8271ed2 100644 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj @@ -5,6 +5,8 @@ <Authors>Vaughn Nugent</Authors> <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> <Version>1.0.1.1</Version> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <PropertyGroup> @@ -38,7 +40,9 @@ <ItemGroup> <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" /> - <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\src\VNLib.Data.Caching.csproj" /> + <ProjectReference Include="..\..\..\..\VNLib\Http\src\VNLib.Net.Http.csproj" /> + <ProjectReference Include="..\..\..\..\VNLib\Utils\src\VNLib.Utils.csproj" /> + <ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\VNLib.Data.Caching.csproj" /> </ItemGroup> </Project> |