| | | 1 | | // Copyright (c) ZeroC, Inc. |
| | | 2 | | |
| | | 3 | | using IceRpc.Internal; |
| | | 4 | | using System.IO.Pipelines; |
| | | 5 | | |
| | | 6 | | namespace IceRpc; |
| | | 7 | | |
| | | 8 | | /// <summary>Represents the base class for outgoing frames.</summary> |
| | | 9 | | public abstract class OutgoingFrame |
| | | 10 | | { |
| | | 11 | | /// <summary>Gets or sets the payload of this frame.</summary> |
| | | 12 | | /// <value>The payload of this frame. Defaults to a <see cref="PipeReader" /> that returns an empty |
| | | 13 | | /// sequence.</value> |
| | | 14 | | /// <remarks>IceRPC completes the payload <see cref="PipeReader" /> with the <see |
| | | 15 | | /// cref="PipeReader.Complete(Exception?)" /> method. It never calls <see |
| | | 16 | | /// cref="PipeReader.CompleteAsync(Exception?)" />. The implementation of <see |
| | | 17 | | /// cref="PipeReader.Complete(Exception?)" /> should not block.</remarks> |
| | 46540 | 18 | | public PipeReader Payload { get; set; } = EmptyPipeReader.Instance; |
| | | 19 | | |
| | | 20 | | /// <summary>Gets or sets the payload continuation of this frame. The payload continuation is a continuation of <see |
| | | 21 | | /// cref="Payload"/>. The receiver cannot distinguish any seam between payload and payload continuation in the <see |
| | | 22 | | /// cref="IncomingFrame.Payload" /> it receives.</summary> |
| | | 23 | | /// <value>The payload continuation of this frame. Defaults to <see langword="null" /> meaning no |
| | | 24 | | /// continuation.</value> |
| | | 25 | | /// <remarks>IceRPC completes the payload continuation <see cref="PipeReader" /> with the <see |
| | | 26 | | /// cref="PipeReader.Complete(Exception?)" /> method. It never calls <see |
| | | 27 | | /// cref="PipeReader.CompleteAsync(Exception?)" />. The implementation of <see |
| | | 28 | | /// cref="PipeReader.Complete(Exception?)" /> should not block.</remarks> |
| | | 29 | | public PipeReader? PayloadContinuation |
| | | 30 | | { |
| | 18622 | 31 | | get => _payloadContinuation; |
| | | 32 | | set |
| | 311 | 33 | | { |
| | 311 | 34 | | _payloadContinuation = Protocol.HasPayloadContinuation || value is null ? |
| | 311 | 35 | | value : throw new NotSupportedException( |
| | 311 | 36 | | $"The '{Protocol}' protocol does not support payload continuation."); |
| | 311 | 37 | | } |
| | | 38 | | } |
| | | 39 | | |
| | | 40 | | /// <summary>Gets the protocol of this frame.</summary> |
| | | 41 | | /// <value>The <see cref="IceRpc.Protocol" /> value of this frame.</value> |
| | 11574 | 42 | | public Protocol Protocol { get; } |
| | | 43 | | |
| | | 44 | | private PipeReader? _payloadContinuation; |
| | | 45 | | |
| | | 46 | | private Stack<Func<PipeWriter, PipeWriter>>? _payloadWriterInterceptorStack; |
| | | 47 | | |
| | | 48 | | /// <summary>Installs a payload writer interceptor in this outgoing frame. This interceptor is executed just |
| | | 49 | | /// before sending <see cref="Payload" />, and is typically used to compress both <see cref="Payload" /> and |
| | | 50 | | /// <see cref="PayloadContinuation" />.</summary> |
| | | 51 | | /// <param name="payloadWriterInterceptor">The payload writer interceptor to install.</param> |
| | | 52 | | /// <returns>This outgoing frame.</returns> |
| | | 53 | | /// <remarks>IceRPC completes the payload writer <see cref="PipeWriter" /> with the <see |
| | | 54 | | /// cref="PipeWriter.Complete(Exception?)" /> method. It never calls <see |
| | | 55 | | /// cref="PipeWriter.CompleteAsync(Exception?)" />. The implementation of <see |
| | | 56 | | /// cref="PipeWriter.Complete(Exception?)" /> should not block.</remarks> |
| | | 57 | | public OutgoingFrame Use(Func<PipeWriter, PipeWriter> payloadWriterInterceptor) |
| | 18 | 58 | | { |
| | 18 | 59 | | if (!Protocol.SupportsPayloadWriterInterceptors) |
| | 0 | 60 | | { |
| | 0 | 61 | | throw new NotSupportedException( |
| | 0 | 62 | | $"The '{Protocol}' protocol does not support payload writer interceptors."); |
| | | 63 | | } |
| | 18 | 64 | | _payloadWriterInterceptorStack ??= new(); |
| | 18 | 65 | | _payloadWriterInterceptorStack.Push(payloadWriterInterceptor); |
| | 18 | 66 | | return this; |
| | 18 | 67 | | } |
| | | 68 | | |
| | | 69 | | /// <summary>Returns the payload writer to use when sending the payload.</summary> |
| | | 70 | | internal PipeWriter GetPayloadWriter(PipeWriter writer) |
| | 3535 | 71 | | { |
| | 3535 | 72 | | if (_payloadWriterInterceptorStack is not null) |
| | 18 | 73 | | { |
| | 90 | 74 | | foreach (Func<PipeWriter, PipeWriter> interceptor in _payloadWriterInterceptorStack) |
| | 18 | 75 | | { |
| | 18 | 76 | | writer = interceptor(writer); |
| | 18 | 77 | | } |
| | 18 | 78 | | } |
| | 3535 | 79 | | return writer; |
| | 3535 | 80 | | } |
| | | 81 | | |
| | | 82 | | /// <summary>Constructs an outgoing frame.</summary> |
| | | 83 | | /// <param name="protocol">The protocol used to send the frame.</param> |
| | 22652 | 84 | | private protected OutgoingFrame(Protocol protocol) => Protocol = protocol; |
| | | 85 | | } |