diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index b87e3a4ce..5c0d43dd5 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -185,6 +185,38 @@ 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 }; + + var exception = Assert.Throws(() => Subject.Get(request)); + + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_log_unsuccessful_status_codes() + { + var request = new HttpRequest($"https://{_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($"https://{_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 3df89fb63..7ca7e9188 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 0f4f8cd3d..a0f5b8e45 100644 --- a/src/NzbDrone.Common/Http/HttpRequest.cs +++ b/src/NzbDrone.Common/Http/HttpRequest.cs @@ -16,6 +16,7 @@ public HttpRequest(string url, HttpAccept httpAccept = null) Headers = new HttpHeader(); AllowAutoRedirect = true; StoreRequestCookie = true; + LogHttpError = true; Cookies = new Dictionary(); if (!RuntimeInfo.IsProduction) @@ -36,10 +37,12 @@ public HttpRequest(string url, HttpAccept httpAccept = null) public string ContentSummary { get; set; } public ICredentials Credentials { 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 ccc3567c6..bdbf3bb1c 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; } @@ -40,6 +41,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) @@ -99,6 +101,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/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index 41535aa5b..55f0b9646 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -88,7 +88,14 @@ protected override string AddFromMagnetLink(RemoteBook remoteBook, string hash, if (!addHasSetShareLimits && setShareLimits) { - Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings); + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } } if (moveToTop) @@ -138,8 +145,15 @@ protected override string AddFromTorrentFile(RemoteBook remoteBook, string hash, if (!addHasSetShareLimits && setShareLimits) { - Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings); - } + try + { + Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings); + } + catch (Exception ex) + { + _logger.Warn(ex, "Failed to set the torrent seed criteria for {0}.", hash); + } + } if (moveToTop) { @@ -171,14 +185,16 @@ protected override string AddFromTorrentFile(RemoteBook remoteBook, string hash, 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 @@ 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; @@ -49,11 +49,9 @@ public class QBittorrentProxySelector : IQBittorrentProxySelector 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 c84c5a99a..b26e4929f 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -99,6 +99,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}"); @@ -298,15 +315,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."); @@ -316,10 +332,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 86fdaf1ca..69d0125ad 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 Dictionary GetLabels(QBittorrentSettings settin 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) {