aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Plugins.Essentials')
-rw-r--r--lib/Plugins.Essentials/src/Accounts/AccountUtils.cs2
-rw-r--r--lib/Plugins.Essentials/src/EventProcessor.cs81
-rw-r--r--lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs65
-rw-r--r--lib/Plugins.Essentials/src/Oauth/IOAuth2Provider.cs44
-rw-r--r--lib/Plugins.Essentials/src/Oauth/OauthHttpExtensions.cs18
-rw-r--r--lib/Plugins.Essentials/src/Oauth/OauthSessionCacheExhaustedException.cs43
-rw-r--r--lib/Plugins.Essentials/src/Sessions/SessionBase.cs4
7 files changed, 118 insertions, 139 deletions
diff --git a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
index 33da10e..137fa4a 100644
--- a/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
+++ b/lib/Plugins.Essentials/src/Accounts/AccountUtils.cs
@@ -320,7 +320,7 @@ namespace VNLib.Plugins.Essentials.Accounts
//Get the "local" account flag from the user object
bool localAccount = user.IsLocalAccount();
- //Set local account flag
+ //Set local account flag on the session
entity.Session.HasLocalAccount(localAccount);
//Return the client encrypted data
diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs
index a207e35..bd4383f 100644
--- a/lib/Plugins.Essentials/src/EventProcessor.cs
+++ b/lib/Plugins.Essentials/src/EventProcessor.cs
@@ -35,10 +35,10 @@ using VNLib.Net.Http;
using VNLib.Utils.IO;
using VNLib.Utils.Logging;
using VNLib.Utils.Resources;
+using VNLib.Plugins.Essentials.Accounts;
using VNLib.Plugins.Essentials.Content;
using VNLib.Plugins.Essentials.Sessions;
using VNLib.Plugins.Essentials.Extensions;
-using VNLib.Plugins.Essentials.Accounts;
#nullable enable
@@ -163,12 +163,10 @@ namespace VNLib.Plugins.Essentials
* Wrapper class for converting IHttpEvent endpoints to
* httpEntityEndpoints
*/
- private sealed class EvEndpointWrapper : IVirtualEndpoint<HttpEntity>
+ private sealed record class EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> Wrapped) : IVirtualEndpoint<HttpEntity>
{
- private readonly IVirtualEndpoint<IHttpEvent> _wrapped;
- public EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> wrapping) => _wrapped = wrapping;
- string IEndpoint.Path => _wrapped.Path;
- ValueTask<VfReturnType> IVirtualEndpoint<HttpEntity>.Process(HttpEntity entity) => _wrapped.Process(entity);
+ string IEndpoint.Path => Wrapped.Path;
+ ValueTask<VfReturnType> IVirtualEndpoint<HttpEntity>.Process(HttpEntity entity) => Wrapped.Process(entity);
}
@@ -318,6 +316,8 @@ namespace VNLib.Plugins.Essentials
//Start cancellation token
CancellationTokenSource timeout = new(Options.ExecutionTimeout);
+ FileProcessArgs args;
+
try
{
//Session handle, default to the shared empty session
@@ -341,24 +341,35 @@ namespace VNLib.Plugins.Essentials
HttpEntity entity = new(httpEvent, this, in sessionHandle, timeout.Token);
//Pre-process entity
- FileProcessArgs preProc = await PreProcessEntityAsync(entity);
+ args = await PreProcessEntityAsync(entity);
//If preprocess returned a value, exit
- if (preProc != FileProcessArgs.Continue)
+ if (args != FileProcessArgs.Continue)
{
- ProcessFile(httpEvent, in preProc);
+ ProcessFile(httpEvent, in args);
return;
}
if (VirtualEndpoints.Count > 0)
{
- //Process a virtual file
- FileProcessArgs virtualArgs = await ProcessVirtualAsync(entity);
+ //See if the virtual file is servicable
+ if (!VirtualEndpoints.TryGetValue(entity.Server.Path, out IVirtualEndpoint<HttpEntity>? vf))
+ {
+ args = FileProcessArgs.Continue;
+ }
+ else
+ {
+ //Invoke the page handler process method
+ VfReturnType rt = await vf.Process(entity);
+
+ //Process a virtual file
+ args = GetArgsFromReturn(entity, rt);
+ }
//If the entity was processed, exit
- if (virtualArgs != FileProcessArgs.Continue)
+ if (args != FileProcessArgs.Continue)
{
- ProcessFile(httpEvent, in virtualArgs);
+ ProcessFile(httpEvent, in args);
return;
}
}
@@ -371,7 +382,7 @@ namespace VNLib.Plugins.Essentials
}
//Finally process as file
- FileProcessArgs args = await RouteFileAsync(entity);
+ args = await RouteFileAsync(entity);
//Finally process the file
ProcessFile(httpEvent, in args);
@@ -582,38 +593,29 @@ namespace VNLib.Plugins.Essentials
}
}
-
/// <summary>
- /// If virtual endpoints are enabled, checks for the existance of an
- /// endpoint and attmepts to process that endpoint.
+ /// Gets the <see cref="FileProcessArgs"/> that will finalize the response from the
+ /// given <see cref="VfReturnType"/>
/// </summary>
- /// <param name="entity">The http entity to proccess</param>
- /// <returns>The results to return to the file processor, or null of the entity requires further processing</returns>
- protected virtual async ValueTask<FileProcessArgs> ProcessVirtualAsync(HttpEntity entity)
+ /// <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)
{
- //See if the virtual file is servicable
- if (!VirtualEndpoints.TryGetValue(entity.Server.Path, out IVirtualEndpoint<HttpEntity>? vf))
- {
- return FileProcessArgs.Continue;
- }
-
- //Invoke the page handler process method
- VfReturnType rt = await vf.Process(entity);
-
- if (rt == VfReturnType.VirtualSkip)
+ if (returnType == VfReturnType.VirtualSkip)
{
//Virtual file was handled by the handler
return FileProcessArgs.VirtualSkip;
}
- else if(rt == VfReturnType.ProcessAsFile)
+ else if (returnType == VfReturnType.ProcessAsFile)
{
return FileProcessArgs.Continue;
}
-
+
//If not a get request, process it directly
if (entity.Server.Method == HttpMethod.GET)
{
- switch (rt)
+ switch (returnType)
{
case VfReturnType.Forbidden:
return FileProcessArgs.Deny;
@@ -625,8 +627,8 @@ namespace VNLib.Plugins.Essentials
break;
}
}
-
- switch (rt)
+
+ switch (returnType)
{
case VfReturnType.Forbidden:
entity.CloseResponse(HttpStatusCode.Forbidden);
@@ -642,7 +644,7 @@ namespace VNLib.Plugins.Essentials
entity.CloseResponse(HttpStatusCode.NotFound);
break;
}
-
+
return FileProcessArgs.VirtualSkip;
}
@@ -654,9 +656,9 @@ namespace VNLib.Plugins.Essentials
/// <returns>The results to return to the file processor, this method must return an argument</returns>
protected virtual async ValueTask<FileProcessArgs> RouteFileAsync(HttpEntity entity)
{
- //Read local copy of the router
-
+ //Read local copy of the router
IPageRouter? router = Router;
+
//Make sure router is set
if (router == null)
{
@@ -668,8 +670,7 @@ namespace VNLib.Plugins.Essentials
PostProcessFile(entity, in routine);
//Return the routine
return routine;
- }
-
+ }
/// <summary>
/// Finds the file specified by the request and the server root the user has requested.
diff --git a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
index a91f196..8cabffd 100644
--- a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
+++ b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs
@@ -24,10 +24,12 @@
using System;
using System.Net;
+using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using VNLib.Net.Http;
+using VNLib.Utils.Extensions;
#nullable enable
@@ -52,6 +54,69 @@ namespace VNLib.Plugins.Essentials.Extensions
/// Cache-Control header value for disabling cache
/// </summary>
public static readonly string NO_CACHE_RESPONSE_HEADER_VALUE = HttpHelpers.GetCacheString(CacheType.NoCache | CacheType.NoStore | CacheType.Revalidate);
+
+
+ /// <summary>
+ /// Determines if the client accepts the response content type
+ /// </summary>
+ /// <param name="server"></param>
+ /// <param name="type">The desired content type</param>
+ /// <returns>True if the client accepts the content type, false otherwise</returns>
+ public static bool Accepts(this IConnectionInfo server, ContentType type)
+ {
+ //Get the content type string from he specified content type
+ string contentType = HttpHelpers.GetContentTypeString(type);
+ return Accepts(server, contentType);
+ }
+
+ /// <summary>
+ /// Determines if the client accepts the response content type
+ /// </summary>
+ /// <param name="server"></param>
+ /// <param name="contentType">The desired content type</param>
+ /// <returns>True if the client accepts the content type, false otherwise</returns>
+ public static bool Accepts(this IConnectionInfo server, string contentType)
+ {
+ if (AcceptsAny(server))
+ {
+ return true;
+ }
+
+ //If client accepts exact requested encoding
+ if (server.Accept.Contains(contentType, StringComparer.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ //Search for the content-sub-type
+
+ //Get prinary side of mime type
+ ReadOnlySpan<char> primary = contentType.AsSpan().SliceBeforeParam('/');
+
+ foreach(string accept in server.Accept)
+ {
+ //The the accept subtype
+ ReadOnlySpan<char> ctSubType = accept.AsSpan().SliceBeforeParam('/');
+
+ //See if accepts any subtype, or the primary sub-type matches
+ if (ctSubType[0] == '*' || ctSubType.Equals(primary, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Determines if the connection accepts any content type
+ /// </summary>
+ /// <returns>true if the connection accepts any content typ, false otherwise</returns>
+ private static bool AcceptsAny(this IConnectionInfo server)
+ {
+ //Accept any if no accept header was present, or accept all value */*
+ return server.Accept.Count == 0 || server.Accept.Where(static t => t.StartsWith("*/*", StringComparison.OrdinalIgnoreCase)).Any();
+ }
/// <summary>
/// Gets the <see cref="HttpRequestHeader.IfModifiedSince"/> header value and converts its value to a datetime value
diff --git a/lib/Plugins.Essentials/src/Oauth/IOAuth2Provider.cs b/lib/Plugins.Essentials/src/Oauth/IOAuth2Provider.cs
deleted file mode 100644
index 30944b8..0000000
--- a/lib/Plugins.Essentials/src/Oauth/IOAuth2Provider.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials
-* File: IOAuth2Provider.cs
-*
-* IOAuth2Provider.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/.
-*/
-
-using System;
-using System.Threading.Tasks;
-
-using VNLib.Plugins.Essentials.Sessions;
-
-namespace VNLib.Plugins.Essentials.Oauth
-{
- /// <summary>
- /// An interface that Oauth2 serice providers must implement
- /// to provide sessions to an <see cref="EventProcessor"/>
- /// processor endpoint processor
- /// </summary>
- public interface IOAuth2Provider : ISessionProvider
- {
- /// <summary>
- /// Gets a value indicating how long a session may be valid for
- /// </summary>
- public TimeSpan MaxTokenLifetime { get; }
- }
-} \ No newline at end of file
diff --git a/lib/Plugins.Essentials/src/Oauth/OauthHttpExtensions.cs b/lib/Plugins.Essentials/src/Oauth/OauthHttpExtensions.cs
index 11ab61a..3bce1f6 100644
--- a/lib/Plugins.Essentials/src/Oauth/OauthHttpExtensions.cs
+++ b/lib/Plugins.Essentials/src/Oauth/OauthHttpExtensions.cs
@@ -134,15 +134,15 @@ namespace VNLib.Plugins.Essentials.Oauth
//Set the error result in the header
ev.Server.Headers[HttpResponseHeader.WwwAuthenticate] = error switch
{
- ErrorType.InvalidRequest => $"Bearer error=\"invalid_request\"",
- ErrorType.UnauthorizedClient => $"Bearer error=\"unauthorized_client\"",
- ErrorType.UnsupportedResponseType => $"Bearer error=\"unsupported_response_type\"",
- ErrorType.InvalidScope => $"Bearer error=\"invalid_scope\"",
- ErrorType.ServerError => $"Bearer error=\"server_error\"",
- ErrorType.TemporarilyUnabavailable => $"Bearer error=\"temporarily_unavailable\"",
- ErrorType.InvalidClient => $"Bearer error=\"invalid_client\"",
- ErrorType.InvalidToken => $"Bearer error=\"invalid_token\"",
- _ => $"Bearer error=\"error\"",
+ ErrorType.InvalidRequest => "Bearer error=\"invalid_request\"",
+ ErrorType.UnauthorizedClient => "Bearer error=\"unauthorized_client\"",
+ ErrorType.UnsupportedResponseType => "Bearer error=\"unsupported_response_type\"",
+ ErrorType.InvalidScope => "Bearer error=\"invalid_scope\"",
+ ErrorType.ServerError => "Bearer error=\"server_error\"",
+ ErrorType.TemporarilyUnabavailable => "Bearer error=\"temporarily_unavailable\"",
+ ErrorType.InvalidClient => "Bearer error=\"invalid_client\"",
+ ErrorType.InvalidToken => "Bearer error=\"invalid_token\"",
+ _ => "Bearer error=\"error\"",
};
//Close the response with the status code
ev.CloseResponse(code);
diff --git a/lib/Plugins.Essentials/src/Oauth/OauthSessionCacheExhaustedException.cs b/lib/Plugins.Essentials/src/Oauth/OauthSessionCacheExhaustedException.cs
deleted file mode 100644
index da91444..0000000
--- a/lib/Plugins.Essentials/src/Oauth/OauthSessionCacheExhaustedException.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Essentials
-* File: OauthSessionCacheExhaustedException.cs
-*
-* OauthSessionCacheExhaustedException.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/.
-*/
-
-using System;
-
-
-namespace VNLib.Plugins.Essentials.Oauth
-{
- /// <summary>
- /// Raised when the session cache space has been exhausted and cannot
- /// load the new session into cache.
- /// </summary>
- public class OauthSessionCacheExhaustedException : Exception
- {
- public OauthSessionCacheExhaustedException(string message) : base(message)
- {}
- public OauthSessionCacheExhaustedException(string message, Exception innerException) : base(message, innerException)
- {}
- public OauthSessionCacheExhaustedException()
- {}
- }
-} \ No newline at end of file
diff --git a/lib/Plugins.Essentials/src/Sessions/SessionBase.cs b/lib/Plugins.Essentials/src/Sessions/SessionBase.cs
index 46e6ec8..100818b 100644
--- a/lib/Plugins.Essentials/src/Sessions/SessionBase.cs
+++ b/lib/Plugins.Essentials/src/Sessions/SessionBase.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials
@@ -31,7 +31,7 @@ using VNLib.Utils;
namespace VNLib.Plugins.Essentials.Sessions
{
/// <summary>
- /// Provides a base class for the <see cref="ISession"/> interface for exclusive use within a multithreaded
+ /// Provides a base class for the <see cref="ISession"/> interface that handles basic housekeeping
/// context
/// </summary>
public abstract class SessionBase : ISession