Example #1
0
        private async Task RunCall(HttpRequestMessage request, TimeSpan?timeout)
        {
            using (StartScope())
            {
                var(diagnosticSourceEnabled, activity) = InitializeCall(request, timeout);

                if (Options.Credentials != null || Channel.CallCredentials?.Count > 0)
                {
                    await ReadCredentials(request).ConfigureAwait(false);
                }

                // Unset variable to check that FinishCall is called in every code path
                bool finished;

                Status?status = null;

                try
                {
                    // Fail early if deadline has already been exceeded
                    _callCts.Token.ThrowIfCancellationRequested();

                    try
                    {
                        // If a HttpClient has been specified then we need to call it with ResponseHeadersRead
                        // so that the response message is available for streaming
                        _httpResponseTask = (Channel.HttpInvoker is HttpClient httpClient)
                            ? httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _callCts.Token)
                            : Channel.HttpInvoker.SendAsync(request, _callCts.Token);

                        HttpResponse = await _httpResponseTask.ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        GrpcCallLog.ErrorStartingCall(Logger, ex);
                        throw;
                    }

                    status = ValidateHeaders(HttpResponse);

                    // A status means either the call has failed or grpc-status was returned in the response header
                    if (status != null)
                    {
                        if (_responseTcs != null)
                        {
                            // gRPC status in the header
                            if (status.Value.StatusCode != StatusCode.OK)
                            {
                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            }
                            else
                            {
                                // The server should never return StatusCode.OK in the header for a unary call.
                                // If it does then throw an error that no message was returned from the server.
                                GrpcCallLog.MessageNotReturned(Logger);

                                // Change the status code to a more accurate status.
                                // This is consistent with Grpc.Core client behavior.
                                status = new Status(StatusCode.Internal, "Failed to deserialize response message.");

                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            }

                            FinishResponseAndCleanUp(status.Value);

                            // Set failed result makes the response task thrown an error. Must be called after
                            // the response is finished. Reasons:
                            // - Finishing the response sets the status. Required for GetStatus to be successful.
                            // - We want GetStatus to always work when called after the response task is done.
                            SetFailedResult(status.Value);
                        }
                        else
                        {
                            finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            FinishResponseAndCleanUp(status.Value);
                        }
                    }
                    else
                    {
                        if (_responseTcs != null)
                        {
                            // Read entire response body immediately and read status from trailers
                            // Trailers are only available once the response body had been read
                            var responseStream = await HttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

                            var message = await ReadMessageAsync(
                                responseStream,
                                GrpcProtocolHelpers.GetGrpcEncoding(HttpResponse),
                                singleMessage : true,
                                _callCts.Token).ConfigureAwait(false);

                            status = GrpcProtocolHelpers.GetResponseStatus(HttpResponse, Channel.OperatingSystem.IsBrowser);

                            if (message == null)
                            {
                                GrpcCallLog.MessageNotReturned(Logger);

                                if (status.Value.StatusCode == StatusCode.OK)
                                {
                                    // Change the status code if OK is returned to a more accurate status.
                                    // This is consistent with Grpc.Core client behavior.
                                    status = new Status(StatusCode.Internal, "Failed to deserialize response message.");
                                }

                                FinishResponseAndCleanUp(status.Value);
                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);

                                // Set failed result makes the response task thrown an error. Must be called after
                                // the response is finished. Reasons:
                                // - Finishing the response sets the status. Required for GetStatus to be successful.
                                // - We want GetStatus to always work when called after the response task is done.
                                SetFailedResult(status.Value);
                            }
                            else
                            {
                                GrpcEventSource.Log.MessageReceived();

                                FinishResponseAndCleanUp(status.Value);
                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);

                                if (status.Value.StatusCode == StatusCode.OK)
                                {
                                    _responseTcs.TrySetResult(message);
                                }
                                else
                                {
                                    // Set failed result makes the response task thrown an error. Must be called after
                                    // the response is finished. Reasons:
                                    // - Finishing the response sets the status. Required for GetStatus to be successful.
                                    // - We want GetStatus to always work when called after the response task is done.
                                    SetFailedResult(status.Value);
                                }
                            }
                        }
                        else
                        {
                            // Duplex or server streaming call
                            Debug.Assert(ClientStreamReader != null);
                            ClientStreamReader.HttpResponseTcs.TrySetResult((HttpResponse, status));

                            // Wait until the response has been read and status read from trailers.
                            // TCS will also be set in Dispose.
                            status = await CallTask.ConfigureAwait(false);

                            finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            Cleanup(status.Value);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Exception resolvedException;
                    ResolveException(ErrorStartingCallMessage, ex, out status, out resolvedException);

                    finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                    _responseTcs?.TrySetException(resolvedException);

                    Cleanup(status.Value);
                }

                // Verify that FinishCall is called in every code path of this method.
                // Should create an "Unassigned variable" compiler error if not set.
                Debug.Assert(finished);
            }
        }
