public async Task InvokeAsync(IQueryContext context)
        {
            try
            {
                await _next(context).ConfigureAwait(false);
            }
            catch (GraphQLException ex)
            {
                context.Exception = ex;
                context.Result    = QueryResult.CreateError(
                    _errorHandler.Handle(ex.Errors));
            }
            catch (SyntaxException ex)
            {
                IError error = _errorHandler.CreateUnexpectedError(ex)
                               .SetMessage(ex.Message)
                               .AddLocation(ex.Line, ex.Column)
                               .Build();

                error = _errorHandler.Handle(error);

                context.Exception = ex;
                context.Result    = QueryResult.CreateError(error);
            }
            catch (Exception ex)
            {
                IError error = _errorHandler.CreateUnexpectedError(ex)
                               .Build();

                error = _errorHandler.Handle(error);

                context.Exception = ex;
                context.Result    = QueryResult.CreateError(error);
            }
        }
        public async Task <IReadOnlyQueryResult> ReadAsync(
            CancellationToken cancellationToken)
        {
            try
            {
                IReadOnlyQueryRequest request = _batch[_index++];

                DocumentNode document = request.Query is QueryDocument d
                    ? d.Document
                    : Utf8GraphQLParser.Parse(request.Query.ToSpan());

                OperationDefinitionNode operation =
                    document.GetOperation(request.OperationName);

                if (document != _previous)
                {
                    _fragments = document.GetFragments();
                    _visitationMap.Initialize(_fragments);
                }

                operation.Accept(
                    _visitor,
                    _visitationMap,
                    n => VisitorAction.Continue);

                _previous = document;
                document  = RewriteDocument(operation);
                operation = (OperationDefinitionNode)document.Definitions[0];
                IReadOnlyDictionary <string, object> variableValues =
                    MergeVariables(request.VariableValues, operation);

                request = QueryRequestBuilder.From(request)
                          .SetQuery(document)
                          .SetVariableValues(variableValues)
                          .AddExportedVariables(_exportedVariables)
                          .SetQueryName(null) // TODO ... should we create a name here?
                          .SetQueryHash(null)
                          .Create();

                var result =
                    (IReadOnlyQueryResult)await _executor.ExecuteAsync(
                        request, cancellationToken)
                    .ConfigureAwait(false);

                IsCompleted = _index >= _batch.Count;
                return(result);
            }
            catch (QueryException ex)
            {
                IsCompleted = true;
                return(QueryResult.CreateError(ex.Errors));
            }
            catch (Exception ex)
            {
                IsCompleted = true;
                return(QueryResult.CreateError(
                           _errorHandler.Handle(
                               _errorHandler.CreateUnexpectedError(ex).Build())));
            }
        }
