diff --git a/docs/authentication.md b/docs/authentication.md index 24fbb977..33a684ec 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -81,7 +81,7 @@ them: #include auto source = /* EndpointTokenSource, SandboxTokenSource, CustomTokenSource, etc. */; -auto credentials = source->fetch(request_options).get(); +auto credentials = source.fetch(request_options).get(); if (!credentials || !room.connect(credentials.value().server_url, credentials.value().participant_token, options)) { // handle failure @@ -122,15 +122,15 @@ Static credentials you already have, or credentials loaded asynchronously #include // Static URL + JWT -auto source = livekit::LiteralTokenSource::fromLiteral(url, jwt); -auto credentials = source->fetch().get(); +livekit::LiteralTokenSource source(url, jwt); +auto credentials = source.fetch().get(); if (!credentials || !room.connect(credentials.value().server_url, credentials.value().participant_token, options)) { return 1; } // Async provider (same contract as JS TokenSource.literal(async () => …)) -auto source2 = livekit::LiteralTokenSource::fromProvider([]() { +livekit::LiteralTokenSource source2([]() { return std::async(std::launch::async, [] { livekit::TokenSourceResponse details; details.server_url = /* ... */; @@ -157,11 +157,11 @@ livekit::TokenEndpointOptions endpoint_options; endpoint_options.method = "POST"; // default; set to "GET" if your server requires it endpoint_options.headers["Authorization"] = "Bearer your-api-token"; -auto source = livekit::EndpointTokenSource::fromEndpoint( +livekit::EndpointTokenSource source( "https://your-backend.example.com/token", std::move(endpoint_options)); -auto credentials = source->fetch(request).get(); +auto credentials = source.fetch(request).get(); if (!credentials || !room.connect(credentials.value().server_url, credentials.value().participant_token, options)) { return 1; @@ -173,12 +173,12 @@ if (!credentials || Uses the LiveKit Cloud sandbox token server. Do not use in production. ```cpp -auto source = livekit::SandboxTokenSource::fromSandboxTokenServer("your-sandbox-id"); +livekit::SandboxTokenSource source("your-sandbox-id"); livekit::TokenRequestOptions request; request.agent_name = "my-agent"; // optional agent dispatch -auto credentials = source->fetch(request).get(); +auto credentials = source.fetch(request).get(); if (!credentials || !room.connect(credentials.value().server_url, credentials.value().participant_token, options)) { return 1; @@ -193,7 +193,7 @@ Integrate an existing auth system without adopting the standard endpoint wire format: ```cpp -auto source = livekit::CustomTokenSource::fromCustom( +livekit::CustomTokenSource source( [](const livekit::TokenRequestOptions& options) -> std::future> { std::promise> promise; @@ -215,10 +215,10 @@ so the next `fetch()` re-queries the inner source — useful when calling session. `cachedResponse()` returns the currently cached credentials, if any. ```cpp -auto inner = livekit::EndpointTokenSource::fromEndpoint("https://your-backend.example.com/token"); -auto source = livekit::CachingTokenSource::wrap(std::move(inner)); +auto inner = std::make_unique("https://your-backend.example.com/token"); +livekit::CachingTokenSource source(std::move(inner)); -auto credentials = source->fetch(request).get(); +auto credentials = source.fetch(request).get(); if (!credentials || !room.connect(credentials.value().server_url, credentials.value().participant_token, options)) { return 1; diff --git a/docs/token-source-factory-recommendation.md b/docs/token-source-factory-recommendation.md index f99cd58a..ced33aaf 100644 --- a/docs/token-source-factory-recommendation.md +++ b/docs/token-source-factory-recommendation.md @@ -3,9 +3,9 @@ This note summarizes possible public API shapes for constructing TokenSource implementations in the C++ SDK. -## 1. Existing API +## 1. Previous API -Concrete classes expose mechanism-specific static factories: +Concrete classes exposed mechanism-specific static factories: ```cpp LiteralTokenSource::fromLiteral(url, token); @@ -103,13 +103,14 @@ auto source = TokenSource::fromSandboxTokenServer("sandbox-id"); ## 4. Constructors -Make concrete sources directly constructible: +Make concrete sources directly constructible. This is the current branch shape: ```cpp LiteralTokenSource source(url, token); CustomTokenSource source(callback); EndpointTokenSource source(url, options); SandboxTokenSource source(sandbox_id, options); +CachingTokenSource source(std::move(inner)); ``` Pros: @@ -119,8 +120,8 @@ Pros: Cons: -- Larger shift from the current `std::unique_ptr` ownership story. -- Caching/wrapping may require heap allocation or additional helper APIs. +- Caching still requires an owned configurable source, typically via + `std::make_unique`. Example: diff --git a/docs/token-source-sdk-comparison.md b/docs/token-source-sdk-comparison.md new file mode 100644 index 00000000..f11a180a --- /dev/null +++ b/docs/token-source-sdk-comparison.md @@ -0,0 +1,123 @@ +# TokenSource SDK API Comparison + +This report compares the C++ branch's public TokenSource API against the LiveKit +client SDKs that expose comparable public TokenSource surfaces. The closest +matches are Android, Swift, Flutter, and JS. + +## Summary + +C++ is conceptually aligned with the newer TokenSource work across LiveKit SDKs: +it has fixed and configurable token source categories, endpoint/sandbox/custom +implementations, request options for room/participant/agent fields, and +JWT-aware caching. + +After the API alignment pass, the main remaining differences are: + +- C++ uses concrete constructors, while Android centralizes factory methods on + `TokenSource` and JS centralizes them on a `TokenSource` object. +- C++ uses snake_case public fields, while Swift/Android/Flutter/JS use the + language-native field naming conventions. + +## SDK Coverage + +| SDK | TokenSource Shape | Factory Pattern? | Notes | +|---|---|---:|---| +| C++ branch | `TokenSourceFixed`, `TokenSourceConfigurable`, concrete classes | No | Direct constructors on concrete sources | +| Android | `TokenSource` companion factories returning `FixedTokenSource` / `ConfigurableTokenSource` | Yes | Closest factory naming precedent | +| JS | `TokenSource.literal/custom/endpoint/sandboxTokenServer` object factories | Yes | Closest conceptual taxonomy | +| Swift | Protocols plus concrete `LiteralTokenSource` / `SandboxTokenSource`; `EndpointTokenSource` is a protocol with default implementation | No | Closest protocol/native shape | +| Flutter | Abstract fixed/configurable classes plus concrete constructors | No | Very close type/class shape | +| Unity | `ITokenSourceFixed`, `ITokenSourceConfigurable`, direct constructors, and `TokenSourceComponentConfig` | Partial | Similar concepts, Unity-specific component/config surface | +| React Native | No native TokenSource surface found; component takes `serverUrl` + `token` | No | Uses URL + token directly | +| ESP32 / Unreal / Node | No first-class TokenSource surface found | No | N/A | +| Swift XCFramework / Unity Web / benchmarks / Expo plugin | No separate public TokenSource surface found | No | Packaging/support repos | + +## Public Signature Comparison + +| Concept | C++ Branch | Android | JS | Swift | Flutter | +|---|---|---|---|---|---| +| Fixed interface | `class TokenSourceFixed` | `interface FixedTokenSource` | `abstract class TokenSourceFixed` | `protocol TokenSourceFixed` | `abstract class TokenSourceFixed` | +| Configurable interface | `class TokenSourceConfigurable` | `interface ConfigurableTokenSource` | `abstract class TokenSourceConfigurable` | `protocol TokenSourceConfigurable` | `abstract class TokenSourceConfigurable` | +| Fixed fetch | `fetch()` | `fetch()` | `fetch()` | `fetch()` | `fetch()` | +| Config fetch | `fetch(const TokenRequestOptions& options = {})` | `fetch(options = TokenRequestOptions())` | `fetch(options, force?)` | `fetch(_ options)` | `fetch(options)` | +| Response | `server_url`, `participant_token`, `room_name?`, `participant_name?` | `serverUrl`, `participantToken`, `roomName?`, `participantName?` | `serverUrl`, `participantToken`, optional response fields | `serverURL`, `participantToken`, `participantName?`, `roomName?` | `serverUrl`, `participantToken`, `participantName?`, `roomName?` | +| Request options | `room_name`, `participant_name`, `participant_identity`, `participant_metadata`, `participant_attributes`, `agent_name`, `agent_metadata`, `agent_deployment` | `roomName`, `participantName`, `participantIdentity`, `participantMetadata`, `participantAttributes`, `agentName`, `agentMetadata`, `agentDeployment` | `roomName`, `participantName`, `participantIdentity`, `participantMetadata`, `participantAttributes`, `agentName`, `agentMetadata`, `deployment` | same as Android | same as Android | + +## Factory And Construction Comparison + +| Mechanism | C++ Branch | Android | JS | Swift | Flutter | Unity | +|---|---|---|---|---|---|---| +| Literal static credentials | `LiteralTokenSource(url, token)` | `TokenSource.fromLiteral(url, token)` | `TokenSource.literal(valueOrFn)` | `LiteralTokenSource(serverURL:participantToken:)` | `LiteralTokenSource(serverUrl:participantToken:)` | `new TokenSourceLiteral(serverUrl, token)` | +| Literal async provider | `LiteralTokenSource(fn)` | Not found | `TokenSource.literal(async () => ...)` | Implement `TokenSourceFixed` | Implement `TokenSourceFixed` | `TokenSourceCustom` is fixed | +| Custom configurable | `CustomTokenSource(fn)` | `TokenSource.fromCustom(block)` | `TokenSource.custom(fn)` | Implement `TokenSourceConfigurable` | `CustomTokenSource(fn)` | Not configurable; `TokenSourceCustom` is fixed | +| Endpoint | `EndpointTokenSource(url, options)` | `TokenSource.fromEndpoint(url, method, headers)` | `TokenSource.endpoint(url, options)` | conform to `EndpointTokenSource` protocol | `EndpointTokenSource(url:, method:, headers:)` | `new TokenSourceEndpoint(url, headers)` | +| Sandbox | `SandboxTokenSource(id, options)` | `TokenSource.fromSandboxTokenServer(id, options)` | `TokenSource.sandboxTokenServer(id, options)` | `SandboxTokenSource(id:)` | `SandboxTokenSource(sandboxId:)` | `new TokenSourceSandbox(sandboxId)` | +| Caching | `CachingTokenSource(inner)` | `source.cached(...)` | built into configurable sources; `fetch(..., force?)` | `source.cached(...)` | `source.cached(...)` | Not found in public TokenSource surface | + +## TokenSource And Connect + +| SDK | Core Room Connect Shape | TokenSource Relationship | +|---|---|---| +| C++ branch | `Room::connect(url, token, options)` | User fetches credentials, then passes URL + token to `connect` | +| JS | `room.connect(url, token, opts?)` | User fetches credentials, then passes URL + token to `connect` | +| Android | `room.connect(url, token, options)` | User fetches credentials, then passes URL + token to `connect` | +| Swift | `room.connect(url:token:connectOptions:roomOptions:)` | Core `Room` takes URL + token. `Session` accepts TokenSource, fetches, then calls `Room.connect` | +| Flutter | `room.connect(url, token, connectOptions:, roomOptions:)` | Core `Room` takes URL + token. `Session` accepts TokenSource, fetches, then calls `Room.connect` | +| Unity | `Room.Connect(serverUrl, participantToken)` | `TokenSourceComponent` or direct source fetch returns connection details before connect | +| React Native | `LiveKitRoom` props include `serverUrl` and `token` | No first-class TokenSource wrapper found | + +## Findings + +### Closest Matches + +Android is the closest API naming precedent if C++ uses factory methods. +Android uses central `TokenSource.fromLiteral`, `fromCustom`, `fromEndpoint`, +and `fromSandboxTokenServer` factories; C++ now uses constructors on the +concrete source classes instead. + +Swift and Flutter are closest in type shape: both distinguish fixed and +configurable token sources and use concrete `LiteralTokenSource`, +`SandboxTokenSource`, `EndpointTokenSource`, and `CachingTokenSource` concepts. +They do not centralize creation behind a factory object. + +JS is closest in user-facing taxonomy: `literal`, `custom`, `endpoint`, and +`sandboxTokenServer`. + +### Remaining Naming Differences + +The most visible remaining C++ construction differences are: + +- C++ constructs concrete sources directly, while Android and JS centralize + creation under `TokenSource`. +- C++ uses `LiteralTokenSource(fn)` for async fixed providers, conceptually close + to JS `TokenSource.literal(async () => ...)`. +- C++ uses `CachingTokenSource(inner)`, while Swift/Android/Flutter expose + `.cached(...)`. + +### Connect Design + +C++ now follows the common core Room pattern: + +1. Fetch credentials from a token source. +2. Pass `serverUrl` and `participantToken` to `Room.connect`. + +### Response Shape + +C++ now matches the other SDK response shape conceptually: + +- `server_url` +- `participant_token` +- `room_name` +- `participant_name` + +Only `server_url` and `participant_token` are needed for connect. The optional +room and participant name fields are preserved as informational response +metadata when a token server provides them. + +## Conclusion + +The largest uniqueness has been removed: C++ no longer exposes TokenSource +`Room::connect` overloads. C++ now favors direct constructors for concrete +TokenSource types; the remaining alignment opportunity is centralizing factories +under a `TokenSource` facade if cross-SDK discoverability becomes more important +than C++-native construction. diff --git a/include/livekit/token_source.h b/include/livekit/token_source.h index b24543b8..6f4349e2 100644 --- a/include/livekit/token_source.h +++ b/include/livekit/token_source.h @@ -34,7 +34,7 @@ namespace livekit { /// This is an output type: it is what a @ref TokenSourceFixed or /// @ref TokenSourceConfigurable returns from @c fetch. Applications typically read /// it rather than construct it. For static credentials, prefer -/// @ref LiteralTokenSource::fromLiteral, which takes the server URL and token +/// @ref LiteralTokenSource, which takes the server URL and token /// directly instead of requiring you to populate this struct. /// /// Mirrors the @c livekit.TokenSourceResponse wire shape for connection @@ -187,7 +187,7 @@ class LIVEKIT_API LiteralTokenSource final : public TokenSourceFixed { /// /// @param server_url WebSocket URL of the LiveKit server. /// @param participant_token JWT access token for the participant. - static std::unique_ptr fromLiteral(std::string server_url, std::string participant_token); + LiteralTokenSource(std::string server_url, std::string participant_token); /// @brief Create a token source from an async provider that returns full credentials. /// @@ -198,15 +198,11 @@ class LIVEKIT_API LiteralTokenSource final : public TokenSourceFixed { /// cannot be influenced by @ref TokenRequestOptions. If you need a configurable /// source whose callback receives request options and can re-fetch on demand, /// use @ref CustomTokenSource instead. - static std::unique_ptr fromProvider( - std::function>()> provider); + explicit LiteralTokenSource(std::function>()> provider); std::future> fetch() override; private: - explicit LiteralTokenSource(TokenSourceResponse details); - explicit LiteralTokenSource(std::function>()> provider); - TokenSourceResponse details_; std::function>()> provider_; }; @@ -222,7 +218,7 @@ class LIVEKIT_API CustomTokenSource final : public TokenSourceConfigurable { /// /// The callback receives @ref TokenRequestOptions for each fetch and returns /// @ref TokenSourceResponse produced by your application. - static std::unique_ptr fromCustom( + explicit CustomTokenSource( std::function>(const TokenRequestOptions&)> provider); /// @note This source holds no cache and invokes the provider fresh on every @@ -230,9 +226,6 @@ class LIVEKIT_API CustomTokenSource final : public TokenSourceConfigurable { std::future> fetch(const TokenRequestOptions& options) override; private: - explicit CustomTokenSource( - std::function>(const TokenRequestOptions&)> provider); - std::function>(const TokenRequestOptions&)> provider_; }; @@ -249,7 +242,7 @@ class LIVEKIT_API EndpointTokenSource final : public TokenSourceConfigurable { /// /// @param endpoint_url URL of your backend token endpoint. /// @param options HTTP transport options (method, headers, timeout). - static std::unique_ptr fromEndpoint(std::string endpoint_url, TokenEndpointOptions options = {}); + explicit EndpointTokenSource(std::string endpoint_url, TokenEndpointOptions options = {}); /// @note Every fetch issues a fresh HTTP request. Wrap it in /// @ref CachingTokenSource to reuse credentials between calls. @@ -287,8 +280,7 @@ class LIVEKIT_API SandboxTokenSource final : public TokenSourceConfigurable { /// /// @param sandbox_id Sandbox identifier from LiveKit Cloud (surrounding whitespace is trimmed). /// @param options Sandbox token server options. - static std::unique_ptr fromSandboxTokenServer(const std::string& sandbox_id, - const SandboxTokenServerOptions& options = {}); + explicit SandboxTokenSource(const std::string& sandbox_id, const SandboxTokenServerOptions& options = {}); std::future> fetch(const TokenRequestOptions& options) override; @@ -311,7 +303,7 @@ class LIVEKIT_API CachingTokenSource final : public TokenSourceConfigurable { /// @brief Wrap @p inner with JWT-aware caching. /// /// Cached values are keyed by @ref TokenRequestOptions. - static std::unique_ptr wrap(std::unique_ptr inner); + explicit CachingTokenSource(std::unique_ptr inner); std::future> fetch(const TokenRequestOptions& options) override; @@ -324,8 +316,6 @@ class LIVEKIT_API CachingTokenSource final : public TokenSourceConfigurable { std::optional cachedResponse() const; private: - explicit CachingTokenSource(std::unique_ptr inner); - std::unique_ptr inner_; mutable std::mutex mutex_; std::optional cached_options_; diff --git a/src/tests/integration/test_room.cpp b/src/tests/integration/test_room.cpp index 7f7b2276..d20f12c6 100644 --- a/src/tests/integration/test_room.cpp +++ b/src/tests/integration/test_room.cpp @@ -94,8 +94,8 @@ TEST_F(RoomTest, ConnectWithLiteralTokenSource) { Room room; RoomOptions options; - auto token_source = LiteralTokenSource::fromLiteral(server_url_, token_); - const auto details = token_source->fetch().get(); + LiteralTokenSource token_source(server_url_, token_); + const auto details = token_source.fetch().get(); ASSERT_TRUE(details); const bool connected = room.connect(details.value().server_url, details.value().participant_token, options); @@ -113,7 +113,7 @@ TEST_F(RoomTest, ConnectWithCustomTokenSource) { Room room; RoomOptions options; - auto token_source = CustomTokenSource::fromCustom( + CustomTokenSource token_source( [this](const TokenRequestOptions& options) -> std::future> { std::promise> promise; TokenSourceResponse details; @@ -127,7 +127,7 @@ TEST_F(RoomTest, ConnectWithCustomTokenSource) { TokenRequestOptions request; request.room_name = "integration-room"; - const auto details = token_source->fetch(request).get(); + const auto details = token_source.fetch(request).get(); ASSERT_TRUE(details); const bool connected = room.connect(details.value().server_url, details.value().participant_token, options); diff --git a/src/tests/integration/test_token_source.cpp b/src/tests/integration/test_token_source.cpp index 14034ed8..fce341df 100644 --- a/src/tests/integration/test_token_source.cpp +++ b/src/tests/integration/test_token_source.cpp @@ -72,7 +72,7 @@ TEST_F(TokenSourceEndpointConnectTest, EndpointMintsConnectableToken) { GTEST_SKIP() << "LIVEKIT_CREATE_TOKEN_URL not set"; } - auto source = EndpointTokenSource::fromEndpoint(create_token_url_); + EndpointTokenSource source(create_token_url_); TokenRequestOptions request; request.room_name = "cpp_endpoint_e2e"; @@ -80,7 +80,7 @@ TEST_F(TokenSourceEndpointConnectTest, EndpointMintsConnectableToken) { Room room; RoomOptions options; - const auto details = source->fetch(request).get(); + const auto details = source.fetch(request).get(); ASSERT_TRUE(details) << "endpoint should mint connectable credentials"; ASSERT_TRUE(room.connect(details.value().server_url, details.value().participant_token, options)) << "endpoint-minted token should connect"; diff --git a/src/tests/unit/test_room.cpp b/src/tests/unit/test_room.cpp index 4e47e54c..02a41ff4 100644 --- a/src/tests/unit/test_room.cpp +++ b/src/tests/unit/test_room.cpp @@ -47,8 +47,8 @@ TEST_F(RoomTest, ConnectWithoutInitialize) { } TEST_F(RoomTest, LiteralTokenSourceEmptyCredentialsFails) { - auto source = LiteralTokenSource::fromLiteral("wss://localhost:7880", ""); - const auto result = source->fetch().get(); + LiteralTokenSource source("wss://localhost:7880", ""); + const auto result = source.fetch().get(); EXPECT_FALSE(result) << "Fetching empty credentials should fail before connect"; } @@ -57,8 +57,8 @@ TEST_F(RoomTest, ConnectWithLiteralTokenSourceWithoutInitialize) { livekit::shutdown(); Room room; - auto source = LiteralTokenSource::fromLiteral("wss://localhost:7880", "jwt-token"); - const auto details = source->fetch().get(); + LiteralTokenSource source("wss://localhost:7880", "jwt-token"); + const auto details = source.fetch().get(); ASSERT_TRUE(details); const bool result = room.connect(details.value().server_url, details.value().participant_token, RoomOptions()); @@ -66,18 +66,18 @@ TEST_F(RoomTest, ConnectWithLiteralTokenSourceWithoutInitialize) { } TEST_F(RoomTest, CustomTokenSourceThrowFails) { - auto source = CustomTokenSource::fromCustom( + CustomTokenSource source( [](const TokenRequestOptions&) -> std::future> { std::promise> promise; promise.set_exception(std::make_exception_ptr(std::runtime_error("token fetch failed"))); return promise.get_future(); }); - EXPECT_THROW((void)source->fetch(TokenRequestOptions{}).get(), std::runtime_error); + EXPECT_THROW((void)source.fetch(TokenRequestOptions{}).get(), std::runtime_error); } TEST_F(RoomTest, CustomTokenSourceErrorFails) { - auto source = CustomTokenSource::fromCustom( + CustomTokenSource source( [](const TokenRequestOptions&) -> std::future> { std::promise> promise; promise.set_value( @@ -85,7 +85,7 @@ TEST_F(RoomTest, CustomTokenSourceErrorFails) { return promise.get_future(); }); - const auto result = source->fetch(TokenRequestOptions{}).get(); + const auto result = source.fetch(TokenRequestOptions{}).get(); EXPECT_FALSE(result) << "Fetching when token source returns error should fail"; } @@ -94,18 +94,17 @@ TEST_F(RoomTest, ConnectWithLiteralProvider) { Room room; int fetch_count = 0; - auto source = - LiteralTokenSource::fromProvider([&fetch_count]() -> std::future> { - ++fetch_count; - TokenSourceResponse details; - details.server_url = "wss://localhost:7880"; - details.participant_token = "fetched-token"; - std::promise> promise; - promise.set_value(Result::success(details)); - return promise.get_future(); - }); - - const auto details = source->fetch().get(); + LiteralTokenSource source([&fetch_count]() -> std::future> { + ++fetch_count; + TokenSourceResponse details; + details.server_url = "wss://localhost:7880"; + details.participant_token = "fetched-token"; + std::promise> promise; + promise.set_value(Result::success(details)); + return promise.get_future(); + }); + + const auto details = source.fetch().get(); ASSERT_TRUE(details); EXPECT_EQ(fetch_count, 1) << "Token source should be invoked once"; diff --git a/src/tests/unit/test_token_source.cpp b/src/tests/unit/test_token_source.cpp index cf5ef401..b9d5a235 100644 --- a/src/tests/unit/test_token_source.cpp +++ b/src/tests/unit/test_token_source.cpp @@ -212,7 +212,7 @@ TEST(TokenSourceSandboxMockTest, SetsSandboxHeaderAndResolvesUrl) { SandboxTokenServerOptions options; options.base_url = "https://cloud-api.livekit.io"; auto source = SandboxTokenSourceTestAccess::create( - " sandbox-123 ", std::move(options), + " sandbox-123 ", options, makeStubTransport(capture, Result::success(successResponseJson()))); const auto result = source->fetch({}).get(); @@ -308,21 +308,21 @@ TEST(TokenSourceJwtTest, ValidAndExpiredTokens) { TEST(TokenSourceJwtTest, UnparseableTokenIsInvalid) { EXPECT_FALSE(isParticipantTokenValid("not-a-jwt")); } -TEST(TokenSourceFactoryTest, LiteralTokenSourceReturnsDetails) { +TEST(TokenSourceConstructionTest, LiteralTokenSourceReturnsDetails) { const std::string server_url = "wss://example.livekit.io"; const std::string participant_token = "jwt-token"; - auto source = LiteralTokenSource::fromLiteral(server_url, participant_token); - const auto result = source->fetch().get(); + LiteralTokenSource source(server_url, participant_token); + const auto result = source.fetch().get(); ASSERT_TRUE(result); EXPECT_EQ(result.value().server_url, server_url); EXPECT_EQ(result.value().participant_token, participant_token); } -TEST(TokenSourceFactoryTest, CustomTokenSourceReceivesOptions) { +TEST(TokenSourceConstructionTest, CustomTokenSourceReceivesOptions) { std::optional captured_room; - auto source = CustomTokenSource::fromCustom([&captured_room](const TokenRequestOptions& options) - -> std::future> { + CustomTokenSource source([&captured_room](const TokenRequestOptions& options) + -> std::future> { captured_room = options.room_name; TokenSourceResponse details; details.server_url = "wss://example.livekit.io"; @@ -334,15 +334,15 @@ TEST(TokenSourceFactoryTest, CustomTokenSourceReceivesOptions) { TokenRequestOptions request; request.room_name = "requested-room"; - const auto result = source->fetch(request).get(); + const auto result = source.fetch(request).get(); ASSERT_TRUE(result); ASSERT_TRUE(captured_room.has_value()); EXPECT_EQ(*captured_room, "requested-room"); } -TEST(TokenSourceFactoryTest, CachingTokenSourceReusesValidToken) { +TEST(TokenSourceConstructionTest, CachingTokenSourceReusesValidToken) { std::atomic fetch_count{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count](const TokenRequestOptions&) -> std::future> { ++fetch_count; TokenSourceResponse details; @@ -353,20 +353,20 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceReusesValidToken) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; request.room_name = "room"; - const auto first = cached->fetch(request).get(); - const auto second = cached->fetch(request).get(); + const auto first = cached.fetch(request).get(); + const auto second = cached.fetch(request).get(); ASSERT_TRUE(first); ASSERT_TRUE(second); EXPECT_EQ(fetch_count.load(), 1); } -TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesAfterInvalidate) { +TEST(TokenSourceConstructionTest, CachingTokenSourceRefetchesAfterInvalidate) { std::atomic fetch_count{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count](const TokenRequestOptions&) -> std::future> { ++fetch_count; TokenSourceResponse details; @@ -377,17 +377,17 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesAfterInvalidate) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; - (void)cached->fetch(request).get(); - cached->invalidate(); - (void)cached->fetch(request).get(); + (void)cached.fetch(request).get(); + cached.invalidate(); + (void)cached.fetch(request).get(); EXPECT_EQ(fetch_count.load(), 2); } -TEST(TokenSourceFactoryTest, CachingTokenSourceExposesCachedResponse) { - auto inner = CustomTokenSource::fromCustom( +TEST(TokenSourceConstructionTest, CachingTokenSourceExposesCachedResponse) { + auto inner = std::make_unique( [](const TokenRequestOptions&) -> std::future> { TokenSourceResponse details; details.server_url = "wss://example.livekit.io"; @@ -397,23 +397,23 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceExposesCachedResponse) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; - EXPECT_FALSE(cached->cachedResponse().has_value()); + EXPECT_FALSE(cached.cachedResponse().has_value()); - (void)cached->fetch(request).get(); - const auto stored = cached->cachedResponse(); + (void)cached.fetch(request).get(); + const auto stored = cached.cachedResponse(); ASSERT_TRUE(stored.has_value()); EXPECT_EQ(stored->server_url, "wss://example.livekit.io"); - cached->invalidate(); - EXPECT_FALSE(cached->cachedResponse().has_value()); + cached.invalidate(); + EXPECT_FALSE(cached.cachedResponse().has_value()); } -TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenOptionsChange) { +TEST(TokenSourceConstructionTest, CachingTokenSourceRefetchesWhenOptionsChange) { std::atomic fetch_count{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count](const TokenRequestOptions&) -> std::future> { ++fetch_count; TokenSourceResponse details; @@ -424,21 +424,21 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenOptionsChange) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions first_request; first_request.room_name = "room-a"; TokenRequestOptions second_request; second_request.room_name = "room-b"; - (void)cached->fetch(first_request).get(); - (void)cached->fetch(second_request).get(); + (void)cached.fetch(first_request).get(); + (void)cached.fetch(second_request).get(); EXPECT_EQ(fetch_count.load(), 2); } -TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenTokenExpired) { +TEST(TokenSourceConstructionTest, CachingTokenSourceRefetchesWhenTokenExpired) { std::atomic fetch_count{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count](const TokenRequestOptions&) -> std::future> { const int count = ++fetch_count; TokenSourceResponse details; @@ -450,12 +450,12 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenTokenExpired) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; request.room_name = "room"; - const auto first = cached->fetch(request).get(); - const auto second = cached->fetch(request).get(); + const auto first = cached.fetch(request).get(); + const auto second = cached.fetch(request).get(); ASSERT_TRUE(first); ASSERT_TRUE(second); @@ -463,9 +463,9 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenTokenExpired) { EXPECT_NE(first.value().participant_token, second.value().participant_token); } -TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenTokenUnparseable) { +TEST(TokenSourceConstructionTest, CachingTokenSourceRefetchesWhenTokenUnparseable) { std::atomic fetch_count{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count](const TokenRequestOptions&) -> std::future> { const int count = ++fetch_count; TokenSourceResponse details; @@ -476,24 +476,24 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceRefetchesWhenTokenUnparseable) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; request.room_name = "room"; - const auto first = cached->fetch(request).get(); - const auto second = cached->fetch(request).get(); + const auto first = cached.fetch(request).get(); + const auto second = cached.fetch(request).get(); ASSERT_TRUE(first); ASSERT_TRUE(second); EXPECT_EQ(fetch_count.load(), 2); } -TEST(TokenSourceFactoryTest, CachingTokenSourceSerializesConcurrentFetches) { +TEST(TokenSourceConstructionTest, CachingTokenSourceSerializesConcurrentFetches) { std::atomic fetch_count{0}; std::atomic concurrent_calls{0}; std::atomic max_concurrent_calls{0}; - auto inner = CustomTokenSource::fromCustom( + auto inner = std::make_unique( [&fetch_count, &concurrent_calls, &max_concurrent_calls]( const TokenRequestOptions&) -> std::future> { ++fetch_count; @@ -513,14 +513,14 @@ TEST(TokenSourceFactoryTest, CachingTokenSourceSerializesConcurrentFetches) { return promise.get_future(); }); - auto cached = CachingTokenSource::wrap(std::move(inner)); + CachingTokenSource cached(std::move(inner)); TokenRequestOptions request; request.room_name = "concurrent-room"; std::vector threads; threads.reserve(4); for (int i = 0; i < 4; ++i) { - threads.emplace_back([&cached, &request]() { (void)cached->fetch(request).get(); }); + threads.emplace_back([&cached, &request]() { (void)cached.fetch(request).get(); }); } for (auto& thread : threads) { thread.join(); diff --git a/src/token_source.cpp b/src/token_source.cpp index 2a631794..aa172863 100644 --- a/src/token_source.cpp +++ b/src/token_source.cpp @@ -105,20 +105,8 @@ ResolvedSandboxEndpoint resolveSandboxEndpoint(const std::string& sandbox_id, To } // namespace -std::unique_ptr LiteralTokenSource::fromLiteral(std::string server_url, - std::string participant_token) { - TokenSourceResponse details; - details.server_url = std::move(server_url); - details.participant_token = std::move(participant_token); - return std::unique_ptr(new LiteralTokenSource(std::move(details))); -} - -std::unique_ptr LiteralTokenSource::fromProvider( - std::function>()> provider) { - return std::unique_ptr(new LiteralTokenSource(std::move(provider))); -} - -LiteralTokenSource::LiteralTokenSource(TokenSourceResponse details) : details_(std::move(details)) {} +LiteralTokenSource::LiteralTokenSource(std::string server_url, std::string participant_token) + : details_{std::move(server_url), std::move(participant_token), std::nullopt, std::nullopt} {} LiteralTokenSource::LiteralTokenSource( std::function>()> provider) @@ -138,11 +126,6 @@ std::future> LiteralTokenSource::f }); } -std::unique_ptr CustomTokenSource::fromCustom( - std::function>(const TokenRequestOptions&)> provider) { - return std::unique_ptr(new CustomTokenSource(std::move(provider))); -} - CustomTokenSource::CustomTokenSource( std::function>(const TokenRequestOptions&)> provider) : provider_(std::move(provider)) {} @@ -152,11 +135,8 @@ std::future> CustomTokenSource::fe return provider_(options); } -std::unique_ptr EndpointTokenSource::fromEndpoint(std::string endpoint_url, - TokenEndpointOptions options) { - return std::unique_ptr( - new EndpointTokenSource(std::move(endpoint_url), std::move(options), &tokenSourceHttpRequest)); -} +EndpointTokenSource::EndpointTokenSource(std::string endpoint_url, TokenEndpointOptions options) + : EndpointTokenSource(std::move(endpoint_url), std::move(options), &tokenSourceHttpRequest) {} EndpointTokenSource::EndpointTokenSource(std::string endpoint_url, TokenEndpointOptions options, HttpTransport transport) @@ -196,11 +176,9 @@ Result EndpointTokenSource::fetchSync(con return parseTokenSourceResponseJson(http_result.value()); } -std::unique_ptr SandboxTokenSource::fromSandboxTokenServer( - const std::string& sandbox_id, const SandboxTokenServerOptions& options) { +SandboxTokenSource::SandboxTokenSource(const std::string& sandbox_id, const SandboxTokenServerOptions& options) { auto resolved = resolveSandboxEndpoint(sandbox_id, {}, options.base_url); - auto endpoint = EndpointTokenSource::fromEndpoint(std::move(resolved.url), std::move(resolved.options)); - return std::unique_ptr(new SandboxTokenSource(std::move(endpoint))); + endpoint_ = std::make_unique(std::move(resolved.url), std::move(resolved.options)); } SandboxTokenSource::SandboxTokenSource(std::unique_ptr endpoint) @@ -220,10 +198,6 @@ std::future> SandboxTokenSource::f return endpoint_->fetch(options); } -std::unique_ptr CachingTokenSource::wrap(std::unique_ptr inner) { - return std::unique_ptr(new CachingTokenSource(std::move(inner))); -} - CachingTokenSource::CachingTokenSource(std::unique_ptr inner) : inner_(std::move(inner)) {} std::future> CachingTokenSource::fetch(