Exemplo n.º 1
0
        private async Task ReadCredentials(HttpRequestMessage request)
        {
            // In C-Core the call credential auth metadata is only applied if the channel is secure
            // The equivalent in grpc-dotnet is only applying metadata if HttpClient is using TLS
            // HttpClient scheme will be HTTP if it is using H2C (HTTP2 without TLS)
            if (Channel.Address.Scheme == Uri.UriSchemeHttps)
            {
                var configurator = new DefaultCallCredentialsConfigurator();

                if (Options.Credentials != null)
                {
                    await GrpcProtocolHelpers.ReadCredentialMetadata(configurator, Channel, request, Method, Options.Credentials).ConfigureAwait(false);
                }
                if (Channel.CallCredentials?.Count > 0)
                {
                    foreach (var credentials in Channel.CallCredentials)
                    {
                        await GrpcProtocolHelpers.ReadCredentialMetadata(configurator, Channel, request, Method, credentials).ConfigureAwait(false);
                    }
                }
            }
            else
            {
                GrpcCallLog.CallCredentialsNotUsed(Logger);
            }
        }
Exemplo n.º 2
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;
            }
        }
Exemplo n.º 3
0
        private void ValidateHeaders()
        {
            Log.ResponseHeadersReceived(Logger);

            Debug.Assert(HttpResponse != null);
            if (HttpResponse.StatusCode != HttpStatusCode.OK)
            {
                _headerValidationError = "Bad gRPC response. Expected HTTP status code 200. Got status code: " + (int)HttpResponse.StatusCode;
            }
            else if (HttpResponse.Content?.Headers.ContentType == null)
            {
                _headerValidationError = "Bad gRPC response. Response did not have a content-type header.";
            }
            else
            {
                var grpcEncoding = HttpResponse.Content.Headers.ContentType.ToString();
                if (!GrpcProtocolHelpers.IsGrpcContentType(grpcEncoding))
                {
                    _headerValidationError = "Bad gRPC response. Invalid content-type value: " + grpcEncoding;
                }
            }

            if (_headerValidationError != null)
            {
                // Response is not valid gRPC
                // Clean up/cancel any pending operations
                DisposeCore();

                throw new InvalidOperationException(_headerValidationError);
            }

            // Success!
        }
Exemplo n.º 4
0
        private HttpRequestMessage CreateHttpRequestMessage()
        {
            var message = new HttpRequestMessage(HttpMethod.Post, _uri);

            message.Version = new Version(2, 0);
            // User agent is optional but recommended
            message.Headers.UserAgent.Add(GrpcProtocolConstants.UserAgentHeader);
            // TE is required by some servers, e.g. C Core
            // A missing TE header results in servers aborting the gRPC call
            message.Headers.TE.Add(GrpcProtocolConstants.TEHeader);

            if (Options.Headers != null && Options.Headers.Count > 0)
            {
                foreach (var entry in Options.Headers)
                {
                    // Deadline is set via CallOptions.Deadline
                    if (entry.Key == GrpcProtocolConstants.TimeoutHeader)
                    {
                        continue;
                    }

                    var value = entry.IsBinary ? Convert.ToBase64String(entry.ValueBytes) : entry.Value;
                    message.Headers.Add(entry.Key, value);
                }
            }

            if (_timeout != null)
            {
                message.Headers.Add(GrpcProtocolConstants.TimeoutHeader, GrpcProtocolHelpers.EncodeTimeout(Convert.ToInt64(_timeout.Value.TotalMilliseconds)));
            }

            return(message);
        }
Exemplo n.º 5
0
        public static Metadata BuildMetadata(HttpResponseHeaders responseHeaders)
        {
            var headers = new Metadata();

            foreach (var header in responseHeaders)
            {
                // ASP.NET Core includes pseudo headers in the set of request headers
                // whereas, they are not in gRPC implementations. We will filter them
                // out when we construct the list of headers on the context.
                if (header.Key.StartsWith(':'))
                {
                    continue;
                }
                // Exclude grpc related headers
                if (header.Key.StartsWith("grpc-", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                else if (header.Key.EndsWith(Metadata.BinaryHeaderSuffix, StringComparison.OrdinalIgnoreCase))
                {
                    headers.Add(header.Key, GrpcProtocolHelpers.ParseBinaryHeader(string.Join(",", header.Value)));
                }
                else
                {
                    headers.Add(header.Key, string.Join(",", header.Value));
                }
            }

            return(headers);
        }
Exemplo n.º 6
0
        private void CreateWriter(HttpRequestMessage message)
        {
            RequestGrpcEncoding = GrpcProtocolHelpers.GetRequestEncoding(message.Headers);
            ClientStreamWriter  = new HttpContentClientStreamWriter <TRequest, TResponse>(this);

            message.Content = new PushStreamContent <TRequest, TResponse>(ClientStreamWriter, GrpcProtocolConstants.GrpcContentTypeHeaderValue);
        }
Exemplo n.º 7
0
        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);
        }
