From d65aa4f010adbd6c8162eb08c5787550e27d3e47 Mon Sep 17 00:00:00 2001 From: vnugent Date: Wed, 19 Apr 2023 01:42:00 -0400 Subject: Fix watcher pause, add base64urlencode --- .../src/IdentityUtility/JsonWebToken.cs | 17 ++-- .../src/IdentityUtility/JwtExtensions.cs | 37 ++++----- lib/Plugins.Essentials/src/HttpEntity.cs | 2 +- .../src/Sessions/SessionHandle.cs | 39 +--------- lib/Plugins.Runtime/src/AsmFileWatcher.cs | 13 +++- lib/Plugins.Runtime/src/RuntimePluginLoader.cs | 3 +- lib/Utils/src/VnEncoding.cs | 91 +++++++++++++++++++++- 7 files changed, 125 insertions(+), 77 deletions(-) (limited to 'lib') diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs index 6ee597e..ef313ca 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs @@ -24,7 +24,6 @@ using System; using System.Text; -using System.Buffers; using System.Buffers.Text; using VNLib.Utils; @@ -250,19 +249,21 @@ namespace VNLib.Hashing.IdentityUtility { //Calculate the proper base64 buffer size int base64BufSize = Base64.GetMaxEncodedToUtf8Length(value.Length); - //Alloc buffer + + //Alloc buffer from out heap using UnsafeMemoryHandle binBuffer = Heap.UnsafeAlloc(base64BufSize); + + //Urlencode without base64 padding characters + ERRNO written = VnEncoding.Base64UrlEncode(value, binBuffer.Span, false); + //Slice off the begiing of the buffer for the base64 encoding - if(Base64.EncodeToUtf8(value, binBuffer.Span, out _, out int written) != OperationStatus.Done) + if(!written) { throw new InternalBufferTooSmallException("Failed to encode the specified value to base64"); } - //Base64 encoded - Span base64Data = binBuffer.Span[..written].Trim(PADDING_BYTES); - //Convert to rfc4648 urlsafe version - VnEncoding.Base64ToUrlSafeInPlace(base64Data); + //Write the endoded buffer to the stream - DataStream.Write(base64Data); + DataStream.Write(binBuffer.Span[..(int)written]); } #endregion diff --git a/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs b/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs index 38c40bf..34211e5 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs @@ -23,7 +23,6 @@ */ using System; -using System.Buffers; using System.Text.Json; using System.Buffers.Text; using System.Security.Cryptography; @@ -348,21 +347,17 @@ namespace VNLib.Hashing.IdentityUtility { throw new InternalBufferTooSmallException("Failed to compute the hash of the JWT data"); } - - //Do an in-place base64 conversion of the signature to base64 - if (Base64.EncodeToUtf8InPlace(signatureBuffer, bytesWritten, out int base64BytesWritten) != OperationStatus.Done) + + //Do an in-place base64 conversion of the signature to base64url + ERRNO encoded = VnEncoding.Base64UrlEncodeInPlace(signatureBuffer, bytesWritten, false); + + if (!encoded) { throw new InternalBufferTooSmallException("Failed to convert the signature buffer to its base64 because the buffer was too small"); - } - - //Trim padding - Span base64 = signatureBuffer[..base64BytesWritten].Trim(JsonWebToken.PADDING_BYTES); - - //Urlencode - VnEncoding.Base64ToUrlSafeInPlace(base64); + } //Verify the signatures and return results - return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, base64); + return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, signatureBuffer[..(int)encoded]); } /// @@ -444,20 +439,16 @@ namespace VNLib.Hashing.IdentityUtility throw new InternalBufferTooSmallException("Failed to compute the hash of the JWT data"); } - //Do an in-place base64 conversion of the signature to base64 - if (Base64.EncodeToUtf8InPlace(signatureBuffer, count, out int base64BytesWritten) != OperationStatus.Done) + //Do an in-place base64 conversion of the signature to base64url + ERRNO encoded = VnEncoding.Base64UrlEncodeInPlace(signatureBuffer, (int)alg, false); + + if (!encoded) { throw new InternalBufferTooSmallException("Failed to convert the signature buffer to its base64 because the buffer was too small"); - } - - //Trim padding - Span base64 = signatureBuffer[..base64BytesWritten].Trim(JsonWebToken.PADDING_BYTES); - - //Urlencode - VnEncoding.Base64ToUrlSafeInPlace(base64); - + } + //Verify the signatures and return results - return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, base64); + return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, signatureBuffer[..(int)encoded]); } /// diff --git a/lib/Plugins.Essentials/src/HttpEntity.cs b/lib/Plugins.Essentials/src/HttpEntity.cs index f2f9387..ad89d14 100644 --- a/lib/Plugins.Essentials/src/HttpEntity.cs +++ b/lib/Plugins.Essentials/src/HttpEntity.cs @@ -58,7 +58,7 @@ namespace VNLib.Plugins.Essentials /// private readonly IHttpEvent Entity; - public HttpEntity(IHttpEvent entity, EventProcessor root, in SessionHandle session, in CancellationToken cancellation) + public HttpEntity(IHttpEvent entity, IWebProcessor root, in SessionHandle session, CancellationToken cancellation) { Entity = entity; RequestedRoot = root; diff --git a/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs b/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs index 8dbb077..68f5764 100644 --- a/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs +++ b/lib/Plugins.Essentials/src/Sessions/SessionHandle.cs @@ -22,9 +22,7 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -using System; using System.Threading.Tasks; -using System.Diagnostics.CodeAnalysis; using VNLib.Net.Http; @@ -44,7 +42,7 @@ namespace VNLib.Plugins.Essentials.Sessions /// A handle that holds exclusive access to a /// session object /// - public readonly struct SessionHandle : IEquatable + public readonly record struct SessionHandle { /// /// An empty instance. (A handle without a session object) @@ -93,40 +91,5 @@ namespace VNLib.Plugins.Essentials.Sessions /// /// The current connection event object public readonly ValueTask ReleaseAsync(IHttpEvent @event) => ReleaseCb?.Invoke(SessionData!, @event) ?? ValueTask.CompletedTask; - - /// - /// Determines if another is equal to the current handle. - /// Handles are equal if neither handle is set or if their SessionData object is equal. - /// - /// The other handle to - /// true if neither handle is set or if their SessionData object is equal, false otherwise - public readonly bool Equals(SessionHandle other) - { - //If neither handle is set, then they are equal, otherwise they are equal if the session objects themselves are equal - return (!IsSet && !other.IsSet) || (SessionData?.Equals(other.SessionData) ?? false); - } - /// - public readonly override bool Equals([NotNullWhen(true)] object? obj) => (obj is SessionHandle other) && Equals(other); - /// - public readonly override int GetHashCode() - { - return IsSet ? SessionData!.GetHashCode() : base.GetHashCode(); - } - - /// - /// Checks if two instances are equal - /// - /// - /// - /// - public static bool operator ==(SessionHandle left, SessionHandle right) => left.Equals(right); - - /// - /// Checks if two instances are not equal - /// - /// - /// - /// - public static bool operator !=(SessionHandle left, SessionHandle right) => !(left == right); } } diff --git a/lib/Plugins.Runtime/src/AsmFileWatcher.cs b/lib/Plugins.Runtime/src/AsmFileWatcher.cs index 0aee21b..f2a0ca7 100644 --- a/lib/Plugins.Runtime/src/AsmFileWatcher.cs +++ b/lib/Plugins.Runtime/src/AsmFileWatcher.cs @@ -52,12 +52,13 @@ namespace VNLib.Plugins.Runtime { Filter = "*.dll", EnableRaisingEvents = false, - IncludeSubdirectories = false, - NotifyFilter = NotifyFilters.LastWrite + IncludeSubdirectories = true, + NotifyFilter = NotifyFilters.LastWrite, }; //Configure listener _watcher.Changed += OnFileChanged; + _watcher.Created += OnFileChanged; _watcher.EnableRaisingEvents = true; @@ -73,16 +74,20 @@ namespace VNLib.Plugins.Runtime return; } + //Set pause flag + _pause = true; + //Restart the timer to trigger reload event on elapsed _delayTimer.Restart(_loaderSource.Config.ReloadDelay); } private void OnTimeout(object? state) { - //Fire event - Handler.OnPluginUnloaded(_loaderSource); _delayTimer.Stop(); + //Fire event, let exception crash app + Handler.OnPluginUnloaded(_loaderSource); + //Clear pause flag _pause = false; } diff --git a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs index e581a86..e7b9404 100644 --- a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs +++ b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs @@ -40,8 +40,7 @@ namespace VNLib.Plugins.Runtime public sealed class RuntimePluginLoader : VnDisposeable, IPluginReloadEventHandler { private static readonly IPluginAssemblyWatcher Watcher = new AssemblyWatcher(); - - //private readonly IPluginAssemblyWatcher Watcher; + private readonly IPluginAssemblyLoader Loader; private readonly JsonDocument HostConfig; private readonly ILogProvider? Log; diff --git a/lib/Utils/src/VnEncoding.cs b/lib/Utils/src/VnEncoding.cs index 4a95405..35d52a7 100644 --- a/lib/Utils/src/VnEncoding.cs +++ b/lib/Utils/src/VnEncoding.cs @@ -452,7 +452,7 @@ namespace VNLib.Utils //Calculate the base32 entropy to alloc an appropriate buffer (minium buffer of 2 chars) int entropy = Base32CalcMaxBufferSize(binBuffer.Length); //Alloc buffer for enough size (2*long bytes) is not an issue - using (UnsafeMemoryHandle charBuffer = Memory.MemoryUtil.UnsafeAlloc(entropy)) + using (UnsafeMemoryHandle charBuffer = MemoryUtil.UnsafeAlloc(entropy)) { //Encode ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span); @@ -890,6 +890,95 @@ namespace VNLib.Utils return Base64UrlDecode(decodeHandle.Span[..count], output); } + + /// + /// Base64url encodes the binary buffer to its utf8 binary representation + /// + /// The intput binary buffer to base64url encode + /// The data within the buffer to encode, must be smaller than the entire buffer + /// A value that indicates if base64 padding should be url encoded(true), or removed(false). + /// The number characters written to the buffer, or if a error occured. + public static ERRNO Base64UrlEncodeInPlace(Span buffer, int dataLength, bool includePadding) + { + //Convert to base64 + if (Base64.EncodeToUtf8InPlace(buffer, dataLength, out int bytesWritten) != OperationStatus.Done) + { + return ERRNO.E_FAIL; + } + + if (includePadding) + { + //Url encode in place + Base64ToUrlSafeInPlace(buffer[..bytesWritten]); + return bytesWritten; + } + else + { + //Remove padding bytes + Span nonPadded = buffer[..bytesWritten].TrimEnd((byte)0x3d); + + Base64ToUrlSafeInPlace(nonPadded); + return nonPadded.Length; + } + + } + + /// + /// Encodes the binary input buffer to its base64url safe utf8 encoding, and writes the output + /// to the supplied buffer. Be sure to call + /// to allocate the correct size buffer for encoding + /// + /// The intput binary buffer to base64url encode + /// The output buffer to write the base64url safe encodded date to + /// A value that indicates if base64 padding should be url encoded(true), or removed(false). + /// The number characters written to the buffer, or if a error occured. + public static ERRNO Base64UrlEncode(ReadOnlySpan input, Span output, bool includePadding) + { + //Write the input buffer to the output buffer + input.CopyTo(output); + + //encode in place + return Base64UrlEncodeInPlace(output, input.Length, includePadding); + } + + /// + /// Encodes the binary intput buffer to its base64url safe encoding, then converts the internal buffer + /// to its character encoding using the supplied , and writes the characters + /// to the output buffer. Defaults to UTF8 character encoding. Base64url is a subset of ASCII,UTF7,UTF8,UTF16 etc + /// so most encodings should be safe. + /// + /// The input binary intput buffer + /// The character output buffer + /// A value that indicates if base64 padding should be url encoded(true), or removed(false). + /// The encoding used to convert the binary buffer to its character representation. + /// The number of characters written to the buffer, or if a error occured + public static ERRNO Base64UrlEncode(ReadOnlySpan input, Span output, bool includePadding, Encoding? encoding = null) + { + encoding ??= Encoding.UTF8; + + //We need to alloc an intermediate buffer, get the base64 max size + int maxSize = Base64.GetMaxEncodedToUtf8Length(input.Length); + + //Alloc buffer + using UnsafeMemoryHandle buffer = MemoryUtil.UnsafeAlloc(maxSize); + + //Encode to url safe binary + ERRNO count = Base64UrlEncode(input, buffer.Span, includePadding); + + if (count <= 0) + { + return count; + } + + //Get char count to return to caller + int charCount = encoding.GetCharCount(buffer.Span[..(int)count]); + + //Encode to characters + encoding.GetChars(buffer.AsSpan(0, count), output); + + return charCount; + } + #endregion } } \ No newline at end of file -- cgit