feat: validate single-client broadcast operations
Build web service image / build (push) Successful in 1m9s
Build web service image / build (push) Successful in 1m9s
This commit is contained in:
@@ -33,6 +33,7 @@ public enum RemoteNodeStatus
|
||||
public enum RemoteTaskStatus
|
||||
{
|
||||
Pending,
|
||||
WaitingForClient,
|
||||
Accepted,
|
||||
Running,
|
||||
Succeeded,
|
||||
@@ -246,7 +247,8 @@ public sealed record RemoteNodeRegistration(
|
||||
[property: JsonPropertyName("protocol_version")] string ProtocolVersion,
|
||||
[property: JsonPropertyName("capabilities")] IReadOnlyList<string> Capabilities,
|
||||
[property: JsonPropertyName("reporting_config_version")] long ReportingConfigVersion,
|
||||
[property: JsonPropertyName("accounts")] IReadOnlyList<RemoteAccountSummary> Accounts);
|
||||
[property: JsonPropertyName("accounts")] IReadOnlyList<RemoteAccountSummary> Accounts,
|
||||
[property: JsonPropertyName("connection_id")] string? ConnectionId = null);
|
||||
|
||||
public sealed record RemoteAccountSummary(
|
||||
[property: JsonPropertyName("account_id")] string AccountId,
|
||||
@@ -259,13 +261,15 @@ public sealed record RemoteNodeRegistrationResponse(
|
||||
[property: JsonPropertyName("node_id")] string NodeId,
|
||||
[property: JsonPropertyName("status")] RemoteNodeStatus Status,
|
||||
[property: JsonPropertyName("authenticated")] bool Authenticated,
|
||||
[property: JsonPropertyName("correlation_id")] string CorrelationId);
|
||||
[property: JsonPropertyName("correlation_id")] string CorrelationId,
|
||||
[property: JsonPropertyName("connection_id")] string? ConnectionId = null);
|
||||
|
||||
public sealed record RemoteHeartbeatResponse(
|
||||
[property: JsonPropertyName("node_id")] string NodeId,
|
||||
[property: JsonPropertyName("status")] RemoteNodeStatus Status,
|
||||
[property: JsonPropertyName("last_heartbeat_at")] DateTimeOffset LastHeartbeatAt,
|
||||
[property: JsonPropertyName("correlation_id")] string CorrelationId);
|
||||
[property: JsonPropertyName("correlation_id")] string CorrelationId,
|
||||
[property: JsonPropertyName("connection_id")] string? ConnectionId = null);
|
||||
|
||||
public sealed record RemoteTaskBatch(
|
||||
[property: JsonPropertyName("tasks")] IReadOnlyList<RemoteTaskEnvelope> Tasks);
|
||||
@@ -282,7 +286,8 @@ public sealed record RemoteHeartbeat(
|
||||
[property: JsonPropertyName("queue_length")] int QueueLength,
|
||||
[property: JsonPropertyName("reporting_config_version")] long ReportingConfigVersion,
|
||||
[property: JsonPropertyName("correlation_id")] string CorrelationId,
|
||||
[property: JsonPropertyName("last_error_code")] string? LastErrorCode = null);
|
||||
[property: JsonPropertyName("last_error_code")] string? LastErrorCode = null,
|
||||
[property: JsonPropertyName("connection_id")] string? ConnectionId = null);
|
||||
|
||||
public sealed record RemoteTaskEnvelope(
|
||||
[property: JsonPropertyName("task_id")] string TaskId,
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
private readonly Uri? _baseAddress;
|
||||
private readonly X509Certificate2? _clientCertificate;
|
||||
private readonly X509Certificate2? _serverCaCertificate;
|
||||
private string? _connectionId;
|
||||
private bool _authenticated;
|
||||
|
||||
public RemoteControlClient(RemoteAgentOptions options, HttpClient? httpClient = null)
|
||||
@@ -57,7 +58,9 @@ public sealed class RemoteControlClient : IDisposable
|
||||
AuthState = RemoteAuthState.Authenticating;
|
||||
try
|
||||
{
|
||||
var response = await SendAsync<RemoteNodeRegistrationResponse>(HttpMethod.Post, "/v1/nodes/register", registration, false, cancellationToken);
|
||||
var connectionId = registration.ConnectionId ?? NewConnectionId();
|
||||
var response = await SendAsync<RemoteNodeRegistrationResponse>(HttpMethod.Post, "/v1/nodes/register", registration with { ConnectionId = connectionId }, false, cancellationToken);
|
||||
_connectionId = response.ConnectionId ?? connectionId;
|
||||
_authenticated = response.Authenticated;
|
||||
AuthState = response.Authenticated ? RemoteAuthState.Authenticated : RemoteAuthState.AuthenticationFailed;
|
||||
LastSuccessAt = DateTimeOffset.UtcNow;
|
||||
@@ -75,7 +78,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
|
||||
public Task<RemoteHeartbeatResponse> HeartbeatAsync(RemoteHeartbeat heartbeat, CancellationToken cancellationToken = default) =>
|
||||
SendAuthenticatedAsync<RemoteHeartbeatResponse>(HttpMethod.Post,
|
||||
$"/v1/nodes/{Escape(heartbeat.NodeId)}/heartbeat", heartbeat, cancellationToken);
|
||||
$"/v1/nodes/{Escape(heartbeat.NodeId)}/heartbeat", heartbeat with { ConnectionId = heartbeat.ConnectionId ?? _connectionId }, cancellationToken);
|
||||
|
||||
public async Task<IReadOnlyList<RemoteTaskEnvelope>> PollTasksAsync(
|
||||
string accountId,
|
||||
@@ -205,7 +208,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var error = TryDeserializeError(responseBytes, correlationId);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || error.Code == "StaleConnection")
|
||||
{
|
||||
_authenticated = false;
|
||||
AuthState = RemoteAuthState.AuthenticationFailed;
|
||||
@@ -292,6 +295,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
}
|
||||
|
||||
private static string Escape(string value) => Uri.EscapeDataString(value);
|
||||
private static string NewConnectionId() => Guid.NewGuid().ToString("N");
|
||||
private static string NewCorrelationId() => Guid.NewGuid().ToString("N");
|
||||
|
||||
public void Dispose()
|
||||
|
||||
Reference in New Issue
Block a user