public static async Task WriteSingleMessageAsync <TResponse>(this PipeWriter pipeWriter, TResponse response, HttpContextServerCallContext serverCallContext, Action <TResponse, SerializationContext> serializer)
            where TResponse : class
        {
            var logger = serverCallContext.Logger;

            try
            {
                // Must call StartAsync before the first pipeWriter.GetSpan() in WriteHeader
                var httpResponse = serverCallContext.HttpContext.Response;
                if (!httpResponse.HasStarted)
                {
                    await httpResponse.StartAsync();
                }

                GrpcServerLog.SendingMessage(logger);

                var serializationContext = serverCallContext.SerializationContext;
                serializationContext.Reset();
                serializationContext.ResponseBufferWriter = pipeWriter;
                serializer(response, serializationContext);

                GrpcServerLog.MessageSent(serverCallContext.Logger);
                GrpcEventSource.Log.MessageSent();
            }
            catch (Exception ex)
            {
                GrpcServerLog.ErrorSendingMessage(logger, ex);
                throw;
            }
        }
Example #2
0
        public override void Complete()
        {
            switch (_state)
            {
            case InternalState.IncompleteBufferWriter:
                _state = InternalState.CompleteBufferWriter;

                if (!DirectSerializationSupported)
                {
                    Debug.Assert(_bufferWriter != null, "Buffer writer has been set to get to this state.");

                    var data = _bufferWriter.WrittenSpan;

                    GrpcServerLog.SerializedMessage(_serverCallContext.Logger, typeof(object), data.Length);
                    WriteMessage(data);
                }
                else
                {
                    GrpcServerLog.SerializedMessage(_serverCallContext.Logger, _serverCallContext.ResponseType, _payloadLength.GetValueOrDefault());
                }
                break;

            default:
                ThrowInvalidState(_state);
                break;
            }
        }
        private async Task DeadlineExceededAsync()
        {
            if (!TryStartExceededDeadline())
            {
                return;
            }

            try
            {
                await _serverCallContext.DeadlineExceededAsync();

                lock (this)
                {
                    IsCallComplete = true;
                }

                // Canceling CTS will trigger registered callbacks.
                // Exception could be thrown from them.
                _deadlineCts?.Cancel();
            }
            catch (Exception ex)
            {
                GrpcServerLog.DeadlineCancellationError(_serverCallContext.Logger, ex);
            }
            finally
            {
                _deadlineExceededCompleteTcs !.TrySetResult(null);
            }
        }
Example #4
0
        public override void Complete(byte[] payload)
        {
            switch (_state)
            {
            case InternalState.Initialized:
                _state = InternalState.CompleteArray;

                GrpcServerLog.SerializedMessage(_serverCallContext.Logger, _serverCallContext.ResponseType, payload.Length);
                WriteMessage(payload);
                break;

            default:
                ThrowInvalidState(_state);
                break;
            }
        }
        public static async Task WriteStreamedMessageAsync <TResponse>(this PipeWriter pipeWriter, TResponse response, HttpContextServerCallContext serverCallContext, Action <TResponse, SerializationContext> serializer, CancellationToken cancellationToken = default)
            where TResponse : class
        {
            var logger = serverCallContext.Logger;

            try
            {
                // Must call StartAsync before the first pipeWriter.GetSpan() in WriteHeader
                var httpResponse = serverCallContext.HttpContext.Response;
                if (!httpResponse.HasStarted)
                {
                    await httpResponse.StartAsync();
                }

                GrpcServerLog.SendingMessage(logger);

                var serializationContext = serverCallContext.SerializationContext;
                serializationContext.Reset();
                serializationContext.ResponseBufferWriter = pipeWriter;
                serializer(response, serializationContext);

                // Flush messages unless WriteOptions.Flags has BufferHint set
                var flush = ((serverCallContext.WriteOptions?.Flags ?? default) & WriteFlags.BufferHint) != WriteFlags.BufferHint;

                if (flush)
                {
                    var flushResult = await pipeWriter.FlushAsync();

                    // Workaround bug where FlushAsync doesn't return IsCanceled = true on request abort.
                    // https://github.com/dotnet/aspnetcore/issues/40788
                    // Also, sometimes the request CT isn't triggered. Also check CT passed into method.
                    if (!flushResult.IsCompleted &&
                        (serverCallContext.CancellationToken.IsCancellationRequested || cancellationToken.IsCancellationRequested))
                    {
                        throw new OperationCanceledException("Request aborted while sending the message.");
                    }
                }

                GrpcServerLog.MessageSent(serverCallContext.Logger);
                GrpcEventSource.Log.MessageSent();
            }
            catch (Exception ex)
            {
                GrpcServerLog.ErrorSendingMessage(logger, ex);
                throw;
            }
        }
