From 53b93326757246fe20428da881265c5fa5f9909f Mon Sep 17 00:00:00 2001 From: bakerboy448 <55419169+bakerboy448@users.noreply.github.com> Date: Sun, 21 Mar 2021 16:47:33 -0500 Subject: [PATCH] Fixed: Qbittorrent api errors when only one of two seed criteria was configured Fixed: Qbittorrent API errors when only one of two seed criteria was configured --- .../Http/HttpClientFixture.cs | 22 +++++++++ src/NzbDrone.Common/Http/HttpClient.cs | 7 ++- src/NzbDrone.Common/Http/HttpRequest.cs | 4 ++ .../Http/HttpRequestBuilder.cs | 3 ++ .../QBittorrentTests/QBittorrentFixture.cs | 12 ++++- .../Clients/QBittorrent/QBittorrent.cs | 34 ++++++++++---- .../QBittorrent/QBittorrentProxySelector.cs | 3 +- .../Clients/QBittorrent/QBittorrentProxyV1.cs | 32 +++++++++---- .../Clients/QBittorrent/QBittorrentProxyV2.cs | 45 +++++++++++++------ 9 files changed, 126 insertions(+), 36 deletions(-) diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index 74bf4919255..e56acfe672a 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -190,6 +190,28 @@ public void should_throw_on_unsuccessful_status_codes(int statusCode) ExceptionVerification.IgnoreWarns(); } + [Test] + public void should_not_throw_on_suppressed_status_codes() + { + var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); + request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound }; + + Assert.Throws(() => Subject.Get(request)); + + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_not_log_unsuccessful_status_codes() + { + var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); + request.LogHttpError = false; + + 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 a7abca8df21..9ea6f985c33 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -83,9 +83,12 @@ public HttpResponse Execute(HttpRequest request) _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 f623c3fe58e..637e979034d 100644 --- a/src/NzbDrone.Common/Http/HttpRequest.cs +++ b/src/NzbDrone.Common/Http/HttpRequest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Text; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -15,6 +16,7 @@ public HttpRequest(string url, HttpAccept httpAccept = null) AllowAutoRedirect = true; StoreRequestCookie = true; IgnorePersistentCookies = false; + LogHttpError = true; Cookies = new Dictionary(); if (!RuntimeInfo.IsProduction) @@ -34,10 +36,12 @@ public HttpRequest(string url, HttpAccept httpAccept = null) 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 IgnorePersistentCookies { get; set; } public bool StoreRequestCookie { get; set; } diff --git a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs index 2e9bf1a5bbc..a8405a15c32 100644 --- a/src/NzbDrone.Common/Http/HttpRequestBuilder.cs +++ b/src/NzbDrone.Common/Http/HttpRequestBuilder.cs @@ -19,6 +19,7 @@ public class HttpRequestBuilder 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 @@ public HttpRequestBuilder(string baseUrl) 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 @@ protected virtual void Apply(HttpRequest request) { request.Method = Method; request.SuppressHttpError = SuppressHttpError; + request.LogHttpError = LogHttpError; request.UseSimplifiedUserAgent = UseSimplifiedUserAgent; request.AllowAutoRedirect = AllowAutoRedirect; request.ConnectionKeepAlive = ConnectionKeepAlive; diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs index 05580ee36eb..160a9b4c71b 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs @@ -74,17 +74,21 @@ protected void GivenFailedDownload() Mocker.GetMock() .Setup(s => s.AddTorrentFromUrl(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); + + Mocker.GetMock() + .Setup(s => s.AddTorrentFromFile(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(); } protected void GivenSuccessfulDownload() { Mocker.GetMock() - .Setup(s => s.AddTorrentFromUrl(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(s => s.AddTorrentFromFile(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback(() => { var torrent = new QBittorrentTorrent { - Hash = "HASH", + Hash = "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951", Name = _title, Size = 1000, Progress = 1.0, @@ -138,6 +142,10 @@ protected virtual void GivenTorrents(List torrents) .Setup(s => s.GetTorrentFiles(torrent.Hash.ToLower(), It.IsAny())) .Returns(new List { new QBittorrentTorrentFile { Name = torrent.Name } }); } + + Mocker.GetMock() + .Setup(s => s.IsTorrentLoaded(It.IsAny(), It.IsAny())) + .Returns((hash, s) => torrents.Any(v => v.Hash.ToLower() == hash)); } private void GivenTorrentFiles(string hash, List files) diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index 5dbb8f4c256..cf9c533efdf 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -89,6 +89,15 @@ protected override string AddFromMagnetLink(RemoteMovie remoteMovie, string hash if (!addHasSetShareLimits && setShareLimits) { Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteMovie.SeedConfiguration, Settings); + + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteMovie.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } } if (moveToTop) @@ -138,8 +147,15 @@ protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string has if (!addHasSetShareLimits && setShareLimits) { - Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteMovie.SeedConfiguration, Settings); - } + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteMovie.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } + } if (moveToTop) { @@ -171,14 +187,16 @@ protected override string AddFromTorrentFile(RemoteMovie remoteMovie, string has 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 { @@ -457,9 +475,9 @@ private ValidationFailure TestConnection() _logger.Error(ex, "Unable to test qBittorrent"); return new NzbDroneValidationFailure("Host", "Unable to connect to qBittorrent") - { - DetailedDescription = ex.Message - }; + { + DetailedDescription = ex.Message + }; } return null; diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs index 158db804e45..112ed5db449 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs @@ -15,6 +15,7 @@ public interface IQBittorrentProxy 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 @@ public interface IQBittorrentProxySelector public class QBittorrentProxySelector : IQBittorrentProxySelector { - private readonly IHttpClient _httpClient; private readonly ICached> _proxyCache; private readonly Logger _logger; @@ -53,7 +53,6 @@ public class QBittorrentProxySelector : IQBittorrentProxySelector 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 47f3a5c9e1e..c094edfb9e3 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -97,6 +97,23 @@ public List GetTorrents(QBittorrentSettings settings) 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}"); @@ -295,15 +312,14 @@ private string ProcessRequest(HttpRequestBuilder requestBuilder, QBittorrentSett 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."); @@ -313,10 +329,10 @@ private string ProcessRequest(HttpRequestBuilder requestBuilder, QBittorrentSett 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 f7cf4949e3f..58aaf901017 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -101,6 +101,24 @@ public List GetTorrents(QBittorrentSettings settings) 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 @@ public void AddTorrentFromUrl(string torrentUrl, TorrentSeedConfiguration seedCo if (seedConfiguration != null) { - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration); } var result = ProcessRequest(request, settings); @@ -176,7 +194,7 @@ public void AddTorrentFromFile(string fileName, byte[] fileContent, TorrentSeedC if (seedConfiguration != null) { - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration); } var result = ProcessRequest(request, settings); @@ -225,17 +243,17 @@ public void AddLabel(string label, QBittorrentSettings settings) 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 @@ public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration .Post() .AddFormParameter("hashes", hash); - AddTorrentSeedingFormParameters(request, seedConfiguration, settings); + AddTorrentSeedingFormParameters(request, seedConfiguration, true); try { @@ -336,15 +354,14 @@ private string ProcessRequest(HttpRequestBuilder requestBuilder, QBittorrentSett 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 @@ private string ProcessRequest(HttpRequestBuilder requestBuilder, QBittorrentSett 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) {