aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-02-29 21:23:26 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-02-29 21:23:26 -0500
commitb679ddd4e647ac915febd0d5a5e488a1e8e48842 (patch)
treecf414be9a53342e8d59194198cde5bf3c2187fc1 /lib/Plugins.Essentials
parent2b1314c1475e7e1831c691cf349cb89c66fa320c (diff)
Squashed commit of the following:
commit 231e26e5c6731e6e156d7c0591518e84a3b82f5a Author: vnugent <public@vaughnnugent.com> Date: Thu Feb 29 20:59:42 2024 -0500 fix: #5 find and patch Windows compression bug and some deployment commit d0bfe14e0a0e27172b8dd41f468265e651784837 Author: vnugent <public@vaughnnugent.com> Date: Wed Feb 21 21:39:19 2024 -0500 fix: #4 fix readme licensing message for accuracy commit 6f37f152fcd105e40af6689192a36b87eda95f51 Author: vnugent <public@vaughnnugent.com> Date: Wed Feb 21 21:37:55 2024 -0500 fix: jwt hashalg sizing and public api for hashalg sizes
Diffstat (limited to 'lib/Plugins.Essentials')
-rw-r--r--lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs16
-rw-r--r--lib/Plugins.Essentials/src/EventProcessor.cs18
-rw-r--r--lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs1
-rw-r--r--lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs98
-rw-r--r--lib/Plugins.Essentials/src/SemiConsistentVeTable.cs25
-rw-r--r--lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs4
-rw-r--r--lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs4
7 files changed, 120 insertions, 46 deletions
diff --git a/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs b/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs
index e346af1..2f2ba91 100644
--- a/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs
+++ b/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -22,6 +22,8 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
+using System;
+
using VNLib.Utils.Memory;
using VNLib.Plugins.Essentials.Users;
@@ -40,7 +42,17 @@ namespace VNLib.Plugins.Essentials.Accounts
public ulong Privileges { get; init; } = AccountUtil.MINIMUM_LEVEL;
///<inheritdoc/>
- public string EmailAddress { get; init; } = string.Empty;
+ public string Username { get; init; } = string.Empty;
+
+ /// <summary>
+ /// Obsolete: Use the Username property instead
+ /// </summary>
+ [Obsolete("Use the Username property instead")]
+ public string EmailAddress
+ {
+ get => Username;
+ init => Username = value;
+ }
///<inheritdoc/>
public bool UseRawPassword { get; init; }
diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs
index 61a9b64..f052c56 100644
--- a/lib/Plugins.Essentials/src/EventProcessor.cs
+++ b/lib/Plugins.Essentials/src/EventProcessor.cs
@@ -188,8 +188,7 @@ namespace VNLib.Plugins.Essentials
//If the processor had an error recovering the session, return the result to the processor
if (entity.EventSessionHandle.EntityStatus != FileProcessArgs.Continue)
{
- ProcessFile(httpEvent, entity.EventSessionHandle.EntityStatus);
- return;
+ goto ProcessRoutine;
}
//Attach the new session to the entity
@@ -209,7 +208,7 @@ namespace VNLib.Plugins.Essentials
//Loop through nodes
while(mwNode != null)
{
- //Process
+ //Invoke mw handler on our event
entity.EventArgs = await mwNode.ValueRef.ProcessAsync(entity);
switch (entity.EventArgs.Routine)
@@ -237,7 +236,7 @@ namespace VNLib.Plugins.Essentials
//Process a virtual file
GetArgsFromVirtualReturn(entity, rt, out entity.EventArgs);
- //If the entity was processed, exit
+ //If the entity was processed by the handler, exit
if (entity.EventArgs != FileProcessArgs.Continue)
{
goto RespondAndExit;
@@ -275,8 +274,10 @@ namespace VNLib.Plugins.Essentials
}
}
+ ProcessRoutine:
+
//Finally process the file
- ProcessFile(httpEvent, in entity.EventArgs);
+ ProcessRoutine(httpEvent, in entity.EventArgs);
}
catch (ContentTypeUnacceptableException)
{
@@ -329,7 +330,7 @@ namespace VNLib.Plugins.Essentials
/// </summary>
/// <param name="entity">The entity to process the file for</param>
/// <param name="args">The selected <see cref="FileProcessArgs"/> to determine what file to process</param>
- protected virtual void ProcessFile(IHttpEvent entity, ref readonly FileProcessArgs args)
+ protected virtual void ProcessRoutine(IHttpEvent entity, ref readonly FileProcessArgs args)
{
try
{
@@ -413,8 +414,7 @@ namespace VNLib.Plugins.Essentials
//Get the content type of he file
ContentType fileType = HttpHelpers.GetContentTypeFromFile(filename);
-
- //Make sure the client accepts the content type
+
if (!entity.Server.Accepts(fileType))
{
//Unacceptable
@@ -729,7 +729,7 @@ namespace VNLib.Plugins.Essentials
WeakReference<object?> wr = Volatile.Read(ref _objects[tableIndex]);
//Try to get the object instance
- wr.TryGetTarget(out object? value);
+ _ = wr.TryGetTarget(out object? value);
instance = (T?)value;
}
diff --git a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
index 92fae08..a99b1ab 100644
--- a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
+++ b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
@@ -354,6 +354,7 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <param name="domain"></param>
/// <param name="path"></param>
/// <param name="sameSite"></param>
+ /// <param name="expires"></param>
/// <param name="httpOnly"></param>
/// <param name="secure"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs
index f49af32..5f59346 100644
--- a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs
+++ b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs
@@ -163,6 +163,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponseJson(this IHttpEvent ev, HttpStatusCode code, JsonDocument data)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if(data == null)
{
ev.CloseResponse(code);
@@ -195,7 +197,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponse<T>(this IHttpEvent ev, T webm) where T:WebMessage
{
- if (webm == null)
+ ArgumentNullException.ThrowIfNull(ev);
+
+ if (webm is null)
{
ev.CloseResponse(HttpStatusCode.OK);
}
@@ -220,6 +224,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, FileInfo file)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(file);
+
//Close with file
ev.CloseResponse(code, file);
//Set content dispostion as attachment (only if successfull)
@@ -236,6 +243,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, FileStream file)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(file);
+
//Close with file
ev.CloseResponse(code, file);
//Set content dispostion as attachment (only if successfull)
@@ -255,6 +265,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, ContentType ct, Stream data, string fileName, long length)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(data);
+
//Close with file
ev.CloseResponse(code, ct, data, length);
//Set content dispostion as attachment (only if successfull)
@@ -275,6 +288,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, FileInfo file)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(file);
+
//Open filestream for file
FileStream fs = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
try
@@ -302,13 +318,16 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, FileStream file)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(file);
+
//Get content type from filename
ContentType ct = HttpHelpers.GetContentTypeFromFile(file.Name);
//Set the input as a stream
ev.CloseResponse(code, ct, file, file.Length);
}
-
+
/// <summary>
/// Close a response to a connection with a character buffer using the server wide
/// <see cref="ConnectionInfo.Encoding"/> encoding
@@ -321,12 +340,10 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ContentTypeUnacceptableException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data)
- {
+ public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data) =>
//Get a memory stream using server built-in encoding
CloseResponse(ev, code, type, data, ev.Server.Encoding);
- }
-
+
/// <summary>
/// Close a response to a connection with a character buffer using the specified encoding type
/// </summary>
@@ -339,6 +356,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data, Encoding encoding)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (data.IsEmpty)
{
ev.CloseResponse(code);
@@ -367,6 +386,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<byte> data)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (data.IsEmpty)
{
ev.CloseResponse(code);
@@ -391,6 +412,9 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CloseWithRelativeFile(this HttpEntity entity, HttpStatusCode code, string filePath)
{
+ ArgumentNullException.ThrowIfNull(entity);
+ ArgumentNullException.ThrowIfNull(filePath);
+
//See if file exists and is within the root's directory
if (entity.RequestedRoot.FindResourceInRoot(filePath, out string realPath))
{
@@ -412,11 +436,9 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <remarks>Sets required headers for redirection, disables cache control, and returns the status code to the client</remarks>
/// <exception cref="UriFormatException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Redirect(this IHttpEvent ev, RedirectType type, string location)
- {
- Redirect(ev, type, new Uri(location, UriKind.RelativeOrAbsolute));
- }
-
+ public static void Redirect(this IHttpEvent ev, RedirectType type, string location)
+ => Redirect(ev, type, new Uri(location, UriKind.RelativeOrAbsolute));
+
/// <summary>
/// Redirects a client using the specified <see cref="RedirectType"/>
/// </summary>
@@ -427,6 +449,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Redirect(this IHttpEvent ev, RedirectType type, Uri location)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(location);
if(type == RedirectType.None)
{
@@ -472,6 +496,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetJsonFromArg<T>(this IHttpEvent ev, string key, JsonSerializerOptions options, out T? obj)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
//Check for key in argument
if (ev.RequestArgs.TryGetNonEmptyValue(key, out string? value))
{
@@ -502,6 +528,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static JsonDocument? GetJsonFromArg(this IHttpEvent ev, string key, in JsonDocumentOptions options = default)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
try
{
//Check for key in argument
@@ -528,6 +556,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? GetJsonFromFile<T>(this IHttpEvent ev, JsonSerializerOptions? options = null, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (ev.Files.Count <= uploadIndex)
{
return default;
@@ -563,6 +593,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static JsonDocument? GetJsonFromFile(this IHttpEvent ev, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (ev.Files.Count <= uploadIndex)
{
return default;
@@ -599,6 +631,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueTask<T?> GetJsonFromFileAsync<T>(this HttpEntity ev, JsonSerializerOptions? options = null, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (ev.Files.Count <= uploadIndex)
{
return ValueTask.FromResult<T?>(default);
@@ -640,6 +674,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task<JsonDocument?> GetJsonFromFileAsync(this HttpEntity ev, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+
if (ev.Files.Count <= uploadIndex)
{
return DocTaskDefault;
@@ -677,6 +713,9 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <exception cref="IndexOutOfRangeException"></exception>
public static Task<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, Task<T?>> parser, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(parser);
+
if (ev.Files.Count <= uploadIndex)
{
return Task.FromResult<T?>(default);
@@ -698,6 +737,9 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <exception cref="IndexOutOfRangeException"></exception>
public static Task<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, string, Task<T?>> parser, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(parser);
+
if (ev.Files.Count <= uploadIndex)
{
return Task.FromResult<T?>(default);
@@ -720,6 +762,9 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <exception cref="IndexOutOfRangeException"></exception>
public static ValueTask<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, ValueTask<T?>> parser, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(parser);
+
if (ev.Files.Count <= uploadIndex)
{
return ValueTask.FromResult<T?>(default);
@@ -741,6 +786,9 @@ namespace VNLib.Plugins.Essentials.Extensions
/// <exception cref="IndexOutOfRangeException"></exception>
public static ValueTask<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, string, ValueTask<T?>> parser, int uploadIndex = 0)
{
+ ArgumentNullException.ThrowIfNull(ev);
+ ArgumentNullException.ThrowIfNull(parser);
+
if (ev.Files.Count <= uploadIndex)
{
return ValueTask.FromResult<T?>(default);
@@ -760,6 +808,8 @@ namespace VNLib.Plugins.Essentials.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAuthorization(this IConnectionInfo ci, [NotNullWhen(true)] out string? token)
{
+ ArgumentNullException.ThrowIfNull(ci);
+
//Get auth header value
string? authorization = ci.Headers[HttpRequestHeader.Authorization];
//Check if its set
@@ -767,7 +817,7 @@ namespace VNLib.Plugins.Essentials.Extensions
{
int bearerIndex = authorization.IndexOf(BEARER_STRING, StringComparison.OrdinalIgnoreCase);
//Calc token offset, get token, and trim any whitespace
- token = authorization[(bearerIndex + BEARER_LEN)..].Trim();
+ token = authorization.AsSpan(bearerIndex + BEARER_LEN).Trim().ToString();
return true;
}
token = null;
@@ -819,11 +869,9 @@ namespace VNLib.Plugins.Essentials.Extensions
)
{
//Must define an accept callback
- _ = socketOpenedCallback ?? throw new ArgumentNullException(nameof(socketOpenedCallback));
-
- bool success = PrepWebSocket(entity, subProtocol);
+ ArgumentNullException.ThrowIfNull(socketOpenedCallback);
- if (success)
+ if (PrepWebSocket(entity, subProtocol))
{
//Set a default keep alive if none was specified
if (keepAlive == default)
@@ -841,8 +889,11 @@ namespace VNLib.Plugins.Essentials.Extensions
//Setup a new websocket session with a new session id
entity.DangerousChangeProtocol(ws);
+
+ return true;
}
- return success;
+
+ return false;
}
/// <summary>
@@ -858,11 +909,10 @@ namespace VNLib.Plugins.Essentials.Extensions
public static bool AcceptWebSocket(this IHttpEvent entity, WebSocketAcceptedCallback socketOpenedCallback, string? subProtocol = null, TimeSpan keepAlive = default)
{
//Must define an accept callback
- _ = socketOpenedCallback ?? throw new ArgumentNullException(nameof(socketOpenedCallback));
+ ArgumentNullException.ThrowIfNull(entity);
+ ArgumentNullException.ThrowIfNull(socketOpenedCallback);
- bool success = PrepWebSocket(entity, subProtocol);
-
- if(success)
+ if(PrepWebSocket(entity, subProtocol))
{
//Set a default keep alive if none was specified
if (keepAlive == default)
@@ -879,15 +929,19 @@ namespace VNLib.Plugins.Essentials.Extensions
//Setup a new websocket session with a new session id
entity.DangerousChangeProtocol(ws);
+
+ return true;
}
- return success;
+ return false;
}
private static string GetNewSocketId() => Guid.NewGuid().ToString("N");
private static bool PrepWebSocket(this IHttpEvent entity, string? subProtocol = null)
{
+ ArgumentNullException.ThrowIfNull(entity);
+
//Make sure this is a websocket request
if (!entity.Server.IsWebSocketRequest)
{
diff --git a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs
index a235b13..e1706f4 100644
--- a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs
+++ b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs
@@ -28,6 +28,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Collections.Frozen;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using VNLib.Net.Http;
using VNLib.Plugins.Essentials.Endpoints;
@@ -65,6 +66,7 @@ namespace VNLib.Plugins.Essentials
new Dictionary<string, IVirtualEndpoint<HttpEntity>>(StringComparer.OrdinalIgnoreCase)
.ToFrozenDictionary();
+ private bool _isEmpty = true;
/*
* A lock that is held by callers that intend to
@@ -73,14 +75,14 @@ namespace VNLib.Plugins.Essentials
private readonly object VeUpdateLock = new();
///<inheritdoc/>
- public bool IsEmpty => VirtualEndpoints.Count == 0;
+ public bool IsEmpty => _isEmpty;
///<inheritdoc/>
public void AddEndpoint(params IEndpoint[] endpoints)
{
//Check
- _ = endpoints ?? throw new ArgumentNullException(nameof(endpoints));
+ ArgumentNullException.ThrowIfNull(endpoints);
//Make sure all endpoints specify a path
if (endpoints.Any(static e => string.IsNullOrWhiteSpace(e?.Path)))
{
@@ -105,6 +107,7 @@ namespace VNLib.Plugins.Essentials
//Uinion endpoints by their paths to combine them
IEnumerable<IVirtualEndpoint<HttpEntity>> allEndpoints = eps.UnionBy(evs, static s => s.Path);
+ //Only allow 1 thread at a time to mutate the table
lock (VeUpdateLock)
{
//Clone the current dictonary
@@ -115,10 +118,11 @@ namespace VNLib.Plugins.Essentials
newTable.Add(ep.Path, ep);
}
- FrozenDictionary<string, IVirtualEndpoint<HttpEntity>> newTableFrozen = newTable.ToFrozenDictionary();
+ //Update is-empty flag
+ _isEmpty = newTable.Count == 0;
- //Store the new table
- _ = Interlocked.Exchange(ref VirtualEndpoints, newTableFrozen);
+ //Create the new table and store the entire table
+ _ = Interlocked.Exchange(ref VirtualEndpoints, newTable.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase));
}
}
@@ -157,24 +161,27 @@ namespace VNLib.Plugins.Essentials
_ = newTable.Remove(eps);
}
- FrozenDictionary<string, IVirtualEndpoint<HttpEntity>> newTableFrozen = newTable.ToFrozenDictionary();
+ //Update is-empty flag
+ _isEmpty = newTable.Count == 0;
//Store the new table
- _ = Interlocked.Exchange(ref VirtualEndpoints, newTableFrozen);
+ _ = Interlocked.Exchange(ref VirtualEndpoints, newTable.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase));
}
}
///<inheritdoc/>
- public bool TryGetEndpoint(string path, out IVirtualEndpoint<HttpEntity>? endpoint) => VirtualEndpoints.TryGetValue(path, out endpoint);
+ public bool TryGetEndpoint(string path, [NotNullWhen(true)] out IVirtualEndpoint<HttpEntity>? endpoint)
+ => VirtualEndpoints.TryGetValue(path, out endpoint);
/*
* Wrapper class for converting IHttpEvent endpoints to
* httpEntityEndpoints
*/
- private sealed record class EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> Wrapped) : IVirtualEndpoint<HttpEntity>
+ private sealed class EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> Wrapped) : IVirtualEndpoint<HttpEntity>
{
string IEndpoint.Path => Wrapped.Path;
+
ValueTask<VfReturnType> IVirtualEndpoint<HttpEntity>.Process(HttpEntity entity) => Wrapped.Process(entity);
}
}
diff --git a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs
index d3fc475..05d6712 100644
--- a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs
+++ b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -49,7 +49,7 @@ namespace VNLib.Plugins.Essentials.Sessions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetOrigin(this ISession session) => session[ORIGIN_ENTRY];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Uri GetOriginUri(this ISession session) => Uri.TryCreate(session[ORIGIN_ENTRY], UriKind.Absolute, out Uri origin) ? origin : null;
+ public static Uri? GetOriginUri(this ISession session) => Uri.TryCreate(session[ORIGIN_ENTRY], UriKind.Absolute, out Uri? origin) ? origin : null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetOrigin(this ISession session, string origin) => session[ORIGIN_ENTRY] = origin;
diff --git a/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs b/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs
index a5b9a30..bd89be1 100644
--- a/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs
+++ b/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs
@@ -44,9 +44,9 @@ namespace VNLib.Plugins.Essentials.Users
ulong Privileges { get; }
/// <summary>
- /// The user's email address
+ /// The user's unique username (may also be an email address
/// </summary>
- string EmailAddress { get; }
+ string Username { get; }
/// <summary>
/// Should the password be stored as-is in the database?