< Summary

Information
Class: IceRpc.Slice.IncomingRequestExtensions
Assembly: IceRpc.Slice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/IncomingRequestExtensions.cs
Tag: 701_22528036593
Line coverage
75%
Covered lines: 72
Uncovered lines: 23
Coverable lines: 95
Total lines: 259
Line coverage: 75.7%
Branch coverage
71%
Covered branches: 20
Total branches: 28
Branch coverage: 71.4%
Method coverage
100%
Covered methods: 9
Total methods: 9
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CheckNonIdempotent(...)50%2.5250%
CreateSliceExceptionResponse(...)66.66%8.44659.25%
DecodeArgsAsync(...)100%44100%
DecodeEmptyArgsAsync(...)100%22100%
DispatchOperationAsync()66.66%6.35678.57%
DispatchOperationAsync()66.66%6.44676.92%
DispatchOperationAsync()100%1.04166.66%
DispatchOperationAsync()100%11100%
GetErrorMessage(...)50%22100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/IncomingRequestExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Features;
 4using IceRpc.Slice.Internal;
 5using System.IO.Pipelines;
 6using ZeroC.Slice;
 7
 8namespace IceRpc.Slice;
 9
 10/// <summary>Provides extension methods for <see cref="IncomingRequest" /> to decode its Slice-encoded payload.
 11/// </summary>
 12public static class IncomingRequestExtensions
 13{
 14    /// <summary>The generated code calls this method to ensure that when an operation is not declared idempotent,
 15    /// the request is not marked idempotent. If the request is marked idempotent, it means the caller incorrectly
 16    /// believes this operation is idempotent.</summary>
 17    /// <param name="request">The request to check.</param>
 18    /// <exception cref="InvalidDataException">Thrown if the request contains the <see cref="RequestFieldKey.Idempotent"
 19    /// field.</exception>
 20    public static void CheckNonIdempotent(this IncomingRequest request)
 10421    {
 10422        if (request.Fields.ContainsKey(RequestFieldKey.Idempotent))
 023        {
 024            throw new InvalidDataException(
 025                $"Invocation mode mismatch for operation '{request.Operation}': received idempotent field for an operati
 26        }
 10427    }
 28
 29    /// <summary>Creates an outgoing response with status code <see cref="StatusCode.ApplicationError" /> with
 30    /// a Slice exception payload.</summary>
 31    /// <param name="request">The incoming request.</param>
 32    /// <param name="sliceException">The Slice exception to encode in the payload.</param>
 33    /// <param name="encoding">The encoding used for the request payload.</param>
 34    /// <returns>The new outgoing response.</returns>
 35    /// <exception cref="NotSupportedException">Thrown when <paramref name="sliceException" /> does not support encoding
 36    /// <paramref name="encoding" />.</exception>
 37    public static OutgoingResponse CreateSliceExceptionResponse(
 38        this IncomingRequest request,
 39        SliceException sliceException,
 40        SliceEncoding encoding)
 1641    {
 1642        SliceEncodeOptions encodeOptions =
 1643            request.Features.Get<ISliceFeature>()?.EncodeOptions ?? SliceEncodeOptions.Default;
 44
 1645        var pipe = new Pipe(encodeOptions.PipeOptions);
 46
 47        try
 1648        {
 1649            var encoder = new SliceEncoder(pipe.Writer, encoding);
 50
 51            // sliceException.Encode can throw NotSupportedException
 1652            if (encoding == SliceEncoding.Slice1)
 1653            {
 1654                sliceException.Encode(ref encoder);
 1655            }
 56            else
 057            {
 058                Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4);
 059                int startPos = encoder.EncodedByteCount;
 060                sliceException.Encode(ref encoder);
 061                SliceEncoder.EncodeVarUInt62((ulong)(encoder.EncodedByteCount - startPos), sizePlaceholder);
 062            }
 63
 1664            pipe.Writer.Complete();
 65
 1666            return new OutgoingResponse(request, StatusCode.ApplicationError, GetErrorMessage(sliceException))
 1667            {
 1668                Payload = pipe.Reader
 1669            };
 70        }
 071        catch
 072        {
 073            pipe.Reader.Complete();
 074            pipe.Writer.Complete();
 075            throw;
 76        }
 1677    }
 78
 79    /// <summary>Decodes a request payload into a list of arguments.</summary>
 80    /// <typeparam name="T">The type of the request parameters.</typeparam>
 81    /// <param name="request">The incoming request.</param>
 82    /// <param name="encoding">The encoding of the request's payload.</param>
 83    /// <param name="decodeFunc">The decode function for the arguments from the payload.</param>
 84    /// <param name="defaultActivator">The activator to use when the activator provided by the request's <see
 85    /// cref="ISliceFeature" /> is <see langword="null" />. Used only when <paramref name="encoding" /> is <see
 86    /// cref="SliceEncoding.Slice1" />.</param>
 87    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 88    /// <returns>The request arguments.</returns>
 89    public static ValueTask<T> DecodeArgsAsync<T>(
 90        this IncomingRequest request,
 91        SliceEncoding encoding,
 92        DecodeFunc<T> decodeFunc,
 93        IActivator? defaultActivator = null,
 94        CancellationToken cancellationToken = default)
 8895    {
 8896        ISliceFeature feature = request.Features.Get<ISliceFeature>() ?? SliceFeature.Default;
 97
 8898        return request.DecodeValueAsync(
 8899            encoding,
 88100            feature,
 88101            feature.BaseProxy,
 88102            decodeFunc,
 88103            feature.Activator ?? defaultActivator,
 88104            cancellationToken);
 88105    }
 106
 107    /// <summary>Verifies that a request payload carries no argument or only unknown tagged arguments.</summary>
 108    /// <param name="request">The incoming request.</param>
 109    /// <param name="encoding">The encoding of the request payload.</param>
 110    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 111    /// <returns>A value task that completes when the checking is complete.</returns>
 112    public static ValueTask DecodeEmptyArgsAsync(
 113        this IncomingRequest request,
 114        SliceEncoding encoding,
 115        CancellationToken cancellationToken = default) =>
 75116        request.DecodeVoidAsync(
 75117            encoding,
 75118            request.Features.Get<ISliceFeature>() ?? SliceFeature.Default,
 75119            cancellationToken);
 120
 121    /// <summary>Dispatches an incoming request to a method that matches the request's operation name.</summary>
 122    /// <typeparam name="TArgs">The type of the operation arguments.</typeparam>
 123    /// <typeparam name="TReturnValue">The type of the operation return value.</typeparam>
 124    /// <param name="request">The incoming request.</param>
 125    /// <param name="decodeArgs">A function that decodes the arguments from the request payload.</param>
 126    /// <param name="method">The user-provided implementation of the operation.</param>
 127    /// <param name="encodeReturnValue">A function that encodes the return value into a PipeReader.</param>
 128    /// <param name="encodeReturnValueStream">A function that encodes the stream portion of the return value.</param>
 129    /// <param name="inExceptionSpecification">A function that returns <see langword="true" /> when the provided Slice
 130    /// exception conforms to the exception specification; otherwise, <see langword="false" />.</param>
 131    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 132    /// <returns>A value task that holds the outgoing response.</returns>
 133    public static async ValueTask<OutgoingResponse> DispatchOperationAsync<TArgs, TReturnValue>(
 134        this IncomingRequest request,
 135        Func<IncomingRequest, CancellationToken, ValueTask<TArgs>> decodeArgs,
 136        Func<TArgs, IFeatureCollection, CancellationToken, ValueTask<TReturnValue>> method,
 137        Func<TReturnValue, SliceEncodeOptions?, PipeReader> encodeReturnValue,
 138        Func<TReturnValue, SliceEncodeOptions?, PipeReader>? encodeReturnValueStream = null,
 139        Func<SliceException, bool>? inExceptionSpecification = null,
 140        CancellationToken cancellationToken = default)
 33141    {
 33142        TArgs args = await decodeArgs(request, cancellationToken).ConfigureAwait(false);
 143        try
 33144        {
 33145            TReturnValue returnValue = await method(args, request.Features, cancellationToken).ConfigureAwait(false);
 33146            return new OutgoingResponse(request)
 33147            {
 33148                Payload = encodeReturnValue(returnValue, request.Features.Get<ISliceFeature>()?.EncodeOptions),
 33149                PayloadContinuation =
 33150                    encodeReturnValueStream?.Invoke(returnValue, request.Features.Get<ISliceFeature>()?.EncodeOptions)
 33151            };
 152        }
 0153        catch (SliceException sliceException) when (inExceptionSpecification?.Invoke(sliceException) ?? false)
 0154        {
 0155            return request.CreateSliceExceptionResponse(sliceException, SliceEncoding.Slice1);
 156        }
 33157    }
 158
 159    /// <summary>Dispatches an incoming request to a method that matches the request's operation name. The operation
 160    /// does not accept any arguments.</summary>
 161    /// <typeparam name="TReturnValue">The type of the operation return value.</typeparam>
 162    /// <param name="request">The incoming request.</param>
 163    /// <param name="decodeArgs">A function that decodes the empty arguments from the request payload.</param>
 164    /// <param name="method">The user-provided implementation of the operation.</param>
 165    /// <param name="encodeReturnValue">A function that encodes the return value into a PipeReader.</param>
 166    /// <param name="encodeReturnValueStream">A function that encodes the stream portion of the return value.</param>
 167    /// <param name="inExceptionSpecification">A function that returns <see langword="true" /> when the provided Slice
 168    /// exception conforms to the exception specification; otherwise, <see langword="false" />.</param>
 169    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 170    /// <returns>A value task that holds the outgoing response.</returns>
 171    public static async ValueTask<OutgoingResponse> DispatchOperationAsync<TReturnValue>(
 172        this IncomingRequest request,
 173        Func<IncomingRequest, CancellationToken, ValueTask> decodeArgs,
 174        Func<IFeatureCollection, CancellationToken, ValueTask<TReturnValue>> method,
 175        Func<TReturnValue, SliceEncodeOptions?, PipeReader> encodeReturnValue,
 176        Func<TReturnValue, SliceEncodeOptions?, PipeReader>? encodeReturnValueStream = null,
 177        Func<SliceException, bool>? inExceptionSpecification = null,
 178        CancellationToken cancellationToken = default)
 15179    {
 15180        await decodeArgs(request, cancellationToken).ConfigureAwait(false);
 181        try
 15182        {
 15183            TReturnValue returnValue = await method(request.Features, cancellationToken).ConfigureAwait(false);
 15184            return new OutgoingResponse(request)
 15185            {
 15186                Payload = encodeReturnValue(returnValue, request.Features.Get<ISliceFeature>()?.EncodeOptions),
 15187                PayloadContinuation = encodeReturnValueStream?.Invoke(returnValue, request.Features.Get<ISliceFeature>()
 15188            };
 189        }
 0190        catch (SliceException sliceException) when (inExceptionSpecification?.Invoke(sliceException) ?? false)
 0191        {
 0192            return request.CreateSliceExceptionResponse(sliceException, SliceEncoding.Slice1);
 193        }
 15194    }
 195
 196    /// <summary>Dispatches an incoming request to a method that matches the request's operation name. The operation
 197    /// does not return anything.</summary>
 198    /// <typeparam name="TArgs">The type of the operation arguments.</typeparam>
 199    /// <param name="request">The incoming request.</param>
 200    /// <param name="decodeArgs">A function that decodes the arguments from the request payload.</param>
 201    /// <param name="method">The user-provided implementation of the operation.</param>
 202    /// <param name="inExceptionSpecification">A function that returns <see langword="true" /> when the provided Slice
 203    /// exception conforms to the exception specification; otherwise, <see langword="false" />.</param>
 204    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 205    /// <returns>A value task that holds the outgoing response.</returns>
 206    public static async ValueTask<OutgoingResponse> DispatchOperationAsync<TArgs>(
 207        this IncomingRequest request,
 208        Func<IncomingRequest, CancellationToken, ValueTask<TArgs>> decodeArgs,
 209        Func<TArgs, IFeatureCollection, CancellationToken, ValueTask> method,
 210        Func<SliceException, bool>? inExceptionSpecification = null,
 211        CancellationToken cancellationToken = default)
 14212    {
 14213        TArgs args = await decodeArgs(request, cancellationToken).ConfigureAwait(false);
 214        try
 14215        {
 14216            await method(args, request.Features, cancellationToken).ConfigureAwait(false);
 14217            return new OutgoingResponse(request);
 218        }
 0219        catch (SliceException sliceException) when (inExceptionSpecification?.Invoke(sliceException) ?? false)
 0220        {
 0221            return request.CreateSliceExceptionResponse(sliceException, SliceEncoding.Slice1);
 222        }
 14223    }
 224
 225    /// <summary>Dispatches an incoming request to a method that matches the request's operation name. The operation
 226    /// does not accept any arguments and does not return anything.</summary>
 227    /// <param name="request">The incoming request.</param>
 228    /// <param name="decodeArgs">A function that decodes the empty arguments from the request payload.</param>
 229    /// <param name="method">The user-provided implementation of the operation.</param>
 230    /// <param name="inExceptionSpecification">A function that returns <see langword="true" /> when the provided Slice
 231    /// exception conforms to the exception specification; otherwise, <see langword="false" />.</param>
 232    /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param>
 233    /// <returns>A value task that holds the outgoing response.</returns>
 234    public static async ValueTask<OutgoingResponse> DispatchOperationAsync(
 235        this IncomingRequest request,
 236        Func<IncomingRequest, CancellationToken, ValueTask> decodeArgs,
 237        Func<IFeatureCollection, CancellationToken, ValueTask> method,
 238        Func<SliceException, bool>? inExceptionSpecification = null,
 239        CancellationToken cancellationToken = default)
 54240    {
 54241        await decodeArgs(request, cancellationToken).ConfigureAwait(false);
 242        try
 54243        {
 54244            await method(request.Features, cancellationToken).ConfigureAwait(false);
 25245            return new OutgoingResponse(request);
 246        }
 21247        catch (SliceException sliceException) when (inExceptionSpecification?.Invoke(sliceException) ?? false)
 16248        {
 16249            return request.CreateSliceExceptionResponse(sliceException, SliceEncoding.Slice1);
 250        }
 41251    }
 252
 253    // The error message includes the inner exception type and message because we don't transmit this inner exception
 254    // with the response.
 255    private static string GetErrorMessage(SliceException exception) =>
 16256        exception.InnerException is Exception innerException ?
 16257            $"{exception.Message} This exception was caused by an exception of type '{innerException.GetType()}' with me
 16258            exception.Message;
 259}