Example #3
0
        public async Task InvokeAsync(IQueryContext context)
        {
            try
            {
                IHttpClientFactory httpClientFactory =
                    context.Services.GetRequiredService <IHttpClientFactory>();

                context.Result = await _client.FetchAsync(
                    context.Request,
                    httpClientFactory.CreateClient(_schemaName),
                    context.Services.GetServices <IHttpQueryRequestInterceptor>(),
                    context.RequestAborted)
                                 .ConfigureAwait(false);
            }
            catch (HttpRequestException ex)
            {
                IError error = _errorHandler.CreateUnexpectedError(ex)
                               .SetCode(ErrorCodes.HttpRequestException)
                               .Build();

                context.Exception = ex;
                context.Result    = QueryResultBuilder.CreateError(error);
            }

            await _next.Invoke(context).ConfigureAwait(false);
        }
    private async Task HandleRequestAsync(HttpContext context)
    {
        // first we need to get the request executor to be able to execute requests.
        IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted);

        IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor();
        IErrorHandler           errorHandler       = requestExecutor.GetErrorHandler();

        context.Items[WellKnownContextData.RequestExecutor] = requestExecutor;

        HttpStatusCode?  statusCode = null;
        IExecutionResult?result;

        // next we parse the GraphQL request.
        GraphQLRequest request;

        using (_diagnosticEvents.ParseHttpRequest(context))
        {
            try
            {
                request = _requestParser.ReadParamsRequest(context.Request.Query);
            }
            catch (GraphQLRequestException ex)
            {
                // A GraphQL request exception is thrown if the HTTP request body couldn't be
                // parsed. In this case we will return HTTP status code 400 and return a
                // GraphQL error result.
                statusCode = HttpStatusCode.BadRequest;
                IReadOnlyList <IError> errors = errorHandler.Handle(ex.Errors);
                result = QueryResultBuilder.CreateError(errors);
                _diagnosticEvents.ParserErrors(context, errors);
                goto HANDLE_RESULT;
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
                IError error = errorHandler.CreateUnexpectedError(ex).Build();
                result = QueryResultBuilder.CreateError(error);
                _diagnosticEvents.HttpRequestError(context, error);
                goto HANDLE_RESULT;
            }
        }

        // after successfully parsing the request we now will attempt to execute the request.
        try
        {
            GraphQLServerOptions?options = context.GetGraphQLServerOptions();
            result = await ExecuteSingleAsync(
                context,
                requestExecutor,
                requestInterceptor,
                _diagnosticEvents,
                request,
                options is null or { AllowedGetOperations : AllowedGetOperations.Query }
                ?_onlyQueries
                : null);
        }
Example #5
0
            private async Task <IQueryResult> ExecuteNextAsync(
                IReadOnlyQueryRequest request,
                CancellationToken cancellationToken)
            {
                try
                {
                    DocumentNode document = request.Query is QueryDocument d
                        ? d.Document
                        : Utf8GraphQLParser.Parse(request.Query !.AsSpan());

                    OperationDefinitionNode operation =
                        document.GetOperation(request.OperationName);

                    if (document != _previous)
                    {
                        _fragments = document.GetFragments();
                        _visitationMap.Initialize(_fragments);
                    }

                    operation.Accept(
                        _visitor,
                        _visitationMap,
                        n => VisitorAction.Continue);

                    _previous = document;
                    document  = RewriteDocument(operation);
                    operation = (OperationDefinitionNode)document.Definitions[0];
                    IReadOnlyDictionary <string, object?>?variableValues =
                        MergeVariables(request.VariableValues, operation);

                    request = QueryRequestBuilder.From(request)
                              .SetQuery(document)
                              .SetVariableValues(variableValues)
                              .AddExportedVariables(_exportedVariables)
                              .SetQueryId(null) // TODO ... should we create a name here?
                              .SetQueryHash(null)
                              .Create();

                    return((IReadOnlyQueryResult)await _requestExecutor.ExecuteAsync(
                               request, cancellationToken)
                           .ConfigureAwait(false));
                }
                catch (GraphQLException ex)
                {
                    return(QueryResultBuilder.CreateError(ex.Errors));
                }
                catch (Exception ex)
                {
                    return(QueryResultBuilder.CreateError(
                               _errorHandler.Handle(
                                   _errorHandler.CreateUnexpectedError(ex).Build())));
                }
            }
