From c9482f3171fee432077bf8689fa9ed4572ff4114 Mon Sep 17 00:00:00 2001 From: GMIKE Date: Sat, 6 May 2023 11:37:03 +0100 Subject: [PATCH 1/2] Timeout as TimeSpan, Support custom request timeout --- src/RestSharp/Options/RestClientOptions.cs | 5 ++--- src/RestSharp/Request/RestRequest.cs | 2 +- src/RestSharp/RestClient.Async.cs | 4 +++- src/RestSharp/RestClient.cs | 4 +++- .../NonProtocolExceptionHandlingTests.cs | 4 ++-- test/RestSharp.Tests.Integrated/PutTests.cs | 2 +- test/RestSharp.Tests.Integrated/RequestTests.cs | 2 +- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/RestSharp/Options/RestClientOptions.cs b/src/RestSharp/Options/RestClientOptions.cs index 49c197ffc..81f88694d 100644 --- a/src/RestSharp/Options/RestClientOptions.cs +++ b/src/RestSharp/Options/RestClientOptions.cs @@ -172,10 +172,9 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba public CookieContainer? CookieContainer { get; set; } /// - /// Maximum request duration in milliseconds. When the request timeout is specified using , - /// the lowest value between the client timeout and request timeout will be used. + /// Request duration. Used when the request timeout is not specified using , /// - public int MaxTimeout { get; set; } + public TimeSpan? Timeout { get; set; } /// /// Default encoding to use when no encoding is specified in the content type header. diff --git a/src/RestSharp/Request/RestRequest.cs b/src/RestSharp/Request/RestRequest.cs index 9748fa9cf..64e603b7d 100644 --- a/src/RestSharp/Request/RestRequest.cs +++ b/src/RestSharp/Request/RestRequest.cs @@ -136,7 +136,7 @@ public RestRequest(Uri resource, Method method = Method.Get) /// /// Custom request timeout /// - public int Timeout { get; set; } + public TimeSpan? Timeout { get; set; } /// /// The Resource URL to make the request against. diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs index d3701e221..f34ed1866 100644 --- a/src/RestSharp/RestClient.Async.cs +++ b/src/RestSharp/RestClient.Async.cs @@ -18,6 +18,8 @@ namespace RestSharp; public partial class RestClient { + // Default HttpClient timeout + public TimeSpan DefaultTimeout = TimeSpan.FromSeconds(100); /// public async Task ExecuteAsync(RestRequest request, CancellationToken cancellationToken = default) { using var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false); @@ -90,7 +92,7 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo message.Headers.Host = Options.BaseHost; message.Headers.CacheControl = request.CachePolicy ?? Options.CachePolicy; - using var timeoutCts = new CancellationTokenSource(request.Timeout > 0 ? request.Timeout : int.MaxValue); + using var timeoutCts = new CancellationTokenSource(request.Timeout ?? Options.Timeout ?? DefaultTimeout); using var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken); var ct = cts.Token; diff --git a/src/RestSharp/RestClient.cs b/src/RestSharp/RestClient.cs index d4ae91867..528e967f8 100644 --- a/src/RestSharp/RestClient.cs +++ b/src/RestSharp/RestClient.cs @@ -216,7 +216,9 @@ public RestClient( : this(new HttpClient(handler, disposeHandler), true, configureRestClient, configureSerialization) { } static void ConfigureHttpClient(HttpClient httpClient, RestClientOptions options) { - if (options.MaxTimeout > 0) httpClient.Timeout = TimeSpan.FromMilliseconds(options.MaxTimeout); + + // We will use Options.Timeout in ExecuteAsInternalAsync method + httpClient.Timeout = Timeout.InfiniteTimeSpan; if (options.UserAgent != null && httpClient.DefaultRequestHeaders.UserAgent.All(x => $"{x.Product?.Name}/{x.Product?.Version}" != options.UserAgent)) { diff --git a/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs b/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs index 6f6d8ebda..febba56a2 100644 --- a/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs +++ b/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs @@ -50,7 +50,7 @@ public async Task Handles_HttpClient_Timeout_Error() { public async Task Handles_Server_Timeout_Error() { var client = new RestClient(_server.Url); - var request = new RestRequest("404") { Timeout = 500 }; + var request = new RestRequest("404") { Timeout = TimeSpan.FromMilliseconds(500) }; var response = await client.ExecuteAsync(request); response.ErrorException.Should().BeOfType(); @@ -60,7 +60,7 @@ public async Task Handles_Server_Timeout_Error() { [Fact] public async Task Handles_Server_Timeout_Error_With_Deserializer() { var client = new RestClient(_server.Url); - var request = new RestRequest("404") { Timeout = 500 }; + var request = new RestRequest("404") { Timeout = TimeSpan.FromMilliseconds(500) }; var response = await client.ExecuteAsync(request); response.Data.Should().BeNull(); diff --git a/test/RestSharp.Tests.Integrated/PutTests.cs b/test/RestSharp.Tests.Integrated/PutTests.cs index 1a0e09f89..ba210dccc 100644 --- a/test/RestSharp.Tests.Integrated/PutTests.cs +++ b/test/RestSharp.Tests.Integrated/PutTests.cs @@ -40,7 +40,7 @@ public async Task Can_Timeout_PUT_Async() { var request = new RestRequest(TimeoutResource, Method.Put).AddBody("Body_Content"); // Half the value of ResponseHandler.Timeout - request.Timeout = 200; + request.Timeout = TimeSpan.FromMilliseconds(200); var response = await _client.ExecuteAsync(request); diff --git a/test/RestSharp.Tests.Integrated/RequestTests.cs b/test/RestSharp.Tests.Integrated/RequestTests.cs index 98c04e65e..066200fd0 100644 --- a/test/RestSharp.Tests.Integrated/RequestTests.cs +++ b/test/RestSharp.Tests.Integrated/RequestTests.cs @@ -57,7 +57,7 @@ public async Task Can_Timeout_GET_Async() { var request = new RestRequest("timeout").AddBody("Body_Content"); // Half the value of ResponseHandler.Timeout - request.Timeout = 200; + request.Timeout = TimeSpan.FromMilliseconds(200); var response = await _client.ExecuteAsync(request); From 1aa5b02a7063b613faf6b5cd78521b7388930503 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Wed, 22 May 2024 11:12:55 +0200 Subject: [PATCH 2/2] Small cleanup --- src/RestSharp/Request/RestRequest.cs | 2 +- src/RestSharp/RestClient.cs | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/RestSharp/Request/RestRequest.cs b/src/RestSharp/Request/RestRequest.cs index af5f3c3a1..6213e5573 100644 --- a/src/RestSharp/Request/RestRequest.cs +++ b/src/RestSharp/Request/RestRequest.cs @@ -163,7 +163,7 @@ public RestRequest(Uri resource, Method method = Method.Get) /// /// Used by the default deserializers to determine where to start deserializing from. - /// Can be used to skip container or root elements that do not have corresponding deserialzation targets. + /// Can be used to skip container or root elements that do not have corresponding deserialization targets. /// public string? RootElement { get; set; } diff --git a/src/RestSharp/RestClient.cs b/src/RestSharp/RestClient.cs index a28e511bb..da8ce9a19 100644 --- a/src/RestSharp/RestClient.cs +++ b/src/RestSharp/RestClient.cs @@ -33,6 +33,7 @@ namespace RestSharp; /// /// Client to translate RestRequests into Http requests and process response result /// +// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global public partial class RestClient : IRestClient { /// /// Content types that will be sent in the Accept header. The list is populated from the known serializers. @@ -55,11 +56,6 @@ public string[] AcceptedContentTypes { /// public DefaultParameters DefaultParameters { get; } - [Obsolete("Use RestClientOptions.Authenticator instead")] - public IAuthenticator? Authenticator => Options.Authenticator; - - // set => Options.Authenticator = value; - /// /// Creates an instance of RestClient using the provided /// @@ -226,7 +222,6 @@ public RestClient( : this(new HttpClient(handler, disposeHandler), true, configureRestClient, configureSerialization) { } static void ConfigureHttpClient(HttpClient httpClient, RestClientOptions options) { - // We will use Options.Timeout in ExecuteAsInternalAsync method httpClient.Timeout = Timeout.InfiniteTimeSpan; @@ -259,6 +254,7 @@ static void ConfigureHttpMessageHandler(HttpClientHandler handler, RestClientOpt handler.AllowAutoRedirect = options.FollowRedirects; #if NET + // ReSharper disable once InvertIf if (!OperatingSystem.IsBrowser() && !OperatingSystem.IsIOS() && !OperatingSystem.IsTvOS()) { #endif if (handler.SupportsProxy) handler.Proxy = options.Proxy; @@ -278,12 +274,12 @@ void ConfigureSerializers(ConfigureSerialization? configureSerialization) { } void ConfigureDefaultParameters(RestClientOptions options) { - if (options.UserAgent != null) { - if (!options.AllowMultipleDefaultParametersWithSameName - && DefaultParameters.Any(parameter => parameter.Type == ParameterType.HttpHeader && parameter.Name == KnownHeaders.UserAgent)) - DefaultParameters.RemoveParameter(KnownHeaders.UserAgent, ParameterType.HttpHeader); - DefaultParameters.AddParameter(Parameter.CreateParameter(KnownHeaders.UserAgent, options.UserAgent, ParameterType.HttpHeader)); - } + if (options.UserAgent == null) return; + + if (!options.AllowMultipleDefaultParametersWithSameName + && DefaultParameters.Any(parameter => parameter.Type == ParameterType.HttpHeader && parameter.Name == KnownHeaders.UserAgent)) + DefaultParameters.RemoveParameter(KnownHeaders.UserAgent, ParameterType.HttpHeader); + DefaultParameters.AddParameter(Parameter.CreateParameter(KnownHeaders.UserAgent, options.UserAgent, ParameterType.HttpHeader)); } readonly bool _disposeHttpClient;