Skip to content

Commit

Permalink
Fixed: Qbittorrent api errors when only one of two seed criteria was …
Browse files Browse the repository at this point in the history
…configured

Fixed: Qbittorrent API errors when only one of two seed criteria was configured
  • Loading branch information
bakerboy448 committed Mar 21, 2021
1 parent bfc969c commit 53b9332
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 36 deletions.
22 changes: 22 additions & 0 deletions src/NzbDrone.Common.Test/Http/HttpClientFixture.cs
Expand Up @@ -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<HttpException>(() => Subject.Get<HttpBinResource>(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<HttpException>(() => Subject.Get<HttpBinResource>(request));

ExceptionVerification.ExpectedWarns(0);
}

[Test]
public void should_not_follow_redirects_when_not_in_production()
{
Expand Down
7 changes: 5 additions & 2 deletions src/NzbDrone.Common/Http/HttpClient.cs
Expand Up @@ -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)
{
Expand Down
4 changes: 4 additions & 0 deletions 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;
Expand All @@ -15,6 +16,7 @@ public HttpRequest(string url, HttpAccept httpAccept = null)
AllowAutoRedirect = true;
StoreRequestCookie = true;
IgnorePersistentCookies = false;
LogHttpError = true;
Cookies = new Dictionary<string, string>();

if (!RuntimeInfo.IsProduction)
Expand All @@ -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<HttpStatusCode> 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<string, string> Cookies { get; private set; }
public bool IgnorePersistentCookies { get; set; }
public bool StoreRequestCookie { get; set; }
Expand Down
3 changes: 3 additions & 0 deletions src/NzbDrone.Common/Http/HttpRequestBuilder.cs
Expand Up @@ -19,6 +19,7 @@ public class HttpRequestBuilder
public Dictionary<string, string> 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; }
Expand All @@ -41,6 +42,7 @@ public HttpRequestBuilder(string baseUrl)
Headers = new HttpHeader();
Cookies = new Dictionary<string, string>();
FormData = new List<HttpFormData>();
LogHttpError = true;
}

public HttpRequestBuilder(bool useHttps, string host, int port, string urlBase = null)
Expand Down Expand Up @@ -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;
Expand Down
Expand Up @@ -74,17 +74,21 @@ protected void GivenFailedDownload()
Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()))
.Throws<InvalidOperationException>();

Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()))
.Throws<InvalidOperationException>();
}

protected void GivenSuccessfulDownload()
{
Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()))
.Setup(s => s.AddTorrentFromFile(It.IsAny<string>(), It.IsAny<byte[]>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()))
.Callback(() =>
{
var torrent = new QBittorrentTorrent
{
Hash = "HASH",
Hash = "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951",
Name = _title,
Size = 1000,
Progress = 1.0,
Expand Down Expand Up @@ -138,6 +142,10 @@ protected virtual void GivenTorrents(List<QBittorrentTorrent> torrents)
.Setup(s => s.GetTorrentFiles(torrent.Hash.ToLower(), It.IsAny<QBittorrentSettings>()))
.Returns(new List<QBittorrentTorrentFile> { new QBittorrentTorrentFile { Name = torrent.Name } });
}

Mocker.GetMock<IQBittorrentProxy>()
.Setup(s => s.IsTorrentLoaded(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()))
.Returns<string, QBittorrentSettings>((hash, s) => torrents.Any(v => v.Hash.ToLower() == hash));
}

private void GivenTorrentFiles(string hash, List<QBittorrentTorrentFile> files)
Expand Down
34 changes: 26 additions & 8 deletions src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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;
Expand Down
Expand Up @@ -15,6 +15,7 @@ public interface IQBittorrentProxy
string GetVersion(QBittorrentSettings settings);
QBittorrentPreferences GetConfig(QBittorrentSettings settings);
List<QBittorrentTorrent> GetTorrents(QBittorrentSettings settings);
bool IsTorrentLoaded(string hash, QBittorrentSettings settings);
QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings);
List<QBittorrentTorrentFile> GetTorrentFiles(string hash, QBittorrentSettings settings);

Expand All @@ -40,7 +41,6 @@ public interface IQBittorrentProxySelector

public class QBittorrentProxySelector : IQBittorrentProxySelector
{
private readonly IHttpClient _httpClient;
private readonly ICached<Tuple<IQBittorrentProxy, Version>> _proxyCache;
private readonly Logger _logger;

Expand All @@ -53,7 +53,6 @@ public class QBittorrentProxySelector : IQBittorrentProxySelector
ICacheManager cacheManager,
Logger logger)
{
_httpClient = httpClient;
_proxyCache = cacheManager.GetCache<Tuple<IQBittorrentProxy, Version>>(GetType());
_logger = logger;

Expand Down
Expand Up @@ -97,6 +97,23 @@ public List<QBittorrentTorrent> 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}");
Expand Down Expand Up @@ -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.");

Expand All @@ -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)
{
Expand Down
Expand Up @@ -101,6 +101,24 @@ public List<QBittorrentTorrent> 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")
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -225,17 +243,17 @@ public void AddLabel(string label, QBittorrentSettings settings)
return Json.Deserialize<Dictionary<string, QBittorrentLabel>>(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);
}
Expand All @@ -247,7 +265,7 @@ public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration
.Post()
.AddFormParameter("hashes", hash);

AddTorrentSeedingFormParameters(request, seedConfiguration, settings);
AddTorrentSeedingFormParameters(request, seedConfiguration, true);

try
{
Expand Down Expand Up @@ -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.");

Expand All @@ -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)
{
Expand Down

0 comments on commit 53b9332

Please sign in to comment.