Exemplo n.º 8
0
        private Status?ValidateHeaders(HttpResponseMessage httpResponse)
        {
            Log.ResponseHeadersReceived(Logger);

            if (httpResponse.StatusCode != HttpStatusCode.OK)
            {
                return(new Status(StatusCode.Cancelled, "Bad gRPC response. Expected HTTP status code 200. Got 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));
            }
            else
            {
                // gRPC status can be returned in the header when there is no message (e.g. unimplemented status)
                if (GrpcProtocolHelpers.TryGetStatusCore(httpResponse.Headers, out var status))
                {
                    return(status);
                }
            }

            // Call is still in progress
            return(null);
        }
Exemplo n.º 9
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;
            }
        }
        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();
            }
        }
Exemplo n.º 11
0
 private void SetMessageContent(TRequest request, HttpRequestMessage message)
 {
     message.Content = new PushUnaryContent <TRequest, TResponse>(
         request,
         this,
         GrpcProtocolHelpers.GetRequestEncoding(message.Headers),
         GrpcProtocolConstants.GrpcContentTypeHeaderValue);
 }
Exemplo n.º 12
0
        private HttpRequestMessage CreateHttpRequestMessage(TimeSpan?timeout)
        {
            var message = new HttpRequestMessage(HttpMethod.Post, _grpcMethodInfo.CallUri);

            message.Version = GrpcProtocolConstants.Http2Version;
#if NET5_0
            message.VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
#endif

            // Set raw headers on request using name/values. Typed headers allocate additional objects.
            var headers = message.Headers;

            // User agent is optional but recommended.
            headers.TryAddWithoutValidation(GrpcProtocolConstants.UserAgentHeader, GrpcProtocolConstants.UserAgentHeaderValue);
            // TE is required by some servers, e.g. C Core.
            // A missing TE header results in servers aborting the gRPC call.
            headers.TryAddWithoutValidation(GrpcProtocolConstants.TEHeader, GrpcProtocolConstants.TEHeaderValue);
            headers.TryAddWithoutValidation(GrpcProtocolConstants.MessageAcceptEncodingHeader, Channel.MessageAcceptEncoding);

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

            if (Options.Headers != null && Options.Headers.Count > 0)
            {
                foreach (var entry in Options.Headers)
                {
                    if (entry.Key == GrpcProtocolConstants.TimeoutHeader)
                    {
                        // grpc-timeout is set via CallOptions.Deadline
                        continue;
                    }
                    else if (entry.Key == GrpcProtocolConstants.CompressionRequestAlgorithmHeader)
                    {
                        // grpc-internal-encoding-request is used in the client to set message compression.
                        // 'grpc-encoding' is sent even if WriteOptions.Flags = NoCompress. In that situation
                        // individual messages will not be written with compression.
                        headers.TryAddWithoutValidation(GrpcProtocolConstants.MessageEncodingHeader, entry.Value);
                    }
                    else
                    {
                        GrpcProtocolHelpers.AddHeader(headers, entry);
                    }
                }
            }

            if (timeout != null)
            {
                headers.TryAddWithoutValidation(GrpcProtocolConstants.TimeoutHeader, GrpcProtocolHelpers.EncodeTimeout(timeout.Value.Ticks / TimeSpan.TicksPerMillisecond));
            }

            return(message);
        }
