Implement InMemory Transport Layer for StellaOps Router
- Added InMemoryTransportOptions class for configuration settings including timeouts and latency. - Developed InMemoryTransportServer class to handle connections, frame processing, and event management. - Created ServiceCollectionExtensions for easy registration of InMemory transport services. - Established project structure and dependencies for InMemory transport library. - Implemented comprehensive unit tests for endpoint discovery, connection management, request/response flow, and streaming capabilities. - Ensured proper handling of cancellation, heartbeat, and hello frames within the transport layer.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Payload for the Cancel frame.
|
||||
/// </summary>
|
||||
public sealed record CancelPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the reason for cancellation.
|
||||
/// </summary>
|
||||
public string? Reason { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a claim requirement for endpoint authorization.
|
||||
/// </summary>
|
||||
public sealed record ClaimRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the claim type that must be present.
|
||||
/// </summary>
|
||||
public required string Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional claim value that must match.
|
||||
/// </summary>
|
||||
public string? Value { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using StellaOps.Router.Common.Enums;
|
||||
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the state of a connection between a microservice and the router.
|
||||
/// </summary>
|
||||
public sealed class ConnectionState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this connection.
|
||||
/// </summary>
|
||||
public required string ConnectionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance descriptor for the connected microservice.
|
||||
/// </summary>
|
||||
public required InstanceDescriptor Instance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the health status of this connection.
|
||||
/// </summary>
|
||||
public InstanceHealthStatus Status { get; set; } = InstanceHealthStatus.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UTC timestamp of the last heartbeat.
|
||||
/// </summary>
|
||||
public DateTime LastHeartbeatUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the average ping time in milliseconds.
|
||||
/// </summary>
|
||||
public double AveragePingMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoints served by this connection.
|
||||
/// </summary>
|
||||
public Dictionary<(string Method, string Path), EndpointDescriptor> Endpoints { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transport type used for this connection.
|
||||
/// </summary>
|
||||
public required TransportType TransportType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Describes an endpoint's identity and metadata.
|
||||
/// </summary>
|
||||
public sealed record EndpointDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the service that owns this endpoint.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the semantic version of the service.
|
||||
/// </summary>
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP method (GET, POST, PUT, PATCH, DELETE).
|
||||
/// </summary>
|
||||
public required string Method { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path template (e.g., "/billing/invoices/{id}").
|
||||
/// </summary>
|
||||
public required string Path { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default timeout for this endpoint.
|
||||
/// </summary>
|
||||
public TimeSpan DefaultTimeout { get; init; } = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the claim requirements for authorization.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ClaimRequirement> RequiringClaims { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this endpoint supports streaming.
|
||||
/// </summary>
|
||||
public bool SupportsStreaming { get; init; }
|
||||
}
|
||||
24
src/__Libraries/StellaOps.Router.Common/Models/Frame.cs
Normal file
24
src/__Libraries/StellaOps.Router.Common/Models/Frame.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using StellaOps.Router.Common.Enums;
|
||||
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a protocol frame in the router transport layer.
|
||||
/// </summary>
|
||||
public sealed record Frame
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of this frame.
|
||||
/// </summary>
|
||||
public required FrameType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the correlation ID for request/response matching.
|
||||
/// </summary>
|
||||
public string? CorrelationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw payload bytes.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<byte> Payload { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using StellaOps.Router.Common.Enums;
|
||||
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Payload for the Heartbeat frame sent periodically by microservices.
|
||||
/// </summary>
|
||||
public sealed record HeartbeatPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance ID.
|
||||
/// </summary>
|
||||
public required string InstanceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the health status.
|
||||
/// </summary>
|
||||
public required InstanceHealthStatus Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current in-flight request count.
|
||||
/// </summary>
|
||||
public int InFlightRequestCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error rate (0.0 to 1.0).
|
||||
/// </summary>
|
||||
public double ErrorRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp when this heartbeat was created.
|
||||
/// </summary>
|
||||
public DateTime TimestampUtc { get; init; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Payload for the Hello frame sent by microservices on connection.
|
||||
/// </summary>
|
||||
public sealed record HelloPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance descriptor.
|
||||
/// </summary>
|
||||
public required InstanceDescriptor Instance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoints registered by this instance.
|
||||
/// </summary>
|
||||
public required IReadOnlyList<EndpointDescriptor> Endpoints { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a microservice instance's identity.
|
||||
/// </summary>
|
||||
public sealed record InstanceDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this instance.
|
||||
/// </summary>
|
||||
public required string InstanceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the service.
|
||||
/// </summary>
|
||||
public required string ServiceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the semantic version of the service.
|
||||
/// </summary>
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the region where this instance is deployed.
|
||||
/// </summary>
|
||||
public required string Region { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for payload and memory limits.
|
||||
/// </summary>
|
||||
public sealed record PayloadLimits
|
||||
{
|
||||
/// <summary>
|
||||
/// Default payload limits.
|
||||
/// </summary>
|
||||
public static readonly PayloadLimits Default = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum request bytes per call.
|
||||
/// Default: 10 MB.
|
||||
/// </summary>
|
||||
public long MaxRequestBytesPerCall { get; init; } = 10 * 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum request bytes per connection.
|
||||
/// Default: 100 MB.
|
||||
/// </summary>
|
||||
public long MaxRequestBytesPerConnection { get; init; } = 100 * 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum aggregate in-flight bytes across all requests.
|
||||
/// Default: 1 GB.
|
||||
/// </summary>
|
||||
public long MaxAggregateInflightBytes { get; init; } = 1024 * 1024 * 1024;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Neutral routing context that does not depend on ASP.NET.
|
||||
/// Gateway will adapt from HttpContext to this neutral model.
|
||||
/// </summary>
|
||||
public sealed record RoutingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the HTTP method (GET, POST, PUT, PATCH, DELETE).
|
||||
/// </summary>
|
||||
public required string Method { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request path.
|
||||
/// </summary>
|
||||
public required string Path { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request headers.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, string> Headers { get; init; } = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resolved endpoint descriptor.
|
||||
/// </summary>
|
||||
public EndpointDescriptor? Endpoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available connections for routing.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ConnectionState> AvailableConnections { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gateway's region for routing decisions.
|
||||
/// </summary>
|
||||
public required string GatewayRegion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requested version, if specified.
|
||||
/// </summary>
|
||||
public string? RequestedVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cancellation token for the request.
|
||||
/// </summary>
|
||||
public CancellationToken CancellationToken { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using StellaOps.Router.Common.Enums;
|
||||
|
||||
namespace StellaOps.Router.Common.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the outcome of a routing decision.
|
||||
/// </summary>
|
||||
public sealed record RoutingDecision
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the selected endpoint.
|
||||
/// </summary>
|
||||
public required EndpointDescriptor Endpoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected connection.
|
||||
/// </summary>
|
||||
public required ConnectionState Connection { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transport type to use.
|
||||
/// </summary>
|
||||
public required TransportType TransportType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective timeout for the request.
|
||||
/// </summary>
|
||||
public required TimeSpan EffectiveTimeout { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user