diff --git a/Assets/Plugins/StreamChat/Core/LowLevelClient/StreamChatLowLevelClient.cs b/Assets/Plugins/StreamChat/Core/LowLevelClient/StreamChatLowLevelClient.cs index f7b7890b..43f37098 100644 --- a/Assets/Plugins/StreamChat/Core/LowLevelClient/StreamChatLowLevelClient.cs +++ b/Assets/Plugins/StreamChat/Core/LowLevelClient/StreamChatLowLevelClient.cs @@ -309,11 +309,12 @@ public StreamChatLowLevelClient(AuthCredentials authCredentials, IWebsocketClien _logs.Prefix = "[Stream Chat] "; - _requestUriFactory = new RequestUriFactory(authProvider: this, connectionProvider: this, _serializer); + var streamClientHeader = BuildStreamClientHeader(applicationInfo); + _requestUriFactory = new RequestUriFactory(authProvider: this, connectionProvider: this, _serializer, + streamClientHeader); _httpClient.AddDefaultCustomHeader("stream-auth-type", DefaultStreamAuthType); - var header = BuildStreamClientHeader(applicationInfo); - _httpClient.AddDefaultCustomHeader("X-Stream-Client", header); + _httpClient.AddDefaultCustomHeader("X-Stream-Client", streamClientHeader); _websocketClient.ConnectionFailed += OnWebsocketsConnectionFailed; _websocketClient.Disconnected += OnWebsocketDisconnected; diff --git a/Assets/Plugins/StreamChat/Core/Web/RequestUriFactory.cs b/Assets/Plugins/StreamChat/Core/Web/RequestUriFactory.cs index 961d9170..7ef48d4c 100644 --- a/Assets/Plugins/StreamChat/Core/Web/RequestUriFactory.cs +++ b/Assets/Plugins/StreamChat/Core/Web/RequestUriFactory.cs @@ -15,11 +15,12 @@ namespace StreamChat.Core.Web internal class RequestUriFactory : IRequestUriFactory { public RequestUriFactory(IAuthProvider authProvider, IStreamChatLowLevelClient connectionProvider, - ISerializer serializer) + ISerializer serializer, string streamClientHeader) { _authProvider = authProvider ?? throw new ArgumentNullException(nameof(authProvider)); _connectionProvider = connectionProvider ?? throw new ArgumentNullException(nameof(connectionProvider)); _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + _streamClientHeader = streamClientHeader ?? throw new ArgumentNullException(nameof(streamClientHeader)); } public Uri CreateConnectionUri() @@ -43,6 +44,7 @@ public Uri CreateConnectionUri() { "api_key", _authProvider.ApiKey }, { "authorization", _authProvider.UserToken }, { "stream-auth-type", _authProvider.StreamAuthType }, + { "X-Stream-Client", Uri.EscapeDataString(_streamClientHeader) }, }; var uriBuilder = new UriBuilder(_connectionProvider.ServerUri) @@ -69,6 +71,7 @@ public Uri CreateEndpointUri(string endpoint, Dictionary paramet private readonly IAuthProvider _authProvider; private readonly ISerializer _serializer; private readonly IStreamChatLowLevelClient _connectionProvider; + private readonly string _streamClientHeader; private Dictionary GetDefaultParameters() => new Dictionary diff --git a/Assets/Plugins/StreamChat/Tests/LowLevelClient/StreamChatLowLevelClientTests.cs b/Assets/Plugins/StreamChat/Tests/LowLevelClient/StreamChatLowLevelClientTests.cs index 075b9ad2..ef6f2420 100644 --- a/Assets/Plugins/StreamChat/Tests/LowLevelClient/StreamChatLowLevelClientTests.cs +++ b/Assets/Plugins/StreamChat/Tests/LowLevelClient/StreamChatLowLevelClientTests.cs @@ -1,6 +1,7 @@ #if STREAM_TESTS_ENABLED using System; using System.Collections.Generic; +using System.Linq; using System.Net.WebSockets; using System.Threading.Tasks; using NSubstitute; @@ -156,6 +157,76 @@ T GetParam(int indexToTestNull) where T : class } } + [Test] + public void when_stream_client_created_expect_http_tracking_headers() + { + var mockHttpClient = Substitute.For(); + var mockApplicationInfo = Substitute.For(); + ConfigureApplicationInfo(mockApplicationInfo); + + var client = new StreamChatLowLevelClient(_authCredentials, _mockWebsocketClient, mockHttpClient, + _mockSerializer, _mockTimeService, _mockNetworkMonitor, mockApplicationInfo, _mockLogs, + _mockStreamClientConfig); + _resourcesToDispose.Add(client); + + var expectedHeader = BuildExpectedStreamClientHeader(mockApplicationInfo); + + mockHttpClient.Received().AddDefaultCustomHeader("stream-auth-type", "jwt"); + mockHttpClient.Received().AddDefaultCustomHeader("X-Stream-Client", expectedHeader); + } + + [Test] + public void when_stream_client_connects_expect_websocket_connect_url_includes_client_tracking() + { + var mockWebsocketClient = Substitute.For(); + var mockApplicationInfo = Substitute.For(); + ConfigureApplicationInfo(mockApplicationInfo); + + _mockSerializer.Serialize(Arg.Any()).Returns("{\"user_id\":\"user123\"}"); + + var client = new StreamChatLowLevelClient(_authCredentials, mockWebsocketClient, _mockHttpClient, + _mockSerializer, _mockTimeService, _mockNetworkMonitor, mockApplicationInfo, _mockLogs, + _mockStreamClientConfig); + _resourcesToDispose.Add(client); + + var expectedHeader = BuildExpectedStreamClientHeader(mockApplicationInfo); + Uri capturedUri = null; + mockWebsocketClient.ConnectAsync(Arg.Do(uri => capturedUri = uri)).Returns(Task.CompletedTask); + + client.Connect(); + + Assert.NotNull(capturedUri); + Assert.That(capturedUri.Query, Does.Contain("X-Stream-Client=")); + + var queryParam = capturedUri.Query.TrimStart('?') + .Split('&') + .First(p => p.StartsWith("X-Stream-Client=")); + var actualHeader = Uri.UnescapeDataString(queryParam.Substring("X-Stream-Client=".Length)); + + Assert.AreEqual(expectedHeader, actualHeader); + } + + private static void ConfigureApplicationInfo(IApplicationInfo applicationInfo) + { + applicationInfo.OperatingSystem.Returns("Windows 10"); + applicationInfo.Platform.Returns("StandaloneWindows64"); + applicationInfo.Engine.Returns("Unity"); + applicationInfo.EngineVersion.Returns("2022.3.0f1"); + applicationInfo.ScreenSize.Returns("1920x1080"); + applicationInfo.MemorySize.Returns(8192); + applicationInfo.GraphicsMemorySize.Returns(4096); + } + + private static string BuildExpectedStreamClientHeader(IApplicationInfo applicationInfo) + => $"stream-chat-unity-client-{StreamChatLowLevelClient.SDKVersion}|" + + $"os={applicationInfo.OperatingSystem}|" + + $"platform={applicationInfo.Platform}|" + + $"engine={applicationInfo.Engine}|" + + $"engine_version={applicationInfo.EngineVersion}|" + + $"screen_size={applicationInfo.ScreenSize}|" + + $"memory_size={applicationInfo.MemorySize}|" + + $"graphics_memory_size={applicationInfo.GraphicsMemorySize}"; + [Test] public void when_stream_client_created_expect_disconnected_state() {