Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/GraphQL.Client/GraphQLHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public GraphQLHttpClient(GraphQLHttpClientOptions options, IGraphQLWebsocketJson
if (!HttpClient.DefaultRequestHeaders.UserAgent.Any())
HttpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(GetType().Assembly.GetName().Name, GetType().Assembly.GetName().Version.ToString()));

_lazyHttpWebSocket = new Lazy<GraphQLHttpWebSocket>(() => new GraphQLHttpWebSocket(Options.EndPoint.GetWebSocketUri(), this));
_lazyHttpWebSocket = new Lazy<GraphQLHttpWebSocket>(() => new GraphQLHttpWebSocket(Options.WebSocketEndPoint ?? Options.EndPoint.GetWebSocketUri(), this));
}

#endregion
Expand Down
12 changes: 11 additions & 1 deletion src/GraphQL.Client/GraphQLHttpClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ namespace GraphQL.Client.Http
public class GraphQLHttpClientOptions
{
/// <summary>
/// The GraphQL EndPoint to be used
/// The GraphQL EndPoint to be used for HTTP connections
/// </summary>
public Uri EndPoint { get; set; }

/// <summary>
/// The GraphQL EndPoint to be used for WebSocket connection
/// </summary>
public Uri WebSocketEndPoint { get; set; }

/// <summary>
/// The <see cref="System.Net.Http.HttpMessageHandler"/> that is going to be used
/// </summary>
Expand Down Expand Up @@ -47,6 +52,11 @@ public class GraphQLHttpClientOptions
public Func<GraphQLRequest, GraphQLHttpClient, Task<GraphQLHttpRequest>> PreprocessRequest { get; set; } = (request, client) =>
Task.FromResult(request is GraphQLHttpRequest graphQLHttpRequest ? graphQLHttpRequest : new GraphQLHttpRequest(request));

/// <summary>
/// Subscription request preprocessing function. Can be used i.e. to inject authorization info into a GraphQL subscription request payload.
/// </summary>
public Func<GraphQLRequest, GraphQLHttpClient, Task<GraphQLHttpRequest>> PreprocessSubscriptionRequest { get; set; }

/// <summary>
/// This callback is called after successfully establishing a websocket connection but before any regular request is made.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQL.Client/GraphQLHttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public GraphQLHttpRequest(string query, object? variables = null, string? operat
{
}

public GraphQLHttpRequest(GraphQLRequest other): base(other.Query, other.Variables, other.OperationName)
public GraphQLHttpRequest(GraphQLRequest other): base(other)
{
}

Expand Down
28 changes: 17 additions & 11 deletions src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,11 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
Observable.Create<GraphQLResponse<TResponse>>(async observer =>
{
Debug.WriteLine($"Create observable thread id: {Thread.CurrentThread.ManagedThreadId}");
await _client.Options.PreprocessRequest(request, _client);
var startRequest = new GraphQLWebSocketRequest
{
Id = Guid.NewGuid().ToString("N"),
Type = GraphQLWebSocketMessageType.GQL_START,
Payload = request
Payload = await (_client.Options.PreprocessSubscriptionRequest?.Invoke(request, _client) ?? _client.Options.PreprocessRequest(request, _client))
};
var closeRequest = new GraphQLWebSocketRequest
{
Expand Down Expand Up @@ -136,6 +135,7 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
var typedResponse =
_client.JsonSerializer.DeserializeToWebsocketResponse<TResponse>(
response.MessageBytes);
Debug.WriteLine($"payload => {System.Text.Encoding.UTF8.GetString(response.MessageBytes)}");
o.OnNext(typedResponse.Payload);

// in case of a GraphQL error, terminate the sequence after the response has been posted
Expand Down Expand Up @@ -194,10 +194,12 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
}
catch (Exception e)
{
Console.WriteLine(e);
Debug.WriteLine(e);
throw;
}

// TODO: wait for "connection_ack" response
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, the client should be waiting for the connection_ack response before proceeding. For now, I put a TODO comment here as I don't have the experience with RX to know how to properly codify this step.

Copy link
Collaborator

@rose-a rose-a Oct 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Apollo GraphQL over WebSocket Protocol description they write that the server may respond with GQL_CONNECTION_ACK... so it might be better to not wait for it?

But it also might resolve the unpredictable tests problem...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Urgh... may is the worst.


Debug.WriteLine($"sending initial message on subscription {startRequest.Id}");
// send subscription request
try
Expand All @@ -206,7 +208,7 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
}
catch (Exception e)
{
Console.WriteLine(e);
Debug.WriteLine(e);
throw;
}

Expand Down Expand Up @@ -255,14 +257,18 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
// unwrap and push results or throw wrapped exceptions
.SelectMany(t =>
{
Debug.WriteLine($"unwrap exception thread id: {Thread.CurrentThread.ManagedThreadId}");
// if the result contains an exception, throw it on the observable
if (t.Item2 != null)
{
Debug.WriteLine($"unwrap exception thread id: {Thread.CurrentThread.ManagedThreadId} => {t.Item2}");
return Observable.Throw<GraphQLResponse<TResponse>>(t.Item2);

return t.Item1 == null
? Observable.Empty<GraphQLResponse<TResponse>>()
: Observable.Return(t.Item1);
}
if (t.Item1 == null)
{
Debug.WriteLine($"empty item thread id: {Thread.CurrentThread.ManagedThreadId}");
return Observable.Empty<GraphQLResponse<TResponse>>();
}
return Observable.Return(t.Item1);
})
// transform to hot observable and auto-connect
.Publish().RefCount();
Expand Down Expand Up @@ -319,7 +325,7 @@ public Task<GraphQLResponse<TResponse>> SendRequest<TResponse>(GraphQLRequest re
}
catch (Exception e)
{
Console.WriteLine(e);
Debug.WriteLine(e);
throw;
}

Expand Down Expand Up @@ -408,7 +414,7 @@ public Task InitializeWebSocket()
#else
_clientWebSocket = new ClientWebSocket();
_clientWebSocket.Options.AddSubProtocol("graphql-ws");
if(!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Create("WEBASSEMBLY")))
if(!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Create("BROWSER")))
{
// the following properties are not supported in Blazor WebAssembly and throw a PlatformNotSupportedException error when accessed
_clientWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates;
Expand Down
10 changes: 9 additions & 1 deletion src/GraphQL.Primitives/GraphQLRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ public GraphQLRequest(string query, object? variables = null, string? operationN
OperationName = operationName;
}

public GraphQLRequest(IEnumerable<KeyValuePair<string, object>> values)
{
foreach(var kv in values)
{
Add(kv.Key, kv.Value);
}
}

/// <summary>
/// Returns a value that indicates whether this instance is equal to a specified object
/// </summary>
Expand Down Expand Up @@ -86,7 +94,7 @@ public override int GetHashCode()
{
unchecked
{
var hashCode = Query.GetHashCode();
var hashCode = Query?.GetHashCode() ?? 0;
hashCode = (hashCode * 397) ^ OperationName?.GetHashCode() ?? 0;
hashCode = (hashCode * 397) ^ Variables?.GetHashCode() ?? 0;
return hashCode;
Expand Down