aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-21 16:02:34 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-21 16:02:34 -0400
commit92e182ceaf843f8d859d38faa8b2c0ff53207ff6 (patch)
tree17711a2c87fc10c42d4f9d30cd6ee293fae24d0e
parentee3620b8168a42c8e571e853c751ad5999a9b907 (diff)
feat: Multi transport listeners
-rw-r--r--lib/Net.Http/src/Core/HttpServerBase.cs48
-rw-r--r--lib/Plugins.Essentials/src/Sessions/SessionInfo.cs36
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs8
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs3
-rw-r--r--lib/Utils/src/Memory/MemoryUtilAlloc.cs10
-rw-r--r--lib/Utils/src/Memory/UnsafeMemoryHandle.cs45
6 files changed, 76 insertions, 74 deletions
diff --git a/lib/Net.Http/src/Core/HttpServerBase.cs b/lib/Net.Http/src/Core/HttpServerBase.cs
index ec6e73d..5954057 100644
--- a/lib/Net.Http/src/Core/HttpServerBase.cs
+++ b/lib/Net.Http/src/Core/HttpServerBase.cs
@@ -36,6 +36,7 @@
*/
using System;
+using System.Linq;
using System.Threading;
using System.Net.Sockets;
using System.Threading.Tasks;
@@ -74,7 +75,7 @@ namespace VNLib.Net.Http
/// </summary>
internal static readonly Memory<byte> WriteOnlyScratchBuffer = new byte[64 * 1024];
- private readonly ITransportProvider Transport;
+ private readonly ITransportProvider[] Transports;
private readonly FrozenDictionary<string, IWebRoot> ServerRoots;
private readonly IWebRoot? _wildcardRoot;
private readonly HttpConfig _config;
@@ -114,10 +115,10 @@ namespace VNLib.Net.Http
/// Immutable data structures are initialzed.
/// </summary>
/// <param name="config">The configuration used to create the instance</param>
- /// <param name="transport">The transport provider to listen to connections from</param>
+ /// <param name="transports">An enumeration of transports to listen for connections on</param>
/// <param name="sites">A collection of <see cref="IWebRoot"/>s that route incomming connetctions</param>
/// <exception cref="ArgumentException"></exception>
- public HttpServer(HttpConfig config, ITransportProvider transport, IEnumerable<IWebRoot> sites)
+ public HttpServer(HttpConfig config, IEnumerable<ITransportProvider> transports, IEnumerable<IWebRoot> sites)
{
//Validate the configuration
ValidateConfig(in config);
@@ -127,12 +128,13 @@ namespace VNLib.Net.Http
ServerRoots = sites.ToFrozenDictionary(static r => r.Hostname, static tv => tv, StringComparer.OrdinalIgnoreCase);
//Compile and store the timeout keepalive header
KeepAliveTimeoutHeaderValue = $"timeout={(int)_config.ConnectionKeepAlive.TotalSeconds}";
- //Setup config copy with the internal http pool
- Transport = transport;
+
+ Transports = transports.ToArray();
+
//Cache supported compression methods, or none if compressor is null
- SupportedCompressionMethods = config.CompressorManager == null ?
- CompressionMethod.None :
- config.CompressorManager.GetSupportedMethods();
+ SupportedCompressionMethods = config.CompressorManager == null
+ ? CompressionMethod.None
+ : config.CompressorManager.GetSupportedMethods();
//Create a new context store
ContextStore = ObjectRental.CreateReusable(() => new HttpContext(this, SupportedCompressionMethods));
@@ -242,16 +244,34 @@ namespace VNLib.Net.Http
public Task Start(CancellationToken cancellationToken)
{
StopToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
- //Start servers with the new token source
- Transport.Start(StopToken.Token);
- //Start the listen task
- return Task.Run(ListenWorkerDoWork, cancellationToken);
+
+ //Start servers with the new token source before listening for connections
+ Array.ForEach(Transports, p => p.Start(StopToken.Token));
+
+ //Listen to connections on all transports async
+ IEnumerable<Task> runTasks = Transports.Select(ListenAsync);
+
+ //Set running flag and will be reset when all listening tasks are done
+ Running = true;
+
+ return Task.WhenAll(runTasks)
+ .ContinueWith(
+ OnAllStopped,
+ CancellationToken.None,
+ TaskContinuationOptions.RunContinuationsAsynchronously,
+ TaskScheduler.Default
+ );
+
+ //Defer listening tasks to the task scheduler to avoid blocking this thread
+ Task ListenAsync(ITransportProvider tp) => Task.Run(() => ListenWorkerDoWork(tp), cancellationToken);
+
+ void OnAllStopped(Task _) => Running = false;
}
/*
* A worker task that listens for connections from the transport
*/
- private async Task ListenWorkerDoWork()
+ private async Task ListenWorkerDoWork(ITransportProvider transport)
{
//Set running flag
Running = true;
@@ -264,7 +284,7 @@ namespace VNLib.Net.Http
try
{
//Listen for new connection
- ITransportContext ctx = await Transport.AcceptAsync(StopToken!.Token);
+ ITransportContext ctx = await transport.AcceptAsync(StopToken!.Token);
//Try to dispatch the received event
_ = DataReceivedAsync(ctx).ConfigureAwait(false);
diff --git a/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs b/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs
index 2edb30c..eccfdfc 100644
--- a/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs
+++ b/lib/Plugins.Essentials/src/Sessions/SessionInfo.cs
@@ -65,9 +65,7 @@ namespace VNLib.Plugins.Essentials.Sessions
{
None = 0x00,
IsSet = 0x01,
- IpMatch = 0x02,
- IsCrossOrigin = 0x04,
- CrossOriginMatch = 0x08,
+ IpMatch = 0x02
}
private readonly ISession UserSession;
@@ -117,24 +115,6 @@ namespace VNLib.Plugins.Essentials.Sessions
}
/// <summary>
- /// If the current connection and stored session have matching cross origin domains
- /// </summary>
- public readonly bool CrossOriginMatch
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _flags.HasFlag(SessionFlags.CrossOriginMatch);
- }
-
- /// <summary>
- /// Was the original session cross origin?
- /// </summary>
- public readonly bool CrossOrigin
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _flags.HasFlag(SessionFlags.IsCrossOrigin);
- }
-
- /// <summary>
/// Was this session just created on this connection?
/// </summary>
public readonly bool IsNew
@@ -252,10 +232,10 @@ namespace VNLib.Plugins.Essentials.Sessions
{
UserSession = session;
- SessionFlags flags = SessionFlags.IsSet;
+ _flags |= SessionFlags.IsSet;
//Set ip match flag if current ip and stored ip match
- flags |= trueIp.Equals(session.UserIP) ? SessionFlags.IpMatch : SessionFlags.None;
+ _flags |= trueIp.Equals(session.UserIP) ? SessionFlags.IpMatch : SessionFlags.None;
//If the session is new, we can store intial security variables
if (session.IsNew)
@@ -266,8 +246,6 @@ namespace VNLib.Plugins.Essentials.Sessions
UserAgent = ci.UserAgent;
SpecifiedOrigin = ci.Origin;
SecurityProcol = ci.GetSslProtocol();
-
- flags |= ci.CrossOrigin ? SessionFlags.IsCrossOrigin : SessionFlags.None;
}
else
{
@@ -275,15 +253,7 @@ namespace VNLib.Plugins.Essentials.Sessions
UserAgent = session.GetUserAgent();
SpecifiedOrigin = session.GetOriginUri();
SecurityProcol = session.GetSecurityProtocol();
-
- flags |= session.IsCrossOrigin() ? SessionFlags.IsCrossOrigin : SessionFlags.None;
}
-
- //Set cross origin orign match flags, if the stored origin, and connection origin
- flags |= ci.Origin != null && ci.Origin.Equals(SpecifiedOrigin) ? SessionFlags.CrossOriginMatch : SessionFlags.None;
-
- //store flags
- _flags = flags;
}
///<inheritdoc/>
diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs
index 16fc555..fbaae95 100644
--- a/lib/Utils/src/Memory/MemoryHandle.cs
+++ b/lib/Utils/src/Memory/MemoryHandle.cs
@@ -229,8 +229,7 @@ namespace VNLib.Utils.Memory
//If adding ref failed, the handle is closed
ObjectDisposedException.ThrowIf(!addRef, this);
-
- //Create a new system.buffers memory handle from the offset ptr address
+
return new MemoryHandle(ptr, pinnable: this);
}
@@ -250,7 +249,10 @@ namespace VNLib.Utils.Memory
/// <exception cref="ObjectDisposedException"></exception>
public bool Equals(MemoryHandle<T>? other)
{
- return other != null && (IsClosed | other.IsClosed) == false && _length == other._length && handle == other.handle;
+ return other != null
+ && (IsClosed | other.IsClosed) == false
+ && _length == other._length
+ && handle == other.handle;
}
///<inheritdoc/>
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index d10efc8..34489a4 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -143,7 +143,8 @@ namespace VNLib.Utils.Memory
* get the heap's stats, otherwise return an empty handle
*/
return _lazyHeap.IsLoaded && _lazyHeap.Instance is TrackedHeapWrapper h
- ? h.GetCurrentStats() : default;
+ ? h.GetCurrentStats()
+ : default;
}
/// <summary>
diff --git a/lib/Utils/src/Memory/MemoryUtilAlloc.cs b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
index 6e4f9b0..742346c 100644
--- a/lib/Utils/src/Memory/MemoryUtilAlloc.cs
+++ b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
@@ -34,7 +34,7 @@ namespace VNLib.Utils.Memory
#region alloc
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool UseUnmanagedHeap<T>(IUnmangedHeap heap, nuint elements)
+ private static bool CanUseUnmanagedHeap<T>(IUnmangedHeap heap, nuint elements)
{
/*
* We may allocate from the share heap only if the heap is not using locks
@@ -123,7 +123,7 @@ namespace VNLib.Utils.Memory
return default;
}
- return UseUnmanagedHeap<T>(Shared, (uint)elements)
+ return CanUseUnmanagedHeap<T>(Shared, (uint)elements)
? UnsafeAlloc<T>(Shared, elements, zero)
: UnsafeAlloc(ArrayPool<T>.Shared, elements, zero);
}
@@ -229,7 +229,7 @@ namespace VNLib.Utils.Memory
{
ArgumentOutOfRangeException.ThrowIfNegative(elements);
- if (UseUnmanagedHeap<T>(Shared, elements))
+ if (CanUseUnmanagedHeap<T>(Shared, elements))
{
return SafeAlloc<T>(Shared, elements, zero);
}
@@ -425,7 +425,7 @@ namespace VNLib.Utils.Memory
return default;
}
- return UseUnmanagedHeap<byte>(Shared, (uint)elements)
+ return CanUseUnmanagedHeap<byte>(Shared, (uint)elements)
? UnsafeAlloc<byte>(Shared, elements, zero)
: UnsafeAlloc(ArrayPool<byte>.Shared, elements, zero);
}
@@ -462,7 +462,7 @@ namespace VNLib.Utils.Memory
{
ArgumentOutOfRangeException.ThrowIfNegative(elements);
- return UseUnmanagedHeap<byte>(Shared, (uint)elements)
+ return CanUseUnmanagedHeap<byte>(Shared, (uint)elements)
? SafeAlloc<byte>(Shared, (nuint)elements, zero)
: SafeAlloc(ArrayPool<byte>.Shared, elements, zero);
}
diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
index d93739d..bda8e2e 100644
--- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
+++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
@@ -29,8 +29,6 @@ using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
-using VNLib.Utils.Extensions;
-
namespace VNLib.Utils.Memory
{
@@ -62,16 +60,7 @@ namespace VNLib.Utils.Memory
public readonly Span<T> Span
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return _handleType switch
- {
- HandleType.None => Span<T>.Empty,
- HandleType.Pool => _poolArr!.AsSpan(0, _length),
- HandleType.PrivateHeap => MemoryUtil.GetSpan<T>(_memoryPtr, _length),
- _ => throw new InvalidOperationException("Invalid handle type"),
- };
- }
+ get => AsSpan();
}
/// <summary>
@@ -153,7 +142,7 @@ namespace VNLib.Utils.Memory
IntPtr unalloc = _memoryPtr;
//Free the unmanaged handle
bool unsafeFreed = _heap!.Free(ref unalloc);
- Debug.Assert(unsafeFreed, "A previously allocated unsafe memhandle failed to free");
+ Debug.Assert(unsafeFreed, "A previously allocated unsafe memhandle failed to free block");
}
break;
}
@@ -193,6 +182,8 @@ namespace VNLib.Utils.Memory
{
switch (_handleType)
{
+ case HandleType.None:
+ return ref Unsafe.NullRef<T>();
case HandleType.Pool:
return ref MemoryMarshal.GetArrayDataReference(_poolArr!);
case HandleType.PrivateHeap:
@@ -207,7 +198,7 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <returns>The memory block that is held by the internl handle</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Span<T> AsSpan() => Span;
+ public readonly Span<T> AsSpan() => AsSpan(0, _length);
/// <summary>
/// Returns a <see cref="Span{T}"/> that represents the memory block pointed to by this handle
@@ -216,7 +207,7 @@ namespace VNLib.Utils.Memory
/// <returns>The desired memory block at the desired element offset</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Span<T> AsSpan(int start) => Span[start..];
+ public readonly Span<T> AsSpan(int start) => AsSpan(start, _length - start);
/// <summary>
/// Returns a <see cref="Span{T}"/> that represents the memory block pointed to by this handle
@@ -226,7 +217,23 @@ namespace VNLib.Utils.Memory
/// <returns>The desired memory block at the desired element offset and length</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Span<T> AsSpan(int start, int length) => Span.Slice(start, length);
+ public readonly Span<T> AsSpan(int start, int length)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(start);
+ ArgumentOutOfRangeException.ThrowIfNegative(length);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(length - start, _length);
+
+ /*
+ * If the handle is empty, a null ref should be returned. The
+ * check above will gaurd against calling this function on non-empty
+ * handles. So adding 0 to 0 on the reference should not cause any issues.
+ */
+
+ return MemoryMarshal.CreateSpan(
+ ref Unsafe.Add(ref GetReference(), start),
+ length
+ );
+ }
///<inheritdoc/>
public readonly override int GetHashCode()
@@ -248,7 +255,9 @@ namespace VNLib.Utils.Memory
/// <returns>True if the other handle points to the same block of memory as the current handle</returns>
public readonly bool Equals(in UnsafeMemoryHandle<T> other)
{
- return _handleType == other._handleType && Length == other.Length && GetHashCode() == other.GetHashCode();
+ return _handleType == other._handleType
+ && Length == other.Length
+ && GetHashCode() == other.GetHashCode();
}
/// <summary>
@@ -288,4 +297,4 @@ namespace VNLib.Utils.Memory
public static bool operator !=(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => !left.Equals(right);
}
-} \ No newline at end of file
+}