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