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; } }
private Status?ValidateHeaders(HttpResponseMessage httpResponse) { GrpcCallLog.ResponseHeadersReceived(Logger); // gRPC status can be returned in the header when there is no message (e.g. unimplemented status) // An explicitly specified status header has priority over other failing statuses if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status)) { // Trailers are in the header because there is no message. // Note that some default headers will end up in the trailers (e.g. Date, Server). _trailers = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers); return(status); } if (httpResponse.StatusCode != HttpStatusCode.OK) { var statusCode = MapHttpStatusToGrpcCode(httpResponse.StatusCode); return(new Status(statusCode, "Bad gRPC response. HTTP status code: " + (int)httpResponse.StatusCode)); } if (httpResponse.Content?.Headers.ContentType == null) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Response did not have a content-type header.")); } var grpcEncoding = httpResponse.Content.Headers.ContentType; if (!GrpcProtocolHelpers.IsGrpcContentType(grpcEncoding)) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Invalid content-type value: " + grpcEncoding)); } // Call is still in progress return(null); }
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; } }
private void BuildMetadata(HttpResponseMessage httpResponse) { try { _metadataTcs.TrySetResult(GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers)); } catch (Exception ex) { _metadataTcs.TrySetException(ex); } }
public Metadata GetTrailers() { using (StartScope()) { if (_trailers == null) { ValidateTrailersAvailable(); Debug.Assert(HttpResponse != null); _trailers = GrpcProtocolHelpers.BuildMetadata(HttpResponse.TrailingHeaders); } return(_trailers); } }
private async Task <Metadata> GetResponseHeadersCoreAsync() { Debug.Assert(_httpResponseTask != null); try { var httpResponse = await _httpResponseTask.ConfigureAwait(false); return(GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers)); } catch (Exception ex) { ResolveException(ex, out _, out var resolvedException); throw resolvedException; } }
private bool TryGetTrailers([NotNullWhen(true)] out Metadata?trailers) { if (_trailers == null) { // Trailers are read from the end of the request. // If the request isn't finished then we can't get the trailers. if (!ResponseFinished) { trailers = null; return(false); } Debug.Assert(HttpResponse != null); _trailers = GrpcProtocolHelpers.BuildMetadata(HttpResponse.TrailingHeaders); } trailers = _trailers; return(true); }
internal static Status?ValidateHeaders(HttpResponseMessage httpResponse, out Metadata?trailers) { // gRPC status can be returned in the header when there is no message (e.g. unimplemented status) // An explicitly specified status header has priority over other failing statuses if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status)) { // Trailers are in the header because there is no message. // Note that some default headers will end up in the trailers (e.g. Date, Server). trailers = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers); return(status); } trailers = null; // ALPN negotiation is sending HTTP/1.1 and HTTP/2. // Check that the response wasn't downgraded to HTTP/1.1. if (httpResponse.Version < GrpcProtocolConstants.Http2Version) { return(new Status(StatusCode.Internal, $"Bad gRPC response. Response protocol downgraded to HTTP/{httpResponse.Version.ToString(2)}.")); } if (httpResponse.StatusCode != HttpStatusCode.OK) { var statusCode = MapHttpStatusToGrpcCode(httpResponse.StatusCode); return(new Status(statusCode, "Bad gRPC response. HTTP status code: " + (int)httpResponse.StatusCode)); } // Don't access Headers.ContentType property because it is not threadsafe. var contentType = GrpcProtocolHelpers.GetHeaderValue(httpResponse.Content?.Headers, "Content-Type"); if (contentType == null) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Response did not have a content-type header.")); } if (!CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, contentType)) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Invalid content-type value: " + contentType)); } // Call is still in progress return(null); }
protected bool TryGetTrailers([NotNullWhen(true)] out Metadata?trailers) { if (Trailers == null) { // Trailers are read from the end of the request. // If the request isn't finished then we can't get the trailers. if (!ResponseFinished) { trailers = null; return(false); } CompatibilityExtensions.Assert(HttpResponse != null); Trailers = GrpcProtocolHelpers.BuildMetadata(HttpResponse.TrailingHeaders()); } trailers = Trailers; return(true); }
private Status?ValidateHeaders(HttpResponseMessage httpResponse) { GrpcCallLog.ResponseHeadersReceived(Logger); // gRPC status can be returned in the header when there is no message (e.g. unimplemented status) // An explicitly specified status header has priority over other failing statuses if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status)) { // Trailers are in the header because there is no message. // Note that some default headers will end up in the trailers (e.g. Date, Server). _trailers = GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers); return(status); } // ALPN negotiation is sending HTTP/1.1 and HTTP/2. // Check that the response wasn't downgraded to HTTP/1.1. if (httpResponse.Version < HttpVersion.Version20) { return(new Status(StatusCode.Internal, $"Bad gRPC response. Response protocol downgraded to HTTP/{httpResponse.Version.ToString(2)}.")); } if (httpResponse.StatusCode != HttpStatusCode.OK) { var statusCode = MapHttpStatusToGrpcCode(httpResponse.StatusCode); return(new Status(statusCode, "Bad gRPC response. HTTP status code: " + (int)httpResponse.StatusCode)); } if (httpResponse.Content?.Headers.ContentType == null) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Response did not have a content-type header.")); } var grpcEncoding = httpResponse.Content.Headers.ContentType; if (!CommonGrpcProtocolHelpers.IsContentType(GrpcProtocolConstants.GrpcContentType, grpcEncoding?.MediaType)) { return(new Status(StatusCode.Cancelled, "Bad gRPC response. Invalid content-type value: " + grpcEncoding)); } // Call is still in progress return(null); }
private async Task <Metadata> GetResponseHeadersCoreAsync() { 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) { // If there was an error fetching response headers then it's likely the same error is reported // by response TCS. The user is unlikely to observe both errors. // Observe the task's exception to prevent TaskScheduler.UnobservedTaskException from firing. _responseTcs?.Task.ObserveException(); if (ResolveException(ErrorStartingCallMessage, ex, out _, out var resolvedException)) { throw resolvedException; } else { throw; } } }
public async Task <Metadata> GetResponseHeadersAsync() { Debug.Assert(SendTask != null); try { using (StartScope()) { await SendTask.ConfigureAwait(false); Debug.Assert(HttpResponse != null); // The task of this method is cached so there is no need to cache the headers here return(GrpcProtocolHelpers.BuildMetadata(HttpResponse.Headers)); } } catch (OperationCanceledException) when(!Channel.ThrowOperationCanceledOnCancellation) { throw CreateCanceledStatusException(); } }