diff options
Diffstat (limited to 'lib')
62 files changed, 486 insertions, 445 deletions
diff --git a/lib/Net.Http/src/Core/HttpServerBase.cs b/lib/Net.Http/src/Core/HttpServerBase.cs index 790d0ff..f9e0418 100644 --- a/lib/Net.Http/src/Core/HttpServerBase.cs +++ b/lib/Net.Http/src/Core/HttpServerBase.cs @@ -79,6 +79,7 @@ namespace VNLib.Net.Http private readonly ITransportProvider Transport; private readonly IReadOnlyDictionary<string, IWebRoot> ServerRoots; private readonly IWebRoot? _wildcardRoot; + private readonly HttpConfig _config; #region caches /// <summary> @@ -98,7 +99,7 @@ namespace VNLib.Net.Http /// <summary> /// The <see cref="HttpConfig"/> for the current server /// </summary> - public HttpConfig Config { get; } + public ref readonly HttpConfig Config => ref _config; /// <summary> /// Gets a value indicating whether the server is listening for connections @@ -129,11 +130,11 @@ namespace VNLib.Net.Http //Validate the configuration ValidateConfig(in config); - Config = config; + _config = config; //Configure roots and their directories ServerRoots = sites.ToDictionary(static r => r.Hostname, static tv => tv, StringComparer.OrdinalIgnoreCase); //Compile and store the timeout keepalive header - KeepAliveTimeoutHeaderValue = $"timeout={(int)Config.ConnectionKeepAlive.TotalSeconds}"; + KeepAliveTimeoutHeaderValue = $"timeout={(int)_config.ConnectionKeepAlive.TotalSeconds}"; //Create a new context store ContextStore = ObjectRental.CreateReusable(() => new HttpContext(this)); //Setup config copy with the internal http pool @@ -303,7 +304,7 @@ namespace VNLib.Net.Http //Set running flag Running = true; - Config.ServerLog.Information("HTTP server {hc} listening for connections", GetHashCode()); + _config.ServerLog.Information("HTTP server {hc} listening for connections", GetHashCode()); //Listen for connections until canceled while (true) @@ -323,15 +324,15 @@ namespace VNLib.Net.Http } catch(AuthenticationException ae) when(ae.HResult == INVALID_FRAME_HRESULT) { - Config.ServerLog.Debug("A TLS connection attempt was made but an invalid TLS frame was received"); + _config.ServerLog.Debug("A TLS connection attempt was made but an invalid TLS frame was received"); } catch (AuthenticationException ae) { - Config.ServerLog.Error(ae); + _config.ServerLog.Error(ae); } catch (Exception ex) { - Config.ServerLog.Error(ex); + _config.ServerLog.Error(ex); } } @@ -340,7 +341,7 @@ namespace VNLib.Net.Http //Clear running flag Running = false; - Config.ServerLog.Information("HTTP server {hc} exiting", GetHashCode()); + _config.ServerLog.Information("HTTP server {hc} exiting", GetHashCode()); } @@ -365,13 +366,13 @@ namespace VNLib.Net.Http case SocketError.ConnectionAborted: break; case SocketError.ConnectionReset: - Config.ServerLog.Debug("Connecion reset by client"); + _config.ServerLog.Debug("Connecion reset by client"); break; case SocketError.TimedOut: - Config.ServerLog.Debug("Socket operation timed out"); + _config.ServerLog.Debug("Socket operation timed out"); break; default: - Config.ServerLog.Information(se); + _config.ServerLog.Information(se); break; } } diff --git a/lib/Net.Http/src/Core/HttpServerProcessing.cs b/lib/Net.Http/src/Core/HttpServerProcessing.cs index 6c5fd43..7d01e23 100644 --- a/lib/Net.Http/src/Core/HttpServerProcessing.cs +++ b/lib/Net.Http/src/Core/HttpServerProcessing.cs @@ -56,7 +56,7 @@ namespace VNLib.Net.Http try { //Set write timeout - transportContext.ConnectionStream.WriteTimeout = Config.SendTimeout; + transportContext.ConnectionStream.WriteTimeout = _config.SendTimeout; //Init stream context.InitializeContext(transportContext); @@ -65,7 +65,7 @@ namespace VNLib.Net.Http do { //Set rx timeout low for initial reading - transportContext.ConnectionStream.ReadTimeout = Config.ActiveConnectionRecvTimeout; + transportContext.ConnectionStream.ReadTimeout = _config.ActiveConnectionRecvTimeout; //Process the request bool keepAlive = await ProcessHttpEventAsync(context); @@ -77,7 +77,7 @@ namespace VNLib.Net.Http } //Reset inactive keeaplive timeout, when expired the following read will throw a cancealltion exception - transportContext.ConnectionStream.ReadTimeout = (int)Config.ConnectionKeepAlive.TotalMilliseconds; + transportContext.ConnectionStream.ReadTimeout = (int)_config.ConnectionKeepAlive.TotalMilliseconds; //"Peek" or wait for more data to begin another request (may throw timeout exception when timmed out) await transportContext.ConnectionStream.ReadAsync(Memory<byte>.Empty, StopToken!.Token); @@ -114,15 +114,15 @@ namespace VNLib.Net.Http //Catch wrapped OC exceptions catch (IOException ioe) when (ioe.InnerException is OperationCanceledException oce) { - Config.ServerLog.Debug("Failed to receive transport data within a timeout period {m} connection closed", oce.Message); + _config.ServerLog.Debug("Failed to receive transport data within a timeout period {m} connection closed", oce.Message); } catch (OperationCanceledException oce) { - Config.ServerLog.Debug("Failed to receive transport data within a timeout period {m} connection closed", oce.Message); + _config.ServerLog.Debug("Failed to receive transport data within a timeout period {m} connection closed", oce.Message); } catch(Exception ex) { - Config.ServerLog.Error(ex); + _config.ServerLog.Error(ex); } //Dec open connection count @@ -142,7 +142,7 @@ namespace VNLib.Net.Http } catch(Exception ex) { - Config.ServerLog.Error(ex); + _config.ServerLog.Error(ex); } } @@ -188,11 +188,11 @@ namespace VNLib.Net.Http //Response context.Response.Compile(ref writer); - server.Config.RequestDebugLog!.Verbose("\r\n{dbg}", writer.ToString()); + server._config.RequestDebugLog!.Verbose("\r\n{dbg}", writer.ToString()); } //Write debug response log - if(Config.RequestDebugLog != null) + if(_config.RequestDebugLog != null) { WriteConnectionDebugLog(this, context); } @@ -248,7 +248,7 @@ namespace VNLib.Net.Http IHttpHeaderParseBuffer parseBuffer = ctx.Buffers.RequestHeaderParseBuffer; //Init parser - TransportReader reader = new (ctx.GetTransport(), parseBuffer, Config.HttpEncoding, HeaderLineTermination); + TransportReader reader = new (ctx.GetTransport(), parseBuffer, _config.HttpEncoding, HeaderLineTermination); try { @@ -323,7 +323,7 @@ namespace VNLib.Net.Http } //Check open connection count (not super accurate, or might not be atomic) - if (OpenConnectionCount > Config.MaxOpenConnections) + if (OpenConnectionCount > _config.MaxOpenConnections) { //Close the connection and return 503 context.Response.Headers[HttpResponseHeader.Connection] = "closed"; @@ -332,7 +332,7 @@ namespace VNLib.Net.Http } //Store keepalive value from request, and check if keepalives are enabled by the configuration - bool keepalive = context.Request.KeepAlive & Config.ConnectionKeepAlive > TimeSpan.Zero; + bool keepalive = context.Request.KeepAlive & _config.ConnectionKeepAlive > TimeSpan.Zero; //Set connection header (only for http1.1) @@ -400,7 +400,7 @@ namespace VNLib.Net.Http catch (TerminateConnectionException tce) { //Log the event as a debug so user can see it was handled - Config.ServerLog.Debug(tce, "User-code requested a connection termination"); + _config.ServerLog.Debug(tce, "User-code requested a connection termination"); //See if the exception requested an error code response if (tce.Code > 0) @@ -424,7 +424,7 @@ namespace VNLib.Net.Http } catch (Exception ex) { - Config.ServerLog.Warn(ex, "Unhandled exception during application code execution."); + _config.ServerLog.Warn(ex, "Unhandled exception during application code execution."); } finally { diff --git a/lib/Net.Http/src/Core/Request/HttpInputStream.cs b/lib/Net.Http/src/Core/Request/HttpInputStream.cs index fbb1d41..3f6b2a5 100644 --- a/lib/Net.Http/src/Core/Request/HttpInputStream.cs +++ b/lib/Net.Http/src/Core/Request/HttpInputStream.cs @@ -74,28 +74,12 @@ namespace VNLib.Net.Http.Core /// and initial data buffer /// </summary> /// <param name="contentLength">The number of bytes to allow being read from the transport or initial buffer</param> - /// <param name="initial">Entity body data captured on initial read</param> - internal void Prepare(long contentLength, in InitDataBuffer initial) + internal ref InitDataBuffer? Prepare(long contentLength) { - ContentLength = contentLength; - _initalData = initial; - - //Cache transport - InputStream = ContextInfo.GetTransport(); - } - - /// <summary> - /// Prepares the input stream for reading from the transport with the specified content length - /// amount of data - /// </summary> - /// <param name="contentLength">The number of bytes to allow being read from the transport</param> - internal void Prepare(long contentLength) - { - ContentLength = contentLength; - _initalData = null; - - //Cache transport + ContentLength = contentLength; + //Cache transport stream InputStream = ContextInfo.GetTransport(); + return ref _initalData; } public override void Close() => throw new NotSupportedException("The HTTP input stream should never be closed!"); diff --git a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs index de65e12..5e7f019 100644 --- a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs +++ b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs @@ -555,21 +555,15 @@ namespace VNLib.Net.Http.Core * with null */ + ref InitDataBuffer? initData = ref Request.InputStream.Prepare(parseState.ContentLength); + if (available > 0) { - //Creates the new initial data buffer - InitDataBuffer initData = InitDataBuffer.AllocBuffer(InputDataBufferPool, available); + //Alloc the buffer and asign it + initData = InitDataBuffer.AllocBuffer(InputDataBufferPool, available); //Read remaining data into the buffer's data segment - _ = reader.ReadRemaining(initData.DataSegment); - - //Setup the input stream and capture the initial data from the reader, and wrap the transport stream to read data directly - Request.InputStream.Prepare(parseState.ContentLength, initData); - } - else - { - //Empty input stream - Request.InputStream.Prepare(parseState.ContentLength); + _ = reader.ReadRemaining(initData.Value.DataSegment); } Request.HasEntityBody = true; diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs index c4d602b..91184bd 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs @@ -150,6 +150,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction servers.AddLast(server); } + return new(servers, sd, GetPluginStack(sd)); + } + + private PluginStackInitializer GetPluginStack(ServiceDomain domain) + { //Always init manual array manualPlugins ??= Array.Empty<IManualPlugin>(); @@ -160,9 +165,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction plugins ??= new EmptyPluginStack(); #pragma warning restore CA2000 // Dispose objects before losing scope - IPluginInitializer init = new PluginStackInitializer(plugins, manualPlugins); - - return new(servers, sd, init); + return new (domain.GetListener(), plugins, manualPlugins); } /* diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs index 6691d6b..acfc6e2 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs @@ -23,7 +23,6 @@ */ using System.Net; -using System.Threading.Tasks; using VNLib.Net.Http; @@ -52,7 +51,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction /// When an error occurs and is handled by the library, this event is invoked /// </para> /// <para> - /// NOTE: This function must be thread-safe! + /// NOTE: This function must be thread-safe! And should not throw exceptions /// </para> /// </summary> /// <param name="errorCode">The error code that was created during processing</param> @@ -64,14 +63,19 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction /// For pre-processing a request entity before all endpoint lookups are performed /// </summary> /// <param name="entity">The http entity to process</param> - /// <returns>The results to return to the file processor, or null of the entity requires further processing</returns> - ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity); + /// <param name="args">The results to return to the file processor, or <see cref="FileProcessArgs.Continue"/> if processing should continue</param> + /// <returns></returns> + void PreProcessEntityAsync(HttpEntity entity, out FileProcessArgs args); /// <summary> - /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity + /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity. + /// <para> + /// This method may mutate the <paramref name="chosenRoutine"/> argument referrence to change the + /// the routine that will be used to process the file. + /// </para> /// </summary> /// <param name="entity">The http entity to process</param> /// <param name="chosenRoutine">The selected file processing routine for the given request</param> - void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine); + void PostProcessFile(HttpEntity entity, ref FileProcessArgs chosenRoutine); } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs index d518218..54b7ff2 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs @@ -316,10 +316,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity); ///<inheritdoc/> - public override void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, chosenRoutine); + public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs) => Hooks.PreProcessEntityAsync(entity, out preProcArgs); ///<inheritdoc/> - public override ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity) => Hooks.PreProcessEntityAsync(entity); + public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, ref chosenRoutine); ///<inheritdoc/> public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath); diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs index 7cd5ac4..16bc1a0 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs @@ -65,7 +65,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack { _servers = servers; _serviceDomain = serviceDomain; - _plugins = new(serviceDomain, plugins); + _plugins = new(plugins); WaitForAllTask = Task.CompletedTask; } diff --git a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs index ac91f45..a9aa103 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs @@ -24,14 +24,11 @@ using VNLib.Utils.Logging; -using VNLib.Plugins.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack { internal interface IPluginInitializer { - void PrepareStack(IPluginEventListener listener); - IManagedPlugin[] InitializePluginStack(ILogProvider eventLogger); void UnloadPlugins(); diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs index 010039d..d93df6d 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs @@ -25,8 +25,8 @@ using System.Linq; using System.Collections.Generic; -using VNLib.Plugins.Runtime; using VNLib.Utils.Logging; +using VNLib.Plugins.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack { @@ -47,7 +47,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// </summary> /// <param name="controller"></param> /// <returns></returns> - internal static IEnumerable<LivePlugin> GetOnlyWebPlugins(this PluginController controller) => controller.Plugins.Where(p => p.Plugin is IWebPlugin); + internal static IEnumerable<LivePlugin> GetOnlyWebPlugins(this PluginController controller) => controller.Plugins.Where(static p => p.Plugin is IWebPlugin); /// <summary> /// Loads all plugins that implement <see cref="IWebPlugin"/> interface into the @@ -56,5 +56,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// <param name="stack"></param> /// <param name="logProvider">A log provider for writing loading logs to</param> public static void LoadPlugins(this HttpServiceStack stack, ILogProvider logProvider) => (stack.PluginManager as PluginManager)!.LoadPlugins(logProvider); + + internal static PluginLoadEventListener GetListener(this ServiceDomain domain) => new(domain); } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs new file mode 100644 index 0000000..0eaa3a8 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: PluginLoadEventListener.cs +* +* PluginLoadEventListener.cs is part of VNLib.Plugins.Essentials.ServiceStack which +* is part of the larger VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack 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 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack 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.Linq; + +using VNLib.Utils.Extensions; +using VNLib.Plugins.Runtime; + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + internal sealed record class PluginLoadEventListener(ServiceDomain Domain) : IPluginEventListener + { + ///<inheritdoc/> + void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state) => OnPluginLoaded((state as IManagedPlugin)!); + + ///<inheritdoc/> + void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state) => OnPluginUnloaded((state as IManagedPlugin)!); + + /// <summary> + /// Called when a plugin has been successfully loaded and + /// should be put into service + /// </summary> + /// <param name="plugin">The plugin that was loaded</param> + public void OnPluginLoaded(IManagedPlugin plugin) + { + //Run onload method before invoking other handlers + plugin.OnPluginLoaded(); + + //Get event listeners at event time because deps may be modified by the domain + ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray(); + + //run onload method + deps.TryForeach(d => d.OnPluginLoaded(plugin)); + } + + /// <summary> + /// Called when a plugin is about to be unloaded and should + /// be removed from service. + /// </summary> + /// <param name="plugin">The plugin instance to unload</param> + public void OnPluginUnloaded(IManagedPlugin plugin) + { + try + { + //Get event listeners at event time because deps may be modified by the domain + ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray(); + + //Run unloaded method + deps.TryForeach(d => d.OnPluginUnloaded(plugin)); + } + finally + { + //always unload the plugin wrapper + plugin.OnPluginUnloaded(); + } + } + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs index 2f57367..ba3b91a 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs @@ -24,13 +24,10 @@ using System; -using System.Linq; using System.Collections.Generic; using VNLib.Utils; using VNLib.Utils.Logging; -using VNLib.Utils.Extensions; -using VNLib.Plugins.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack { @@ -39,9 +36,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// A sealed type that manages the plugin interaction layer. Manages the lifetime of plugin /// instances, exposes controls, and relays stateful plugin events. /// </summary> - internal sealed class PluginManager : VnDisposeable, IHttpPluginManager, IPluginEventListener - { - private readonly ServiceDomain _dependents; + internal sealed class PluginManager : VnDisposeable, IHttpPluginManager + { private readonly IPluginInitializer _stack; /// <summary> @@ -51,9 +47,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack private IManagedPlugin[] _loadedPlugins; - public PluginManager(ServiceDomain dependents, IPluginInitializer stack) + public PluginManager(IPluginInitializer stack) { - _dependents = dependents; _stack = stack; _loadedPlugins = Array.Empty<IManagedPlugin>(); } @@ -62,11 +57,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// Configures the manager to capture and manage plugins within a plugin stack /// </summary> /// <param name="debugLog"></param> + /// <exception cref="InvalidOperationException"></exception> + /// <exception cref="AggregateException"></exception> public void LoadPlugins(ILogProvider debugLog) { _ = _stack ?? throw new InvalidOperationException("Plugin stack has not been set."); - _stack.PrepareStack(this); + Check(); //Initialize the plugin stack and store the loaded plugins _loadedPlugins = _stack.InitializePluginStack(debugLog); @@ -124,38 +121,5 @@ namespace VNLib.Plugins.Essentials.ServiceStack //Dispose the plugin stack _stack.Dispose(); } - - void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state) - { - IManagedPlugin mp = (state as IManagedPlugin)!; - - //Run onload method before invoking other handlers - mp.OnPluginLoaded(); - - //Get event listeners at event time because deps may be modified by the domain - ServiceGroup[] deps = _dependents.ServiceGroups.Select(static d => d).ToArray(); - - //run onload method - deps.TryForeach(d => d.OnPluginLoaded(mp)); - } - - void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state) - { - IManagedPlugin plugin = (state as IManagedPlugin)!; - - try - { - //Get event listeners at event time because deps may be modified by the domain - ServiceGroup[] deps = _dependents.ServiceGroups.Select(static d => d).ToArray(); - - //Run unloaded method - deps.TryForeach(d => d.OnPluginUnloaded(plugin)); - } - finally - { - //always unload the plugin wrapper - plugin.OnPluginUnloaded(); - } - } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs index 6ccd862..8a4e801 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs @@ -40,13 +40,12 @@ using VNLib.Plugins.Attributes; namespace VNLib.Plugins.Essentials.ServiceStack { - internal sealed record class PluginStackInitializer(IPluginStack Stack, IManualPlugin[] ManualPlugins) : IPluginInitializer + internal sealed record class PluginStackInitializer(PluginLoadEventListener Listener, IPluginStack Stack, IManualPlugin[] ManualPlugins) : IPluginInitializer { private readonly LinkedList<IManagedPlugin> _managedPlugins = new(); private readonly LinkedList<ManualPluginWrapper> _manualPlugins = new(); - - ///<inheritdoc/> - public void PrepareStack(IPluginEventListener listener) + + private void PrepareStack() { /* * Since we own the plugin stack, it is safe to build it here. @@ -62,7 +61,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack Array.ForEach(wrapper, p => _managedPlugins.AddLast(p)); //Register for all plugins and pass the plugin instance as the state object - Array.ForEach(wrapper, p => p.Plugin.Controller.Register(listener, p)); + Array.ForEach(wrapper, p => p.Plugin.Controller.Register(Listener, p)); //Add manual plugins to list of managed plugins Array.ForEach(ManualPlugins, p => _manualPlugins.AddLast(new ManualPluginWrapper(p))); @@ -71,6 +70,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack ///<inheritdoc/> public IManagedPlugin[] InitializePluginStack(ILogProvider debugLog) { + //Prepare the plugin stack before initializing + PrepareStack(); + //single thread initialziation LinkedList<IManagedPlugin> _loadedPlugins = new(); @@ -96,6 +98,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack public void UnloadPlugins() { Stack.UnloadAll(); + + //Unload manual plugins in listener + _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp)); _manualPlugins.TryForeach(static mp => mp.Unload()); } @@ -104,9 +109,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack { Stack.ReloadAll(); - //Reload manual plugins + //Unload manual plugins in listener, then call the unload method + _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp)); _manualPlugins.TryForeach(static mp => mp.Unload()); + + //Load, then invoke on-loaded events _manualPlugins.TryForeach(static mp => mp.Load()); + _managedPlugins.TryForeach(mp => Listener.OnPluginLoaded(mp)); } ///<inheritdoc/> @@ -155,7 +164,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack return false; } - private static void LoadPlugin(IManagedPlugin plugin, ILogProvider debugLog) + private void LoadPlugin(IManagedPlugin plugin, ILogProvider debugLog) { Stopwatch sw = new(); try @@ -170,6 +179,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack else if (plugin is ManualPluginWrapper mpw) { mpw.Load(); + //Call the on-load event in listener explicitly + Listener.OnPluginLoaded(plugin); } else { @@ -313,13 +324,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack return false; } + + void IManagedPlugin.OnPluginLoaded() + { } - - /* - * SHOULD NEVER BE CALLED - */ - void IManagedPlugin.OnPluginLoaded() => throw new NotImplementedException(); - void IManagedPlugin.OnPluginUnloaded() => throw new NotImplementedException(); + void IManagedPlugin.OnPluginUnloaded() + { } ///<inheritdoc/> public override string ToString() => Plugin.Name; diff --git a/lib/Plugins.Essentials/src/Accounts/AccountData.cs b/lib/Plugins.Essentials/src/Accounts/AccountData.cs index d4a4d12..f5db245 100644 --- a/lib/Plugins.Essentials/src/Accounts/AccountData.cs +++ b/lib/Plugins.Essentials/src/Accounts/AccountData.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -29,24 +29,24 @@ namespace VNLib.Plugins.Essentials.Accounts public class AccountData { [JsonPropertyName("email")] - public string EmailAddress { get; set; } + public string? EmailAddress { get; set; } [JsonPropertyName("phone")] - public string PhoneNumber { get; set; } + public string? PhoneNumber { get; set; } [JsonPropertyName("first")] - public string First { get; set; } + public string? First { get; set; } [JsonPropertyName("last")] - public string Last { get; set; } + public string? Last { get; set; } [JsonPropertyName("company")] - public string Company { get; set; } + public string? Company { get; set; } [JsonPropertyName("street")] - public string Street { get; set; } + public string? Street { get; set; } [JsonPropertyName("city")] - public string City { get; set; } + public string? City { get; set; } [JsonPropertyName("state")] - public string State { get; set; } + public string? State { get; set; } [JsonPropertyName("zip")] - public string Zip { get; set; } + public string? Zip { get; set; } [JsonPropertyName("created")] - public string Created { get; set; } + public string? Created { get; set; } } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs index 54deb8c..259f52a 100644 --- a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs +++ b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs @@ -23,7 +23,6 @@ */ using System; -using System.Buffers; using System.Threading; using System.Threading.Tasks; using System.Security.Cryptography; @@ -37,8 +36,6 @@ using VNLib.Utils.Extensions; using VNLib.Plugins.Essentials.Users; using VNLib.Plugins.Essentials.Sessions; -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { @@ -51,7 +48,7 @@ namespace VNLib.Plugins.Essentials.Accounts { /// <summary> - /// The size in bytes of the random passwords generated when invoking the <see cref="SetRandomPasswordAsync(PasswordHashing, IUserManager, IUser, int)"/> + /// The size in bytes of the random passwords generated when invoking the <see cref="SetRandomPasswordAsync(IPasswordHashingProvider, IUserManager, IUser, int)"/> /// </summary> public const int RANDOM_PASS_SIZE = 240; @@ -188,8 +185,10 @@ namespace VNLib.Plugins.Essentials.Accounts /// <returns>A <see cref="PrivateString"/> that contains the new password hash</returns> public static PrivateString GetRandomPassword(this IPasswordHashingProvider hashing, int size = RANDOM_PASS_SIZE) { + _ = hashing ?? throw new ArgumentNullException(nameof(hashing)); + //Get random bytes - byte[] randBuffer = ArrayPool<byte>.Shared.Rent(size); + using UnsafeMemoryHandle<byte> randBuffer = MemoryUtil.UnsafeAlloc(size); try { Span<byte> span = randBuffer.AsSpan(0, size); @@ -203,8 +202,7 @@ namespace VNLib.Plugins.Essentials.Accounts finally { //Zero the block and return to pool - MemoryUtil.InitializeBlock(randBuffer.AsSpan()); - ArrayPool<byte>.Shared.Return(randBuffer); + MemoryUtil.InitializeBlock(randBuffer.Span); } } @@ -221,6 +219,7 @@ namespace VNLib.Plugins.Essentials.Accounts /// <exception cref="ArgumentNullException"></exception> public static async Task<bool> VerifyPasswordAsync(this IUserManager manager, string userId, PrivateString rawPassword, IPasswordHashingProvider hashing, CancellationToken cancellation) { + _ = manager ?? throw new ArgumentNullException(nameof(manager)); _ = userId ?? throw new ArgumentNullException(nameof(userId)); _ = rawPassword ?? throw new ArgumentNullException(nameof(rawPassword)); _ = hashing ?? throw new ArgumentNullException(nameof(hashing)); @@ -228,7 +227,17 @@ namespace VNLib.Plugins.Essentials.Accounts //Get the user, may be null if the user does not exist using IUser? user = await manager.GetUserAndPassFromIDAsync(userId, cancellation); - return user != null && hashing.Verify(user.PassHash.ToReadOnlySpan(), rawPassword.ToReadOnlySpan()); + if(user == null) + { + return false; + } + + if(user.PassHash == null) + { + return false; + } + + return hashing.Verify(user.PassHash.ToReadOnlySpan(), rawPassword.ToReadOnlySpan()); } /// <summary> @@ -242,7 +251,11 @@ namespace VNLib.Plugins.Essentials.Accounts [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool VerifyPassword(this IUser user, PrivateString rawPassword, IPasswordHashingProvider hashing) { - return user.PassHash != null && hashing.Verify(user.PassHash, rawPassword); + _ = user ?? throw new ArgumentNullException(nameof(user)); + _ = rawPassword ?? throw new ArgumentNullException(nameof(rawPassword)); + _ = hashing ?? throw new ArgumentNullException(nameof(hashing)); + + return user.PassHash != null && hashing.Verify(user.PassHash.ToReadOnlySpan(), rawPassword.ToReadOnlySpan()); } /// <summary> @@ -256,8 +269,12 @@ namespace VNLib.Plugins.Essentials.Accounts [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Verify(this IPasswordHashingProvider provider, PrivateString passHash, PrivateString password) { + _ = provider ?? throw new ArgumentNullException(nameof(provider)); + _ = password ?? throw new ArgumentNullException(nameof(password)); + _ = passHash ?? throw new ArgumentNullException(nameof(passHash)); + //Casting PrivateStrings to spans will reference the base string directly - return provider.Verify((ReadOnlySpan<char>)passHash, (ReadOnlySpan<char>)password); + return provider.Verify(passHash.ToReadOnlySpan(), password.ToReadOnlySpan()); } /// <summary> @@ -270,7 +287,10 @@ namespace VNLib.Plugins.Essentials.Accounts [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PrivateString Hash(this IPasswordHashingProvider provider, PrivateString password) { - return provider.Hash((ReadOnlySpan<char>)password); + _ = provider ?? throw new ArgumentNullException(nameof(provider)); + _ = password ?? throw new ArgumentNullException(nameof(password)); + + return provider.Hash(password.ToReadOnlySpan()); } #endregion diff --git a/lib/Plugins.Essentials/src/Accounts/AuthorzationCheckLevel.cs b/lib/Plugins.Essentials/src/Accounts/AuthorzationCheckLevel.cs index aa09bf4..be6df08 100644 --- a/lib/Plugins.Essentials/src/Accounts/AuthorzationCheckLevel.cs +++ b/lib/Plugins.Essentials/src/Accounts/AuthorzationCheckLevel.cs @@ -23,8 +23,6 @@ */ -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/ClientSecurityToken.cs b/lib/Plugins.Essentials/src/Accounts/ClientSecurityToken.cs index 0d4aa58..0d81344 100644 --- a/lib/Plugins.Essentials/src/Accounts/ClientSecurityToken.cs +++ b/lib/Plugins.Essentials/src/Accounts/ClientSecurityToken.cs @@ -23,8 +23,6 @@ */ -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/FailedLoginLockout.cs b/lib/Plugins.Essentials/src/Accounts/FailedLoginLockout.cs index a67eee2..1ab476d 100644 --- a/lib/Plugins.Essentials/src/Accounts/FailedLoginLockout.cs +++ b/lib/Plugins.Essentials/src/Accounts/FailedLoginLockout.cs @@ -26,8 +26,6 @@ using System; using VNLib.Plugins.Essentials.Users; -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/IAccountSecurityProvider.cs b/lib/Plugins.Essentials/src/Accounts/IAccountSecurityProvider.cs index c30796b..4a2d479 100644 --- a/lib/Plugins.Essentials/src/Accounts/IAccountSecurityProvider.cs +++ b/lib/Plugins.Essentials/src/Accounts/IAccountSecurityProvider.cs @@ -27,8 +27,6 @@ using System; using VNLib.Utils; using VNLib.Plugins.Essentials.Users; -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/IClientAuthorization.cs b/lib/Plugins.Essentials/src/Accounts/IClientAuthorization.cs index 02bc96e..73f97c0 100644 --- a/lib/Plugins.Essentials/src/Accounts/IClientAuthorization.cs +++ b/lib/Plugins.Essentials/src/Accounts/IClientAuthorization.cs @@ -23,8 +23,6 @@ */ -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/IClientSecInfo.cs b/lib/Plugins.Essentials/src/Accounts/IClientSecInfo.cs index 6990191..0071311 100644 --- a/lib/Plugins.Essentials/src/Accounts/IClientSecInfo.cs +++ b/lib/Plugins.Essentials/src/Accounts/IClientSecInfo.cs @@ -23,8 +23,6 @@ */ -#nullable enable - namespace VNLib.Plugins.Essentials.Accounts { /// <summary> diff --git a/lib/Plugins.Essentials/src/Accounts/IPasswordHashingProvider.cs b/lib/Plugins.Essentials/src/Accounts/IPasswordHashingProvider.cs index fc45727..788f69d 100644 --- a/lib/Plugins.Essentials/src/Accounts/IPasswordHashingProvider.cs +++ b/lib/Plugins.Essentials/src/Accounts/IPasswordHashingProvider.cs @@ -53,7 +53,7 @@ namespace VNLib.Plugins.Essentials.Accounts bool Verify(ReadOnlySpan<byte> passHash, ReadOnlySpan<byte> password); /// <summary> - /// Hashes the specified character encoded password to it's secured hashed form. + /// Calculates the cryptographic hash of the specified binary encoded password. /// </summary> /// <param name="password">The character encoded password to encrypt</param> /// <returns>A <see cref="PrivateString"/> containing the new password hash.</returns> @@ -61,7 +61,7 @@ namespace VNLib.Plugins.Essentials.Accounts PrivateString Hash(ReadOnlySpan<char> password); /// <summary> - /// Hashes the specified binary encoded password to it's secured hashed form. + /// Calculates the cryptographic hash of the specified binary encoded password. /// </summary> /// <param name="password">The binary encoded password to encrypt</param> /// <returns>A <see cref="PrivateString"/> containing the new password hash.</returns> diff --git a/lib/Plugins.Essentials/src/Accounts/LoginMessage.cs b/lib/Plugins.Essentials/src/Accounts/LoginMessage.cs index 96bf261..df7a206 100644 --- a/lib/Plugins.Essentials/src/Accounts/LoginMessage.cs +++ b/lib/Plugins.Essentials/src/Accounts/LoginMessage.cs @@ -24,6 +24,7 @@ using System; using System.Text.Json.Serialization; + using VNLib.Utils.Memory; namespace VNLib.Plugins.Essentials.Accounts @@ -48,7 +49,7 @@ namespace VNLib.Plugins.Essentials.Accounts /// may represent a user's password /// </summary> [JsonPropertyName("password")] - public string Password + public string? Password { get => base[0]; set => base[0] = value; @@ -70,7 +71,7 @@ namespace VNLib.Plugins.Essentials.Accounts /// The clients specified local-language /// </summary> [JsonPropertyName("locallanguage")] - public string LocalLanguage { get; set; } + public string? LocalLanguage { get; set; } /// <summary> /// The clients shared public key used for encryption, this property is not protected /// </summary> diff --git a/lib/Plugins.Essentials/src/Accounts/PasswordChallengeResult.cs b/lib/Plugins.Essentials/src/Accounts/PasswordChallengeResult.cs index 3ad05ab..ee48cb0 100644 --- a/lib/Plugins.Essentials/src/Accounts/PasswordChallengeResult.cs +++ b/lib/Plugins.Essentials/src/Accounts/PasswordChallengeResult.cs @@ -22,9 +22,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -using System; - -#nullable enable namespace VNLib.Plugins.Essentials.Accounts { diff --git a/lib/Plugins.Essentials/src/Content/IPageRouter.cs b/lib/Plugins.Essentials/src/Content/IPageRouter.cs index e6952f4..af99b5a 100644 --- a/lib/Plugins.Essentials/src/Content/IPageRouter.cs +++ b/lib/Plugins.Essentials/src/Content/IPageRouter.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -24,8 +24,6 @@ using System.Threading.Tasks; -//Import account system for privilage masks - namespace VNLib.Plugins.Essentials.Content { /// <summary> diff --git a/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs b/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs index 7e1e394..4d09165 100644 --- a/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs +++ b/lib/Plugins.Essentials/src/Endpoints/ProtectionSettings.cs @@ -22,7 +22,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -using System; namespace VNLib.Plugins.Essentials.Endpoints { diff --git a/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs b/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs index 92643d9..2177838 100644 --- a/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs +++ b/lib/Plugins.Essentials/src/Endpoints/ResourceEndpointBase.cs @@ -200,24 +200,28 @@ namespace VNLib.Plugins.Essentials.Endpoints /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> PostAsync(HttpEntity entity) => ValueTask.FromResult(Post(entity)); + /// <summary> /// This method gets invoked when an incoming GET request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> GetAsync(HttpEntity entity) => ValueTask.FromResult(Get(entity)); + /// <summary> /// This method gets invoked when an incoming DELETE request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> DeleteAsync(HttpEntity entity) => ValueTask.FromResult(Delete(entity)); + /// <summary> /// This method gets invoked when an incoming PUT request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> PutAsync(HttpEntity entity) => ValueTask.FromResult(Put(entity)); + /// <summary> /// This method gets invoked when an incoming PATCH request to the endpoint has been requested. /// </summary> @@ -225,6 +229,11 @@ namespace VNLib.Plugins.Essentials.Endpoints /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> PatchAsync(HttpEntity entity) => ValueTask.FromResult(Patch(entity)); + /// <summary> + /// This method gets invoked when an incoming OPTIONS request to the endpoint has been requested. + /// </summary> + /// <param name="entity">The entity to be processed</param> + /// <returns>The result of the operation to return to the file processor</returns> protected virtual ValueTask<VfReturnType> OptionsAsync(HttpEntity entity) => ValueTask.FromResult(Options(entity)); /// <summary> @@ -247,63 +256,43 @@ namespace VNLib.Plugins.Essentials.Endpoints /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> - protected virtual VfReturnType Post(HttpEntity entity) - { - //Return method not allowed - entity.CloseResponse(HttpStatusCode.MethodNotAllowed); - return VfReturnType.VirtualSkip; - } + protected virtual VfReturnType Post(HttpEntity entity) => VirtualClose(entity, HttpStatusCode.MethodNotAllowed); + /// <summary> /// This method gets invoked when an incoming GET request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> - protected virtual VfReturnType Get(HttpEntity entity) - { - return VfReturnType.ProcessAsFile; - } + protected virtual VfReturnType Get(HttpEntity entity) => VfReturnType.ProcessAsFile; + /// <summary> /// This method gets invoked when an incoming DELETE request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> - protected virtual VfReturnType Delete(HttpEntity entity) - { - entity.CloseResponse(HttpStatusCode.MethodNotAllowed); - return VfReturnType.VirtualSkip; - } + protected virtual VfReturnType Delete(HttpEntity entity) => VirtualClose(entity, HttpStatusCode.MethodNotAllowed); + /// <summary> /// This method gets invoked when an incoming PUT request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> - protected virtual VfReturnType Put(HttpEntity entity) - { - entity.CloseResponse(HttpStatusCode.MethodNotAllowed); - return VfReturnType.VirtualSkip; - } + protected virtual VfReturnType Put(HttpEntity entity) => VirtualClose(entity, HttpStatusCode.MethodNotAllowed); + /// <summary> /// This method gets invoked when an incoming PATCH request to the endpoint has been requested. /// </summary> /// <param name="entity">The entity to be processed</param> /// <returns>The result of the operation to return to the file processor</returns> - protected virtual VfReturnType Patch(HttpEntity entity) - { - entity.CloseResponse(HttpStatusCode.MethodNotAllowed); - return VfReturnType.VirtualSkip; - } + protected virtual VfReturnType Patch(HttpEntity entity) => VirtualClose(entity, HttpStatusCode.MethodNotAllowed); + /// <summary> /// Invoked when a request is received for a method other than GET, POST, DELETE, or PUT; /// </summary> /// <param name="entity">The entity that </param> /// <param name="method">The request method</param> /// <returns>The results of the processing</returns> - protected virtual VfReturnType AlternateMethod(HttpEntity entity, HttpMethod method) - { - //Return method not allowed - entity.CloseResponse(HttpStatusCode.MethodNotAllowed); - return VfReturnType.VirtualSkip; - } + protected virtual VfReturnType AlternateMethod(HttpEntity entity, HttpMethod method) => VirtualClose(entity, HttpStatusCode.MethodNotAllowed); protected virtual VfReturnType Options(HttpEntity entity) => VfReturnType.Forbidden; diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs index a9cd98a..f565707 100644 --- a/lib/Plugins.Essentials/src/EventProcessor.cs +++ b/lib/Plugins.Essentials/src/EventProcessor.cs @@ -41,7 +41,7 @@ using VNLib.Plugins.Essentials.Extensions; using VNLib.Plugins.Essentials.Middleware; using VNLib.Plugins.Essentials.Endpoints; -#nullable enable +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task namespace VNLib.Plugins.Essentials { @@ -105,18 +105,24 @@ namespace VNLib.Plugins.Essentials public abstract bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity); /// <summary> - /// For pre-processing a request entity before all endpoint lookups are performed + /// For pre-processing a request entity before all processing happens, but after + /// a session is attached to the entity. /// </summary> /// <param name="entity">The http entity to process</param> - /// <returns>The results to return to the file processor, or null of the entity requires further processing</returns> - public abstract ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity); + /// <param name="result">The results to return to the file processor, or <see cref="FileProcessArgs.Continue"/> to continue further processing</param> + public abstract void PreProcessEntity(HttpEntity entity, out FileProcessArgs result); /// <summary> /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity + /// <para> + /// Post processing may mutate the <paramref name="chosenRoutine"/> to change the + /// result of the operation. Consider events with the <see cref="FileProcessArgs.VirtualSkip"/> + /// have already been responded to. + /// </para> /// </summary> /// <param name="entity">The http entity to process</param> /// <param name="chosenRoutine">The selected file processing routine for the given request</param> - public abstract void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine); + public abstract void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine); ///<inheritdoc/> public abstract IAccountSecurityProvider AccountSecurity { get; } @@ -163,129 +169,126 @@ namespace VNLib.Plugins.Essentials /// </summary> /// <param name="router"><see cref="IPageRouter"/> to route incomming connections</param> public void SetPageRouter(IPageRouter? router) => _ = Interlocked.Exchange(ref Router, router); - - ///<inheritdoc/> public virtual async ValueTask ClientConnectedAsync(IHttpEvent httpEvent) { - //load ref to session provider + //read local ref to session provider and page router ISessionProvider? _sessions = Sessions; + IPageRouter? router = Router; + + //event cancellation token + HttpEntity entity = new(httpEvent, this); + LinkedListNode<IHttpMiddleware>? mwNode; + //Set ambient processor context _currentProcessor.Value = this; - //Start cancellation token - CancellationTokenSource timeout = new(Options.ExecutionTimeout); - - FileProcessArgs args; - try { - //Session handle, default to the shared empty session - SessionHandle sessionHandle = SessionHandle.Empty; - //If sessions are set, get a session for the current connection if (_sessions != null) { //Get the session - sessionHandle = await _sessions.GetSessionAsync(httpEvent, timeout.Token); + entity.EventSessionHandle = await _sessions.GetSessionAsync(httpEvent, entity.EventCancellation); + //If the processor had an error recovering the session, return the result to the processor - if (sessionHandle.EntityStatus != FileProcessArgs.Continue) + if (entity.EventSessionHandle.EntityStatus != FileProcessArgs.Continue) { - ProcessFile(httpEvent, sessionHandle.EntityStatus); + ProcessFile(httpEvent, entity.EventSessionHandle.EntityStatus); return; } + + //Attach the new session to the entity + entity.AttachSession(); } + try { - //Setup entity - HttpEntity entity = new(httpEvent, this, in sessionHandle, timeout.Token); - //Pre-process entity - args = await PreProcessEntityAsync(entity); + PreProcessEntity(entity, out entity.EventArgs); //If preprocess returned a value, exit - if (args != FileProcessArgs.Continue) + if (entity.EventArgs != FileProcessArgs.Continue) { - ProcessFile(httpEvent, in args); - return; + goto RespondAndExit; } //Handle middleware before file processing - LinkedListNode<IHttpMiddleware>? mwNode = MiddlewareChain.GetCurrentHead(); + mwNode = MiddlewareChain.GetCurrentHead(); - //Loop though nodes + //Loop through nodes while(mwNode != null) { //Process - args = await mwNode.ValueRef.ProcessAsync(entity); + entity.EventArgs = await mwNode.ValueRef.ProcessAsync(entity); - switch (args.Routine) + switch (entity.EventArgs.Routine) { //move next if continue is returned case FpRoutine.Continue: - mwNode = mwNode.Next; break; //Middleware completed the connection, time to exit default: - goto MwExit; + goto RespondAndExit; } - } + + mwNode = mwNode.Next; + } if (!EndpointTable.IsEmpty) { //See if the virtual file is servicable - if (!EndpointTable.TryGetEndpoint(entity.Server.Path, out IVirtualEndpoint<HttpEntity>? vf)) - { - args = FileProcessArgs.Continue; - } - else + if (EndpointTable.TryGetEndpoint(entity.Server.Path, out IVirtualEndpoint<HttpEntity>? vf)) { //Invoke the page handler process method VfReturnType rt = await vf.Process(entity); //Process a virtual file - args = GetArgsFromReturn(entity, rt); - } + GetArgsFromVirtualReturn(entity, rt, out entity.EventArgs); - //If the entity was processed, exit - if (args != FileProcessArgs.Continue) - { - ProcessFile(httpEvent, in args); - return; + //If the entity was processed, exit + if (entity.EventArgs != FileProcessArgs.Continue) + { + goto RespondAndExit; + } } } //If no virtual processor handled the ws request, deny it if (entity.Server.IsWebSocketRequest) { - ProcessFile(httpEvent, in FileProcessArgs.Deny); - return; + entity.EventArgs = FileProcessArgs.Deny; + } + else + { + //Finally route the connection as a file + entity.EventArgs = await RouteFileAsync(router, entity); } - //Finally process as file - args = await RouteFileAsync(entity); - - MwExit: + RespondAndExit: - //Finally process the file - ProcessFile(httpEvent, in args); + //Call post processor method + PostProcessEntity(entity, ref entity.EventArgs); } - finally + finally { //Capture all session release exceptions try { //Release the session - await sessionHandle.ReleaseAsync(httpEvent); + await entity.EventSessionHandle.ReleaseAsync(httpEvent); } catch (Exception ex) { Log.Error(ex, "Exception raised while releasing the assocated session"); } } + + //Finally process the file + ProcessFile(httpEvent, in entity.EventArgs); } catch (ContentTypeUnacceptableException) { @@ -309,7 +312,6 @@ namespace VNLib.Plugins.Essentials { Log.Warn(se, "An exception was raised while attempting to get or save a session"); CloseWithError(HttpStatusCode.ServiceUnavailable, httpEvent); - return; } catch (OperationCanceledException oce) { @@ -328,7 +330,7 @@ namespace VNLib.Plugins.Essentials } finally { - timeout.Dispose(); + entity.Dispose(); _currentProcessor.Value = null; } } @@ -485,17 +487,19 @@ namespace VNLib.Plugins.Essentials /// </summary> /// <param name="entity">The entity to be processed</param> /// <param name="returnType">The virtual file processor return type</param> - /// <returns>The process args to end processing for the virtual endpoint</returns> - protected virtual FileProcessArgs GetArgsFromReturn(HttpEntity entity, VfReturnType returnType) + /// <param name="args">The process args to end processing for the virtual endpoint</param> + protected virtual void GetArgsFromVirtualReturn(HttpEntity entity, VfReturnType returnType, out FileProcessArgs args) { if (returnType == VfReturnType.VirtualSkip) { //Virtual file was handled by the handler - return FileProcessArgs.VirtualSkip; + args = FileProcessArgs.VirtualSkip; + return; } else if (returnType == VfReturnType.ProcessAsFile) { - return FileProcessArgs.Continue; + args = FileProcessArgs.Continue; + return; } //If not a get request, process it directly @@ -504,11 +508,14 @@ namespace VNLib.Plugins.Essentials switch (returnType) { case VfReturnType.Forbidden: - return FileProcessArgs.Deny; + args = FileProcessArgs.Deny; + return; case VfReturnType.NotFound: - return FileProcessArgs.NotFound; + args = FileProcessArgs.NotFound; + return; case VfReturnType.Error: - return FileProcessArgs.Error; + args = FileProcessArgs.Error; + return; default: break; } @@ -531,31 +538,31 @@ namespace VNLib.Plugins.Essentials break; } - return FileProcessArgs.VirtualSkip; + args = FileProcessArgs.VirtualSkip; } /// <summary> /// Determines the best <see cref="FileProcessArgs"/> processing response for the given connection. /// Alternativley may respond to the entity directly. /// </summary> + /// <param name="router">A reference to the current <see cref="IPageRouter"/> instance if cofigured</param> /// <param name="entity">The http entity to process</param> /// <returns>The results to return to the file processor, this method must return an argument</returns> - protected virtual async ValueTask<FileProcessArgs> RouteFileAsync(HttpEntity entity) + protected virtual async ValueTask<FileProcessArgs> RouteFileAsync(IPageRouter? router, HttpEntity entity) { - //Read local copy of the router - IPageRouter? router = Router; - - //Make sure router is set - if (router == null) + if(router != null) + { + //Get a file routine + FileProcessArgs routine = await router.RouteAsync(entity); + //Call post processor method + PostProcessEntity(entity, ref routine); + //Return the routine + return routine; + } + else { return FileProcessArgs.Continue; } - //Get a file routine - FileProcessArgs routine = await router.RouteAsync(entity); - //Call post processor method - PostProcessFile(entity, in routine); - //Return the routine - return routine; } /// <summary> diff --git a/lib/Plugins.Essentials/src/Extensions/CollectionsExtensions.cs b/lib/Plugins.Essentials/src/Extensions/CollectionsExtensions.cs index 9500d5e..bc82827 100644 --- a/lib/Plugins.Essentials/src/Extensions/CollectionsExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/CollectionsExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -26,8 +26,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs index 393c838..0039efa 100644 --- a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs @@ -31,8 +31,6 @@ using System.Runtime.CompilerServices; using VNLib.Net.Http; using VNLib.Utils.Extensions; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { diff --git a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs index cb64cd1..976eed5 100644 --- a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs @@ -38,8 +38,6 @@ using VNLib.Utils; using VNLib.Utils.Memory.Caching; using static VNLib.Plugins.Essentials.Statics; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { diff --git a/lib/Plugins.Essentials/src/Extensions/HttpCookie.cs b/lib/Plugins.Essentials/src/Extensions/HttpCookie.cs index 332e3d6..6158a69 100644 --- a/lib/Plugins.Essentials/src/Extensions/HttpCookie.cs +++ b/lib/Plugins.Essentials/src/Extensions/HttpCookie.cs @@ -26,8 +26,6 @@ using System; using VNLib.Net.Http; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Extensions/IJsonSerializerBuffer.cs b/lib/Plugins.Essentials/src/Extensions/IJsonSerializerBuffer.cs index 34811f4..5e02c54 100644 --- a/lib/Plugins.Essentials/src/Extensions/IJsonSerializerBuffer.cs +++ b/lib/Plugins.Essentials/src/Extensions/IJsonSerializerBuffer.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -24,14 +24,12 @@ using System.IO; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { /// <summary> /// Interface for a buffer that can be used to serialize objects to JSON /// </summary> - interface IJsonSerializerBuffer + internal interface IJsonSerializerBuffer { /// <summary> /// Gets a stream used for writing serialzation data to diff --git a/lib/Plugins.Essentials/src/Extensions/InternalSerializerExtensions.cs b/lib/Plugins.Essentials/src/Extensions/InternalSerializerExtensions.cs index 3d441a1..817b673 100644 --- a/lib/Plugins.Essentials/src/Extensions/InternalSerializerExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/InternalSerializerExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -26,8 +26,6 @@ using System; using System.IO; using System.Text.Json; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { diff --git a/lib/Plugins.Essentials/src/Extensions/InvalidJsonRequestException.cs b/lib/Plugins.Essentials/src/Extensions/InvalidJsonRequestException.cs index b2352b2..bd7bfe3 100644 --- a/lib/Plugins.Essentials/src/Extensions/InvalidJsonRequestException.cs +++ b/lib/Plugins.Essentials/src/Extensions/InvalidJsonRequestException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -24,8 +24,6 @@ using System.Text.Json; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Extensions/JsonResponse.cs b/lib/Plugins.Essentials/src/Extensions/JsonResponse.cs index d087c06..549d746 100644 --- a/lib/Plugins.Essentials/src/Extensions/JsonResponse.cs +++ b/lib/Plugins.Essentials/src/Extensions/JsonResponse.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -23,17 +23,15 @@ */ using System; -using System.Buffers; using System.IO; +using System.Buffers; using VNLib.Net.Http; -using VNLib.Utils.Extensions; using VNLib.Utils.IO; using VNLib.Utils.Memory; +using VNLib.Utils.Extensions; using VNLib.Utils.Memory.Caching; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { internal sealed class JsonResponse : IJsonSerializerBuffer, IMemoryResponseReader @@ -99,6 +97,7 @@ namespace VNLib.Plugins.Essentials.Extensions _written += written; Remaining -= written; } + ///<inheritdoc/> void IMemoryResponseReader.Close() { @@ -110,10 +109,6 @@ namespace VNLib.Plugins.Essentials.Extensions } ///<inheritdoc/> - ReadOnlyMemory<byte> IMemoryResponseReader.GetMemory() - { - //Get memory from the memory owner and offet the slice, - return _memoryOwner.Memory.Slice(_written, Remaining); - } + ReadOnlyMemory<byte> IMemoryResponseReader.GetMemory() => _memoryOwner.Memory.Slice(_written, Remaining); } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/Extensions/SimpleMemoryResponse.cs b/lib/Plugins.Essentials/src/Extensions/SimpleMemoryResponse.cs index a0f2b17..29ae7b1 100644 --- a/lib/Plugins.Essentials/src/Extensions/SimpleMemoryResponse.cs +++ b/lib/Plugins.Essentials/src/Extensions/SimpleMemoryResponse.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -24,10 +24,9 @@ using System; using System.Text; -using VNLib.Net.Http; using System.Buffers; -#nullable enable +using VNLib.Net.Http; namespace VNLib.Plugins.Essentials.Extensions { @@ -70,12 +69,14 @@ namespace VNLib.Plugins.Essentials.Extensions ///<inheritdoc/> public int Remaining { get; private set; } + ///<inheritdoc/> void IMemoryResponseReader.Advance(int written) { Remaining -= written; _written += written; } + ///<inheritdoc/> void IMemoryResponseReader.Close() { @@ -83,6 +84,7 @@ namespace VNLib.Plugins.Essentials.Extensions ArrayPool<byte>.Shared.Return(_buffer!); _buffer = null; } + ///<inheritdoc/> ReadOnlyMemory<byte> IMemoryResponseReader.GetMemory() => _buffer!.AsMemory(_written, Remaining); } diff --git a/lib/Plugins.Essentials/src/Extensions/UserExtensions.cs b/lib/Plugins.Essentials/src/Extensions/UserExtensions.cs index 9223b1d..77e6e3e 100644 --- a/lib/Plugins.Essentials/src/Extensions/UserExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/UserExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -28,8 +28,6 @@ using System.Text.Json; using VNLib.Plugins.Essentials.Users; using VNLib.Plugins.Essentials.Accounts; -#nullable enable - namespace VNLib.Plugins.Essentials.Extensions { /// <summary> @@ -62,6 +60,7 @@ namespace VNLib.Plugins.Essentials.Extensions profile.EmailAddress = null; ud.SetObject(PROFILE_ENTRY, profile); } + /// <summary> /// Stores the serialized string user's profile to their entry. /// <br/> @@ -70,6 +69,7 @@ namespace VNLib.Plugins.Essentials.Extensions /// <param name="ud"></param> /// <param name="jsonProfile">The JSON serialized "raw" profile data</param> public static void SetProfile(this IUser ud, string jsonProfile) => ud[PROFILE_ENTRY] = jsonProfile; + /// <summary> /// Recovers the user's stored profile /// </summary> diff --git a/lib/Plugins.Essentials/src/FileProcessArgs.cs b/lib/Plugins.Essentials/src/FileProcessArgs.cs index 5ed4667..0f90160 100644 --- a/lib/Plugins.Essentials/src/FileProcessArgs.cs +++ b/lib/Plugins.Essentials/src/FileProcessArgs.cs @@ -22,11 +22,8 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -using System; using System.Net; -#nullable enable - namespace VNLib.Plugins.Essentials { /// <summary> diff --git a/lib/Plugins.Essentials/src/HttpEntity.cs b/lib/Plugins.Essentials/src/HttpEntity.cs index ad89d14..6487ca3 100644 --- a/lib/Plugins.Essentials/src/HttpEntity.cs +++ b/lib/Plugins.Essentials/src/HttpEntity.cs @@ -33,8 +33,6 @@ using VNLib.Net.Http; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Extensions; -#nullable enable - /* * HttpEntity was converted to an object as during profiling * it was almost always heap allcated due to async opertaions @@ -53,16 +51,20 @@ namespace VNLib.Plugins.Essentials /// </summary> public sealed class HttpEntity : IHttpEvent { + /// <summary> /// The connection event entity /// </summary> private readonly IHttpEvent Entity; - public HttpEntity(IHttpEvent entity, IWebProcessor root, in SessionHandle session, CancellationToken cancellation) + private readonly CancellationTokenSource EventCts; + + public HttpEntity(IHttpEvent entity, IWebProcessor root) { Entity = entity; RequestedRoot = root; - EventCancellation = cancellation; + //Init event cts + EventCts = new(root.Options.ExecutionTimeout); //See if the connection is coming from an downstream server IsBehindDownStreamServer = root.Options.DownStreamServers.Contains(entity.Server.RemoteEndpoint.Address); @@ -72,8 +74,6 @@ namespace VNLib.Plugins.Essentials * otherwise use the remote ep ip address */ TrustedRemoteIp = entity.Server.GetTrustedIp(IsBehindDownStreamServer); - //Initialize the session - Session = session.IsSet ? new(session.SessionData, entity.Server, TrustedRemoteIp) : new(); //Local connection IsLocalConnection = entity.Server.LocalEndpoint.Address.IsLocalSubnet(TrustedRemoteIp); //Cache value @@ -83,14 +83,38 @@ namespace VNLib.Plugins.Essentials RequestedTimeUtc = DateTimeOffset.UtcNow; } + private SessionInfo _session; + internal FileProcessArgs EventArgs; + internal SessionHandle EventSessionHandle; + + /// <summary> + /// Internal call to attach a new session to the entity from the + /// internal session handle + /// </summary> + internal void AttachSession() + { + if (EventSessionHandle.IsSet) + { + _session = new(EventSessionHandle.SessionData!, Entity.Server, TrustedRemoteIp); + } + } + + /// <summary> + /// Internal call to cleanup any internal resources + /// </summary> + internal void Dispose() + { + EventCts.Dispose(); + } + /// <summary> /// A token that has a scheduled timeout to signal the cancellation of the entity event /// </summary> - public readonly CancellationToken EventCancellation; + public CancellationToken EventCancellation => EventCts.Token; /// <summary> - /// The session assocaited with the event + /// The session associated with the event /// </summary> - public readonly SessionInfo Session; + public ref readonly SessionInfo Session => ref _session; /// <summary> /// A value that indicates if the connecion came from a trusted downstream server /// </summary> diff --git a/lib/Plugins.Essentials/src/IEpProcessingOptions.cs b/lib/Plugins.Essentials/src/IEpProcessingOptions.cs index b9efdeb..d7f3da7 100644 --- a/lib/Plugins.Essentials/src/IEpProcessingOptions.cs +++ b/lib/Plugins.Essentials/src/IEpProcessingOptions.cs @@ -27,8 +27,6 @@ using System.IO; using System.Net; using System.Collections.Generic; -#nullable enable - namespace VNLib.Plugins.Essentials { /// <summary> diff --git a/lib/Plugins.Essentials/src/IVirtualEndpointTable.cs b/lib/Plugins.Essentials/src/IVirtualEndpointTable.cs index c260b45..d5aff2a 100644 --- a/lib/Plugins.Essentials/src/IVirtualEndpointTable.cs +++ b/lib/Plugins.Essentials/src/IVirtualEndpointTable.cs @@ -23,11 +23,10 @@ */ using System; +using System.Diagnostics.CodeAnalysis; using VNLib.Plugins.Essentials.Endpoints; -#nullable enable - namespace VNLib.Plugins.Essentials { /// <summary> @@ -70,6 +69,6 @@ namespace VNLib.Plugins.Essentials /// <param name="path">The connection path to recover the endpoint from</param> /// <param name="endpoint"></param> /// <returns></returns> - bool TryGetEndpoint(string path, out IVirtualEndpoint<HttpEntity>? endpoint); + bool TryGetEndpoint(string path, [NotNullWhen(true)] out IVirtualEndpoint<HttpEntity>? endpoint); } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/IWebProcessorInfo.cs b/lib/Plugins.Essentials/src/IWebProcessorInfo.cs index 93a9211..ae920ea 100644 --- a/lib/Plugins.Essentials/src/IWebProcessorInfo.cs +++ b/lib/Plugins.Essentials/src/IWebProcessorInfo.cs @@ -22,9 +22,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ - -#nullable enable - using VNLib.Net.Http; using VNLib.Plugins.Essentials.Accounts; diff --git a/lib/Plugins.Essentials/src/Middleware/IHttpMiddlewareChain.cs b/lib/Plugins.Essentials/src/Middleware/IHttpMiddlewareChain.cs index 54da6c1..ace0c86 100644 --- a/lib/Plugins.Essentials/src/Middleware/IHttpMiddlewareChain.cs +++ b/lib/Plugins.Essentials/src/Middleware/IHttpMiddlewareChain.cs @@ -24,8 +24,6 @@ using System.Collections.Generic; -#nullable enable - namespace VNLib.Plugins.Essentials.Middleware { /// <summary> diff --git a/lib/Plugins.Essentials/src/Middleware/SemiConistentMiddlewareChain.cs b/lib/Plugins.Essentials/src/Middleware/SemiConistentMiddlewareChain.cs index 197ba12..1e1db22 100644 --- a/lib/Plugins.Essentials/src/Middleware/SemiConistentMiddlewareChain.cs +++ b/lib/Plugins.Essentials/src/Middleware/SemiConistentMiddlewareChain.cs @@ -24,8 +24,6 @@ using System.Collections.Generic; -#nullable enable - namespace VNLib.Plugins.Essentials.Middleware { /// <summary> diff --git a/lib/Plugins.Essentials/src/Oauth/O2EndpointBase.cs b/lib/Plugins.Essentials/src/Oauth/O2EndpointBase.cs index a1a4d35..60004c7 100644 --- a/lib/Plugins.Essentials/src/Oauth/O2EndpointBase.cs +++ b/lib/Plugins.Essentials/src/Oauth/O2EndpointBase.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -31,8 +31,8 @@ using System.Threading.Tasks; using VNLib.Utils; using VNLib.Utils.Logging; using VNLib.Net.Http; -using VNLib.Plugins.Essentials.Extensions; using VNLib.Plugins.Essentials.Endpoints; +using VNLib.Plugins.Essentials.Extensions; namespace VNLib.Plugins.Essentials.Oauth { @@ -54,21 +54,25 @@ namespace VNLib.Plugins.Essentials.Oauth { VfReturnType rt; ERRNO preProc = PreProccess(entity); + //Entity was responded to by the pre-processor if (preProc < 0) { return VfReturnType.VirtualSkip; } + if (preProc == ERRNO.E_FAIL) { rt = VfReturnType.Forbidden; goto Exit; } + //If websockets are quested allow them to be processed in a logged-in/secure context if (entity.Server.IsWebSocketRequest) { return await WebsocketRequestedAsync(entity); } + //Capture return type rt = entity.Server.Method switch { @@ -81,29 +85,18 @@ namespace VNLib.Plugins.Essentials.Oauth HttpMethod.OPTIONS => await OptionsAsync(entity), _ => await AlternateMethodAsync(entity, entity.Server.Method), }; + Exit: //Write a standard Ouath2 error messag - switch (rt) + return rt switch { - case VfReturnType.VirtualSkip: - return VfReturnType.VirtualSkip; - case VfReturnType.ProcessAsFile: - return VfReturnType.ProcessAsFile; - case VfReturnType.NotFound: - entity.CloseResponseError(HttpStatusCode.NotFound, ErrorType.InvalidRequest, "The requested resource could not be found"); - return VfReturnType.VirtualSkip; - case VfReturnType.BadRequest: - entity.CloseResponseError(HttpStatusCode.BadRequest, ErrorType.InvalidRequest, "Your request was not properlty formatted and could not be proccessed"); - return VfReturnType.VirtualSkip; - case VfReturnType.Error: - entity.CloseResponseError(HttpStatusCode.InternalServerError, ErrorType.ServerError, "There was a server error processing your request"); - return VfReturnType.VirtualSkip; - - case VfReturnType.Forbidden: - default: - entity.CloseResponseError(HttpStatusCode.Forbidden, ErrorType.InvalidClient, "You do not have access to this resource"); - return VfReturnType.VirtualSkip; - } + VfReturnType.VirtualSkip => VfReturnType.VirtualSkip, + VfReturnType.ProcessAsFile => VfReturnType.ProcessAsFile, + VfReturnType.NotFound => O2VirtualClose(entity, HttpStatusCode.NotFound, ErrorType.InvalidRequest, "The requested resource could not be found"), + VfReturnType.BadRequest => O2VirtualClose(entity, HttpStatusCode.BadRequest, ErrorType.InvalidRequest, "Your request was not properlty formatted and could not be proccessed"), + VfReturnType.Error => O2VirtualClose(entity, HttpStatusCode.InternalServerError, ErrorType.ServerError, "There was a server error processing your request"), + _ => O2VirtualClose(entity, HttpStatusCode.Forbidden, ErrorType.InvalidClient, "You do not have access to this resource"), + }; } catch (TerminateConnectionException) { @@ -118,22 +111,19 @@ namespace VNLib.Plugins.Essentials.Oauth catch (ContentTypeUnacceptableException) { //Respond with an 406 error message - entity.CloseResponseError(HttpStatusCode.NotAcceptable, ErrorType.InvalidRequest, "The response type is not acceptable for this endpoint"); - return VfReturnType.VirtualSkip; + return O2VirtualClose(entity, HttpStatusCode.NotAcceptable, ErrorType.InvalidRequest, "The response type is not acceptable for this endpoint"); } catch (InvalidJsonRequestException) { //Respond with an error message - entity.CloseResponseError(HttpStatusCode.BadRequest, ErrorType.InvalidRequest, "The request body was not a proper JSON schema"); - return VfReturnType.VirtualSkip; + return O2VirtualClose(entity, HttpStatusCode.BadRequest, ErrorType.InvalidRequest, "The request body was not a proper JSON schema"); } catch (Exception ex) { //Log an uncaught excetpion and return an error code (log may not be initialized) Log?.Error(ex); //Respond with an error message - entity.CloseResponseError(HttpStatusCode.InternalServerError, ErrorType.ServerError, "There was a server error processing your request"); - return VfReturnType.VirtualSkip; + return O2VirtualClose(entity, HttpStatusCode.InternalServerError, ErrorType.ServerError, "There was a server error processing your request"); } } @@ -158,5 +148,11 @@ namespace VNLib.Plugins.Essentials.Oauth } return base.PreProccess(entity); } + + public static VfReturnType O2VirtualClose(HttpEntity entity, HttpStatusCode statusCode, ErrorType type, string message) + { + entity.CloseResponseError(statusCode, type, message); + return VfReturnType.VirtualSkip; + } } } diff --git a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs index 483dc35..4849107 100644 --- a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs +++ b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs @@ -31,8 +31,6 @@ using System.Collections.Generic; using VNLib.Net.Http; using VNLib.Plugins.Essentials.Endpoints; -#nullable enable - namespace VNLib.Plugins.Essentials { internal class SemiConsistentVeTable : IVirtualEndpointTable diff --git a/lib/Plugins.Essentials/src/Sessions/ISession.cs b/lib/Plugins.Essentials/src/Sessions/ISession.cs index d2e0ee1..d990d8e 100644 --- a/lib/Plugins.Essentials/src/Sessions/ISession.cs +++ b/lib/Plugins.Essentials/src/Sessions/ISession.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -27,22 +27,10 @@ using System.Net; using VNLib.Utils; +#nullable disable + namespace VNLib.Plugins.Essentials.Sessions { - /// <summary> - /// Flags to specify <see cref="ISession"/> session types - /// </summary> - public enum SessionType - { - /// <summary> - /// The session is a "basic" or web based session - /// </summary> - Web, - /// <summary> - /// The session is an OAuth2 session type - /// </summary> - OAuth2 - } /// <summary> /// Represents a connection oriented session data @@ -70,6 +58,11 @@ namespace VNLib.Plugins.Essentials.Sessions string SessionID { get; } /// <summary> + /// A value that indicates this session was newly created + /// </summary> + bool IsNew { get; } + + /// <summary> /// User ID associated with session /// </summary> string UserID { get; set; } @@ -95,11 +88,6 @@ namespace VNLib.Plugins.Essentials.Sessions void RegenID(); /// <summary> - /// A value that indicates this session was newly created - /// </summary> - bool IsNew { get; } - - /// <summary> /// This is a special function that requests the session to be detached from the current http connection /// but allow it to remain available. /// </summary> diff --git a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs index 44063f9..81d6ad3 100644 --- a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs +++ b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs @@ -30,6 +30,8 @@ using VNLib.Net.Http; using VNLib.Utils; using VNLib.Utils.Extensions; +#nullable disable + namespace VNLib.Plugins.Essentials.Sessions { public static class ISessionExtensions diff --git a/lib/Plugins.Essentials/src/Sessions/SessionBase.cs b/lib/Plugins.Essentials/src/Sessions/SessionBase.cs index 038fd2c..bf20b9d 100644 --- a/lib/Plugins.Essentials/src/Sessions/SessionBase.cs +++ b/lib/Plugins.Essentials/src/Sessions/SessionBase.cs @@ -28,6 +28,8 @@ using System.Runtime.CompilerServices; using VNLib.Utils; +#nullable disable + namespace VNLib.Plugins.Essentials.Sessions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs b/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs index 68f5764..823012e 100644 --- a/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs +++ b/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs @@ -26,8 +26,6 @@ using System.Threading.Tasks; using VNLib.Net.Http; -#nullable enable - namespace VNLib.Plugins.Essentials.Sessions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs b/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs index d6d5456..6c1141a 100644 --- a/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs +++ b/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs @@ -44,8 +44,6 @@ using static VNLib.Plugins.Essentials.Statics; #pragma warning disable CA1051 // Do not declare visible instance fields -#nullable enable - namespace VNLib.Plugins.Essentials.Sessions { /// <summary> diff --git a/lib/Plugins.Essentials/src/Sessions/SessionType.cs b/lib/Plugins.Essentials/src/Sessions/SessionType.cs new file mode 100644 index 0000000..9dc991c --- /dev/null +++ b/lib/Plugins.Essentials/src/Sessions/SessionType.cs @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials +* File: SessionType.cs +* +* SessionType.cs is part of VNLib.Plugins.Essentials which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials 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 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/. +*/ + + +namespace VNLib.Plugins.Essentials.Sessions +{ + /// <summary> + /// Flags to specify <see cref="ISession"/> session types + /// </summary> + public enum SessionType + { + /// <summary> + /// The session is a "basic" or web based session + /// </summary> + Web, + /// <summary> + /// The session is an OAuth2 session type + /// </summary> + OAuth2 + } +}
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/Users/IUser.cs b/lib/Plugins.Essentials/src/Users/IUser.cs index 635f5e4..a36ba70 100644 --- a/lib/Plugins.Essentials/src/Users/IUser.cs +++ b/lib/Plugins.Essentials/src/Users/IUser.cs @@ -29,8 +29,6 @@ using VNLib.Utils; using VNLib.Utils.Async; using VNLib.Utils.Memory; -#nullable enable - namespace VNLib.Plugins.Essentials.Users { /// <summary> diff --git a/lib/Plugins.Essentials/src/Users/IUserManager.cs b/lib/Plugins.Essentials/src/Users/IUserManager.cs index 42d482e..b731033 100644 --- a/lib/Plugins.Essentials/src/Users/IUserManager.cs +++ b/lib/Plugins.Essentials/src/Users/IUserManager.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -29,8 +29,6 @@ using System.Threading.Tasks; using VNLib.Utils; using VNLib.Utils.Memory; -#nullable enable - namespace VNLib.Plugins.Essentials.Users { /// <summary> @@ -46,6 +44,7 @@ namespace VNLib.Plugins.Essentials.Users /// <returns>The user's <see cref="IUser"/> object, null if the user was not found</returns> /// <exception cref="ArgumentNullException"></exception> Task<IUser?> GetUserFromIDAsync(string userId, CancellationToken cancellationToken = default); + /// <summary> /// Attempts to get a user object without their password from the database asynchronously /// </summary> @@ -54,6 +53,7 @@ namespace VNLib.Plugins.Essentials.Users /// <returns>The user's <see cref="IUser"/> object, null if the user was not found</returns> /// <exception cref="ArgumentNullException"></exception> Task<IUser?> GetUserFromEmailAsync(string emailAddress, CancellationToken cancellationToken = default); + /// <summary> /// Attempts to get a user object with their password from the database on the current thread /// </summary> @@ -62,6 +62,7 @@ namespace VNLib.Plugins.Essentials.Users /// <returns>The user's <see cref="IUser"/> object, null if the user was not found</returns> /// <exception cref="ArgumentNullException"></exception> Task<IUser?> GetUserAndPassFromIDAsync(string userid, CancellationToken cancellation = default); + /// <summary> /// Attempts to get a user object with their password from the database asynchronously /// </summary> @@ -70,6 +71,7 @@ namespace VNLib.Plugins.Essentials.Users /// <returns>The user's <see cref="IUser"/> object, null if the user was not found</returns> /// <exception cref="ArgumentNullException"></exception> Task<IUser?> GetUserAndPassFromEmailAsync(string emailAddress, CancellationToken cancellationToken = default); + /// <summary> /// Creates a new user in the current user's table and if successful returns the new user object (without password) /// </summary> @@ -83,6 +85,7 @@ namespace VNLib.Plugins.Essentials.Users /// <exception cref="ArgumentNullException"></exception> /// <exception cref="UserCreationFailedException"></exception> Task<IUser> CreateUserAsync(string userid, string emailAddress, ulong privileges, PrivateString passHash, CancellationToken cancellation = default); + /// <summary> /// Updates a password associated with the specified user. If the update fails, the transaction /// is rolled back. diff --git a/lib/Plugins.Essentials/src/Users/UserCreationFailedException.cs b/lib/Plugins.Essentials/src/Users/UserCreationFailedException.cs index 9f509ac..0c901f2 100644 --- a/lib/Plugins.Essentials/src/Users/UserCreationFailedException.cs +++ b/lib/Plugins.Essentials/src/Users/UserCreationFailedException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -24,9 +24,8 @@ using System; using System.Runtime.Serialization; -using VNLib.Utils.Resources; -#nullable enable +using VNLib.Utils.Resources; namespace VNLib.Plugins.Essentials.Users { diff --git a/lib/Plugins.Essentials/src/Users/UserDeleteException.cs b/lib/Plugins.Essentials/src/Users/UserDeleteException.cs index cd26543..6e1fe59 100644 --- a/lib/Plugins.Essentials/src/Users/UserDeleteException.cs +++ b/lib/Plugins.Essentials/src/Users/UserDeleteException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -23,6 +23,7 @@ */ using System; + using VNLib.Utils.Resources; namespace VNLib.Plugins.Essentials.Users @@ -36,9 +37,9 @@ namespace VNLib.Plugins.Essentials.Users public UserDeleteException(string message, Exception cause) : base(message, cause) { } public UserDeleteException() - {} + { } public UserDeleteException(string message) : base(message) - {} + { } } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/Users/UserExistsException.cs b/lib/Plugins.Essentials/src/Users/UserExistsException.cs index 5c63547..e9b9586 100644 --- a/lib/Plugins.Essentials/src/Users/UserExistsException.cs +++ b/lib/Plugins.Essentials/src/Users/UserExistsException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -35,15 +35,18 @@ namespace VNLib.Plugins.Essentials.Users { ///<inheritdoc/> public UserExistsException() - {} + { } + ///<inheritdoc/> public UserExistsException(string message) : base(message) - {} + { } + ///<inheritdoc/> public UserExistsException(string message, Exception innerException) : base(message, innerException) - {} + { } + ///<inheritdoc/> protected UserExistsException(SerializationInfo info, StreamingContext context) : base(info, context) - {} + { } } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/Users/UserUpdateException.cs b/lib/Plugins.Essentials/src/Users/UserUpdateException.cs index 391bb05..46c986c 100644 --- a/lib/Plugins.Essentials/src/Users/UserUpdateException.cs +++ b/lib/Plugins.Essentials/src/Users/UserUpdateException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -23,6 +23,7 @@ */ using System; + using VNLib.Utils.Resources; namespace VNLib.Plugins.Essentials.Users @@ -35,9 +36,9 @@ namespace VNLib.Plugins.Essentials.Users public UserUpdateException(string message, Exception cause) : base(message, cause) { } public UserUpdateException() - {} + { } public UserUpdateException(string message) : base(message) - {} + { } } }
\ No newline at end of file diff --git a/lib/Plugins.Essentials/src/VNLib.Plugins.Essentials.csproj b/lib/Plugins.Essentials/src/VNLib.Plugins.Essentials.csproj index 47fd60d..65fff58 100644 --- a/lib/Plugins.Essentials/src/VNLib.Plugins.Essentials.csproj +++ b/lib/Plugins.Essentials/src/VNLib.Plugins.Essentials.csproj @@ -8,6 +8,7 @@ <EnableNETAnalyzers>True</EnableNETAnalyzers> <GenerateDocumentationFile>True</GenerateDocumentationFile> <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild> + <Nullable>enable</Nullable> </PropertyGroup> <PropertyGroup> @@ -17,8 +18,8 @@ <Product>VNLib Essentials Plugin Library</Product> <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> <Description>Provides essential web, sessions, users abstractions for building extensable web applications with satefull sessions, user based intraction with login and account security extensions.</Description> - <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Core</PackageProjectUrl> <PackageTags>VNLib, Plugins, VNLib.Plugins.Essentials, Essentials, Essential Plugins, HTTP Essentials, OAuth2</PackageTags> + <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Core</PackageProjectUrl> <RepositoryUrl>https://github.com/VnUgE/VNLib.Core/tree/main/lib/Plugins.Essentials</RepositoryUrl> <PackageReadmeFile>README.md</PackageReadmeFile> <PackageLicenseFile>LICENSE</PackageLicenseFile> diff --git a/lib/Plugins.Essentials/src/WebSocketSession.cs b/lib/Plugins.Essentials/src/WebSocketSession.cs index c43a876..e39f352 100644 --- a/lib/Plugins.Essentials/src/WebSocketSession.cs +++ b/lib/Plugins.Essentials/src/WebSocketSession.cs @@ -30,8 +30,6 @@ using System.Threading.Tasks; using VNLib.Net.Http; -#nullable enable - namespace VNLib.Plugins.Essentials { /// <summary> @@ -134,24 +132,16 @@ namespace VNLib.Plugins.Essentials /// <param name="buffer">The buffer to store read data</param> /// <returns>A task that resolves a <see cref="WebSocketReceiveResult"/> which contains the status of the operation</returns> /// <exception cref="OperationCanceledException"></exception> - public Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer) - { - //Begin receive operation only with the internal token - return WsHandle!.ReceiveAsync(buffer, CancellationToken.None); - } - + public Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer) => WsHandle!.ReceiveAsync(buffer, CancellationToken.None); + /// <summary> /// Asynchronously receives data from the Websocket and copies the data to the specified buffer /// </summary> /// <param name="buffer">The buffer to store read data</param> /// <returns></returns> /// <exception cref="OperationCanceledException"></exception> - public ValueTask<ValueWebSocketReceiveResult> ReceiveAsync(Memory<byte> buffer) - { - //Begin receive operation only with the internal token - return WsHandle!.ReceiveAsync(buffer, CancellationToken.None); - } - + public ValueTask<ValueWebSocketReceiveResult> ReceiveAsync(Memory<byte> buffer) => WsHandle!.ReceiveAsync(buffer, CancellationToken.None); + /// <summary> /// Asynchronously sends the specified buffer to the client of the specified type /// </summary> @@ -160,12 +150,8 @@ namespace VNLib.Plugins.Essentials /// <param name="endOfMessage">A value that indicates this message is the final message of the transaction</param> /// <returns></returns> /// <exception cref="OperationCanceledException"></exception> - public Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType type, bool endOfMessage) - { - //Create a send request with - return WsHandle!.SendAsync(buffer, type, endOfMessage, CancellationToken.None); - } - + public Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType type, bool endOfMessage) => WsHandle!.SendAsync(buffer, type, endOfMessage, CancellationToken.None); + /// <summary> /// Asynchronously sends the specified buffer to the client of the specified type @@ -189,11 +175,7 @@ namespace VNLib.Plugins.Essentials /// <param name="flags">Websocket message flags</param> /// <returns></returns> /// <exception cref="OperationCanceledException"></exception> - public ValueTask SendAsync(ReadOnlyMemory<byte> buffer, WebSocketMessageType type, WebSocketMessageFlags flags) - { - //Create a send request with - return WsHandle!.SendAsync(buffer, type, flags, CancellationToken.None); - } + public ValueTask SendAsync(ReadOnlyMemory<byte> buffer, WebSocketMessageType type, WebSocketMessageFlags flags) => WsHandle!.SendAsync(buffer, type, flags, CancellationToken.None); /// <summary> @@ -202,11 +184,8 @@ namespace VNLib.Plugins.Essentials /// <param name="status">Set the close status</param> /// <param name="reason">Set the close reason</param> /// <exception cref="ObjectDisposedException"></exception> - public Task CloseSocketAsync(WebSocketCloseStatus status, string reason) - { - return WsHandle!.CloseAsync(status, reason, CancellationToken.None); - } - + public Task CloseSocketAsync(WebSocketCloseStatus status, string reason) => WsHandle!.CloseAsync(status, reason, CancellationToken.None); + /// <summary> /// /// </summary> diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs index dc69e76..6af2392 100644 --- a/lib/Utils/src/Memory/MemoryUtil.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -660,7 +660,7 @@ namespace VNLib.Utils.Memory { if (offset + count > (ulong)block.LongLength) { - throw new ArgumentOutOfRangeException("The offset or count is outside of the range of the block of memory"); + throw new ArgumentOutOfRangeException(nameof(offset), "The offset or count is outside of the range of the block of memory"); } } |