コード例 #1
0
        public async void TestTimeout()
        {
            var cachedToken = "cachedToken";
            var req         = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == cachedToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.GatewayTimeout
            }, TimeSpan.FromSeconds(12));

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(10));

            var invoker = new HttpMessageInvoker(fh);

            req.Properties.Add(ForgeConfiguration.ScopeKey, scope);
            await Assert.ThrowsAsync <Polly.Timeout.TimeoutRejectedException>(async() => await invoker.SendAsync(req, new CancellationToken()));

            sink.VerifyAll();
        }
コード例 #2
0
        public async void TestRefreshExpiredTokenByOneThreadOnly()
        {
            var newToken    = "newToken";
            var cachedToken = "cachedToken";
            var requestUri  = new Uri("http://example.com");
            var config      = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == config.AuthenticationAddress), It.IsAny <CancellationToken>()))
            // some artifical delay to ensure that the other thread will attempt to enter the critical section
            .ReturnsAsync(new HttpResponseMessage()
            {
                Content = new StringContent(JsonConvert.SerializeObject(new Dictionary <string, string> {
                    { "token_type", "Bearer" }, { "access_token", newToken }, { "expires_in", "3" }
                })),
                StatusCode = System.Net.HttpStatusCode.OK
            }, TweakableForgeHandler.DefaultTimeout / 2
                          );
            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == requestUri && r.Headers.Authorization.Parameter == newToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.OK
            });

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            //we have token but it is expired already
            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(0));

            //launch 2 threads to make parallel requests
            Func <Task> lambda = async() =>
            {
                var req = new HttpRequestMessage();
                req.RequestUri = requestUri;
                var invoker = new HttpMessageInvoker(fh);

                req.Options.Set(ForgeConfiguration.ScopeKey, scope);
                await invoker.SendAsync(req, CancellationToken.None);
            };

            await Task.WhenAll(lambda(), lambda());

            // We expect exactly one auth call
            sink.Protected().As <HttpMessageInvoker>().Verify(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == config.AuthenticationAddress), It.IsAny <CancellationToken>()), Times.Once());

            sink.VerifyAll();
        }
コード例 #3
0
        public async void TestCorrectNumberOfRetries()
        {
            var cachedToken = "cachedToken";
            var req         = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };

            var gatewayTimeout = new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.GatewayTimeout
            };
            var tooManyRequests = new HttpResponseMessage {
                StatusCode = (System.Net.HttpStatusCode) 429
            };

            tooManyRequests.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromSeconds(2));
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().SetupSequence(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == cachedToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(tooManyRequests)
            .ReturnsAsync(tooManyRequests)
            .ReturnsAsync(tooManyRequests)
            .ThrowsAsync(new HttpRequestException())
            .ReturnsAsync(gatewayTimeout)
            .ReturnsAsync(gatewayTimeout);


            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(10));

            var invoker = new HttpMessageInvoker(fh);

            req.Options.Set(ForgeConfiguration.ScopeKey, scope);
            var resp = await invoker.SendAsync(req, CancellationToken.None);

            Assert.Equal(System.Net.HttpStatusCode.GatewayTimeout, resp.StatusCode);

            // We retry 5 times so expect 6 calls
            sink.Protected().As <HttpMessageInvoker>().Verify(o => o.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()), Times.Exactly(6));

            sink.VerifyAll();
        }
コード例 #4
0
        public async void TestRetryOnceOnAuthenticationFailure()
        {
            var newToken    = "newToken";
            var cachedToken = "cachedToken";
            var req         = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == cachedToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode     = System.Net.HttpStatusCode.Unauthorized,
                RequestMessage = req
            });
            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == config.AuthenticationAddress), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                Content = new StringContent(JsonConvert.SerializeObject(new Dictionary <string, string> {
                    { "token_type", "Bearer" }, { "access_token", newToken }, { "expires_in", "3" }
                })),
                StatusCode = System.Net.HttpStatusCode.OK
            });
            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == newToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.OK
            });

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            //we have token but it bad for some reason (maybe revoked)
            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(300));

            var invoker = new HttpMessageInvoker(fh);

            req.Options.Set(ForgeConfiguration.ScopeKey, scope);
            await invoker.SendAsync(req, CancellationToken.None);

            sink.VerifyAll();
        }
