| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | using IceRpc.Slice.Internal; |
| | 4 | | using System.IO.Pipelines; |
| | 5 | | using ZeroC.Slice; |
| | 6 | |
|
| | 7 | | namespace IceRpc.Slice; |
| | 8 | |
|
| | 9 | | /// <summary>Provides extension methods for <see cref="IncomingRequest" /> to decode its Slice-encoded payload. |
| | 10 | | /// </summary> |
| | 11 | | public static class IncomingRequestExtensions |
| | 12 | | { |
| | 13 | | /// <summary>The generated code calls this method to ensure that when an operation is not declared idempotent, |
| | 14 | | /// the request is not marked idempotent. If the request is marked idempotent, it means the caller incorrectly |
| | 15 | | /// believes this operation is idempotent.</summary> |
| | 16 | | /// <param name="request">The request to check.</param> |
| | 17 | | /// <exception cref="InvalidDataException">Thrown if the request contains the <see cref="RequestFieldKey.Idempotent" |
| | 18 | | /// field.</exception> |
| | 19 | | public static void CheckNonIdempotent(this IncomingRequest request) |
| 107 | 20 | | { |
| 107 | 21 | | if (request.Fields.ContainsKey(RequestFieldKey.Idempotent)) |
| 0 | 22 | | { |
| 0 | 23 | | throw new InvalidDataException( |
| 0 | 24 | | $"Invocation mode mismatch for operation '{request.Operation}': received idempotent field for an operati |
| | 25 | | } |
| 107 | 26 | | } |
| | 27 | |
|
| | 28 | | /// <summary>Creates an outgoing response with status code <see cref="StatusCode.ApplicationError" /> with |
| | 29 | | /// a Slice exception payload.</summary> |
| | 30 | | /// <param name="request">The incoming request.</param> |
| | 31 | | /// <param name="sliceException">The Slice exception to encode in the payload.</param> |
| | 32 | | /// <param name="encoding">The encoding used for the request payload.</param> |
| | 33 | | /// <returns>The new outgoing response.</returns> |
| | 34 | | /// <exception cref="NotSupportedException">Thrown when <paramref name="sliceException" /> does not support encoding |
| | 35 | | /// <paramref name="encoding" />.</exception> |
| | 36 | | public static OutgoingResponse CreateSliceExceptionResponse( |
| | 37 | | this IncomingRequest request, |
| | 38 | | SliceException sliceException, |
| | 39 | | SliceEncoding encoding) |
| 16 | 40 | | { |
| 16 | 41 | | SliceEncodeOptions encodeOptions = |
| 16 | 42 | | request.Features.Get<ISliceFeature>()?.EncodeOptions ?? SliceEncodeOptions.Default; |
| | 43 | |
|
| 16 | 44 | | var pipe = new Pipe(encodeOptions.PipeOptions); |
| | 45 | |
|
| | 46 | | try |
| 16 | 47 | | { |
| 16 | 48 | | var encoder = new SliceEncoder(pipe.Writer, encoding); |
| | 49 | |
|
| | 50 | | // sliceException.Encode can throw NotSupportedException |
| 16 | 51 | | if (encoding == SliceEncoding.Slice1) |
| 16 | 52 | | { |
| 16 | 53 | | sliceException.Encode(ref encoder); |
| 16 | 54 | | } |
| | 55 | | else |
| 0 | 56 | | { |
| 0 | 57 | | Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4); |
| 0 | 58 | | int startPos = encoder.EncodedByteCount; |
| 0 | 59 | | sliceException.Encode(ref encoder); |
| 0 | 60 | | SliceEncoder.EncodeVarUInt62((ulong)(encoder.EncodedByteCount - startPos), sizePlaceholder); |
| 0 | 61 | | } |
| | 62 | |
|
| 16 | 63 | | pipe.Writer.Complete(); |
| | 64 | |
|
| 16 | 65 | | return new OutgoingResponse(request, StatusCode.ApplicationError, GetErrorMessage(sliceException)) |
| 16 | 66 | | { |
| 16 | 67 | | Payload = pipe.Reader |
| 16 | 68 | | }; |
| | 69 | | } |
| 0 | 70 | | catch |
| 0 | 71 | | { |
| 0 | 72 | | pipe.Reader.Complete(); |
| 0 | 73 | | pipe.Writer.Complete(); |
| 0 | 74 | | throw; |
| | 75 | | } |
| 16 | 76 | | } |
| | 77 | |
|
| | 78 | | /// <summary>Decodes a request payload into a list of arguments.</summary> |
| | 79 | | /// <typeparam name="T">The type of the request parameters.</typeparam> |
| | 80 | | /// <param name="request">The incoming request.</param> |
| | 81 | | /// <param name="encoding">The encoding of the request's payload.</param> |
| | 82 | | /// <param name="decodeFunc">The decode function for the arguments from the payload.</param> |
| | 83 | | /// <param name="defaultActivator">The activator to use when the activator provided by the request's <see |
| | 84 | | /// cref="ISliceFeature" /> is <see langword="null" />. Used only when <paramref name="encoding" /> is <see |
| | 85 | | /// cref="SliceEncoding.Slice1" />.</param> |
| | 86 | | /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param> |
| | 87 | | /// <returns>The request arguments.</returns> |
| | 88 | | public static ValueTask<T> DecodeArgsAsync<T>( |
| | 89 | | this IncomingRequest request, |
| | 90 | | SliceEncoding encoding, |
| | 91 | | DecodeFunc<T> decodeFunc, |
| | 92 | | IActivator? defaultActivator = null, |
| | 93 | | CancellationToken cancellationToken = default) |
| 91 | 94 | | { |
| 91 | 95 | | ISliceFeature feature = request.Features.Get<ISliceFeature>() ?? SliceFeature.Default; |
| | 96 | |
|
| 91 | 97 | | return request.DecodeValueAsync( |
| 91 | 98 | | encoding, |
| 91 | 99 | | feature, |
| 91 | 100 | | feature.BaseProxy, |
| 91 | 101 | | decodeFunc, |
| 91 | 102 | | feature.Activator ?? defaultActivator, |
| 91 | 103 | | cancellationToken); |
| 91 | 104 | | } |
| | 105 | |
|
| | 106 | | /// <summary>Verifies that a request payload carries no argument or only unknown tagged arguments.</summary> |
| | 107 | | /// <param name="request">The incoming request.</param> |
| | 108 | | /// <param name="encoding">The encoding of the request payload.</param> |
| | 109 | | /// <param name="cancellationToken">A cancellation token that receives the cancellation requests.</param> |
| | 110 | | /// <returns>A value task that completes when the checking is complete.</returns> |
| | 111 | | public static ValueTask DecodeEmptyArgsAsync( |
| | 112 | | this IncomingRequest request, |
| | 113 | | SliceEncoding encoding, |
| | 114 | | CancellationToken cancellationToken = default) => |
| 75 | 115 | | request.DecodeVoidAsync( |
| 75 | 116 | | encoding, |
| 75 | 117 | | request.Features.Get<ISliceFeature>() ?? SliceFeature.Default, |
| 75 | 118 | | cancellationToken); |
| | 119 | |
|
| | 120 | | // The error message includes the inner exception type and message because we don't transmit this inner exception |
| | 121 | | // with the response. |
| | 122 | | private static string GetErrorMessage(SliceException exception) => |
| 16 | 123 | | exception.InnerException is Exception innerException ? |
| 16 | 124 | | $"{exception.Message} This exception was caused by an exception of type '{innerException.GetType()}' with me |
| 16 | 125 | | exception.Message; |
| | 126 | | } |