From baa1fe4602c5366bec1c16e7a425e8f9f015b216 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Fri, 19 Mar 2021 21:31:46 +0100 Subject: [PATCH] Fixed: Qbittorrent api errors when only one of two seed criteria was configured closes #2099 (cherry picked from commit 652d44722b96a7a830ec45fe83260ddcecc525a7) --- .../Http/HttpClientFixture.cs | 32 +++++++++++++ src/NzbDrone.Common/Http/HttpClient.cs | 7 ++- src/NzbDrone.Common/Http/HttpRequest.cs | 5 +++ .../Http/HttpRequestBuilder.cs | 3 ++ .../Clients/QBittorrent/QBittorrent.cs | 28 +++++++++--- .../QBittorrent/QBittorrentProxySelector.cs | 4 +- .../Clients/QBittorrent/QBittorrentProxyV1.cs | 32 +++++++++---- .../Clients/QBittorrent/QBittorrentProxyV2.cs | 45 +++++++++++++------ 8 files changed, 123 insertions(+), 33 deletions(-) diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index ee0829823..de37f5ec1 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -190,6 +190,38 @@ namespace NzbDrone.Common.Test.Http ExceptionVerification.IgnoreWarns(); } + [Test] + public void should_not_throw_on_suppressed_status_codes() + { + var request = new HttpRequest($"http://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); + request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound }; + + var exception = Assert.Throws(() => Subject.Get(request)); + + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_log_unsuccessful_status_codes() + { + var request = new HttpRequest($"http://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); + + var exception = Assert.Throws(() => Subject.Get(request)); + + ExceptionVerification.ExpectedWarns(1); + } + + [Test] + public void should_not_log_unsuccessful_status_codes() + { + var request = new HttpRequest($"http://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); + request.LogHttpError = false; + + var exception = Assert.Throws(() => Subject.Get(request)); + + ExceptionVerification.ExpectedWarns(0); + } + [Test] public void should_not_follow_redirects_when_not_in_production() { diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index a2ac9d2d5..ce19b1056 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -83,9 +83,12 @@ namespace NzbDrone.Common.Http _logger.Error("Server requested a redirect to [{0}] while in developer mode. Update the request URL to avoid this redirect.", response.Headers["Location"]); } - if (!request.SuppressHttpError && response.HasHttpError) + if (!request.SuppressHttpError && response.HasHttpError && (request.SuppressHttpErrorStatusCodes == null || !request.SuppressHttpErrorStatusCodes.Contains(response.StatusCode))) { - _logger.Warn("HTTP Error - {0}", response); + if (request.LogHttpError) + { + _logger.Warn("HTTP Error - {0}", response); + } if ((int)response.StatusCode == 429) { diff --git a/src/NzbDrone.Common/Http/HttpRequest.cs b/src/NzbDrone.Common/Http/HttpRequest.cs index 12efd6229..30c554427 100644 --- a/src/NzbDrone.Common/Http/HttpRequest.cs +++ b/src/NzbDrone.Common/Http/HttpRequest.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Net; using System.Text; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -14,6 +16,7 @@ namespace NzbDrone.Common.Http Headers = new HttpHeader(); AllowAutoRedirect = true; StoreRequestCookie = true; + LogHttpError = true; Cookies = new Dictionary(); if (!RuntimeInfo.IsProduction) @@ -33,10 +36,12 @@ namespace NzbDrone.Common.Http public byte[] ContentData { get; set; } public string ContentSummary { get; set; } public bool SuppressHttpError { get; set; } + public IEnumerable SuppressHttpErrorStatusCodes { get; set; } public bool UseSimplifiedUserAgent { get; set; } public bool AllowAutoRedirect { get; set; } public bool ConnectionKeepAlive { get; set; } public bool LogResponseContent { get; set; } + public bool LogHttpError { get; set; } public Dictionary Cookies { get; private set; } public bool StoreRequestCookie { get; set; } public bool StoreResponseCookie { get; set; } diff --git a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs index 2e9bf1a5b..a8405a15c 100644 --- a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs +++ b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs @@ -19,6 +19,7 @@ namespace NzbDrone.Common.Http public Dictionary Segments { get; private set; } public HttpHeader Headers { get; private set; } public bool SuppressHttpError { get; set; } + public bool LogHttpError { get; set; } public bool UseSimplifiedUserAgent { get; set; } public bool AllowAutoRedirect { get; set; } public bool ConnectionKeepAlive { get; set; } @@ -41,6 +42,7 @@ namespace NzbDrone.Common.Http Headers = new HttpHeader(); Cookies = new Dictionary(); FormData = new List(); + LogHttpError = true; } public HttpRequestBuilder(bool useHttps, string host, int port, string urlBase = null) @@ -100,6 +102,7 @@ namespace NzbDrone.Common.Http { request.Method = Method; request.SuppressHttpError = SuppressHttpError; + request.LogHttpError = LogHttpError; request.UseSimplifiedUserAgent = UseSimplifiedUserAgent; request.AllowAutoRedirect = AllowAutoRedirect; request.ConnectionKeepAlive = ConnectionKeepAlive; diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index d9a91a336..5dbdee81e 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -88,7 +88,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (!addHasSetShareLimits && setShareLimits) { - Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings); + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } } if (moveToTop) @@ -138,8 +145,15 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (!addHasSetShareLimits && setShareLimits) { - Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings); - } + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteAlbum.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } + } if (moveToTop) { @@ -171,14 +185,16 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent protected bool WaitForTorrent(string hash) { - var count = 5; + var count = 10; while (count != 0) { try { - Proxy.GetTorrentProperties(hash.ToLower(), Settings); - return true; + if (Proxy.IsTorrentLoaded(hash.ToLower(), Settings)) + { + return true; + } } catch { diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs index 158db804e..cfada22fb 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent string GetVersion(QBittorrentSettings settings); QBittorrentPreferences GetConfig(QBittorrentSettings settings); List GetTorrents(QBittorrentSettings settings); + bool IsTorrentLoaded(string hash, QBittorrentSettings settings); QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings); List GetTorrentFiles(string hash, QBittorrentSettings settings); @@ -40,7 +41,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent public class QBittorrentProxySelector : IQBittorrentProxySelector { - private readonly IHttpClient _httpClient; private readonly ICached> _proxyCache; private readonly Logger _logger; @@ -49,11 +49,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent public QBittorrentProxySelector(QBittorrentProxyV1 proxyV1, QBittorrentProxyV2 proxyV2, - IHttpClient httpClient, ICacheManager cacheManager, Logger logger) { - _httpClient = httpClient; _proxyCache = cacheManager.GetCache>(GetType()); _logger = logger; diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs index 6f439fc62..d8549479d 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -99,6 +99,23 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent return response; } + public bool IsTorrentLoaded(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource($"/query/propertiesGeneral/{hash}"); + request.LogHttpError = false; + + try + { + ProcessRequest(request, settings); + + return true; + } + catch + { + return false; + } + } + public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource($"/query/propertiesGeneral/{hash}"); @@ -298,15 +315,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent var request = requestBuilder.Build(); request.LogResponseContent = true; + request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.Forbidden }; HttpResponse response; try { response = _httpClient.Execute(request); - } - catch (HttpException ex) - { - if (ex.Response.StatusCode == HttpStatusCode.Forbidden) + + if (response.StatusCode == HttpStatusCode.Forbidden) { _logger.Debug("Authentication required, logging in."); @@ -316,10 +332,10 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent response = _httpClient.Execute(request); } - else - { - throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex); - } + } + catch (HttpException ex) + { + throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex); } catch (WebException ex) { diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs index 3231d161b..879106cae 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -101,6 +101,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent return response; } + public bool IsTorrentLoaded(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource("/api/v2/torrents/properties") + .AddQueryParam("hash", hash); + request.LogHttpError = false; + + try + { + ProcessRequest(request, settings); + + return true; + } + catch + { + return false; + } + } + public QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/api/v2/torrents/properties") @@ -141,7 +159,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (seedConfiguration != null) { - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration); } var result = ProcessRequest(request, settings); @@ -176,7 +194,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (seedConfiguration != null) { - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration); } var result = ProcessRequest(request, settings); @@ -225,17 +243,17 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent return Json.Deserialize>(ProcessRequest(request, settings)); } - private void AddTorrentSeedingFormParameters(HttpRequestBuilder request, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings) + private void AddTorrentSeedingFormParameters(HttpRequestBuilder request, TorrentSeedConfiguration seedConfiguration, bool always = false) { var ratioLimit = seedConfiguration.Ratio.HasValue ? seedConfiguration.Ratio : -2; var seedingTimeLimit = seedConfiguration.SeedTime.HasValue ? (long)seedConfiguration.SeedTime.Value.TotalMinutes : -2; - if (ratioLimit != -2) + if (ratioLimit != -2 || always) { request.AddFormParameter("ratioLimit", ratioLimit); } - if (seedingTimeLimit != -2) + if (seedingTimeLimit != -2 || always) { request.AddFormParameter("seedingTimeLimit", seedingTimeLimit); } @@ -247,7 +265,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent .Post() .AddFormParameter("hashes", hash); - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration, true); try { @@ -336,15 +354,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent var request = requestBuilder.Build(); request.LogResponseContent = true; + request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.Forbidden }; HttpResponse response; try { response = _httpClient.Execute(request); - } - catch (HttpException ex) - { - if (ex.Response.StatusCode == HttpStatusCode.Forbidden) + + if (response.StatusCode == HttpStatusCode.Forbidden) { _logger.Debug("Authentication required, logging in."); @@ -354,10 +371,10 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent response = _httpClient.Execute(request); } - else - { - throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex); - } + } + catch (HttpException ex) + { + throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex); } catch (WebException ex) {