Example #6
0
 public static IError UnexpectedLeafValueSerializationError(
     Exception exception,
     IErrorHandler errorHandler,
     FieldNode field,
     Path path)
 {
     return(errorHandler
            .CreateUnexpectedError(exception)
            .AddLocation(field)
            .SetPath(path)
            .SetCode(ErrorCodes.Execution.CannotSerializeLeafValue)
            .Build());
 }
        /// <summary>
        /// Execute the current HttpContext provided using HotChocolate GraphQL and the currently configured pipeline.
        /// ALL parsing, and processing of the Query of the request will be handled by existing HotChoclate HttpPost & HttpGet
        /// middleware, this class will only proxy the context into HC for execution, and handle unexpected errors.
        /// NOTE: An EmptyResult() response is returned because that is what must be returned by the AzureFunction; because HotChocolate
        ///     has already processed the request and started the response (e.g. StatusCode will be set by HC) an EmptyResult() response
        ///     will prevent the Azure Functions framework from attempting to set it again resulting in a a Host error/crash.
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="logger"></param>
        /// <param name="cancellationToken"></param>
        /// <returns>
        /// Returns an EmptyResult() response because HotChocolate has already processed the request and started the response
        /// (e.g. StatusCode will be set by HC) and an EmptyResult() response will prevent the Azure Functions framework from
        /// attempting to set it again resulting in a a Host error/crash.
        /// </returns>
        public async Task <IActionResult> ExecuteFunctionsQueryAsync(HttpContext httpContext, ILogger logger, CancellationToken cancellationToken)
        {
            try
            {
                //Use the Middleware Proxy to Invoke the pre-configured pipeline for Http POST & GET processing...
                var httpMiddlewareProxy = this.AzureFunctionsMiddlewareProxy;
                await httpMiddlewareProxy.InvokeAsync(httpContext).ConfigureAwait(false);
            }
            //NOTE: We Implement error handling that matches the Existing HttpPostMiddleware, to ensure that all code
            //  has top level error handling for Graph processing.
            catch (GraphQLRequestException ex)
            {
                //If Debugging is enabled then Log the Errors to AzureFunctions framework (e.g. Application Insights)
                logger.LogDebug(ex, $"{nameof(GraphQLRequestException)} occurred while processing the GraphQL request; {ex.Message}.");

                // A GraphQL request exception is thrown if the HTTP request body couldn't be parsed.
                // In this case we will return HTTP status code 400 and return a GraphQL error result.
                IErrorHandler errorHandler = await this.AzureFunctionsMiddlewareProxy.GetErrorHandlerAsync(cancellationToken).ConfigureAwait(false);

                IQueryResult errorResult = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));

                await HandleGraphQLErrorResponseAsync(httpContext, HttpStatusCode.BadRequest, errorResult);
            }
            catch (Exception exc)
            {
                //Log all Unknown Exceptions as GraphQLExceptions to Azure Framework (e.g. Application Insights).
                logger.LogError(exc, "An unhandled exception occurred while processing the GraphQL request.");

                // An unknown and unexpected GraphQL request exception was encountered.
                // In this case we will return HTTP status code 500 and return a GraphQL error result.
                IErrorHandler errorHandler = await this.AzureFunctionsMiddlewareProxy.GetErrorHandlerAsync(cancellationToken).ConfigureAwait(false);

                IError       error       = errorHandler.CreateUnexpectedError(exc).Build();
                IQueryResult errorResult = QueryResultBuilder.CreateError(error);

                HttpStatusCode statusCode = exc is HttpRequestException
                    ? HttpStatusCode.BadRequest
                    : HttpStatusCode.InternalServerError;

                await HandleGraphQLErrorResponseAsync(httpContext, statusCode, errorResult).ConfigureAwait(false);
            }

            //Safely resolve the .Net Core request with Empty Result because the Response has already been handled!
            //NOTE: We Must return EmptyResult() so that No Action is taken on the Response or else an error will occur
            //      since HotChocolate has ALREADY started (e.g. processed) the response, and the Status Code is already set!
            //For More Info See: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.emptyresult?view=aspnetcore-5.0
            return(new EmptyResult());
        }
Example #8
0
 public async ValueTask InvokeAsync(IRequestContext context)
 {
     try
     {
         await _next(context).ConfigureAwait(false);
     }
     catch (GraphQLException ex)
     {
         context.Exception = ex;
         context.Result    = QueryResultBuilder.CreateError(_errorHandler.Handle(ex.Errors));
     }
     catch (Exception ex)
     {
         context.Exception = ex;
         IError error = _errorHandler.CreateUnexpectedError(ex).Build();
         context.Result = QueryResultBuilder.CreateError(_errorHandler.Handle(error));
     }
 }
