diff --git a/src/RestSharp/Options/RestClientOptions.cs b/src/RestSharp/Options/RestClientOptions.cs index 9b28c66ad..1c81ecd84 100644 --- a/src/RestSharp/Options/RestClientOptions.cs +++ b/src/RestSharp/Options/RestClientOptions.cs @@ -180,10 +180,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 16be4837d..6213e5573 100644 --- a/src/RestSharp/Request/RestRequest.cs +++ b/src/RestSharp/Request/RestRequest.cs @@ -139,7 +139,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. @@ -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.Async.cs b/src/RestSharp/RestClient.Async.cs index 4655c0780..ba28ff3d0 100644 --- a/src/RestSharp/RestClient.Async.cs +++ b/src/RestSharp/RestClient.Async.cs @@ -19,6 +19,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); @@ -113,7 +115,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 c41c41953..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,8 @@ 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.Expect100Continue != null) httpClient.DefaultRequestHeaders.ExpectContinue = options.Expect100Continue; } @@ -257,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; @@ -276,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; diff --git a/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs b/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs index bac3d052d..981b552ee 100644 --- a/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs +++ b/test/RestSharp.Tests.Integrated/NonProtocolExceptionHandlingTests.cs @@ -45,7 +45,7 @@ public async Task Handles_HttpClient_Timeout_Error() { public async Task Handles_Server_Timeout_Error() { using var client = new RestClient(_server.Url!); - var request = new RestRequest("timeout") { Timeout = 500 }; + var request = new RestRequest("404") { Timeout = TimeSpan.FromMilliseconds(500) }; var response = await client.ExecuteAsync(request); response.ErrorException.Should().BeOfType(); @@ -56,7 +56,7 @@ public async Task Handles_Server_Timeout_Error() { public async Task Handles_Server_Timeout_Error_With_Deserializer() { using var client = new RestClient(_server.Url!); - var request = new RestRequest("timeout") { Timeout = 500 }; + var request = new RestRequest("timeout") { 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 ac26c9144..dbe5907e6 100644 --- a/test/RestSharp.Tests.Integrated/PutTests.cs +++ b/test/RestSharp.Tests.Integrated/PutTests.cs @@ -31,7 +31,7 @@ public async Task Can_Timeout_PUT_Async() { var request = new RestRequest("/timeout", 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 81aafbb53..70c7661fd 100644 --- a/test/RestSharp.Tests.Integrated/RequestTests.cs +++ b/test/RestSharp.Tests.Integrated/RequestTests.cs @@ -55,7 +55,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);