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(); } }
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(); } }
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); } }
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); } }
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(); } }
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); } }