Example #9
0
 private static async Task ExecuteMiddlewareAsync(
     ResolverContext resolverContext,
     IErrorHandler errorHandler)
 {
     try
     {
         await resolverContext.Middleware.Invoke(resolverContext)
         .ConfigureAwait(false);
     }
     catch (QueryException ex)
     {
         resolverContext.Result = ex.Errors;
     }
     catch (Exception ex)
     {
         resolverContext.Result =
             errorHandler.CreateUnexpectedError(ex)
             .SetPath(resolverContext.Path)
             .AddLocation(resolverContext.FieldSelection)
             .Build();
     }
 }
        private static async Task ExecuteMiddlewareAsync(
            ResolverContext resolverContext,
            IErrorHandler errorHandler)
        {
            try
            {
                await resolverContext.Middleware.Invoke(resolverContext)
                .ConfigureAwait(false);

                // TODO : this should be handled more elegant
                if (resolverContext.Result is IQueryable q)
                {
                    resolverContext.Result =
                        await Task.Run(() =>
                    {
                        var items = new List <object>();
                        foreach (object o in q)
                        {
                            items.Add(o);
                        }
                        return(items);
                    })
                        .ConfigureAwait(false);
                }
            }
            catch (GraphQLException ex)
            {
                resolverContext.Result = ex.Errors;
            }
            catch (Exception ex)
            {
                resolverContext.Result =
                    errorHandler.CreateUnexpectedError(ex)
                    .SetPath(resolverContext.Path)
                    .AddLocation(resolverContext.FieldSelection)
                    .Build();
            }
        }
        private async Task HandleRequestAsync(HttpContext context)
        {
            // first we need to get the request executor to be able to execute requests.
            IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted);

            IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor();
            IErrorHandler           errorHandler       = requestExecutor.GetErrorHandler();

            HttpStatusCode?  statusCode = null;
            IExecutionResult?result;

            try
            {
                // next we parse the GraphQL request.
                GraphQLRequest request = _requestParser.ReadParamsRequest(context.Request.Query);
                result = await ExecuteSingleAsync(
                    context, requestExecutor, requestInterceptor, request);
            }
            catch (GraphQLRequestException ex)
            {
                // A GraphQL request exception is thrown if the HTTP request body couldn't be
                // parsed. In this case we will return HTTP status code 400 and return a
                // GraphQL error result.
                statusCode = HttpStatusCode.BadRequest;
                result     = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
                IError error = errorHandler.CreateUnexpectedError(ex).Build();
                result = QueryResultBuilder.CreateError(error);
            }

            // in any case we will have a valid GraphQL result at this point that can be written
            // to the HTTP response stream.
            Debug.Assert(result is not null, "No GraphQL result was created.");
            await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted);
        }
