diff --git a/Runtime/Scripts/TokenSource/TokenSource.cs b/Runtime/Scripts/TokenSource/TokenSource.cs index d923c4c0..9c9ba09e 100644 --- a/Runtime/Scripts/TokenSource/TokenSource.cs +++ b/Runtime/Scripts/TokenSource/TokenSource.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using LiveKit.Internal.Threading; using Newtonsoft.Json; namespace LiveKit @@ -21,7 +22,7 @@ public interface ITokenSource /// public interface ITokenSourceFixed : ITokenSource { - public Task FetchConnectionDetails(); + public TaskYieldInstruction FetchConnectionDetails(); } /// @@ -30,7 +31,7 @@ public interface ITokenSourceFixed : ITokenSource /// public interface ITokenSourceConfigurable : ITokenSource { - public Task FetchConnectionDetails(TokenSourceFetchOptions options); + public TaskYieldInstruction FetchConnectionDetails(TokenSourceFetchOptions options); } /// @@ -48,10 +49,10 @@ public TokenSourceLiteral(string serverUrl, string participantToken) _participantToken = participantToken; } - public Task FetchConnectionDetails() + public TaskYieldInstruction FetchConnectionDetails() { var result = new ConnectionDetails { ServerUrl = _serverUrl, ParticipantToken = _participantToken }; - return Task.FromResult(result); + return new TaskYieldInstruction(Task.FromResult(result)); } } @@ -70,9 +71,22 @@ public TokenSourceCustom(CustomTokenFunction customTokenFunction) _customTokenFunction = customTokenFunction; } - public Task FetchConnectionDetails() + public TaskYieldInstruction FetchConnectionDetails() { - return _customTokenFunction(); + // Route a synchronous throw (or a null return) from the user's function through the + // instruction's IsError/Exception, so callers never have to guard the call itself. + Task task; + try + { + task = _customTokenFunction() + ?? Task.FromException( + new InvalidOperationException("Custom token function returned a null task")); + } + catch (Exception e) + { + task = Task.FromException(e); + } + return new TaskYieldInstruction(task); } } @@ -94,7 +108,14 @@ public TokenSourceEndpoint(string endpointUrl, IEnumerable headers) _headers = headers; } - public async Task FetchConnectionDetails(TokenSourceFetchOptions options) + public TaskYieldInstruction FetchConnectionDetails(TokenSourceFetchOptions options) + { + // Async methods can't return the (non-awaitable) instruction directly, so the actual + // request lives in the helper below; the returned task carries any synchronous throw. + return new TaskYieldInstruction(FetchConnectionDetailsAsync(options)); + } + + private async Task FetchConnectionDetailsAsync(TokenSourceFetchOptions options) { var requestBody = BuildRequest(options); var jsonBody = JsonConvert.SerializeObject(requestBody); diff --git a/Runtime/Scripts/TokenSource/TokenSourceComponent.cs b/Runtime/Scripts/TokenSource/TokenSourceComponent.cs index 9031f66e..ebd104d6 100644 --- a/Runtime/Scripts/TokenSource/TokenSourceComponent.cs +++ b/Runtime/Scripts/TokenSource/TokenSourceComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using UnityEngine; using LiveKit.Internal; @@ -60,23 +59,19 @@ public void Awake() /// public TaskYieldInstruction FetchConnectionDetails(TokenSourceFetchOptions options) { - Task task; switch (_tokenSource) { case ITokenSourceConfigurable configurableSource: - task = configurableSource.FetchConnectionDetails(Coalesce(_config, options)); - break; + return configurableSource.FetchConnectionDetails(Coalesce(_config, options)); case ITokenSourceFixed fixedSource: if (options != null) Utils.Warning("TokenSourceComponent uses a fixed config, so fetch options are ignored."); - task = fixedSource.FetchConnectionDetails(); - break; + return fixedSource.FetchConnectionDetails(); default: throw new InvalidOperationException("Unknown token source type"); } - return new TaskYieldInstruction(task); } private static TokenSourceFetchOptions Coalesce(TokenSourceComponentConfig config, TokenSourceFetchOptions? options)