From d9ca6349f14f6b1cba76ee462f116b4d7aef8101 Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:09:21 +1100 Subject: [PATCH 1/6] Add connection_init payload --- .../GraphSubscriptionExecute.cs | 2 +- .../WSClient.cs | 31 ++++++++++++------- src/Linq2GraphQL.Client/GraphClient.cs | 1 + src/Linq2GraphQL.Client/GraphClientOptions.cs | 2 ++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Linq2GraphQL.Client.Subscriptions/GraphSubscriptionExecute.cs b/src/Linq2GraphQL.Client.Subscriptions/GraphSubscriptionExecute.cs index 39d4e03e..8968bdf3 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/GraphSubscriptionExecute.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/GraphSubscriptionExecute.cs @@ -26,7 +26,7 @@ public async Task> StartAsync() return sseClient.Subscription.Select(e => ConvertResult(queryExecutor.ProcessResponse(e, QueryNode.Name, payload.Query))); } - var wsClient = new WSClient(client.SubscriptionUrl, client.SubscriptionProtocol, payload); + var wsClient = new WSClient(client, payload); await wsClient.Start(); return wsClient.Subscription.Select(e => ConvertResult(queryExecutor.ProcessResponse(e, QueryNode.Name, payload.Query))); } diff --git a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs index b9ef6361..c5e84f68 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs @@ -9,19 +9,17 @@ namespace Linq2GraphQL.Client.Subscriptions; public class WSClient : IAsyncDisposable { + private readonly GraphClient _graphClient; private readonly GraphQLRequest payload; - private readonly SubscriptionProtocol subscriptionProtocol; private readonly Subject subscriptionSubject = new(); - private readonly string url; private readonly WebsocketClient client; private readonly JsonSerializerOptions jsonOptions; - public WSClient(string url, SubscriptionProtocol subprotocol, GraphQLRequest payload) + public WSClient(GraphClient graphClient, GraphQLRequest payload) { - this.url = url; - subscriptionProtocol = subprotocol; + _graphClient = graphClient; this.payload = payload; jsonOptions = new JsonSerializerOptions { @@ -35,7 +33,7 @@ public WSClient(string url, SubscriptionProtocol subprotocol, GraphQLRequest pay return ws; }); - client = new WebsocketClient(new Uri(url), factory) + client = new WebsocketClient(new Uri(_graphClient.SubscriptionUrl), factory) { ReconnectTimeout = TimeSpan.FromSeconds(30) }; @@ -74,7 +72,18 @@ public async Task Start() }); await client.Start(); - SendRequest(new WebsocketRequest("connection_init")); + + if (_graphClient.WSConnectionInitPayload is not null) + { + var initPayload = _graphClient.WSConnectionInitPayload(_graphClient); + if (initPayload is not null) + { + SendRequest(new WebsocketRequest("connection_init") + { + Payload = await _graphClient.WSConnectionInitPayload(_graphClient) + }); + } + } var subscriptionRequest = new WebsocketRequest(GetSubscribeCommand()) { @@ -87,7 +96,7 @@ public async Task Start() private string GetSubprotocolString() { - switch (subscriptionProtocol) + switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: return "graphql-transport-ws"; @@ -96,13 +105,13 @@ private string GetSubprotocolString() return "graphql-ws"; default: - throw new Exception($"{subscriptionProtocol} is unknown"); + throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); } } private string GetSubscribeCommand() { - switch (subscriptionProtocol) + switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: return "subscribe"; @@ -111,7 +120,7 @@ private string GetSubscribeCommand() return "start"; default: - throw new Exception($"{subscriptionProtocol} is unknown"); + throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); } } diff --git a/src/Linq2GraphQL.Client/GraphClient.cs b/src/Linq2GraphQL.Client/GraphClient.cs index 46087a2b..58752eec 100644 --- a/src/Linq2GraphQL.Client/GraphClient.cs +++ b/src/Linq2GraphQL.Client/GraphClient.cs @@ -36,6 +36,7 @@ public GraphClient(HttpClient httpClient, IOptions options, public HttpClient HttpClient { get; } public JsonSerializerOptions SerializerOptions { get; } + public Func> WSConnectionInitPayload => options.Value.WSConnectionInitPayload; private string GetSubscriptionUrl() { var baseUrl = HttpClient?.BaseAddress.ToString(); diff --git a/src/Linq2GraphQL.Client/GraphClientOptions.cs b/src/Linq2GraphQL.Client/GraphClientOptions.cs index fc370db8..81e2621a 100644 --- a/src/Linq2GraphQL.Client/GraphClientOptions.cs +++ b/src/Linq2GraphQL.Client/GraphClientOptions.cs @@ -1,7 +1,9 @@ + namespace Linq2GraphQL.Client; public class GraphClientOptions { public bool UseSafeMode { get; set; } = false; public SubscriptionProtocol SubscriptionProtocol { get; set; } = default; + public Func> WSConnectionInitPayload { get; internal set; } = opts => null; } \ No newline at end of file From d6276e3c0bb468dbf1bcbd8acb6d7a47242dd84a Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:14:45 +1100 Subject: [PATCH 2/6] dont invoke twice --- src/Linq2GraphQL.Client.Subscriptions/WSClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs index c5e84f68..822db422 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs @@ -75,12 +75,12 @@ public async Task Start() if (_graphClient.WSConnectionInitPayload is not null) { - var initPayload = _graphClient.WSConnectionInitPayload(_graphClient); + var initPayload = await _graphClient.WSConnectionInitPayload(_graphClient); if (initPayload is not null) { SendRequest(new WebsocketRequest("connection_init") { - Payload = await _graphClient.WSConnectionInitPayload(_graphClient) + Payload = initPayload }); } } From d99265bb79dd052eaf00bd6e9695a476043a38f7 Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:17:36 +1100 Subject: [PATCH 3/6] conditionally add the payload --- src/Linq2GraphQL.Client.Subscriptions/WSClient.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs index 822db422..7825e733 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs @@ -73,17 +73,16 @@ public async Task Start() await client.Start(); + var initRequest = new WebsocketRequest("connection_init"); if (_graphClient.WSConnectionInitPayload is not null) { var initPayload = await _graphClient.WSConnectionInitPayload(_graphClient); if (initPayload is not null) { - SendRequest(new WebsocketRequest("connection_init") - { - Payload = initPayload - }); + initRequest.Payload = initPayload; } } + SendRequest(initRequest); var subscriptionRequest = new WebsocketRequest(GetSubscribeCommand()) { From 365f1e5728b507aefdb25673530043713452b0de Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:44:43 +1100 Subject: [PATCH 4/6] publicly set WSConnectionInitPayload --- src/Linq2GraphQL.Client.Subscriptions/WebsocketRequest.cs | 2 +- src/Linq2GraphQL.Client/GraphClientOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequest.cs b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequest.cs index 5079fca5..96f76c0d 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequest.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequest.cs @@ -13,7 +13,7 @@ public WebsocketRequest(string type) [JsonPropertyName("type")] public string Type { get; set; } - [JsonPropertyName("payload")] public GraphQLRequest Payload { get; set; } + [JsonPropertyName("payload")] public object Payload { get; set; } } //public class WebsocketRequestPayload diff --git a/src/Linq2GraphQL.Client/GraphClientOptions.cs b/src/Linq2GraphQL.Client/GraphClientOptions.cs index 81e2621a..3fec0ce2 100644 --- a/src/Linq2GraphQL.Client/GraphClientOptions.cs +++ b/src/Linq2GraphQL.Client/GraphClientOptions.cs @@ -5,5 +5,5 @@ public class GraphClientOptions { public bool UseSafeMode { get; set; } = false; public SubscriptionProtocol SubscriptionProtocol { get; set; } = default; - public Func> WSConnectionInitPayload { get; internal set; } = opts => null; + public Func> WSConnectionInitPayload { get; set; } = opts => null; } \ No newline at end of file From 0fe6e197c6bd1829b4e53d59e732e12253a0dd93 Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:53:52 +1100 Subject: [PATCH 5/6] reduce string allocs --- .../SubscribeCommands.cs | 8 ++++++++ .../SubscriptionProtocols.cs | 14 +++++++++++++ .../WSClient.cs | 20 ++++++++++--------- .../WebsocketRequestTypes.cs | 16 +++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs create mode 100644 src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs create mode 100644 src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs diff --git a/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs b/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs new file mode 100644 index 00000000..ab6cb3cd --- /dev/null +++ b/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs @@ -0,0 +1,8 @@ +namespace Linq2GraphQL.Client.Subscriptions +{ + internal class SubscribeCommands + { + internal const string Subscribe = "subscribe"; + internal const string Start = "start"; + } +} diff --git a/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs b/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs new file mode 100644 index 00000000..266d64ee --- /dev/null +++ b/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Linq2GraphQL.Client.Subscriptions +{ + internal static class SubscriptionProtocols + { + internal const string GraphQl_Transport_WS = "graphql-transport-ws"; + internal const string GraphQl_WS = "graphql-ws"; + } +} diff --git a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs index 7825e733..aa11e612 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs @@ -1,4 +1,5 @@ -using System.Net.WebSockets; +using System.Diagnostics; +using System.Net.WebSockets; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text.Json; @@ -64,7 +65,7 @@ public async Task Start() //Filter General response var tt = client.MessageReceived.Select(m => JsonSerializer.Deserialize(m.ToString())); - tt.Where(e => e.Type == "ping").Subscribe(msg => SendRequest(new WebsocketRequest("pong"))); + tt.Where(e => e.Type == WebsocketRequestTypes.PING).Subscribe(msg => SendRequest(new WebsocketRequest(WebsocketRequestTypes.PONG))); tt.Where(e => !string.IsNullOrEmpty(e?.Id)).Subscribe(r => { @@ -73,7 +74,7 @@ public async Task Start() await client.Start(); - var initRequest = new WebsocketRequest("connection_init"); + var initRequest = new WebsocketRequest(WebsocketRequestTypes.CONNECTION_INIT); if (_graphClient.WSConnectionInitPayload is not null) { var initPayload = await _graphClient.WSConnectionInitPayload(_graphClient); @@ -98,10 +99,10 @@ private string GetSubprotocolString() switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: - return "graphql-transport-ws"; + return SubscriptionProtocols.GraphQl_Transport_WS; case SubscriptionProtocol.ApolloWebSocket: - return "graphql-ws"; + return SubscriptionProtocols.GraphQl_WS; default: throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); @@ -113,19 +114,20 @@ private string GetSubscribeCommand() switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: - return "subscribe"; + return SubscribeCommands.Subscribe; case SubscriptionProtocol.ApolloWebSocket: - return "start"; + return SubscribeCommands.Start; default: throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); } } - private void LogMessage(string message) + private static void LogMessage(string message) { - Console.WriteLine($"{message} - {DateTime.Now.ToString("T")}"); + // Write logs to debug console + Debug.WriteLine($"{message} - {DateTime.Now.ToString("T")}"); } private void SendRequest(WebsocketRequest request) diff --git a/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs new file mode 100644 index 00000000..1c125164 --- /dev/null +++ b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Linq2GraphQL.Client.Subscriptions +{ + internal class WebsocketRequestTypes + { + internal const string PING = "ping"; + internal const string PONG = "pong"; + internal const string CONNECTION_INIT = "connection_init"; + + } +} From 8ece6151593cffcc643dba5d214cc20acb39f8ff Mon Sep 17 00:00:00 2001 From: private_wire Date: Tue, 11 Feb 2025 12:59:09 +1100 Subject: [PATCH 6/6] capitalize constants, remove unnessesary usings --- src/Linq2GraphQL.Client.Subscriptions/SSEClient.cs | 3 ++- .../SubscribeCommands.cs | 4 ++-- .../SubscriptionProtocols.cs | 12 +++--------- src/Linq2GraphQL.Client.Subscriptions/WSClient.cs | 8 ++++---- .../WebsocketRequestTypes.cs | 8 +------- 5 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/Linq2GraphQL.Client.Subscriptions/SSEClient.cs b/src/Linq2GraphQL.Client.Subscriptions/SSEClient.cs index 8e7e7903..79f971a5 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/SSEClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/SSEClient.cs @@ -1,4 +1,5 @@ using System.Net.Http.Headers; +using System.Net.Mime; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text; @@ -34,7 +35,7 @@ public async Task Start() var request = new HttpRequestMessage(HttpMethod.Post, "") { - Content = new StringContent(json, Encoding.UTF8, "application/json") + Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json) }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream")); diff --git a/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs b/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs index ab6cb3cd..10ac37b1 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/SubscribeCommands.cs @@ -2,7 +2,7 @@ { internal class SubscribeCommands { - internal const string Subscribe = "subscribe"; - internal const string Start = "start"; + internal const string SUBSCRIBE = "subscribe"; + internal const string START = "start"; } } diff --git a/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs b/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs index 266d64ee..c3707cb6 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/SubscriptionProtocols.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Linq2GraphQL.Client.Subscriptions +namespace Linq2GraphQL.Client.Subscriptions { internal static class SubscriptionProtocols { - internal const string GraphQl_Transport_WS = "graphql-transport-ws"; - internal const string GraphQl_WS = "graphql-ws"; + internal const string GRAPGQL_TRANSPORT_WS = "graphql-transport-ws"; + internal const string GRAPHQL_WS = "graphql-ws"; } } diff --git a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs index aa11e612..af0fdb1a 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WSClient.cs @@ -99,10 +99,10 @@ private string GetSubprotocolString() switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: - return SubscriptionProtocols.GraphQl_Transport_WS; + return SubscriptionProtocols.GRAPGQL_TRANSPORT_WS; case SubscriptionProtocol.ApolloWebSocket: - return SubscriptionProtocols.GraphQl_WS; + return SubscriptionProtocols.GRAPHQL_WS; default: throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); @@ -114,10 +114,10 @@ private string GetSubscribeCommand() switch (_graphClient.SubscriptionProtocol) { case SubscriptionProtocol.GraphQLWebSocket: - return SubscribeCommands.Subscribe; + return SubscribeCommands.SUBSCRIBE; case SubscriptionProtocol.ApolloWebSocket: - return SubscribeCommands.Start; + return SubscribeCommands.START; default: throw new Exception($"{_graphClient.SubscriptionProtocol} is unknown"); diff --git a/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs index 1c125164..5bb6bc1a 100644 --- a/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs +++ b/src/Linq2GraphQL.Client.Subscriptions/WebsocketRequestTypes.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Linq2GraphQL.Client.Subscriptions +namespace Linq2GraphQL.Client.Subscriptions { internal class WebsocketRequestTypes {