Example #2
0
        private async ValueTask RunCall(HttpRequestMessage request, TimeSpan?timeout)
        {
            using (StartScope())
            {
                var(diagnosticSourceEnabled, activity) = InitializeCall(request, timeout);

                if (Options.Credentials != null || Channel.CallCredentials?.Count > 0)
                {
                    await ReadCredentials(request).ConfigureAwait(false);
                }

                // Unset variable to check that FinishCall is called in every code path
                bool finished;

                Status?status = null;

                try
                {
                    // Fail early if deadline has already been exceeded
                    _callCts.Token.ThrowIfCancellationRequested();

                    try
                    {
                        _httpResponseTask = Channel.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _callCts.Token);
                        HttpResponse      = await _httpResponseTask.ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        GrpcCallLog.ErrorStartingCall(Logger, ex);
                        throw;
                    }

                    status = ValidateHeaders(HttpResponse);

                    // A status means either the call has failed or grpc-status was returned in the response header
                    if (status != null)
                    {
                        if (_responseTcs != null)
                        {
                            // gRPC status in the header
                            if (status.Value.StatusCode != StatusCode.OK)
                            {
                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                                SetFailedResult(status.Value);
                            }
                            else
                            {
                                // The server should never return StatusCode.OK in the header for a unary call.
                                // If it does then throw an error that no message was returned from the server.
                                GrpcCallLog.MessageNotReturned(Logger);

                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                                _responseTcs.TrySetException(new InvalidOperationException("Call did not return a response message."));
                            }

                            FinishResponseAndCleanUp(status.Value);
                        }
                        else
                        {
                            finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            FinishResponseAndCleanUp(status.Value);
                        }
                    }
                    else
                    {
                        if (_responseTcs != null)
                        {
                            // Read entire response body immediately and read status from trailers
                            // Trailers are only available once the response body had been read
                            var responseStream = await HttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

                            var message = await responseStream.ReadMessageAsync(
                                Logger,
                                Method.ResponseMarshaller.ContextualDeserializer,
                                GrpcProtocolHelpers.GetGrpcEncoding(HttpResponse),
                                Channel.ReceiveMaxMessageSize,
                                Channel.CompressionProviders,
                                singleMessage : true,
                                _callCts.Token).ConfigureAwait(false);

                            status = GrpcProtocolHelpers.GetResponseStatus(HttpResponse);
                            FinishResponseAndCleanUp(status.Value);

                            if (message == null)
                            {
                                GrpcCallLog.MessageNotReturned(Logger);

                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                                SetFailedResult(status.Value);
                            }
                            else
                            {
                                GrpcEventSource.Log.MessageReceived();

                                finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);

                                if (status.Value.StatusCode == StatusCode.OK)
                                {
                                    _responseTcs.TrySetResult(message);
                                }
                                else
                                {
                                    SetFailedResult(status.Value);
                                }
                            }
                        }
                        else
                        {
                            // Duplex or server streaming call
                            Debug.Assert(ClientStreamReader != null);
                            ClientStreamReader.HttpResponseTcs.TrySetResult((HttpResponse, status));

                            // Wait until the response has been read and status read from trailers.
                            // TCS will also be set in Dispose.
                            status = await CallTask.ConfigureAwait(false);

                            finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                            Cleanup(status.Value);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Exception resolvedException;
                    ResolveException(ex, out status, out resolvedException);

                    finished = FinishCall(request, diagnosticSourceEnabled, activity, status.Value);
                    _responseTcs?.TrySetException(resolvedException);

                    Cleanup(status.Value);
                }

                // Verify that FinishCall is called in every code path of this method.
                // Should create an "Unassigned variable" compiler error if not set.
                Debug.Assert(finished);
            }
        }