コード例 #5
0
        /// <summary>
        /// Create all required components for custom timeout validation.
        /// </summary>
        /// <param name="allowedTimeInSec">Allowed time in seconds.</param>
        /// <param name="responseTime">Actual response time.</param>
        /// <returns>
        /// Tuple with:
        /// * mock to validate after tests are complete.
        /// * functor to perform mocked HTTP request/response operation.
        /// </returns>
        private (Mock <HttpMessageHandler> sink, Func <Task <HttpResponseMessage> > requestSender) GetReady(int allowedTimeInSec, TimeSpan responseTime)
        {
            var req  = RequestWithTimeout(allowedTimeInSec);
            var sink = MakeSink(req, responseTime);

            var fh = new TweakableForgeHandler(Options.Create(_forgeConfig))
            {
                InnerHandler = sink.Object
            };

            fh.TokenCache.Add(Scope, $"Bearer {CachedToken}", TimeSpan.FromSeconds(10));

            var invoker = new HttpMessageInvoker(fh);

            return(sink, () => invoker.SendAsync(req, new CancellationToken()));
        }
コード例 #6
0
        public async void TestCircuitBreaker()
        {
            var cachedToken = "cachedToken";
            var req         = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == cachedToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.InternalServerError
            });

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(10));

            var invoker = new HttpMessageInvoker(fh);

            req.Options.Set(ForgeConfiguration.ScopeKey, scope);
            // We tolerate 3 failures before we break the circuit
            for (int i = 0; i < 3; i++)
            {
                var resp = await invoker.SendAsync(req, CancellationToken.None);

                Assert.Equal(System.Net.HttpStatusCode.InternalServerError, resp.StatusCode);
            }

            await Assert.ThrowsAsync <Polly.CircuitBreaker.BrokenCircuitException <HttpResponseMessage> >(async() => await invoker.SendAsync(req, CancellationToken.None));


            sink.Protected().As <HttpMessageInvoker>().Verify(o => o.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()), Times.Exactly(3));

            sink.VerifyAll();
        }
コード例 #7
0
        public async void TestUseGoodToken()
        {
            var cachedToken = "cachedToken";
            var req         = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == cachedToken), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.OK
            });

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            fh.TokenCache.Add(scope, $"Bearer {cachedToken}", TimeSpan.FromSeconds(10));

            var invoker = new HttpMessageInvoker(fh);

            req.Options.Set(ForgeConfiguration.ScopeKey, scope);
            var resp = await invoker.SendAsync(req, CancellationToken.None);

            Assert.Equal(System.Net.HttpStatusCode.OK, resp.StatusCode);

            // We expect exactly one network call
            sink.Protected().As <HttpMessageInvoker>().Verify(o => o.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()), Times.Once());

            sink.VerifyAll();
        }
コード例 #8
0
        public async void TestNoRefreshOnClientProvidedToken()
        {
            var token = "blabla";
            var req   = new HttpRequestMessage();

            req.RequestUri = new Uri("http://example.com");
            var config = new ForgeConfiguration()
            {
                ClientId     = "ClientId",
                ClientSecret = "ClientSecret"
            };
            var sink = new Mock <HttpMessageHandler>(MockBehavior.Strict);

            sink.Protected().As <HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is <HttpRequestMessage>(r => r.RequestUri == req.RequestUri && r.Headers.Authorization.Parameter == token), It.IsAny <CancellationToken>()))
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = System.Net.HttpStatusCode.Unauthorized
            });

            var fh = new TweakableForgeHandler(Options.Create(config))
            {
                InnerHandler = sink.Object
            };

            var scope = "somescope";

            var invoker = new HttpMessageInvoker(fh);

            req.Options.Set(ForgeConfiguration.ScopeKey, scope);
            req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
            var resp = await invoker.SendAsync(req, CancellationToken.None);

            Assert.Equal(System.Net.HttpStatusCode.Unauthorized, resp.StatusCode);

            // We expect exactly one network call
            sink.Protected().As <HttpMessageInvoker>().Verify(o => o.SendAsync(It.IsAny <HttpRequestMessage>(), It.IsAny <CancellationToken>()), Times.Once());

            sink.VerifyAll();
        }