#703 drop ConnectionLeaseTimeout
This commit is contained in:
parent
1b7aec690b
commit
cc5d181895
@ -233,34 +233,6 @@ namespace Flurl.Test.Http
|
|||||||
Assert.IsFalse(cts.Token.IsCancellationRequested);
|
Assert.IsFalse(cts.Token.IsCancellationRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Ignore("failing on AppVeyor, holding up bugfix release")]
|
|
||||||
public async Task connection_lease_timeout_doesnt_disrupt_calls() {
|
|
||||||
// testing this quickly is tricky. HttpClient will be replaced by a new instance after 1 timeout and disposed
|
|
||||||
// after another, so the timeout period (typically minutes in real-world scenarios) needs to be long enough
|
|
||||||
// that we don't dispose before the response from google is received. 1 second seems to work.
|
|
||||||
var cli = new FlurlClient("http://www.google.com");
|
|
||||||
cli.Settings.ConnectionLeaseTimeout = TimeSpan.FromMilliseconds(1000);
|
|
||||||
|
|
||||||
var httpClients = new List<HttpClient>();
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
|
|
||||||
// ping google for about 2.5 seconds
|
|
||||||
for (var i = 0; i < 25; i++) {
|
|
||||||
if (!httpClients.Contains(cli.HttpClient))
|
|
||||||
httpClients.Add(cli.HttpClient);
|
|
||||||
tasks.Add(cli.Request().HeadAsync());
|
|
||||||
await Task.Delay(100);
|
|
||||||
}
|
|
||||||
await Task.WhenAll(tasks); // failed HTTP status, etc, would throw here and fail the test.
|
|
||||||
|
|
||||||
Assert.AreEqual(3, httpClients.Count);
|
|
||||||
|
|
||||||
// only the first one should be disposed, which isn't particularly simple to check
|
|
||||||
Assert.ThrowsAsync<ObjectDisposedException>(() => httpClients[0].GetAsync("http://www.google.com"));
|
|
||||||
await httpClients[1].GetAsync("http://www.google.com");
|
|
||||||
await httpClients[2].GetAsync("http://www.google.com");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task test_settings_override_client_settings() {
|
public async Task test_settings_override_client_settings() {
|
||||||
var cli1 = new FlurlClient();
|
var cli1 = new FlurlClient();
|
||||||
|
@ -278,20 +278,6 @@ namespace Flurl.Test.Http
|
|||||||
Assert.IsInstanceOf<SomeCustomHttpClient>(cli.HttpClient);
|
Assert.IsInstanceOf<SomeCustomHttpClient>(cli.HttpClient);
|
||||||
Assert.IsInstanceOf<SomeCustomMessageHandler>(cli.HttpMessageHandler);
|
Assert.IsInstanceOf<SomeCustomMessageHandler>(cli.HttpMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task connection_lease_timeout_creates_new_HttpClient() {
|
|
||||||
var cli = new FlurlClient("http://api.com");
|
|
||||||
cli.Settings.ConnectionLeaseTimeout = TimeSpan.FromMilliseconds(50);
|
|
||||||
var hc = cli.HttpClient;
|
|
||||||
|
|
||||||
await Task.Delay(25);
|
|
||||||
Assert.That(hc == cli.HttpClient);
|
|
||||||
|
|
||||||
// exceed the timeout
|
|
||||||
await Task.Delay(25);
|
|
||||||
Assert.That(hc != cli.HttpClient);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestFixture, Parallelizable]
|
[TestFixture, Parallelizable]
|
||||||
|
@ -174,16 +174,6 @@ namespace Flurl.Http.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ClientFlurlHttpSettings : FlurlHttpSettings
|
public class ClientFlurlHttpSettings : FlurlHttpSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Specifies the time to keep the underlying HTTP/TCP connection open. When expired, a Connection: close header
|
|
||||||
/// is sent with the next request, which should force a new connection and DSN lookup to occur on the next call.
|
|
||||||
/// Default is null, effectively disabling the behavior.
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan? ConnectionLeaseTimeout {
|
|
||||||
get => Get<TimeSpan?>();
|
|
||||||
set => Set(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a factory used to create the HttpClient and HttpMessageHandler used for HTTP calls.
|
/// Gets or sets a factory used to create the HttpClient and HttpMessageHandler used for HTTP calls.
|
||||||
/// Whenever possible, custom factory implementations should inherit from DefaultHttpClientFactory,
|
/// Whenever possible, custom factory implementations should inherit from DefaultHttpClientFactory,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Flurl.Http.Configuration;
|
using Flurl.Http.Configuration;
|
||||||
using Flurl.Http.Testing;
|
using Flurl.Http.Testing;
|
||||||
@ -64,7 +62,7 @@ namespace Flurl.Http
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseUrl">The base URL associated with this client.</param>
|
/// <param name="baseUrl">The base URL associated with this client.</param>
|
||||||
public FlurlClient(string baseUrl = null) {
|
public FlurlClient(string baseUrl = null) {
|
||||||
_httpClient = new Lazy<HttpClient>(CreateHttpClient);
|
_httpClient = new Lazy<HttpClient>(() => Settings.HttpClientFactory.CreateHttpClient(HttpMessageHandler));
|
||||||
_httpMessageHandler = new Lazy<HttpMessageHandler>(() => Settings.HttpClientFactory.CreateMessageHandler());
|
_httpMessageHandler = new Lazy<HttpMessageHandler>(() => Settings.HttpClientFactory.CreateMessageHandler());
|
||||||
BaseUrl = baseUrl;
|
BaseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
@ -86,7 +84,7 @@ namespace Flurl.Http
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ClientFlurlHttpSettings Settings {
|
public ClientFlurlHttpSettings Settings {
|
||||||
get => _settings ?? (_settings = new ClientFlurlHttpSettings());
|
get => _settings ??= new ClientFlurlHttpSettings();
|
||||||
set => _settings = value;
|
set => _settings = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,47 +92,7 @@ namespace Flurl.Http
|
|||||||
public INameValueList<string> Headers { get; } = new NameValueList<string>(false); // header names are case-insensitive https://stackoverflow.com/a/5259004/62600
|
public INameValueList<string> Headers { get; } = new NameValueList<string>(false); // header names are case-insensitive https://stackoverflow.com/a/5259004/62600
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public HttpClient HttpClient => HttpTest.Current?.HttpClient ?? _injectedClient ?? GetHttpClient();
|
public HttpClient HttpClient => HttpTest.Current?.HttpClient ?? _injectedClient ?? _httpClient.Value;
|
||||||
|
|
||||||
private DateTime? _clientCreatedAt;
|
|
||||||
private HttpClient _zombieClient;
|
|
||||||
private readonly object _connectionLeaseLock = new object();
|
|
||||||
|
|
||||||
private HttpClient GetHttpClient() {
|
|
||||||
if (ConnectionLeaseExpired()) {
|
|
||||||
lock (_connectionLeaseLock) {
|
|
||||||
if (ConnectionLeaseExpired()) {
|
|
||||||
// when the connection lease expires, force a new HttpClient to be created, but don't
|
|
||||||
// dispose the old one just yet - it might have pending requests. Instead, it becomes
|
|
||||||
// a zombie and is disposed on the _next_ lease timeout, which should be safe.
|
|
||||||
_zombieClient?.Dispose();
|
|
||||||
_zombieClient = _httpClient.Value;
|
|
||||||
_httpClient = new Lazy<HttpClient>(CreateHttpClient);
|
|
||||||
_httpMessageHandler = new Lazy<HttpMessageHandler>(() => Settings.HttpClientFactory.CreateMessageHandler());
|
|
||||||
_clientCreatedAt = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _httpClient.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpClient CreateHttpClient() {
|
|
||||||
var cli = Settings.HttpClientFactory.CreateHttpClient(HttpMessageHandler);
|
|
||||||
_clientCreatedAt = DateTime.UtcNow;
|
|
||||||
return cli;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ConnectionLeaseExpired() {
|
|
||||||
// for thread safety, capture these to temp variables
|
|
||||||
var createdAt = _clientCreatedAt;
|
|
||||||
var timeout = Settings.ConnectionLeaseTimeout;
|
|
||||||
|
|
||||||
return
|
|
||||||
_httpClient.IsValueCreated &&
|
|
||||||
createdAt.HasValue &&
|
|
||||||
timeout.HasValue &&
|
|
||||||
DateTime.UtcNow - createdAt.Value > timeout.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public HttpMessageHandler HttpMessageHandler => HttpTest.Current?.HttpMessageHandler ?? _httpMessageHandler?.Value;
|
public HttpMessageHandler HttpMessageHandler => HttpTest.Current?.HttpMessageHandler ?? _httpMessageHandler?.Value;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user