< Summary

Information
Class: IceRpc.Deadline.DeadlineMiddleware
Assembly: IceRpc.Deadline
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Deadline/DeadlineMiddleware.cs
Tag: 592_20856082467
Line coverage
84%
Covered lines: 28
Uncovered lines: 5
Coverable lines: 33
Total lines: 74
Line coverage: 84.8%
Branch coverage
70%
Covered branches: 7
Total branches: 10
Branch coverage: 70%
Method coverage
100%
Covered methods: 3
Total methods: 3
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
DispatchAsync(...)62.5%9.37872.22%
PerformDispatchAsync()100%11100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Deadline/DeadlineMiddleware.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Extensions.DependencyInjection;
 4using IceRpc.Features;
 5using ZeroC.Slice;
 6
 7namespace IceRpc.Deadline;
 8
 9/// <summary>Represents a middleware that decodes deadline fields into deadline features. When the decoded deadline
 10/// expires, this middleware cancels the dispatch and returns an <see cref="OutgoingResponse" /> with status code
 11/// <see cref="StatusCode.DeadlineExceeded" />.</summary>
 12/// <seealso cref="DeadlineRouterExtensions"/>
 13/// <seealso cref="DeadlineDispatcherBuilderExtensions"/>
 14public class DeadlineMiddleware : IDispatcher
 15{
 16    private readonly IDispatcher _next;
 17    private readonly TimeProvider _timeProvider;
 18
 19    /// <summary>Constructs a deadline middleware.</summary>
 20    /// <param name="next">The next dispatcher in the dispatch pipeline.</param>
 21    /// <param name="timeProvider">The optional time provider used to obtain the current time. If <see langword="null"/>
 22    /// <see cref="TimeProvider.System"/>.</param>
 223    public DeadlineMiddleware(IDispatcher next, TimeProvider? timeProvider = null)
 224    {
 225        _next = next;
 226        _timeProvider = timeProvider ?? TimeProvider.System;
 227    }
 28
 29    /// <inheritdoc/>
 30    public ValueTask<OutgoingResponse> DispatchAsync(
 31        IncomingRequest request,
 32        CancellationToken cancellationToken = default)
 233    {
 234        TimeSpan? timeout = null;
 35
 36        // not found returns default == DateTime.MinValue.
 237        DateTime deadline = request.Fields.DecodeValue(
 238            RequestFieldKey.Deadline,
 439            (ref SliceDecoder decoder) => decoder.DecodeTimeStamp());
 40
 241        if (deadline != DateTime.MinValue)
 242        {
 243            timeout = deadline - _timeProvider.GetUtcNow().UtcDateTime;
 44
 245            if (timeout <= TimeSpan.Zero)
 046            {
 047                return new(new OutgoingResponse(
 048                    request,
 049                    StatusCode.DeadlineExceeded,
 050                    "The request deadline has expired."));
 51            }
 52
 253            request.Features = request.Features.With<IDeadlineFeature>(new DeadlineFeature(deadline));
 254        }
 55
 256        return timeout is null ? _next.DispatchAsync(request, cancellationToken) : PerformDispatchAsync(timeout.Value);
 57
 58        async ValueTask<OutgoingResponse> PerformDispatchAsync(TimeSpan timeout)
 259        {
 260            using var timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 261            timeoutTokenSource.CancelAfter(timeout);
 62
 63            try
 264            {
 265                return await _next.DispatchAsync(request, timeoutTokenSource.Token).ConfigureAwait(false);
 66            }
 167            catch (OperationCanceledException exception) when (exception.CancellationToken == timeoutTokenSource.Token)
 168            {
 169                cancellationToken.ThrowIfCancellationRequested();
 170                return new OutgoingResponse(request, StatusCode.DeadlineExceeded, "The request deadline has expired.");
 71            }
 272        }
 273    }
 74}