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); }
async Task CreateAndDisposeClientWithGarbageCollection(RoundRobinHttpClientFactory factory, Func <HttpClient, Task> clientInvoker = null) { var client = factory.GetClient(); if (clientInvoker != null) { await clientInvoker(client); } client.Dispose(); }
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); }
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(); }
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); }
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(); }
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); }