Example #12
0
        private async Task HandleRequestAsync(
            HttpContext context,
            AllowedContentType contentType)
        {
            // first we need to get the request executor to be able to execute requests.
            IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted);

            IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor();
            IErrorHandler           errorHandler       = requestExecutor.GetErrorHandler();

            HttpStatusCode?  statusCode = null;
            IExecutionResult?result;

            try
            {
                // next we parse the GraphQL request.
                IReadOnlyList <GraphQLRequest> requests =
                    await _requestParser.ReadJsonRequestAsync(
                        context.Request.Body, context.RequestAborted);

                switch (requests.Count)
                {
                // if the HTTP request body contains no GraphQL request structure the
                // whole request is invalid and we will create a GraphQL error response.
                case 0:
                {
                    statusCode = HttpStatusCode.BadRequest;
                    IError error = errorHandler.Handle(ErrorHelper.RequestHasNoElements());
                    result = QueryResultBuilder.CreateError(error);
                    break;
                }

                // if the HTTP request body contains a single GraphQL request and we do have
                // the batch operations query parameter specified we need to execute an
                // operation batch.
                //
                // An operation batch consists of a single GraphQL request document that
                // contains multiple operations. The batch operation query parameter
                // defines the order in which the operations shall be executed.
                case 1 when context.Request.Query.ContainsKey(_batchOperations):
                {
                    string operationNames = context.Request.Query[_batchOperations];

                    if (TryParseOperations(operationNames, out IReadOnlyList <string>?ops))
                    {
                        result = await ExecuteOperationBatchAsync(
                            context, requestExecutor, requestInterceptor, requests[0], ops);
                    }
                    else
                    {
                        IError error = errorHandler.Handle(ErrorHelper.InvalidRequest());
                        statusCode = HttpStatusCode.BadRequest;
                        result     = QueryResultBuilder.CreateError(error);
                    }
                    break;
                }

                // if the HTTP request body contains a single GraphQL request and
                // no batch query parameter is specified we need to execute a single
                // GraphQL request.
                //
                // Most GraphQL requests will be of this type where we want to execute
                // a single GraphQL query or mutation.
                case 1:
                {
                    result = await ExecuteSingleAsync(
                        context, requestExecutor, requestInterceptor, requests[0]);

                    break;
                }

                // if the HTTP request body contains more than one GraphQL request than
                // we need to execute a request batch where we need to execute multiple
                // fully specified GraphQL requests at once.
                default:
                    result = await ExecuteBatchAsync(
                        context, requestExecutor, requestInterceptor, requests);

                    break;
                }
            }
            catch (GraphQLRequestException ex)
            {
                // A GraphQL request exception is thrown if the HTTP request body couldn't be
                // parsed. In this case we will return HTTP status code 400 and return a
                // GraphQL error result.
                statusCode = HttpStatusCode.BadRequest;
                result     = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
                IError error = errorHandler.CreateUnexpectedError(ex).Build();
                result = QueryResultBuilder.CreateError(error);
            }

            // in any case we will have a valid GraphQL result at this point that can be written
            // to the HTTP response stream.
            Debug.Assert(result is not null, "No GraphQL result was created.");
            await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted);
        }
Example #13
0
    protected override async Task HandleAsync(
        ISocketConnection connection,
        DataStartMessage message,
        CancellationToken cancellationToken)
    {
        var session  = new CancellationTokenSource();
        var combined = CancellationTokenSource.CreateLinkedTokenSource(
            session.Token, cancellationToken);
        var sessionIsHandled = false;

        IExecutionResult result = await ExecuteAsync(combined.Token);

        try
        {
            switch (result)
            {
            case SubscriptionResult subscriptionResult:
                // first we add the cts to the result so that they are disposed when the
                // subscription is disposed.
                subscriptionResult.RegisterDisposable(combined);

                // while a subscription result must be disposed we are not handling it here
                // and leave this responsibility to the subscription session.
                ISubscription subscription = GetSubscription(result);

                var subscriptionSession = new SubscriptionSession(
                    session,
                    _socketSessionInterceptor,
                    connection,
                    subscriptionResult,
                    subscription,
                    _diagnosticEvents,
                    message.Id);

                connection.Subscriptions.Register(subscriptionSession);
                sessionIsHandled = true;
                break;

            case IResponseStream streamResult:
                // stream results represent deferred execution streams that use execution
                // resources. We need to ensure that these are disposed when we are
                // finished.
                await using (streamResult)
                {
                    await HandleStreamResultAsync(
                        connection,
                        message,
                        streamResult,
                        cancellationToken);
                }

                break;

            case IQueryResult queryResult:
                // query results use pooled memory an need to be disposed after we have
                // used them.
                using (queryResult)
                {
                    await HandleQueryResultAsync(
                        connection,
                        message,
                        queryResult,
                        cancellationToken);
                }

                break;

            default:
                throw DataStartMessageHandler_RequestTypeNotSupported();
            }
        }
        finally
        {
            if (!sessionIsHandled)
            {
                session.Dispose();
                combined.Dispose();
            }
        }

        async ValueTask <IExecutionResult> ExecuteAsync(CancellationToken cancellationToken)
        {
            try
            {
                IQueryRequestBuilder requestBuilder =
                    QueryRequestBuilder.From(message.Payload)
                    .SetServices(connection.RequestServices);

                await _socketSessionInterceptor.OnRequestAsync(
                    connection, requestBuilder, cancellationToken);

                return(await _requestExecutor.ExecuteAsync(
                           requestBuilder.Create(), cancellationToken));
            }
            catch (Exception ex)
            {
                IErrorBuilder error = _errorHandler.CreateUnexpectedError(ex);
                return(QueryResultBuilder.CreateError(error.Build()));
            }
        }
    }
