• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ThreeMammals / Ocelot / 21291719193

23 Jan 2026 03:37PM UTC coverage: 93.499% (-0.01%) from 93.509%
21291719193

Pull #2354

github

web-flow
Merge 814d375eb into 55095d8ee
Pull Request #2354: #2344 Upgrade solutions to Visual Studio 2026 format

6515 of 6968 relevant lines covered (93.5%)

4098.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.48
src/Ocelot/Requester/MessageInvokerPool.cs
1
using Ocelot.Configuration;
2
using Ocelot.Configuration.File;
3
using Ocelot.Logging;
4
using System.Net.Security;
5

6
namespace Ocelot.Requester;
7

8
public class MessageInvokerPool : IMessageInvokerPool
9
{
10
    private readonly ConcurrentDictionary<MessageInvokerCacheKey, Lazy<HttpMessageInvoker>> _handlersPool;
11
    private readonly IDelegatingHandlerFactory _handlerFactory;
12
    private readonly IOcelotLogger _logger;
13

14
    public MessageInvokerPool(
20✔
15
        IDelegatingHandlerFactory handlerFactory,
20✔
16
        IOcelotLoggerFactory loggerFactory)
20✔
17
    {
18
        ArgumentNullException.ThrowIfNull(handlerFactory);
20✔
19
        ArgumentNullException.ThrowIfNull(loggerFactory);
20✔
20

21
        _handlersPool = new();
20✔
22
        _handlerFactory = handlerFactory;
20✔
23
        _logger = loggerFactory.CreateLogger<MessageInvokerPool>();
20✔
24
    }
20✔
25

26
    public virtual HttpMessageInvoker Get(DownstreamRoute downstreamRoute)
27
    {
28
        // Since the comparison is based on the downstream route object reference,
29
        // and the QoS Options properties can't be changed after the route is created,
30
        // we don't need to use the timeout value as part of the cache key.
31
        return _handlersPool.GetOrAdd(
20✔
32
            new MessageInvokerCacheKey(downstreamRoute),
20✔
33
            cacheKey => new Lazy<HttpMessageInvoker>(() => CreateMessageInvoker(cacheKey.Route))
38✔
34
        ).Value;
20✔
35
    }
36

37
    public virtual void Clear() => _handlersPool.Clear();
1✔
38

39
    protected HttpMessageInvoker CreateMessageInvoker(DownstreamRoute route)
40
    {
41
        HttpMessageHandler baseHandler = CreateHandler(route);
19✔
42
        List<DelegatingHandler> handlers = _handlerFactory.Get(route);
19✔
43
        handlers.Reverse();
19✔
44
        foreach (DelegatingHandler handler in handlers)
42✔
45
        {
46
            handler.InnerHandler = baseHandler;
2✔
47
            baseHandler = handler;
2✔
48
        }
49

50
        int milliseconds = EnsureRouteTimeoutIsGreaterThanQosOne(route);
19✔
51
        TimeSpan timeout = TimeSpan.FromMilliseconds(milliseconds);
19✔
52

53
        // Adding timeout handler to the top of the chain.
54
        // It's standard behavior to throw TimeoutException after the defined timeout (90 seconds by default)
55
        HttpMessageHandler timeoutHandler = new TimeoutDelegatingHandler(timeout)
19✔
56
        {
19✔
57
            InnerHandler = baseHandler,
19✔
58
        };
19✔
59
        return new(timeoutHandler, true);
19✔
60
    }
61

62
    /// <summary>
63
    /// Ensures that the route timeout is greater than the QoS timeout. If the route timeout is less than or equal to the QoS timeout, returns double the QoS timeout value and logs a warning.
64
    /// </summary>
65
    /// <remarks>The method is open for overriding because it is declared as <see langword="virtual"/>.</remarks>
66
    /// <param name="route">Current processing route.</param>
67
    /// <returns>An <see cref="int"/> value representing the timeout in milliseconds, to be assigned in the upper context.</returns>
68
    protected virtual int EnsureRouteTimeoutIsGreaterThanQosOne(DownstreamRoute route)
69
    {
70
        var qos = route.QosOptions;
19✔
71
        int routeMilliseconds = 1_000 * (route.Timeout ?? DownstreamRoute.DefaultTimeoutSeconds);
19✔
72
        if (!qos.UseQos || !qos.Timeout.HasValue || routeMilliseconds > qos.Timeout)
19✔
73
        {
74
            return routeMilliseconds;
14✔
75
        }
76

77
        int milliseconds = routeMilliseconds;
5✔
78
        int doubledTimeout = 2 * qos.Timeout.Value;
5✔
79
        Func<string> getWarning = route.Timeout.HasValue
5✔
80
            ? () => $"Route '{route.Name()}' has Quality of Service settings ({nameof(FileRoute.QoSOptions)}) enabled, but either the route {nameof(route.Timeout)} or the QoS {nameof(QoSOptions.Timeout)} is misconfigured: specifically, the route {nameof(route.Timeout)} ({milliseconds} ms) {EqualitySentence(milliseconds, qos.Timeout.Value)} the QoS {nameof(QoSOptions.Timeout)} ({qos.Timeout} ms). To mitigate potential request failures, logged errors, or unexpected behavior caused by Polly's timeout strategy, Ocelot auto-doubled the QoS {nameof(QoSOptions.Timeout)} and applied {doubledTimeout} ms to the route {nameof(route.Timeout)}. However, this adjustment does not guarantee correct Polly behavior. Therefore, it's essential to assign correct values to both timeouts as soon as possible!"
3✔
81
            : () => $"Route '{route.Name()}' has Quality of Service settings ({nameof(FileRoute.QoSOptions)}) enabled, but either the {nameof(DownstreamRoute)}.{nameof(DownstreamRoute.DefaultTimeoutSeconds)} or the QoS {nameof(QoSOptions.Timeout)} is misconfigured: specifically, the {nameof(DownstreamRoute)}.{nameof(DownstreamRoute.DefaultTimeoutSeconds)} ({milliseconds} ms) {EqualitySentence(milliseconds, qos.Timeout.Value)} the QoS {nameof(QoSOptions.Timeout)} ({qos.Timeout} ms). To mitigate potential request failures, logged errors, or unexpected behavior caused by Polly's timeout strategy, Ocelot auto-doubled the QoS {nameof(QoSOptions.Timeout)} and applied {doubledTimeout} ms to the route {nameof(route.Timeout)} instead of using {nameof(DownstreamRoute)}.{nameof(DownstreamRoute.DefaultTimeoutSeconds)}. However, this adjustment does not guarantee correct Polly behavior. Therefore, it's essential to assign correct values to both timeouts as soon as possible!";
7✔
82
        _logger.LogWarning(getWarning);
5✔
83
        return doubledTimeout;
5✔
84
    }
85

86
    public static string EqualitySentence(int left, int right)
87
        => left < right ? "is shorter than" : left == right ? "is equal to" : "is longer than";
8✔
88

89
    protected virtual SocketsHttpHandler CreateHandler(DownstreamRoute route)
90
    {
91
        var options = route.HttpHandlerOptions;
19✔
92
        var handler = new SocketsHttpHandler
19✔
93
        {
19✔
94
            AllowAutoRedirect = options.AllowAutoRedirect,
19✔
95
            UseCookies = options.UseCookieContainer,
19✔
96
            UseProxy = options.UseProxy,
19✔
97
            MaxConnectionsPerServer = options.MaxConnectionsPerServer,
19✔
98
            PooledConnectionLifetime = options.PooledConnectionLifeTime,
19✔
99
        };
19✔
100

101
        if (options.UseCookieContainer)
19✔
102
        {
103
            handler.CookieContainer = new CookieContainer();
×
104
        }
105

106
        if (!route.DangerousAcceptAnyServerCertificateValidator)
19✔
107
        {
108
            return handler;
17✔
109
        }
110

111
        handler.SslOptions = new SslClientAuthenticationOptions
2✔
112
        {
2✔
113
            RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
1✔
114
        };
2✔
115
        _logger.LogWarning(() =>
2✔
116
            $"You have ignored all SSL warnings by using {nameof(DownstreamRoute.DangerousAcceptAnyServerCertificateValidator)} for this {nameof(DownstreamRoute)} -> {route.Name()}");
4✔
117
        return handler;
2✔
118
    }
119

120
    public readonly struct MessageInvokerCacheKey : IEquatable<MessageInvokerCacheKey>
121
    {
122
        public MessageInvokerCacheKey(DownstreamRoute route) => Route = route;
28✔
123

124
        public DownstreamRoute Route { get; }
51✔
125

126
        public override bool Equals(object obj) => obj is MessageInvokerCacheKey key && Equals(key);
4✔
127

128
        public bool Equals(MessageInvokerCacheKey other) =>
129
            EqualityComparer<DownstreamRoute>.Default.Equals(Route, other.Route);
6✔
130

131
        public override int GetHashCode() => Route.GetHashCode();
20✔
132

133
        public static bool operator ==(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => left.Equals(right);
2✔
134
        public static bool operator !=(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => !(left == right);
1✔
135
    }
136
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc