Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ public static IRequestExecutorBuilder AddGraphQLServer(
(sp, _) =>
{
var environment = sp.GetService<IHostEnvironment>();
return environment?.IsDevelopment() == false;
return environment?.IsDevelopment() != true;
});
builder.AddMaxAllowedFieldCycleDepthRule(
isEnabled: (sp, _) =>
{
var environment = sp.GetService<IHostEnvironment>();
return environment?.IsDevelopment() == false;
return environment?.IsDevelopment() != true;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ private static IServiceProvider CreateServices(
Action<AuthorizationOptions>? configure = null)
=> new ServiceCollection()
.AddGraphQLServer()
.DisableIntrospection(disable: false)
.AddQueryType<Query>()
.AddUnionType<ICityOrStreet>()
.AddType<Street>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ private static IServiceProvider CreateServices(
Action<AuthorizationOptions>? configure = null)
=> new ServiceCollection()
.AddGraphQLServer()
.DisableIntrospection(disable: false)
.AddQueryType<QueryType>()
.AddGlobalObjectIdentification(o => o.EnsureAllNodesCanBeResolved = false)
.AddAuthorizationHandler(_ => handler)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public async Task ExecuteRequestAsync_OptInFeatureStability_MatchesSnapshot()
{
(await new ServiceCollection()
.AddGraphQLServer()
.DisableIntrospection(disable: false)
.ModifyOptions(o => o.EnableOptInFeatures = true)
.AddQueryType(d => d.Name("Query").Field("foo").Resolve("bar"))
.OptInFeatureStability("feature1", "stability1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,9 @@ public async Task SubscribeToReview()
}
""");

var results = subscriptionResult.ReadResultsAsync();
// Get the enumerator before publishing so the consumer is registered
// and won't race with event dispatch.
await using var enumerator = subscriptionResult.ReadResultsAsync().GetAsyncEnumerator();

await executor.ExecuteAsync(
"""
Expand All @@ -834,17 +836,8 @@ await executor.ExecuteAsync(
}
""");

OperationResult? eventResult = null;

using (var cts = new CancellationTokenSource(2000))
{
await foreach (var queryResult in results.WithCancellation(cts.Token)
.ConfigureAwait(false))
{
eventResult = queryResult;
break;
}
}
Assert.True(await enumerator.MoveNextAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(30)));
var eventResult = enumerator.Current;

snapshot.Add(eventResult);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,24 +152,35 @@ query FailureCoalesce {
public async Task Follower_Cancellation_Should_Not_Cancel_Leader_Compilation()
{
// arrange
using var leaderCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
using var followerCts = new CancellationTokenSource(TimeSpan.FromMilliseconds(200));
using var testCts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using var followerCts = new CancellationTokenSource();
var compileCount = 0;
var compileGate = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var leaderEntered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

var executor = await new ServiceCollection()
.AddGraphQL()
.AddQueryType(d => d.Field("foo").Resolve("bar"))
.UseDefaultPipeline()
.AddDiagnosticEventListener(_ => new CompileCountListener(() => Interlocked.Increment(ref compileCount)))
.UseRequest(
(_, next) => CreateBlockingMiddleware(next, compileGate),
(_, next) => async context =>
{
// Only block the leader.
if (context.Features.Get<TaskCompletionSource<Operation>>() is not null)
{
leaderEntered.TrySetResult();
await compileGate.Task;
}

await next(context);
},
key: "Blocking",
before: WellKnownRequestMiddleware.OperationResolverMiddleware,
allowMultiple: true)
.Services
.BuildServiceProvider()
.GetRequestExecutorAsync(cancellationToken: leaderCts.Token);
.GetRequestExecutorAsync(cancellationToken: testCts.Token);

const string operationText =
"""
Expand All @@ -180,27 +191,27 @@ query CancelFollowerOnly {

// act
var leaderTask = Task.Run(
() => executor.ExecuteAsync(operationText, leaderCts.Token),
() => executor.ExecuteAsync(operationText, testCts.Token),
CancellationToken.None);

// Give the leader time to enter the pipeline and register in-flight.
await Task.Delay(50, leaderCts.Token);
// Wait for the leader to enter the pipeline and register in-flight.
await leaderEntered.Task.WaitAsync(testCts.Token);

var followerTask = Task.Run(
() => executor.ExecuteAsync(operationText, followerCts.Token),
CancellationToken.None);

// Wait for the follower to cancel.
var followerCompletion = await Task.WhenAny(
followerTask,
Task.Delay(TimeSpan.FromSeconds(3), leaderCts.Token));
// Give the follower a brief moment to register as a waiter behind the leader,
// then explicitly cancel it.
await Task.Delay(50, testCts.Token);
followerCts.Cancel();

// Wait for the follower to observe cancellation (hang-guard only; normal completion is fast).
var followerResult = await followerTask.WaitAsync(testCts.Token);

// Release the leader.
compileGate.TrySetResult();
Assert.Same(followerTask, followerCompletion);

var followerResult = await followerTask;
var leaderResult = await leaderTask;
var leaderResult = await leaderTask.WaitAsync(testCts.Token);

// assert
var followerErrors = Assert.IsType<OperationResult>(followerResult).Errors;
Expand Down Expand Up @@ -245,20 +256,6 @@ private static RequestDelegate CreateThrowingMiddleware(
await next(context);
};

private static RequestDelegate CreateBlockingMiddleware(
RequestDelegate next,
TaskCompletionSource gate)
=> async context =>
{
// Only block the leader.
if (context.Features.Get<TaskCompletionSource<Operation>>() is not null)
{
await gate.Task;
}

await next(context);
};

private sealed class RequestGate(int expectedRequests)
{
private readonly TaskCompletionSource _allArrived =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ public static class FusionServerAspNetCoreHostingBuilderExtensions
/// <param name="maxAllowedRequestSize">
/// The max allowed GraphQL request size.
/// </param>
/// <param name="disableDefaultSecurity">
/// Defines if the default security policy should be disabled.
/// </param>
/// <returns>
/// The <see cref="IFusionGatewayBuilder"/> for configuration chaining.
/// </returns>
public static IFusionGatewayBuilder AddGraphQLGateway(
this IHostApplicationBuilder builder,
string? name = null,
int maxAllowedRequestSize = ServerDefaults.MaxAllowedRequestSize)
=> builder.Services.AddGraphQLGatewayServer(name, maxAllowedRequestSize);
int maxAllowedRequestSize = ServerDefaults.MaxAllowedRequestSize,
bool disableDefaultSecurity = false)
=> builder.Services.AddGraphQLGatewayServer(name, maxAllowedRequestSize, disableDefaultSecurity);
Comment thread
michaelstaib marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using HotChocolate.Fusion.Configuration;
using HotChocolate.Language;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;

namespace Microsoft.Extensions.DependencyInjection;

Expand All @@ -18,17 +19,36 @@ public static class FusionServerServiceCollectionExtensions
public static IFusionGatewayBuilder AddGraphQLGatewayServer(
this IServiceCollection services,
string? name = null,
int maxAllowedRequestSize = ServerDefaults.MaxAllowedRequestSize)
int maxAllowedRequestSize = ServerDefaults.MaxAllowedRequestSize,
bool disableDefaultSecurity = false)
Comment thread
michaelstaib marked this conversation as resolved.
{
ArgumentNullException.ThrowIfNull(services);
ArgumentOutOfRangeException.ThrowIfNegative(maxAllowedRequestSize);

return services
var builder = services
.AddGraphQLGateway(name)
.AddGraphQLGatewayServerCore(maxAllowedRequestSize)
.AddStartupInitialization()
.AddDefaultHttpRequestInterceptor()
.AddSubscriptionServices();

if (!disableDefaultSecurity)
{
builder.DisableIntrospection(
(sp, _) =>
{
var environment = sp.GetService<IHostEnvironment>();
return environment?.IsDevelopment() != true;
});
builder.AddMaxAllowedFieldCycleDepthRule(
isEnabled: (sp, _) =>
{
var environment = sp.GetService<IHostEnvironment>();
return environment?.IsDevelopment() != true;
});
}

return builder;
}

private static IFusionGatewayBuilder AddGraphQLGatewayServerCore(
Expand Down
Loading
Loading