Exemplo n.º 13
0
 private void BuildMetadata(HttpResponseMessage httpResponse)
 {
     try
     {
         _metadataTcs.TrySetResult(GrpcProtocolHelpers.BuildMetadata(httpResponse.Headers));
     }
     catch (Exception ex)
     {
         _metadataTcs.TrySetException(ex);
     }
 }
 public HttpContentClientStreamWriter(
     GrpcCall <TRequest, TResponse> call,
     HttpRequestMessage message)
 {
     _call          = call;
     WriteStreamTcs = new TaskCompletionSource <Stream>(TaskCreationOptions.RunContinuationsAsynchronously);
     CompleteTcs    = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
     _writeLock     = new object();
     WriteOptions   = _call.Options.WriteOptions;
     _grpcEncoding  = GrpcProtocolHelpers.GetRequestEncoding(message.Headers);
 }
Exemplo n.º 15
0
        /// <summary>
        /// Resolve the specified exception to an end-user exception that will be thrown from the client.
        /// The resolved exception is normally a RpcException. Returns true when the resolved exception is changed.
        /// </summary>
        internal bool ResolveException(string summary, Exception ex, [NotNull] out Status?status, out Exception resolvedException)
        {
            if (ex is OperationCanceledException)
            {
                status = (CallTask.IsCompletedSuccessfully()) ? CallTask.Result : new Status(StatusCode.Cancelled, string.Empty);
                if (!Channel.ThrowOperationCanceledOnCancellation)
                {
                    resolvedException = CreateRpcException(status.Value);
                    return(true);
                }
            }
            else if (ex is RpcException rpcException)
            {
                status = rpcException.Status;

                // If trailers have been set, and the RpcException isn't using them, then
                // create new RpcException with trailers. Want to try and avoid this as
                // the exact stack location will be lost.
                //
                // Trailers could be set in another thread so copy to local variable.
                var trailers = Trailers;
                if (trailers != null && rpcException.Trailers != trailers)
                {
                    resolvedException = CreateRpcException(status.Value);
                    return(true);
                }
            }
            else
            {
                var s = GrpcProtocolHelpers.CreateStatusFromException(summary, ex);

                // The server could exceed the deadline and return a CANCELLED status before the
                // client's deadline timer is triggered. When CANCELLED is received check the
                // deadline against the clock and change status to DEADLINE_EXCEEDED if required.
                if (s.StatusCode == StatusCode.Cancelled)
                {
                    lock (this)
                    {
                        if (IsDeadlineExceededUnsynchronized())
                        {
                            s = new Status(StatusCode.DeadlineExceeded, s.Detail, s.DebugException);
                        }
                    }
                }

                status            = s;
                resolvedException = CreateRpcException(s);
                return(true);
            }

            resolvedException = ex;
            return(false);
        }
 public HttpContentClientStreamWriter(
     GrpcCall <TRequest, TResponse> call,
     HttpRequestMessage message,
     Task <Stream> writeStreamTask,
     TaskCompletionSource <bool> completeTcs)
 {
     _call            = call;
     _writeStreamTask = writeStreamTask;
     _completeTcs     = completeTcs;
     _writeLock       = new object();
     WriteOptions     = _call.Options.WriteOptions;
     _grpcEncoding    = GrpcProtocolHelpers.GetRequestEncoding(message.Headers);
 }
Exemplo n.º 17
0
        public Metadata GetTrailers()
        {
            using (StartScope())
            {
                if (_trailers == null)
                {
                    ValidateTrailersAvailable();

                    Debug.Assert(HttpResponse != null);
                    _trailers = GrpcProtocolHelpers.BuildMetadata(HttpResponse.TrailingHeaders);
                }

                return(_trailers);
            }
        }
Exemplo n.º 18
0
        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;
            }
        }
Exemplo n.º 19
0
        private void SetMessageContent(TRequest request, HttpRequestMessage message)
        {
            message.Content = new PushStreamContent(
                (stream) =>
            {
                var grpcEncoding = GrpcProtocolHelpers.GetRequestEncoding(message.Headers);

                return(stream.WriteMessage <TRequest>(
                           Logger,
                           request,
                           Method.RequestMarshaller.Serializer,
                           grpcEncoding,
                           Options.CancellationToken));
            },
                GrpcProtocolConstants.GrpcContentTypeHeaderValue);
        }
