Exemple #1
0
        public async Task evicts_handlers_after_timeout()
        {
            var createdHandlers = new List <HttpMessageHandler>();

            HttpMessageHandler createHandler()
            {
                var handler =
                    new DelegateHandler((message, token) => Task.FromResult(new HttpResponseMessage()));

                createdHandlers.Add(handler);
                return(handler);
            }

            var factory = new RoundRobinHttpClientFactory(1, createHandler, TimeSpan.FromSeconds(5));

            var clients = Enumerable.Range(0, 10).Select(i => factory.GetClient()).ToList();

            clients.Count.ShouldBe(10);
            createdHandlers.Count.ShouldBe(1);

            await Task.Delay(TimeSpan.FromSeconds(6));

            clients = Enumerable.Range(0, 10).Select(i => factory.GetClient()).ToList();
            clients.Count.ShouldBe(10);
            createdHandlers.Count.ShouldBe(2);
        }
Exemple #2
0
        async Task CreateAndDisposeClientWithGarbageCollection(RoundRobinHttpClientFactory factory,
                                                               Func <HttpClient, Task> clientInvoker = null)
        {
            var client = factory.GetClient();

            if (clientInvoker != null)
            {
                await clientInvoker(client);
            }
            client.Dispose();
        }
Exemple #3
0
        public static T ReverseProxy <T>(this T uses, ReverseProxyOptions options = null) where T : IUses
        {
            options = options ?? new ReverseProxyOptions();

            if (options.HttpClient.RoundRobin.Enabled)
            {
                var handler = options.HttpClient.Handler;
                if (options.HttpClient.RoundRobin.ClientPerNode)
                {
                    handler = () => new LockToIPAddress(options.HttpClient.Handler(), options.HttpClient.RoundRobin.DnsResolver);
                }

                var factory = new RoundRobinHttpClientFactory(
                    options.HttpClient.RoundRobin.ClientCount,
                    handler,
                    options.HttpClient.RoundRobin.LeaseTime);

                uses.Dependency(d => d.Singleton(() => new ReverseProxy(
                                                     options.Timeout,
                                                     options.ForwardedHeaders.ConvertLegacyHeaders,
                                                     options.Via.Pseudonym,
                                                     factory.GetClient,
                                                     options.OnSend,
                                                     options.OnProxyResponse
                                                     )));
            }
            else
            {
                uses.Dependency(d => d.Singleton(() => new ReverseProxy(
                                                     options.Timeout,
                                                     options.ForwardedHeaders.ConvertLegacyHeaders,
                                                     options.Via.Pseudonym,
                                                     options.HttpClient.Factory,
                                                     options.OnSend,
                                                     options.OnProxyResponse
                                                     )));
            }

            var has = (IHas)uses;

            has.ResourcesOfType <ReverseProxyResponse>()
            .WithoutUri
            .TranscodedBy <ReverseProxyResponseCodec>()
            .ForMediaType("*/*");

            if (options.ForwardedHeaders.RunAsForwardedHost)
            {
                uses.PipelineContributor <RewriteAppBaseUsingForwardedHeaders>();
            }

            return(uses);
        }
Exemple #4
0
        public async Task disposes_handlers_once_out_of_scope()
        {
            DisposeTrackingHandler handler = null;

            HttpMessageHandler createHandler()
            {
                return(handler =
                           new DisposeTrackingHandler(
                               new DelegateHandler((message, token) => Task.FromResult(new HttpResponseMessage()))));
            }

            var factory = new RoundRobinHttpClientFactory(1, createHandler, TimeSpan.FromMilliseconds(1));

            await CreateAndDisposeClientWithGarbageCollection(factory);

            WaitForDispose(handler, TimeSpan.FromSeconds(15));

            handler.IsDisposed.ShouldBeTrue();
        }
Exemple #5
0
        public void creates_as_many_handlers_as_requested_count()
        {
            var createdHandlers = new List <HttpMessageHandler>();

            HttpMessageHandler createHandler()
            {
                var handler =
                    new DelegateHandler((message, token) => Task.FromResult(new HttpResponseMessage()));

                createdHandlers.Add(handler);
                return(handler);
            }

            var factory = new RoundRobinHttpClientFactory(5, createHandler, Timeout.InfiniteTimeSpan);

            var clients = Enumerable.Range(0, 10).Select(i => factory.GetClient()).ToList();

            clients.Distinct().Count().ShouldBe(10);
            createdHandlers.Count.ShouldBe(5);
        }