Example #3
0
        private async ValueTask RunCall(HttpRequestMessage request)
        {
            using (StartScope())
            {
                var(diagnosticSourceEnabled, activity) = InitializeCall(request);

                if (Options.Credentials != null || Channel.CallCredentials?.Count > 0)
                {
                    await ReadCredentials(request).ConfigureAwait(false);
                }

                Status?status = null;

                try
                {
                    try
                    {
                        HttpResponse = await Channel.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _callCts.Token).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        GrpcCallLog.ErrorStartingCall(Logger, ex);
                        throw;
                    }

                    BuildMetadata(HttpResponse);

                    status = ValidateHeaders(HttpResponse);

                    // A status means either the call has failed or grpc-status was returned in the response header
                    if (status != null)
                    {
                        if (_responseTcs != null)
                        {
                            // gRPC status in the header
                            if (status.Value.StatusCode != StatusCode.OK)
                            {
                                SetFailedResult(status.Value);
                            }
                            else
                            {
                                // The server should never return StatusCode.OK in the header for a unary call.
                                // If it does then throw an error that no message was returned from the server.
                                GrpcCallLog.MessageNotReturned(Logger);
                                _responseTcs.TrySetException(new InvalidOperationException("Call did not return a response message."));
                            }
                        }

                        FinishResponse(status.Value);
                    }
                    else
                    {
                        if (_responseTcs != null)
                        {
                            // Read entire response body immediately and read status from trailers
                            // Trailers are only available once the response body had been read
                            var responseStream = await HttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

                            var message = await responseStream.ReadMessageAsync(
                                Logger,
                                Method.ResponseMarshaller.ContextualDeserializer,
                                GrpcProtocolHelpers.GetGrpcEncoding(HttpResponse),
                                Channel.ReceiveMaxMessageSize,
                                Channel.CompressionProviders,
                                singleMessage : true,
                                _callCts.Token).ConfigureAwait(false);

                            status = GrpcProtocolHelpers.GetResponseStatus(HttpResponse);
                            FinishResponse(status.Value);

                            if (message == null)
                            {
                                GrpcCallLog.MessageNotReturned(Logger);
                                SetFailedResult(status.Value);
                            }
                            else
                            {
                                GrpcEventSource.Log.MessageReceived();

                                if (status.Value.StatusCode == StatusCode.OK)
                                {
                                    _responseTcs.TrySetResult(message);
                                }
                                else
                                {
                                    SetFailedResult(status.Value);
                                }
                            }
                        }
                        else
                        {
                            // Duplex or server streaming call
                            Debug.Assert(ClientStreamReader != null);
                            ClientStreamReader.HttpResponseTcs.TrySetResult((HttpResponse, status));

                            // Wait until the response has been read and status read from trailers.
                            // TCS will also be set in Dispose.
                            status = await CallTask.ConfigureAwait(false);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Exception resolvedException;
                    if (ex is OperationCanceledException)
                    {
                        status            = (CallTask.IsCompletedSuccessfully) ? CallTask.Result : new Status(StatusCode.Cancelled, string.Empty);
                        resolvedException = Channel.ThrowOperationCanceledOnCancellation ? ex : CreateRpcException(status.Value);
                    }
                    else if (ex is RpcException rpcException)
                    {
                        status            = rpcException.Status;
                        resolvedException = CreateRpcException(status.Value);
                    }
                    else
                    {
                        status            = new Status(StatusCode.Internal, "Error starting gRPC call: " + ex.Message);
                        resolvedException = CreateRpcException(status.Value);
                    }

                    _metadataTcs.TrySetException(resolvedException);
                    _responseTcs?.TrySetException(resolvedException);

                    Cleanup(status !.Value);
                }

                FinishCall(request, diagnosticSourceEnabled, activity, status);
            }
        }