diff options
author | vnugent <public@vaughnnugent.com> | 2023-04-19 01:42:00 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-04-19 01:42:00 -0400 |
commit | d65aa4f010adbd6c8162eb08c5787550e27d3e47 (patch) | |
tree | 3849d77ef8694d7c24c0922560e7a9edb064d9bb | |
parent | e67084af248f1302aa65f31e824d075f992b304c (diff) |
Fix watcher pause, add base64urlencode
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs | 17 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs | 37 | ||||
-rw-r--r-- | lib/Plugins.Essentials/src/HttpEntity.cs | 2 | ||||
-rw-r--r-- | lib/Plugins.Essentials/src/Sessions/SessionHandle.cs | 39 | ||||
-rw-r--r-- | lib/Plugins.Runtime/src/AsmFileWatcher.cs | 13 | ||||
-rw-r--r-- | lib/Plugins.Runtime/src/RuntimePluginLoader.cs | 3 | ||||
-rw-r--r-- | lib/Utils/src/VnEncoding.cs | 91 |
7 files changed, 125 insertions, 77 deletions
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<byte> binBuffer = Heap.UnsafeAlloc<byte>(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<byte> 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<byte> 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]); } /// <summary> @@ -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<byte> 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]); } /// <summary> 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 /// </summary> 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 <see cref="ISession"/> /// session object /// </summary> - public readonly struct SessionHandle : IEquatable<SessionHandle> + public readonly record struct SessionHandle { /// <summary> /// An empty <see cref="SessionHandle"/> instance. (A handle without a session object) @@ -93,40 +91,5 @@ namespace VNLib.Plugins.Essentials.Sessions /// </summary> /// <param name="event">The current connection event object</param> public readonly ValueTask ReleaseAsync(IHttpEvent @event) => ReleaseCb?.Invoke(SessionData!, @event) ?? ValueTask.CompletedTask; - - /// <summary> - /// Determines if another <see cref="SessionHandle"/> is equal to the current handle. - /// Handles are equal if neither handle is set or if their SessionData object is equal. - /// </summary> - /// <param name="other">The other handle to</param> - /// <returns>true if neither handle is set or if their SessionData object is equal, false otherwise</returns> - 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); - } - ///<inheritdoc/> - public readonly override bool Equals([NotNullWhen(true)] object? obj) => (obj is SessionHandle other) && Equals(other); - ///<inheritdoc/> - public readonly override int GetHashCode() - { - return IsSet ? SessionData!.GetHashCode() : base.GetHashCode(); - } - - /// <summary> - /// Checks if two <see cref="SessionHandle"/> instances are equal - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns></returns> - public static bool operator ==(SessionHandle left, SessionHandle right) => left.Equals(right); - - /// <summary> - /// Checks if two <see cref="SessionHandle"/> instances are not equal - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns></returns> - 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<char> charBuffer = Memory.MemoryUtil.UnsafeAlloc<char>(entropy)) + using (UnsafeMemoryHandle<char> charBuffer = MemoryUtil.UnsafeAlloc<char>(entropy)) { //Encode ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span); @@ -890,6 +890,95 @@ namespace VNLib.Utils return Base64UrlDecode(decodeHandle.Span[..count], output); } + + /// <summary> + /// Base64url encodes the binary buffer to its utf8 binary representation + /// </summary> + /// <param name="buffer">The intput binary buffer to base64url encode</param> + /// <param name="dataLength">The data within the buffer to encode, must be smaller than the entire buffer</param> + /// <param name="includePadding">A value that indicates if base64 padding should be url encoded(true), or removed(false).</param> + /// <returns>The number characters written to the buffer, or <see cref="ERRNO.E_FAIL"/> if a error occured.</returns> + public static ERRNO Base64UrlEncodeInPlace(Span<byte> 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<byte> nonPadded = buffer[..bytesWritten].TrimEnd((byte)0x3d); + + Base64ToUrlSafeInPlace(nonPadded); + return nonPadded.Length; + } + + } + + /// <summary> + /// Encodes the binary input buffer to its base64url safe utf8 encoding, and writes the output + /// to the supplied buffer. Be sure to call <see cref="Base64.GetMaxEncodedToUtf8Length(int)"/> + /// to allocate the correct size buffer for encoding + /// </summary> + /// <param name="input">The intput binary buffer to base64url encode</param> + /// <param name="output">The output buffer to write the base64url safe encodded date to</param> + /// <param name="includePadding">A value that indicates if base64 padding should be url encoded(true), or removed(false).</param> + /// <returns>The number characters written to the buffer, or <see cref="ERRNO.E_FAIL"/> if a error occured.</returns> + public static ERRNO Base64UrlEncode(ReadOnlySpan<byte> input, Span<byte> output, bool includePadding) + { + //Write the input buffer to the output buffer + input.CopyTo(output); + + //encode in place + return Base64UrlEncodeInPlace(output, input.Length, includePadding); + } + + /// <summary> + /// Encodes the binary intput buffer to its base64url safe encoding, then converts the internal buffer + /// to its character encoding using the supplied <paramref name="encoding"/>, 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. + /// </summary> + /// <param name="input">The input binary intput buffer</param> + /// <param name="output">The character output buffer</param> + /// <param name="includePadding">A value that indicates if base64 padding should be url encoded(true), or removed(false).</param> + /// <param name="encoding">The encoding used to convert the binary buffer to its character representation.</param> + /// <returns>The number of characters written to the buffer, or <see cref="ERRNO.E_FAIL"/> if a error occured</returns> + public static ERRNO Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> 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<byte> 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 |