product advisories, stella router improval, tests streghthening

This commit is contained in:
StellaOps Bot
2025-12-24 14:20:26 +02:00
parent 5540ce9430
commit 2c2bbf1005
171 changed files with 58943 additions and 135 deletions

View File

@@ -35,6 +35,61 @@ public sealed class GatewayTransportOptions
public GatewayTcpTransportOptions Tcp { get; set; } = new();
public GatewayTlsTransportOptions Tls { get; set; } = new();
public GatewayMessagingTransportOptions Messaging { get; set; } = new();
}
public sealed class GatewayMessagingTransportOptions
{
/// <summary>
/// Whether messaging (Valkey) transport is enabled.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Valkey connection string (e.g., "localhost:6379" or "valkey:6379,password=secret").
/// </summary>
public string ConnectionString { get; set; } = "localhost:6379";
/// <summary>
/// Valkey database number.
/// </summary>
public int? Database { get; set; }
/// <summary>
/// Queue name template for incoming requests. Use {service} placeholder.
/// </summary>
public string RequestQueueTemplate { get; set; } = "router:requests:{service}";
/// <summary>
/// Queue name for gateway responses.
/// </summary>
public string ResponseQueueName { get; set; } = "router:responses";
/// <summary>
/// Consumer group name for request processing.
/// </summary>
public string ConsumerGroup { get; set; } = "router-gateway";
/// <summary>
/// Timeout for RPC requests.
/// </summary>
public string RequestTimeout { get; set; } = "30s";
/// <summary>
/// Lease duration for message processing.
/// </summary>
public string LeaseDuration { get; set; } = "5m";
/// <summary>
/// Batch size for leasing messages.
/// </summary>
public int BatchSize { get; set; } = 10;
/// <summary>
/// Heartbeat interval.
/// </summary>
public string HeartbeatInterval { get; set; } = "10s";
}
public sealed class GatewayTcpTransportOptions

View File

@@ -21,6 +21,10 @@ using StellaOps.Router.Gateway.RateLimit;
using StellaOps.Router.Gateway.Routing;
using StellaOps.Router.Transport.Tcp;
using StellaOps.Router.Transport.Tls;
using StellaOps.Router.Transport.Messaging;
using StellaOps.Router.Transport.Messaging.Options;
using StellaOps.Messaging.DependencyInjection;
using StellaOps.Messaging.Transport.Valkey;
using StellaOps.Router.AspNet;
var builder = WebApplication.CreateBuilder(args);
@@ -53,6 +57,13 @@ builder.Services.AddSingleton<GatewayMetrics>();
builder.Services.AddTcpTransportServer();
builder.Services.AddTlsTransportServer();
// Messaging transport (Valkey)
if (bootstrapOptions.Transports.Messaging.Enabled)
{
builder.Services.AddMessagingTransport<ValkeyTransportPlugin>(builder.Configuration, "Gateway:Transports:Messaging");
builder.Services.AddMessagingTransportServer();
}
builder.Services.AddSingleton<GatewayTransportClient>();
builder.Services.AddSingleton<ITransportClient>(sp => sp.GetRequiredService<GatewayTransportClient>());
@@ -246,4 +257,25 @@ static void ConfigureGatewayOptionsMapping(WebApplicationBuilder builder, Gatewa
options.RequireClientCertificate = tls.RequireClientCertificate;
options.AllowSelfSigned = tls.AllowSelfSigned;
});
builder.Services.AddOptions<MessagingTransportOptions>()
.Configure<IOptions<GatewayOptions>>((options, gateway) =>
{
var messaging = gateway.Value.Transports.Messaging;
options.RequestQueueTemplate = messaging.RequestQueueTemplate;
options.ResponseQueueName = messaging.ResponseQueueName;
options.ConsumerGroup = messaging.ConsumerGroup;
options.RequestTimeout = GatewayValueParser.ParseDuration(messaging.RequestTimeout, TimeSpan.FromSeconds(30));
options.LeaseDuration = GatewayValueParser.ParseDuration(messaging.LeaseDuration, TimeSpan.FromMinutes(5));
options.BatchSize = messaging.BatchSize;
options.HeartbeatInterval = GatewayValueParser.ParseDuration(messaging.HeartbeatInterval, TimeSpan.FromSeconds(10));
});
builder.Services.AddOptions<ValkeyTransportOptions>()
.Configure<IOptions<GatewayOptions>>((options, gateway) =>
{
var messaging = gateway.Value.Transports.Messaging;
options.ConnectionString = messaging.ConnectionString;
options.Database = messaging.Database;
});
}

