示例#1
0
        private async Task <Metadata> GetResponseHeadersCoreAsync()
        {
            Debug.Assert(_httpResponseTask != null);

            try
            {
                var httpResponse = await _httpResponseTask.ConfigureAwait(false);

                // Check if the headers have a status. If they do then wait for the overall call task
                // to complete before returning headers. This means that if the call failed with a
                // a status then it is possible to await response headers and then call GetStatus().
                var grpcStatus = GrpcProtocolHelpers.GetHeaderValue(httpResponse.Headers, GrpcProtocolConstants.StatusTrailer);
                if (grpcStatus != null)
                {
                    await CallTask.ConfigureAwait(false);
                }

                return(GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers));
            }
            catch (Exception ex)
            {
                ResolveException(ex, out _, out var resolvedException);
                throw resolvedException;
            }
        }
示例#2
0
        private async Task <Metadata> GetResponseHeadersCoreAsync()
        {
            CompatibilityExtensions.Assert(_httpResponseTask != null);

            try
            {
                var httpResponse = await _httpResponseTask.ConfigureAwait(false);

                // Check if the headers have a status. If they do then wait for the overall call task
                // to complete before returning headers. This means that if the call failed with a
                // a status then it is possible to await response headers and then call GetStatus().
                var grpcStatus = GrpcProtocolHelpers.GetHeaderValue(httpResponse.Headers, GrpcProtocolConstants.StatusTrailer);
                if (grpcStatus != null)
                {
                    await CallTask.ConfigureAwait(false);
                }

                var metadata = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers);

                // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#exposed-retry-metadata
                if (_attemptCount > 1)
                {
                    metadata.Add(GrpcProtocolConstants.RetryPreviousAttemptsHeader, (_attemptCount - 1).ToString(CultureInfo.InvariantCulture));
                }

                return(metadata);
            }
            catch (Exception ex) when(ResolveException(ErrorStartingCallMessage, ex, out _, out var resolvedException))
            {
                throw resolvedException;
            }
        }
示例#3
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);
                            FinishResponseAndCleanUp(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);
            }
        }
示例#4
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)
                    {
                        // Don't log OperationCanceledException if deadline has exceeded.
                        if (ex is OperationCanceledException &&
                            _callTcs.Task.IsCompletedSuccessfully &&
                            _callTcs.Task.Result.StatusCode == StatusCode.DeadlineExceeded)
                        {
                            throw;
                        }
                        else
                        {
                            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);
            }
        }
示例#5
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);
            }
        }