private async Task <bool> MoveNextCore(CancellationToken cancellationToken)
        {
            CancellationTokenSource?cts = null;

            try
            {
                // Linking tokens is expensive. Only create a linked token if the token passed in requires it
                if (cancellationToken.CanBeCanceled)
                {
                    cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _call.CancellationToken);
                    cancellationToken = cts.Token;
                }
                else
                {
                    cancellationToken = _call.CancellationToken;
                }

                cancellationToken.ThrowIfCancellationRequested();

                if (_httpResponse == null)
                {
                    Debug.Assert(_call.SendTask != null);
                    await _call.SendTask.ConfigureAwait(false);

                    Debug.Assert(_call.HttpResponse != null);
                    _httpResponse = _call.HttpResponse;
                }
                if (_responseStream == null)
                {
                    _responseStream = await _httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
                }

                Current = await _responseStream.ReadStreamedMessageAsync(
                    _call.Logger,
                    _call.Method.ResponseMarshaller.ContextualDeserializer,
                    GrpcProtocolHelpers.GetGrpcEncoding(_httpResponse),
                    _call.Channel.ReceiveMaxMessageSize,
                    _call.Channel.CompressionProviders,
                    cancellationToken).ConfigureAwait(false);

                if (Current == null)
                {
                    // No more content in response so mark as finished
                    _call.FinishResponse();
                    return(false);
                }

                GrpcEventSource.Log.MessageReceived();
                return(true);
            }
            catch (OperationCanceledException)
            {
                throw _call.CreateCanceledStatusException();
            }
            finally
            {
                cts?.Dispose();
            }
        }
示例#2
0
        public async Task <TResponse> GetResponseAsync()
        {
            Debug.Assert(SendTask != null);

            try
            {
                using (StartScope())
                {
                    await SendTask.ConfigureAwait(false);

                    Debug.Assert(HttpResponse != null);

                    // Trailers are only available once the response body had been read
                    var responseStream = await HttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

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

                    FinishResponse();

                    if (message == null)
                    {
                        Log.MessageNotReturned(Logger);
                        throw new InvalidOperationException("Call did not return a response message");
                    }

                    GrpcEventSource.Log.MessageReceived();

                    // The task of this method is cached so there is no need to cache the message here
                    return(message);
                }
            }
            catch (OperationCanceledException)
            {
                EnsureNotDisposed();
                throw CreateCanceledStatusException();
            }
        }
示例#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
        public async Task <TResponse> GetResponseAsync()
        {
            Debug.Assert(SendTask != null);

            try
            {
                using (StartScope())
                {
                    // Wait for send to finish so the HttpResponse is available
                    await SendTask.ConfigureAwait(false);

                    // Verify the call is not complete. The call should be complete once the grpc-status
                    // has been read from trailers, which happens AFTER the message has been read.
                    if (CallTask.IsCompletedSuccessfully)
                    {
                        var status = CallTask.Result;
                        if (status.StatusCode != StatusCode.OK)
                        {
                            throw new RpcException(status);
                        }
                        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.
                            Log.MessageNotReturned(Logger);
                            throw new InvalidOperationException("Call did not return a response message");
                        }
                    }

                    Debug.Assert(HttpResponse != null);

                    // Trailers are only available once the response body had been read
                    var responseStream = await HttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

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

                    FinishResponse(throwOnFail: true);

                    if (message == null)
                    {
                        Log.MessageNotReturned(Logger);
                        throw new InvalidOperationException("Call did not return a response message");
                    }

                    GrpcEventSource.Log.MessageReceived();

                    // The task of this method is cached so there is no need to cache the message here
                    return(message);
                }
            }
            catch (OperationCanceledException) when(!Channel.ThrowOperationCanceledOnCancellation)
            {
                throw CreateCanceledStatusException();
            }
        }
        private async Task <bool> MoveNextCore(CancellationToken cancellationToken)
        {
            CancellationTokenSource?cts = null;

            try
            {
                // Linking tokens is expensive. Only create a linked token if the token passed in requires it
                if (cancellationToken.CanBeCanceled)
                {
                    cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _call.CancellationToken);
                    cancellationToken = cts.Token;
                }
                else
                {
                    cancellationToken = _call.CancellationToken;
                }

                cancellationToken.ThrowIfCancellationRequested();

                if (_httpResponse == null)
                {
                    Debug.Assert(_call.SendTask != null);
                    await _call.SendTask.ConfigureAwait(false);

                    Debug.Assert(_call.HttpResponse != null);
                    _httpResponse = _call.HttpResponse;
                }
                if (_responseStream == null)
                {
                    try
                    {
                        _responseStream = await _httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
                    }
                    catch (ObjectDisposedException)
                    {
                        // The response was disposed while waiting for the content stream to start.
                        // This will happen if there is no content stream (e.g. a streaming call finishes with no messages).
                        if (_call.ResponseFinished)
                        {
                            // Call status will have been set before dispose.
                            var status = await _call.CallTask.ConfigureAwait(false);

                            if (status.StatusCode != StatusCode.OK)
                            {
                                throw new RpcException(status);
                            }

                            // Return false to indicate that the stream is complete without a message.
                            return(false);
                        }

                        throw;
                    }
                }

                Current = await _responseStream.ReadStreamedMessageAsync(
                    _call.Logger,
                    _call.Method.ResponseMarshaller.ContextualDeserializer,
                    GrpcProtocolHelpers.GetGrpcEncoding(_httpResponse),
                    _call.Channel.ReceiveMaxMessageSize,
                    _call.Channel.CompressionProviders,
                    cancellationToken).ConfigureAwait(false);

                if (Current == null)
                {
                    // No more content in response so mark as finished
                    _call.FinishResponse(throwOnFail: true);
                    return(false);
                }

                GrpcEventSource.Log.MessageReceived();
                return(true);
            }
            catch (OperationCanceledException) when(!_call.Channel.ThrowOperationCanceledExceptionOnCancellation)
            {
                throw _call.CreateCanceledStatusException();
            }
            finally
            {
                cts?.Dispose();
            }
        }
示例#7
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);
            }
        }