diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 923f841bd848..0cf344ecafd9 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -164,6 +164,8 @@ private WebSocketNotificationsClientConnector setUpNotificationsClient() public string AccessToken => authentication.RequestAccessToken(); + public Guid SessionIdentifier { get; } = Guid.NewGuid(); + /// /// Number of consecutive requests which failed due to network issues. /// diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 960941fc05f7..0af76537cd76 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -39,6 +39,8 @@ public partial class DummyAPIAccess : Component, IAPIProvider public string AccessToken => "token"; + public Guid SessionIdentifier { get; } = Guid.NewGuid(); + /// public bool IsLoggedIn => State.Value > APIState.Offline; diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 7b95b68ec3b4..d8194dc32b19 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -44,6 +44,12 @@ public interface IAPIProvider /// string AccessToken { get; } + /// + /// Used as an identifier of a single local lazer session. + /// Sent across the wire for the purposes of concurrency control to spectator server. + /// + Guid SessionIdentifier { get; } + /// /// Returns whether the local user is logged in. /// diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 9d414deade75..9288a32052dd 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -19,6 +19,9 @@ public class HubClientConnector : PersistentEndpointClientConnector, IHubClientC { public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down."; + public const string VERSION_HASH_HEADER = @"X-Osu-Version-Hash"; + public const string CLIENT_SESSION_ID_HEADER = @"X-Client-Session-ID"; + /// /// Invoked whenever a new hub connection is built, to configure it before it's started. /// @@ -68,8 +71,11 @@ protected override Task BuildConnectionAsync(Cancellat options.Proxy.Credentials = CredentialCache.DefaultCredentials; } - options.Headers.Add("Authorization", $"Bearer {API.AccessToken}"); - options.Headers.Add("OsuVersionHash", versionHash); + options.Headers.Add(@"Authorization", @$"Bearer {API.AccessToken}"); + // non-standard header name kept for backwards compatibility, can be removed after server side has migrated to `VERSION_HASH_HEADER` + options.Headers.Add(@"OsuVersionHash", versionHash); + options.Headers.Add(VERSION_HASH_HEADER, versionHash); + options.Headers.Add(CLIENT_SESSION_ID_HEADER, API.SessionIdentifier.ToString()); }); if (RuntimeFeature.IsDynamicCodeCompiled && preferMessagePack)