Example #14
0
        public async Task HandleRequestAsync(
            HttpContext context,
            AllowedContentType contentType,
            CancellationToken cancellationToken)
        {
            // first we need to gather an executor to start execution the request.
            IRequestExecutor executor = await GetExecutorAsync(context.RequestAborted)
                                        .ConfigureAwait(false);

            IErrorHandler errorHandler = executor.Services.GetRequiredService <IErrorHandler>();

            IExecutionResult?result = null;
            int?statusCode          = null;

            try
            {
                IReadOnlyList <GraphQLRequest>?requests = await ReadRequestAsync(
                    contentType, context.Request.Body, context.RequestAborted)
                                                          .ConfigureAwait(false);

                if (requests.Count == 0)
                {
                    // IError error = ErrorHandler.Handle(ErrorHelper.RequestHasNoElements());
                    // httpHelper.Result = CreateError(error);
                }
                else if (requests.Count == 1)
                {
                    string operations = context.Request.Query[_batchOperations];

                    if (operations is null)
                    {
                        result = await ExecuteQueryAsync(context, executor, requests[0])
                                 .ConfigureAwait(false);
                    }
                    else if (TryParseOperations(operations, out IReadOnlyList <string>?operationNames))
                    {
                        // await ExecuteOperationBatchAsync(
                        //     httpHelper, batch[0], operationNames)
                        //    .ConfigureAwait(false);
                    }
                    else
                    {
                        // IError error = ErrorHandler.Handle(ErrorHelper.InvalidRequest());
                        // httpHelper.StatusCode = BadRequest;
                        // httpHelper.Result = CreateError(error);
                    }
                }
                else
                {
                }
            }
            catch (GraphQLRequestException ex)
            {
                statusCode = 400;
                IEnumerable <IError> errors = errorHandler.Handle(ex.Errors);
                result = QueryResultBuilder.CreateError(errors);
            }
            catch (Exception ex)
            {
                statusCode = 500;
                IError error = errorHandler.CreateUnexpectedError(ex).Build();
                result = QueryResultBuilder.CreateError(error);
            }

            Debug.Assert(result is { });
    protected async Task HandleRequestAsync(
        HttpContext context,
        AllowedContentType contentType)
    {
        // first we need to get the request executor to be able to execute requests.
        IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted);

        IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor();
        IErrorHandler           errorHandler       = requestExecutor.GetErrorHandler();

        context.Items[WellKnownContextData.RequestExecutor] = requestExecutor;

        HttpStatusCode?  statusCode = null;
        IExecutionResult?result;

        // next we parse the GraphQL request.
        IReadOnlyList <GraphQLRequest> requests;

        using (DiagnosticEvents.ParseHttpRequest(context))
        {
            try
            {
                requests = await GetRequestsFromBody(context.Request, context.RequestAborted);
            }
            catch (GraphQLRequestException ex)
            {
                // A GraphQL request exception is thrown if the HTTP request body couldn't be
                // parsed. In this case we will return HTTP status code 400 and return a
                // GraphQL error result.
                statusCode = HttpStatusCode.BadRequest;
                IReadOnlyList <IError> errors = errorHandler.Handle(ex.Errors);
                result = QueryResultBuilder.CreateError(errors);
                DiagnosticEvents.ParserErrors(context, errors);
                goto HANDLE_RESULT;
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
                IError error = errorHandler.CreateUnexpectedError(ex).Build();
                result = QueryResultBuilder.CreateError(error);
                DiagnosticEvents.HttpRequestError(context, error);
                goto HANDLE_RESULT;
            }
        }

        // after successfully parsing the request we now will attempt to execute the request.
        try
        {
            switch (requests.Count)
            {
            // if the HTTP request body contains no GraphQL request structure the
            // whole request is invalid and we will create a GraphQL error response.
            case 0:
            {
                statusCode = HttpStatusCode.BadRequest;
                IError error = errorHandler.Handle(ErrorHelper.RequestHasNoElements());
                result = QueryResultBuilder.CreateError(error);
                DiagnosticEvents.HttpRequestError(context, error);
                break;
            }

            // if the HTTP request body contains a single GraphQL request and we do have
            // the batch operations query parameter specified we need to execute an
            // operation batch.
            //
            // An operation batch consists of a single GraphQL request document that
            // contains multiple operations. The batch operation query parameter
            // defines the order in which the operations shall be executed.
            case 1 when context.Request.Query.ContainsKey(_batchOperations):
            {
                string operationNames = context.Request.Query[_batchOperations];

                if (TryParseOperations(operationNames, out IReadOnlyList <string>?ops))
                {
                    result = await ExecuteOperationBatchAsync(
                        context,
                        requestExecutor,
                        requestInterceptor,
                        DiagnosticEvents,
                        requests[0],
                        ops);
                }
                else
                {
                    IError error = errorHandler.Handle(ErrorHelper.InvalidRequest());
                    statusCode = HttpStatusCode.BadRequest;
                    result     = QueryResultBuilder.CreateError(error);
                    DiagnosticEvents.HttpRequestError(context, error);
                }

                break;
            }

            // if the HTTP request body contains a single GraphQL request and
            // no batch query parameter is specified we need to execute a single
            // GraphQL request.
            //
            // Most GraphQL requests will be of this type where we want to execute
            // a single GraphQL query or mutation.
            case 1:
            {
                result = await ExecuteSingleAsync(
                    context,
                    requestExecutor,
                    requestInterceptor,
                    DiagnosticEvents,
                    requests[0]);

                break;
            }

            // if the HTTP request body contains more than one GraphQL request than
            // we need to execute a request batch where we need to execute multiple
            // fully specified GraphQL requests at once.
            default:
                result = await ExecuteBatchAsync(
                    context,
                    requestExecutor,
                    requestInterceptor,
                    DiagnosticEvents,
                    requests);

                break;
            }
        }
        catch (GraphQLException ex)
        {
            // This allows extensions to throw GraphQL exceptions in the GraphQL interceptor.
            statusCode = null; // we let the serializer determine the status code.
            result     = QueryResultBuilder.CreateError(ex.Errors);

            foreach (IError error in ex.Errors)
            {
                DiagnosticEvents.HttpRequestError(context, error);
            }
        }
        catch (Exception ex)
        {
            statusCode = HttpStatusCode.InternalServerError;
            IError error = errorHandler.CreateUnexpectedError(ex).Build();
            result = QueryResultBuilder.CreateError(error);
            DiagnosticEvents.HttpRequestError(context, error);
        }

HANDLE_RESULT:
        IDisposable? formatScope = null;

        try
        {
            // if cancellation is requested we will not try to attempt to write the result to the
            // response stream.
            if (context.RequestAborted.IsCancellationRequested)
            {
                return;
            }

            // in any case we will have a valid GraphQL result at this point that can be written
            // to the HTTP response stream.
            Debug.Assert(result is not null, "No GraphQL result was created.");

            if (result is IQueryResult queryResult)
            {
                formatScope = DiagnosticEvents.FormatHttpResponse(context, queryResult);
            }

            await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted);
        }
        finally
        {
            // we must dispose the diagnostic scope first.
            formatScope?.Dispose();

            // query results use pooled memory an need to be disposed after we have
            // used them.
            if (result is IAsyncDisposable asyncDisposable)
            {
                await asyncDisposable.DisposeAsync();
            }

            if (result is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }
    }