diff options
author | vnugent <public@vaughnnugent.com> | 2023-08-05 00:52:48 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-08-05 00:52:48 -0400 |
commit | 46c6450fdc9b62aa04bae545b03d93a2e8c8895a (patch) | |
tree | c5648e1ce68acd28cb29807ba65d90af3ba1caef /lib | |
parent | a3ded6043a926735142f3bb48093e83135044c06 (diff) |
Response compression buffer patch
Diffstat (limited to 'lib')
16 files changed, 235 insertions, 236 deletions
diff --git a/lib/Hashing.Portable/src/IdentityUtility/JwtClaim.cs b/lib/Hashing.Portable/src/IdentityUtility/JwtPayload.cs index be8c682..eb33e00 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JwtClaim.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JwtPayload.cs @@ -3,9 +3,9 @@ * * Library: VNLib * Package: VNLib.Hashing.Portable -* File: JwtClaim.cs +* File: JwtPayload.cs * -* JwtClaim.cs is part of VNLib.Hashing.Portable which is part of the larger +* JwtPayload.cs is part of VNLib.Hashing.Portable which is part of the larger * VNLib collection of libraries and utilities. * * VNLib.Hashing.Portable is free software: you can redistribute it and/or modify diff --git a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs index af77329..0695791 100644 --- a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs +++ b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs @@ -36,9 +36,9 @@ using System; using System.Buffers; +using System.Text.Json; using System.Diagnostics; using System.IO.Compression; -using System.Text.Json; using System.Runtime.CompilerServices; using VNLib.Net.Http; @@ -170,13 +170,6 @@ namespace VNLib.Net.Compression throw new InvalidOperationException("This compressor instance has not been initialized, cannot free compressor"); } - /* - * We only alloc the buffer on the first call because we can assume this is the - * largest input data the compressor will see, and the block size should be used - * as a reference for callers. If its too small it will just have to be flushed - */ - - //Compress the block return CompressBlock(compressor.Instance, output, input, false); } diff --git a/lib/Net.Compression/vnlib_compress/compression.c b/lib/Net.Compression/vnlib_compress/compression.c index 0e563ff..959540d 100644 --- a/lib/Net.Compression/vnlib_compress/compression.c +++ b/lib/Net.Compression/vnlib_compress/compression.c @@ -63,11 +63,11 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv VNLIB_EXPORT CompressorType VNLIB_CC GetSupportedCompressors(void); -VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(void* compressor); +VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(_In_ void* compressor); -VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(void* compressor); +VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(_In_ void* compressor); -VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(void* compressor); +VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(_In_ void* compressor); VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionLevel level); @@ -106,7 +106,7 @@ VNLIB_EXPORT CompressorType VNLIB_CC GetSupportedCompressors(void) return supported; } -VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(void* compressor) +VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(_In_ void* compressor) { if (!compressor) { @@ -116,7 +116,7 @@ VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(void* compressor) return ((CompressorState*)compressor)->type; } -VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(void* compressor) +VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(_In_ void* compressor) { if (!compressor) { @@ -126,7 +126,7 @@ VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(void* compressor) return ((CompressorState*)compressor)->level; } -VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(void* compressor) +VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(_In_ void* compressor) { if (!compressor) { @@ -195,7 +195,6 @@ VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionL } - /* If result was successfull return the context pointer, if the creation failed, free the state if it was allocated @@ -290,7 +289,7 @@ VNLIB_EXPORT int VNLIB_CC FreeCompressor(void* compressor) return errorCode; } -VNLIB_EXPORT int VNLIB_CC GetCompressedSize(void* compressor, int inputLength, int flush) +VNLIB_EXPORT int VNLIB_CC GetCompressedSize(_In_ void* compressor, int inputLength, int flush) { CompressorState* comp; int result; @@ -343,7 +342,7 @@ VNLIB_EXPORT int VNLIB_CC GetCompressedSize(void* compressor, int inputLength, i * indicate failure. * @param compressor */ -VNLIB_EXPORT int VNLIB_CC CompressBlock(void* compressor, CompressionOperation* operation) +VNLIB_EXPORT int VNLIB_CC CompressBlock(_In_ void* compressor, CompressionOperation* operation) { int result; CompressorState* comp; diff --git a/lib/Net.Compression/vnlib_compress/compression.h b/lib/Net.Compression/vnlib_compress/compression.h index 153b7fc..930ef1c 100644 --- a/lib/Net.Compression/vnlib_compress/compression.h +++ b/lib/Net.Compression/vnlib_compress/compression.h @@ -40,8 +40,6 @@ #include "util.h" #include <stdint.h> -#include <stddef.h> -#include <stdlib.h> #define ERR_COMP_TYPE_NOT_SUPPORTED -9 #define ERR_COMP_LEVEL_NOT_SUPPORTED -10 @@ -140,13 +138,13 @@ typedef struct CompressionOperationStruct { /* * Input stream data */ - const uint8_t* bytesIn; + const void* bytesIn; const int bytesInLength; /* * Output buffer/data stream */ - uint8_t* bytesOut; + void* bytesOut; const int bytesOutLength; /* diff --git a/lib/Net.Compression/vnlib_compress/util.h b/lib/Net.Compression/vnlib_compress/util.h index 6e7b59e..583e10b 100644 --- a/lib/Net.Compression/vnlib_compress/util.h +++ b/lib/Net.Compression/vnlib_compress/util.h @@ -24,6 +24,8 @@ #ifndef UTIL_H_ #define UTIL_H_ +#include <stdlib.h> + /* * Stub missing types and constants for GCC */ @@ -46,6 +48,11 @@ #define NULL 0 #endif /* !NULL */ +#ifndef _In_ +#define _In_ +#endif // !_In_ + + /* * Stub method for malloc. All calls to vnmalloc should be freed with vnfree. */ diff --git a/lib/Net.Http/src/Core/ConnectionInfo.cs b/lib/Net.Http/src/Core/ConnectionInfo.cs index e680152..8a1525c 100644 --- a/lib/Net.Http/src/Core/ConnectionInfo.cs +++ b/lib/Net.Http/src/Core/ConnectionInfo.cs @@ -24,13 +24,11 @@ using System; using System.Net; -using System.Linq; using System.Text; using System.Collections.Generic; using System.Security.Authentication; using VNLib.Net.Http.Core; -using VNLib.Utils.Extensions; namespace VNLib.Net.Http { @@ -76,65 +74,16 @@ namespace VNLib.Net.Http ///<inheritdoc/> public IReadOnlyDictionary<string, string> RequestCookies => Context.Request.Cookies; ///<inheritdoc/> - public IEnumerable<string> Accept => Context.Request.Accept; + public IReadOnlyCollection<string> Accept => Context.Request.Accept; ///<inheritdoc/> public TransportSecurityInfo? TransportSecurity => Context.GetSecurityInfo(); ///<inheritdoc/> - public bool Accepts(ContentType type) - { - //Get the content type string from he specified content type - string contentType = HttpHelpers.GetContentTypeString(type); - return Accepts(contentType); - } - - ///<inheritdoc/> - public bool Accepts(string contentType) - { - if (AcceptsAny()) - { - return true; - } - - //If client accepts exact requested encoding - if (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('/'); - - for (int i = 0; i < Context.Request.Accept.Count; i++) - { - //The the accept subtype - ReadOnlySpan<char> ctSubType = Context.Request.Accept[i].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 bool AcceptsAny() - { - //Accept any if no accept header was present, or accept all value */* - return Context.Request.Accept.Count == 0 || Accept.Where(static t => t.StartsWith("*/*", StringComparison.OrdinalIgnoreCase)).Any(); - } - - ///<inheritdoc/> public void SetCookie(string name, string value, string? domain, string? path, TimeSpan Expires, CookieSameSite sameSite, bool httpOnly, bool secure) { + //name MUST not be null + _ = name ?? throw new ArgumentNullException(nameof(name)); + //Create the new cookie HttpCookie cookie = new(name) { diff --git a/lib/Net.Http/src/Core/Response/ResponseWriter.cs b/lib/Net.Http/src/Core/Response/ResponseWriter.cs index d67fc01..dcb220f 100644 --- a/lib/Net.Http/src/Core/Response/ResponseWriter.cs +++ b/lib/Net.Http/src/Core/Response/ResponseWriter.cs @@ -242,7 +242,7 @@ namespace VNLib.Net.Http.Core } //Compress the buffered data and flush if required - if(CompressNextSegment(buffer, comp, writer)) + if (CompressNextSegment(buffer, read, comp, writer)) { //Time to flush await writer.FlushAsync(false); @@ -309,13 +309,13 @@ namespace VNLib.Net.Http.Core return writer.Advance(res.BytesWritten) == 0; } - private static bool CompressNextSegment(Memory<byte> readSegment, IResponseCompressor comp, IResponseDataWriter writer) + private static bool CompressNextSegment(Memory<byte> readSegment, int read, IResponseCompressor comp, IResponseDataWriter writer) { //Get output buffer Memory<byte> output = writer.GetMemory(); //Compress the trimmed block - CompressionResult res = comp.CompressBlock(readSegment, output); + CompressionResult res = comp.CompressBlock(readSegment[..read], output); return writer.Advance(res.BytesWritten) == 0; } diff --git a/lib/Net.Http/src/IConnectionInfo.cs b/lib/Net.Http/src/IConnectionInfo.cs index 17ee16f..0feedd9 100644 --- a/lib/Net.Http/src/IConnectionInfo.cs +++ b/lib/Net.Http/src/IConnectionInfo.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -113,25 +113,12 @@ namespace VNLib.Net.Http /// <summary> /// Gets an <see cref="IEnumerator{T}"/> for the parsed accept header values /// </summary> - IEnumerable<string> Accept { get; } + IReadOnlyCollection<string> Accept { get; } /// <summary> /// Gets the underlying transport security information for the current connection /// </summary> TransportSecurityInfo? TransportSecurity { get; } - - /// <summary> - /// Determines if the client accepts the response content type - /// </summary> - /// <param name="type">The desired content type</param> - /// <returns>True if the client accepts the content type, false otherwise</returns> - bool Accepts(ContentType type); - - /// <summary> - /// Determines if the client accepts the response content type - /// </summary> - /// <param name="contentType">The desired content type</param> - /// <returns>True if the client accepts the content type, false otherwise</returns> - bool Accepts(string contentType); + /// <summary> /// Adds a new cookie to the response. If a cookie with the same name and value diff --git a/lib/Net.Rest.Client/src/Construction/Extensions.cs b/lib/Net.Rest.Client/src/Construction/Extensions.cs index 52a1301..bbc8b1d 100644 --- a/lib/Net.Rest.Client/src/Construction/Extensions.cs +++ b/lib/Net.Rest.Client/src/Construction/Extensions.cs @@ -153,6 +153,21 @@ namespace VNLib.Net.Rest.Client.Construction } /// <summary> + /// Sets a callback that will create a query string argument value + /// </summary> + /// <typeparam name="TModel">The request entity type</typeparam> + /// <param name="builder"></param> + /// <param name="value">The constant query value</param> + /// <param name="parameter">The name of the query paramter to set</param> + /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns> + public static IRestRequestBuilder<TModel> WithQuery<TModel>(this IRestRequestBuilder<TModel> builder, string parameter, string value) + { + //Get a query item string value from the callback and sets the query paremter + builder.WithModifier((m, r) => r.AddQueryParameter(parameter, value)); + return builder; + } + + /// <summary> /// Specifies a model that will handle its own request body builder /// </summary> /// <typeparam name="TModel"></typeparam> @@ -182,6 +197,18 @@ namespace VNLib.Net.Rest.Client.Construction } /// <summary> + /// Allows building of a single endpoint from an <see cref="IRestEndpointDefinition"/> and stores it in the + /// adapter store. + /// </summary> + /// <param name="site"></param> + /// <returns>A chainable <see cref="IRestEndpointBuilder"/> to build the endpoint</returns> + public static IRestEndpointBuilder DefineSingleEndpoint(this IRestSiteEndpointStore site) + { + EndpointAdapterBuilder builder = new(site); + return builder; + } + + /// <summary> /// Sets the uri for all new request messages /// </summary> /// <typeparam name="TModel">The request entity type</typeparam> @@ -195,6 +222,35 @@ namespace VNLib.Net.Rest.Client.Construction return builder; } + + /// <summary> + /// Sets the uri for all new request messages + /// </summary> + /// <typeparam name="TModel">The request entity type</typeparam> + /// <param name="builder"></param> + /// <param name="uri">Specifies the uri for the request builder</param> + /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns> + public static IRestRequestBuilder<TModel> WithUrl<TModel>(this IRestRequestBuilder<TModel> builder, Uri uri) + { + //Use the supplied method to convert the uri to a string + builder.WithUrl((m) => uri.ToString()); + return builder; + } + + /// <summary> + /// Sets the uri for all new request messages + /// </summary> + /// <typeparam name="TModel">The request entity type</typeparam> + /// <param name="builder"></param> + /// <param name="url">Specifies the uri for the request builder</param> + /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns> + public static IRestRequestBuilder<TModel> WithUrl<TModel>(this IRestRequestBuilder<TModel> builder, string url) + { + //Use the supplied method to convert the uri to a string + builder.WithUrl((m) => url); + return builder; + } + /// <summary> /// Specifies a connection header to set for every request /// </summary> @@ -226,6 +282,37 @@ namespace VNLib.Net.Rest.Client.Construction } /// <summary> + /// Sets a callback that will create a parameter string argument value + /// </summary> + /// <typeparam name="TModel">The request entity type</typeparam> + /// <param name="builder"></param> + /// <param name="callback">The callback method that gets the query value</param> + /// <param name="parameter">The body paramter value to set</param> + /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns> + public static IRestRequestBuilder<TModel> WithParameter<TModel>(this IRestRequestBuilder<TModel> builder, string parameter, Func<TModel, string> callback) + { + //Get a query item string value from the callback and sets the query paremter + builder.WithModifier((m, r) => r.AddParameter(parameter, callback(m), ParameterType.GetOrPost)); + return builder; + } + + /// <summary> + /// Sets a constant parameter string argument value + /// </summary> + /// <typeparam name="TModel">The request entity type</typeparam> + /// <param name="builder"></param> + /// <param name="value">The constant value</param> + /// <param name="parameter">The query paramter value to set</param> + /// <returns>The chainable <see cref="IRestRequestBuilder{TModel}"/></returns> + public static IRestRequestBuilder<TModel> WithParameter<TModel>(this IRestRequestBuilder<TModel> builder, string parameter, string value) + { + //Get a query item string value from the callback and sets the query paremter + builder.WithModifier((m, r) => r.AddParameter(parameter, value, ParameterType.GetOrPost)); + return builder; + } + + + /// <summary> /// Converts a task that resolves a <see cref="RestResponse"/> to a task that deserializes /// the response data as json. /// </summary> 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 |