Example #6
0
        private ReadOnlySpan <byte> CompressMessage(ReadOnlySpan <byte> messageData)
        {
            Debug.Assert(_compressionProvider != null, "Compression provider is not null to get here.");

            GrpcServerLog.CompressingMessage(_serverCallContext.Logger, _compressionProvider.EncodingName);

            var output = new MemoryStream();

            // Compression stream must be disposed before its content is read.
            // GZipStream writes final Adler32 at the end of the stream on dispose.
            using (var compressionStream = _compressionProvider.CreateCompressionStream(output, _serverCallContext.Options.ResponseCompressionLevel))
            {
                compressionStream.Write(messageData);
            }

            return(output.GetBuffer().AsSpan(0, (int)output.Length));
        }
        private void DeadlineExceededCallback(object?state)
        {
            var remaining = Deadline - _systemClock !.UtcNow;

            if (remaining <= TimeSpan.Zero)
            {
                DeadlineExceeded();
            }
            else
            {
                // Deadline has not been reached because timer maximum due time was smaller than deadline.
                // Reschedule DeadlineExceeded again until deadline has been exceeded.
                GrpcServerLog.DeadlineTimerRescheduled(ServerCallContext.Logger, remaining);

                _longDeadlineTimer !.Change(GetTimerDueTime(remaining), Timeout.Infinite);
            }
        }
        private static void DeadlineExceededLongCallback(object?state)
        {
            var(manager, maxTimerDueTime) = (ValueTuple <ServerCallDeadlineManager, long>)state !;
            var remaining = manager.Deadline - manager._systemClock.UtcNow;

            if (remaining <= TimeSpan.Zero)
            {
                _ = manager.DeadlineExceededAsync();
            }
            else
            {
                // Deadline has not been reached because timer maximum due time was smaller than deadline.
                // Reschedule DeadlineExceeded again until deadline has been exceeded.
                GrpcServerLog.DeadlineTimerRescheduled(manager._serverCallContext.Logger, remaining);

                manager._longDeadlineTimer.Change(CommonGrpcProtocolHelpers.GetTimerDueTime(remaining, maxTimerDueTime), Timeout.Infinite);
            }
        }
Example #9
0
        public static async Task WriteMessageAsync <TResponse>(this PipeWriter pipeWriter, TResponse response, HttpContextServerCallContext serverCallContext, Action <TResponse, SerializationContext> serializer, bool canFlush)
            where TResponse : class
        {
            var logger = serverCallContext.Logger;

            try
            {
                // Must call StartAsync before the first pipeWriter.GetSpan() in WriteHeader
                var httpResponse = serverCallContext.HttpContext.Response;
                if (!httpResponse.HasStarted)
                {
                    await httpResponse.StartAsync();
                }

                GrpcServerLog.SendingMessage(logger);

                var serializationContext = serverCallContext.SerializationContext;
                serializationContext.Reset();
                serializationContext.ResponseBufferWriter = pipeWriter;
                serializer(response, serializationContext);

                // Flush messages unless WriteOptions.Flags has BufferHint set
                var flush = canFlush && ((serverCallContext.WriteOptions?.Flags ?? default) & WriteFlags.BufferHint) != WriteFlags.BufferHint;

                if (flush)
                {
                    serverCallContext.HasBufferedMessage = false;
                    await pipeWriter.FlushAsync();
                }
                else
                {
                    // Set flag so buffered message will be written at the end
                    serverCallContext.HasBufferedMessage = true;
                }

                GrpcServerLog.MessageSent(serverCallContext.Logger);
                GrpcEventSource.Log.MessageSent();
            }
            catch (Exception ex)
            {
                GrpcServerLog.ErrorSendingMessage(logger, ex);
                throw;
            }
        }
Example #10
0
        public static async Task WriteMessageAsync <TResponse>(this PipeWriter pipeWriter, TResponse response, HttpContextServerCallContext serverCallContext, Action <TResponse, SerializationContext> serializer, bool canFlush)
            where TResponse : class
        {
            var logger = serverCallContext.Logger;

            try
            {
                GrpcServerLog.SendingMessage(logger);

                var serializationContext = serverCallContext.SerializationContext;
                serializer(response, serializationContext);
                var responsePayload = serializationContext.Payload;
                serializationContext.Payload = null;

                if (responsePayload == null)
                {
                    throw new InvalidOperationException("Serialization did not return a payload.");
                }

                GrpcServerLog.SerializedMessage(serverCallContext.Logger, typeof(TResponse), responsePayload.Length);

                // Must call StartAsync before the first pipeWriter.GetSpan() in WriteHeader
                var httpResponse = serverCallContext.HttpContext.Response;
                if (!httpResponse.HasStarted)
                {
                    await httpResponse.StartAsync();
                }

                var canCompress =
                    GrpcProtocolHelpers.CanWriteCompressed(serverCallContext.WriteOptions) &&
                    !string.Equals(serverCallContext.ResponseGrpcEncoding, GrpcProtocolConstants.IdentityGrpcEncoding, StringComparison.Ordinal);

                var isCompressed = false;
                if (canCompress)
                {
                    Debug.Assert(
                        serverCallContext.ServiceOptions.ResolvedCompressionProviders != null,
                        "Compression providers should have been resolved for service.");

                    if (TryCompressMessage(
                            serverCallContext.Logger,
                            serverCallContext.ResponseGrpcEncoding !,
                            serverCallContext.ServiceOptions.ResponseCompressionLevel,
                            serverCallContext.ServiceOptions.ResolvedCompressionProviders,
                            responsePayload,
                            out var result))
                    {
                        responsePayload = result;
                        isCompressed    = true;
                    }
                }

                if (responsePayload.Length > serverCallContext.ServiceOptions.MaxSendMessageSize)
                {
                    throw new RpcException(SendingMessageExceedsLimitStatus);
                }

                WriteHeader(pipeWriter, responsePayload.Length, isCompressed);
                pipeWriter.Write(responsePayload);

                // Flush messages unless WriteOptions.Flags has BufferHint set
                var flush = canFlush && ((serverCallContext.WriteOptions?.Flags ?? default) & WriteFlags.BufferHint) != WriteFlags.BufferHint;

                if (flush)
                {
                    serverCallContext.HasBufferedMessage = false;
                    await pipeWriter.FlushAsync();
                }
                else
                {
                    // Set flag so buffered message will be written at the end
                    serverCallContext.HasBufferedMessage = true;
                }

                GrpcServerLog.MessageSent(serverCallContext.Logger);
                GrpcEventSource.Log.MessageSent();
            }
            catch (Exception ex)
            {
                GrpcServerLog.ErrorSendingMessage(logger, ex);
                throw;
            }
        }