using System;
using System.Threading;
using System.Threading.Tasks;
using RudderSdk.Core.Abstractions;
namespace RudderSdk.Core;
///
/// Realtime websocket channel. Requires
/// and
/// .
///
public sealed class RealtimeService
{
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
private readonly RudderClient _client;
private RealtimeSession? _session;
internal RealtimeService(RudderClient client) => _client = client;
/// The current session, or null when not connected.
public RealtimeSession? Session => _session;
/// True while a session is connected.
public bool IsConnected => _session?.IsConnected == true;
/// Connects to the configured realtime URL.
public Task ConnectAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(_client.RealtimeUrl))
throw new InvalidOperationException("RealtimeUrl is not configured.");
return ConnectAsync(new Uri(_client.RealtimeUrl), timeout, cancellationToken);
}
/// Connects to an explicit realtime URL.
public async Task ConnectAsync(Uri uri, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
var factory = _client.Options.RealtimeTransportFactory
?? throw new InvalidOperationException("RealtimeTransportFactory is not configured.");
var token = _client.TokenStore.GetAccessToken();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("LiveOps access token is required for realtime authorization.");
var transport = factory.Create();
await transport.ConnectAsync(uri, timeout ?? DefaultTimeout, cancellationToken).ConfigureAwait(false);
_session = new RealtimeSession(transport, token);
return _session;
}
/// Closes the current session, if any.
public Task DisconnectAsync(CancellationToken cancellationToken = default)
=> _session?.DisconnectAsync(cancellationToken) ?? Task.CompletedTask;
/// Pumps the underlying transport; call every frame.
public void Update(float deltaTime)
{
_session?.Update(deltaTime);
}
}
/// An open realtime connection.
public sealed class RealtimeSession
{
private readonly IRealtimeTransport _transport;
internal RealtimeSession(IRealtimeTransport transport, string accessToken)
{
_transport = transport;
AccessToken = accessToken;
_transport.Closed += () => Closed?.Invoke();
_transport.Error += ex => Error?.Invoke(ex);
_transport.Received += data => MessageReceived?.Invoke(data);
}
/// Access token the connection was authorized with.
public string AccessToken { get; }
/// True while the connection is open.
public bool IsConnected => _transport.IsConnected;
/// Raised when the connection closes.
public event Action? Closed;
/// Raised on transport errors.
public event Action? Error;
/// Raised for every incoming message.
public event Action>? MessageReceived;
/// Sends one message.
public Task SendAsync(byte[] payload, CancellationToken cancellationToken = default)
=> _transport.SendAsync(new ArraySegment(payload ?? Array.Empty()), cancellationToken);
/// Closes the connection.
public Task DisconnectAsync(CancellationToken cancellationToken = default)
=> _transport.CloseAsync(cancellationToken);
internal void Update(float deltaTime)
{
_transport.Update(deltaTime);
}
}