View File

@@ -9,6 +9,7 @@ using StellaOps.Router.Common.Models;
using StellaOps.Router.Gateway.OpenApi;
using StellaOps.Router.Transport.Tcp;
using StellaOps.Router.Transport.Tls;
using StellaOps.Router.Transport.Messaging;
namespace StellaOps.Gateway.WebService.Services;
@@ -16,6 +17,7 @@ public sealed class GatewayHostedService : IHostedService
{
private readonly TcpTransportServer _tcpServer;
private readonly TlsTransportServer _tlsServer;
private readonly MessagingTransportServer? _messagingServer;
private readonly IGlobalRoutingState _routingState;
private readonly GatewayTransportClient _transportClient;
private readonly IEffectiveClaimsStore _claimsStore;
@@ -26,6 +28,7 @@ public sealed class GatewayHostedService : IHostedService
private readonly JsonSerializerOptions _jsonOptions;
private bool _tcpEnabled;
private bool _tlsEnabled;
private bool _messagingEnabled;
public GatewayHostedService(
TcpTransportServer tcpServer,
@@ -36,10 +39,12 @@ public sealed class GatewayHostedService : IHostedService
IOptions<GatewayOptions> options,
GatewayServiceStatus status,
ILogger<GatewayHostedService> logger,
IRouterOpenApiDocumentCache? openApiCache = null)
IRouterOpenApiDocumentCache? openApiCache = null,
MessagingTransportServer? messagingServer = null)
{
_tcpServer = tcpServer;
_tlsServer = tlsServer;
_messagingServer = messagingServer;
_routingState = routingState;
_transportClient = transportClient;
_claimsStore = claimsStore;
@@ -59,8 +64,9 @@ public sealed class GatewayHostedService : IHostedService
var options = _options.Value;
_tcpEnabled = options.Transports.Tcp.Enabled;
_tlsEnabled = options.Transports.Tls.Enabled;
_messagingEnabled = options.Transports.Messaging.Enabled && _messagingServer is not null;
if (!_tcpEnabled && !_tlsEnabled)
if (!_tcpEnabled && !_tlsEnabled && !_messagingEnabled)
{
_logger.LogWarning("No transports enabled; gateway will not accept microservice connections.");
_status.MarkStarted();
@@ -84,6 +90,17 @@ public sealed class GatewayHostedService : IHostedService
_logger.LogInformation("TLS transport started on port {Port}", options.Transports.Tls.Port);
}
if (_messagingEnabled && _messagingServer is not null)
{
_messagingServer.OnHelloReceived += HandleMessagingHello;
_messagingServer.OnHeartbeatReceived += HandleMessagingHeartbeat;
_messagingServer.OnResponseReceived += HandleMessagingResponse;
_messagingServer.OnConnectionClosed += HandleMessagingDisconnection;
await _messagingServer.StartAsync(cancellationToken);
_logger.LogInformation("Messaging transport started (Valkey connection: {Connection})",
options.Transports.Messaging.ConnectionString);
}
_status.MarkStarted();
_status.MarkReady();
}
@@ -110,6 +127,15 @@ public sealed class GatewayHostedService : IHostedService
_tlsServer.OnFrame -= HandleTlsFrame;
_tlsServer.OnDisconnection -= HandleTlsDisconnection;
}
if (_messagingEnabled && _messagingServer is not null)
{
await _messagingServer.StopAsync(cancellationToken);
_messagingServer.OnHelloReceived -= HandleMessagingHello;
_messagingServer.OnHeartbeatReceived -= HandleMessagingHeartbeat;
_messagingServer.OnResponseReceived -= HandleMessagingResponse;
_messagingServer.OnConnectionClosed -= HandleMessagingDisconnection;
}
}
private void HandleTcpFrame(string connectionId, Frame frame)
@@ -438,8 +464,55 @@ public sealed class GatewayHostedService : IHostedService
{
_tlsServer.GetConnection(connectionId)?.Close();
}
// Messaging transport connections are managed by the queue system
// and do not support explicit close operations
}
#region Messaging Transport Event Handlers
private Task HandleMessagingHello(ConnectionState state, HelloPayload payload)
{
// The MessagingTransportServer already built the ConnectionState with TransportType.Messaging
// We need to add it to the routing state and update the claims store
_routingState.AddConnection(state);
_claimsStore.UpdateFromMicroservice(payload.Instance.ServiceName, payload.Endpoints);
_openApiCache?.Invalidate();
_logger.LogInformation(
"Messaging connection registered: {ConnectionId} service={ServiceName} version={Version}",
state.ConnectionId,
state.Instance.ServiceName,
state.Instance.Version);
return Task.CompletedTask;
}
private Task HandleMessagingHeartbeat(ConnectionState state, HeartbeatPayload payload)
{
_routingState.UpdateConnection(state.ConnectionId, conn =>
{
conn.LastHeartbeatUtc = DateTime.UtcNow;
conn.Status = payload.Status;
});
return Task.CompletedTask;
}
private Task HandleMessagingResponse(ConnectionState state, Frame frame)
{
_transportClient.HandleResponseFrame(frame);
return Task.CompletedTask;
}
private Task HandleMessagingDisconnection(string connectionId)
{
HandleDisconnect(connectionId);
return Task.CompletedTask;
}
#endregion
private sealed class EndpointKeyComparer : IEqualityComparer<(string Method, string Path)>
{
public bool Equals((string Method, string Path) x, (string Method, string Path) y)

View File

@@ -6,6 +6,7 @@ using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Models;
using StellaOps.Router.Transport.Tcp;
using StellaOps.Router.Transport.Tls;
using StellaOps.Router.Transport.Messaging;
namespace StellaOps.Gateway.WebService.Services;
@@ -13,6 +14,7 @@ public sealed class GatewayTransportClient : ITransportClient
{
private readonly TcpTransportServer _tcpServer;
private readonly TlsTransportServer _tlsServer;
private readonly MessagingTransportServer? _messagingServer;
private readonly ILogger<GatewayTransportClient> _logger;
private readonly ConcurrentDictionary<string, TaskCompletionSource<Frame>> _pendingRequests = new();
private readonly ConcurrentDictionary<string, Channel<Frame>> _streamingResponses = new();
@@ -20,10 +22,12 @@ public sealed class GatewayTransportClient : ITransportClient
public GatewayTransportClient(
TcpTransportServer tcpServer,
TlsTransportServer tlsServer,
ILogger<GatewayTransportClient> logger)
ILogger<GatewayTransportClient> logger,
MessagingTransportServer? messagingServer = null)
{
_tcpServer = tcpServer;
_tlsServer = tlsServer;
_messagingServer = messagingServer;
_logger = logger;
}
@@ -147,6 +151,13 @@ public sealed class GatewayTransportClient : ITransportClient
case TransportType.Certificate:
await _tlsServer.SendFrameAsync(connection.ConnectionId, frame, cancellationToken);
break;
case TransportType.Messaging:
if (_messagingServer is null)
{
throw new InvalidOperationException("Messaging transport is not enabled");
}
await _messagingServer.SendToMicroserviceAsync(connection.ConnectionId, frame, cancellationToken);
break;
default:
throw new NotSupportedException($"Transport type {connection.TransportType} is not supported by the gateway.");
}

View File

@@ -10,6 +10,9 @@
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Gateway\StellaOps.Router.Gateway.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Transport.Tcp\StellaOps.Router.Transport.Tcp.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Transport.Tls\StellaOps.Router.Transport.Tls.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Transport.Messaging\StellaOps.Router.Transport.Messaging.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Messaging.Transport.Valkey\StellaOps.Messaging.Transport.Valkey.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Auth.Security\StellaOps.Auth.Security.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Configuration\StellaOps.Configuration.csproj" />
<ProjectReference Include="..\..\Authority\StellaOps.Authority\StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj" />