Exemple #6
0
        public async Task evicts_throwing_handlers()
        {
            var handler = new DisposeTrackingHandler(
                new DelegateHandler((message, token) => throw new HttpRequestException()));
            var factory = new RoundRobinHttpClientFactory(1, () => handler);

            await CreateAndDisposeClientWithGarbageCollection(factory, async client =>
            {
                try
                {
                    await client.GetAsync("http://nowhere.example");
                }
                catch
                {
                }
            });

            WaitForDispose(handler);

            handler.IsDisposed.ShouldBeTrue();
        }
Exemple #7
0
        public async Task evicts_503_handlers_temporarily()
        {
            var activeHandler = new DelegateHandler((message, token) => Task.FromResult(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content    = new StringContent("active")
            }));
            int timedOut     = 0;
            var retryHandler = new DelegateHandler((message, token) =>
            {
                if (Interlocked.Exchange(ref timedOut, 1) == 0)
                {
                    return(Task.FromResult(new HttpResponseMessage()
                    {
                        StatusCode = HttpStatusCode.ServiceUnavailable,
                        Headers = { RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(5)) }
                    }));
                }
                return(Task.FromResult(new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent("reactivated")
                }));
            });
            var handlerIndex = 0;
            var factory      = new RoundRobinHttpClientFactory(2, () => handlerIndex++ *2 == 0 ? activeHandler : retryHandler);

            var responsesBefore = await execute();

            await Task.Delay(TimeSpan.FromSeconds(6));

            var responsesAfter = await execute();

            responsesBefore.Count(r => r.response.StatusCode == HttpStatusCode.ServiceUnavailable).ShouldBe(1);
            responsesBefore.Any(r => r.content == "reactivated").ShouldBeFalse();

            responsesAfter.All(r => r.response.StatusCode == HttpStatusCode.OK).ShouldBeTrue();
            responsesAfter.Count(r => r.content == "reactivated").ShouldBe(5);
            responsesAfter.Count(r => r.content == "active").ShouldBe(5);


            async Task <List <(HttpResponseMessage response, string content)> > execute()
            {
                List <(HttpResponseMessage httpResponseMessage, string content)> response = new List <(HttpResponseMessage httpResponseMessage, string content)>();

                for (int i = 0; i < 10; i++)
                {
                    using (var client = factory.GetClient())
                    {
                        var httpResponseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://nowhere.example/path"));

                        var content = httpResponseMessage.Content == null
              ? null
              : await httpResponseMessage.Content?.ReadAsStringAsync();

                        response.Add((httpResponseMessage, content));
                    }
                }

                return(response);
            }
        }
        public static T ReverseProxy <T>(this T uses, ReverseProxyOptions options = null) where T : IUses
        {
            options = options ?? new ReverseProxyOptions();

            if (options.HttpClient.RoundRobin.Enabled)
            {
                var handler = options.HttpClient.Handler;
                Func <ActiveHandler, bool> shouldEvict = null;

                if (options.HttpClient.RoundRobin.ClientPerNode)
                {
                    var hostResolver = new ServiceResolver(
                        options.HttpClient.RoundRobin.DnsResolver,
                        options.HttpClient.RoundRobin.OnHostEvicted,
                        TimeSpan.FromSeconds(10));

                    handler = () => new OverrideHostNameResolver(
                        options.HttpClient.Handler(),
                        hostResolver.Resolve,
                        options.HttpClient.RoundRobin.OnError);

                    // shouldEvict = ShouldEvict(hostResolver);
                }

                var factory = new RoundRobinHttpClientFactory(
                    options.HttpClient.RoundRobin.ClientCount,
                    handler,
                    options.HttpClient.RoundRobin.LeaseTime,
                    shouldEvict);

                uses.Dependency(d => d.Singleton(() => new ReverseProxy(
                                                     options.Timeout,
                                                     options.ForwardedHeaders.ConvertLegacyHeaders,
                                                     options.Via.Pseudonym,
                                                     factory.GetClient,
                                                     options.OnSend,
                                                     options.OnProxyResponse
                                                     )));
            }
            else
            {
                uses.Dependency(d => d.Singleton(() => new ReverseProxy(
                                                     options.Timeout,
                                                     options.ForwardedHeaders.ConvertLegacyHeaders,
                                                     options.Via.Pseudonym,
                                                     options.HttpClient.Factory,
                                                     options.OnSend,
                                                     options.OnProxyResponse
                                                     )));
            }

            var has = (IHas)uses;

            has.ResourcesOfType <ReverseProxyResponse>()
            .WithoutUri
            .TranscodedBy <ReverseProxyResponseCodec>()
            .ForMediaType("*/*");

            if (options.ForwardedHeaders.RunAsForwardedHost)
            {
                uses.PipelineContributor <RewriteAppBaseUsingForwardedHeaders>();
            }

            return(uses);
        }