Exemplo n.º 20
0
        private HttpRequestMessage CreateHttpRequestMessage(TimeSpan?timeout)
        {
            var message = new HttpRequestMessage(HttpMethod.Post, _grpcMethodInfo.CallUri);

            message.Version = HttpVersion.Version20;

            // Set raw headers on request using name/values. Typed headers allocate additional objects.
            var headers = message.Headers;

            // User agent is optional but recommended.
            headers.Add(GrpcProtocolConstants.UserAgentHeader, GrpcProtocolConstants.UserAgentHeaderValue);
            // TE is required by some servers, e.g. C Core.
            // A missing TE header results in servers aborting the gRPC call.
            headers.Add(GrpcProtocolConstants.TEHeader, GrpcProtocolConstants.TEHeaderValue);
            headers.Add(GrpcProtocolConstants.MessageAcceptEncodingHeader, Channel.MessageAcceptEncoding);

            if (Options.Headers != null && Options.Headers.Count > 0)
            {
                foreach (var entry in Options.Headers)
                {
                    if (entry.Key == GrpcProtocolConstants.TimeoutHeader)
                    {
                        // grpc-timeout is set via CallOptions.Deadline
                        continue;
                    }
                    else if (entry.Key == GrpcProtocolConstants.CompressionRequestAlgorithmHeader)
                    {
                        // grpc-internal-encoding-request is used in the client to set message compression.
                        // 'grpc-encoding' is sent even if WriteOptions.Flags = NoCompress. In that situation
                        // individual messages will not be written with compression.
                        headers.Add(GrpcProtocolConstants.MessageEncodingHeader, entry.Value);
                    }
                    else
                    {
                        GrpcProtocolHelpers.AddHeader(headers, entry);
                    }
                }
            }

            if (timeout != null)
            {
                headers.Add(GrpcProtocolConstants.TimeoutHeader, GrpcProtocolHelpers.EncodeTimeout(timeout.Value.Ticks / TimeSpan.TicksPerMillisecond));
            }

            return(message);
        }
Exemplo n.º 21
0
        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);
        }
Exemplo n.º 22
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();
            }
        }
Exemplo n.º 23
0
        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);
        }
Exemplo n.º 24
0
        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);
        }
Exemplo n.º 25
0
        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);
        }
Exemplo n.º 26
0
        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;
                }
            }
        }
Exemplo n.º 27
0
        private void SetMessageContent(TRequest request, HttpRequestMessage message)
        {
            RequestGrpcEncoding = GrpcProtocolHelpers.GetRequestEncoding(message.Headers);

            if (!Channel.IsWinHttp)
            {
                message.Content = new PushUnaryContent <TRequest, TResponse>(
                    request,
                    this,
                    GrpcProtocolConstants.GrpcContentTypeHeaderValue);
            }
            else
            {
                // WinHttp doesn't support streaming request data so a length needs to be specified.
                message.Content = new LengthUnaryContent <TRequest, TResponse>(
                    request,
                    this,
                    GrpcProtocolConstants.GrpcContentTypeHeaderValue);
            }
        }
Exemplo n.º 28
0
        private bool IsCancellationOrDeadlineException(Exception ex)
        {
            // Don't log OperationCanceledException if deadline has exceeded
            // or the call has been canceled.
            if (ex is OperationCanceledException &&
                _callTcs.Task.IsCompletedSuccessfully() &&
                (_callTcs.Task.Result.StatusCode == StatusCode.DeadlineExceeded || _callTcs.Task.Result.StatusCode == StatusCode.Cancelled))
            {
                return(true);
            }

            // Exception may specify RST_STREAM or abort code that resolves to cancellation.
            // If protocol error is cancellation and deadline has been exceeded then that
            // means the server canceled and the local deadline timer hasn't triggered.
            if (GrpcProtocolHelpers.ResolveRpcExceptionStatusCode(ex) == StatusCode.Cancelled)
            {
                return(true);
            }

            return(false);
        }
Exemplo n.º 29
0
        internal void ResolveException(string summary, Exception ex, [NotNull] out Status?status, out 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
            {
                var exceptionMessage = CommonGrpcProtocolHelpers.ConvertToRpcExceptionMessage(ex);
                var statusCode       = GrpcProtocolHelpers.ResolveRpcExceptionStatusCode(ex);

                status            = new Status(statusCode, summary + " " + exceptionMessage, ex);
                resolvedException = CreateRpcException(status.Value);
            }
        }
Exemplo n.º 30
0
        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();
            }
        }