New: Async HttpClient

pull/4016/head
Bogdan 10 months ago
parent fd0b240438
commit fd9bd5f691

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation; using FluentValidation;
using Lidarr.Http; using Lidarr.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -65,7 +66,7 @@ namespace Lidarr.Api.V1.Indexers
} }
[HttpPost] [HttpPost]
public ActionResult<ReleaseResource> Create(ReleaseResource release) public async Task<ActionResult<ReleaseResource>> DownloadRelease(ReleaseResource release)
{ {
ValidateResource(release); ValidateResource(release);
@ -126,7 +127,7 @@ namespace Lidarr.Api.V1.Indexers
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Unable to parse albums in the release"); throw new NzbDroneClientException(HttpStatusCode.NotFound, "Unable to parse albums in the release");
} }
_downloadService.DownloadReport(remoteAlbum); await _downloadService.DownloadReport(remoteAlbum);
} }
catch (ReleaseDownloadException ex) catch (ReleaseDownloadException ex)
{ {
@ -138,26 +139,26 @@ namespace Lidarr.Api.V1.Indexers
} }
[HttpGet] [HttpGet]
public List<ReleaseResource> GetReleases(int? albumId, int? artistId) public async Task<List<ReleaseResource>> GetReleases(int? albumId, int? artistId)
{ {
if (albumId.HasValue) if (albumId.HasValue)
{ {
return GetAlbumReleases(int.Parse(Request.Query["albumId"])); return await GetAlbumReleases(int.Parse(Request.Query["albumId"]));
} }
if (artistId.HasValue) if (artistId.HasValue)
{ {
return GetArtistReleases(int.Parse(Request.Query["artistId"])); return await GetArtistReleases(int.Parse(Request.Query["artistId"]));
} }
return GetRss(); return await GetRss();
} }
private List<ReleaseResource> GetAlbumReleases(int albumId) private async Task<List<ReleaseResource>> GetAlbumReleases(int albumId)
{ {
try try
{ {
var decisions = _releaseSearchService.AlbumSearch(albumId, true, true, true); var decisions = await _releaseSearchService.AlbumSearch(albumId, true, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions); var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions); return MapDecisions(prioritizedDecisions);
@ -169,11 +170,11 @@ namespace Lidarr.Api.V1.Indexers
} }
} }
private List<ReleaseResource> GetArtistReleases(int artistId) private async Task<List<ReleaseResource>> GetArtistReleases(int artistId)
{ {
try try
{ {
var decisions = _releaseSearchService.ArtistSearch(artistId, false, true, true); var decisions = await _releaseSearchService.ArtistSearch(artistId, false, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions); var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions); return MapDecisions(prioritizedDecisions);
@ -185,9 +186,9 @@ namespace Lidarr.Api.V1.Indexers
} }
} }
private List<ReleaseResource> GetRss() private async Task<List<ReleaseResource>> GetRss()
{ {
var reports = _rssFetcherAndParser.Fetch(); var reports = await _rssFetcherAndParser.Fetch();
var decisions = _downloadDecisionMaker.GetRssDecision(reports); var decisions = _downloadDecisionMaker.GetRssDecision(reports);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions); var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
@ -198,6 +199,7 @@ namespace Lidarr.Api.V1.Indexers
{ {
var resource = base.MapDecision(decision, initialWeight); var resource = base.MapDecision(decision, initialWeight);
_remoteAlbumCache.Set(GetCacheKey(resource), decision.RemoteAlbum, TimeSpan.FromMinutes(30)); _remoteAlbumCache.Set(GetCacheKey(resource), decision.RemoteAlbum, TimeSpan.FromMinutes(30));
return resource; return resource;
} }

@ -61,7 +61,7 @@ namespace Lidarr.Api.V1.Indexers
lock (PushLock) lock (PushLock)
{ {
decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info }); decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions); _downloadDecisionProcessor.ProcessDecisions(decisions).GetAwaiter().GetResult();
} }
var firstDecision = decisions.FirstOrDefault(); var firstDecision = decisions.FirstOrDefault();

@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Lidarr.Http; using Lidarr.Http;
using Lidarr.Http.REST; using Lidarr.Http.REST;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -20,7 +21,7 @@ namespace Lidarr.Api.V1.Queue
} }
[HttpPost("grab/{id:int}")] [HttpPost("grab/{id:int}")]
public object Grab(int id) public async Task<object> Grab(int id)
{ {
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id); var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
@ -29,14 +30,14 @@ namespace Lidarr.Api.V1.Queue
throw new NotFoundException(); throw new NotFoundException();
} }
_downloadService.DownloadReport(pendingRelease.RemoteAlbum); await _downloadService.DownloadReport(pendingRelease.RemoteAlbum);
return new { }; return new { };
} }
[HttpPost("grab/bulk")] [HttpPost("grab/bulk")]
[Consumes("application/json")] [Consumes("application/json")]
public object Grab([FromBody] QueueBulkResource resource) public async Task<object> Grab([FromBody] QueueBulkResource resource)
{ {
foreach (var id in resource.Ids) foreach (var id in resource.Ids)
{ {
@ -47,7 +48,7 @@ namespace Lidarr.Api.V1.Queue
throw new NotFoundException(); throw new NotFoundException();
} }
_downloadService.DownloadReport(pendingRelease.RemoteAlbum); await _downloadService.DownloadReport(pendingRelease.RemoteAlbum);
} }
return new { }; return new { };

@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NLog; using NLog;
@ -121,21 +122,21 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_execute_simple_get() public async Task should_execute_simple_get()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Execute(request); var response = await Subject.ExecuteAsync(request);
response.Content.Should().NotBeNullOrWhiteSpace(); response.Content.Should().NotBeNullOrWhiteSpace();
} }
[Test] [Test]
public void should_execute_https_get() public async Task should_execute_https_get()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Execute(request); var response = await Subject.ExecuteAsync(request);
response.Content.Should().NotBeNullOrWhiteSpace(); response.Content.Should().NotBeNullOrWhiteSpace();
} }
@ -147,47 +148,47 @@ namespace NzbDrone.Common.Test.Http
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(validationType); Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(validationType);
var request = new HttpRequest($"https://expired.badssl.com"); var request = new HttpRequest($"https://expired.badssl.com");
Assert.Throws<HttpRequestException>(() => Subject.Execute(request)); Assert.ThrowsAsync<HttpRequestException>(async () => await Subject.ExecuteAsync(request));
ExceptionVerification.ExpectedErrors(1); ExceptionVerification.ExpectedErrors(1);
} }
[Test] [Test]
public void bad_ssl_should_pass_if_remote_validation_disabled() public async Task bad_ssl_should_pass_if_remote_validation_disabled()
{ {
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(CertificateValidationType.Disabled); Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(CertificateValidationType.Disabled);
var request = new HttpRequest($"https://expired.badssl.com"); var request = new HttpRequest($"https://expired.badssl.com");
Subject.Execute(request); await Subject.ExecuteAsync(request);
ExceptionVerification.ExpectedErrors(0); ExceptionVerification.ExpectedErrors(0);
} }
[Test] [Test]
public void should_execute_typed_get() public async Task should_execute_typed_get()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get?test=1"); var request = new HttpRequest($"https://{_httpBinHost}/get?test=1");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Url.EndsWith("/get?test=1"); response.Resource.Url.EndsWith("/get?test=1");
response.Resource.Args.Should().Contain("test", "1"); response.Resource.Args.Should().Contain("test", "1");
} }
[Test] [Test]
public void should_execute_simple_post() public async Task should_execute_simple_post()
{ {
var message = "{ my: 1 }"; var message = "{ my: 1 }";
var request = new HttpRequest($"https://{_httpBinHost}/post"); var request = new HttpRequest($"https://{_httpBinHost}/post");
request.SetContent(message); request.SetContent(message);
var response = Subject.Post<HttpBinResource>(request); var response = await Subject.PostAsync<HttpBinResource>(request);
response.Resource.Data.Should().Be(message); response.Resource.Data.Should().Be(message);
} }
[Test] [Test]
public void should_execute_post_with_content_type() public async Task should_execute_post_with_content_type()
{ {
var message = "{ my: 1 }"; var message = "{ my: 1 }";
@ -195,16 +196,16 @@ namespace NzbDrone.Common.Test.Http
request.SetContent(message); request.SetContent(message);
request.Headers.ContentType = "application/json"; request.Headers.ContentType = "application/json";
var response = Subject.Post<HttpBinResource>(request); var response = await Subject.PostAsync<HttpBinResource>(request);
response.Resource.Data.Should().Be(message); response.Resource.Data.Should().Be(message);
} }
[Test] [Test]
public void should_execute_get_using_gzip() public async Task should_execute_get_using_gzip()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/gzip"); var request = new HttpRequest($"https://{_httpBinHost}/gzip");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("gzip"); response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("gzip");
@ -214,10 +215,10 @@ namespace NzbDrone.Common.Test.Http
[Test] [Test]
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")] [Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
public void should_execute_get_using_brotli() public async Task should_execute_get_using_brotli()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/brotli"); var request = new HttpRequest($"https://{_httpBinHost}/brotli");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("br"); response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("br");
@ -235,7 +236,7 @@ namespace NzbDrone.Common.Test.Http
{ {
var request = new HttpRequest($"https://{_httpBinHost}/status/{statusCode}"); var request = new HttpRequest($"https://{_httpBinHost}/status/{statusCode}");
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request)); var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
((int)exception.Response.StatusCode).Should().Be(statusCode); ((int)exception.Response.StatusCode).Should().Be(statusCode);
@ -248,7 +249,7 @@ namespace NzbDrone.Common.Test.Http
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound }; request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound };
Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request)); Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
ExceptionVerification.IgnoreWarns(); ExceptionVerification.IgnoreWarns();
} }
@ -258,7 +259,7 @@ namespace NzbDrone.Common.Test.Http
{ {
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request)); var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
ExceptionVerification.ExpectedWarns(1); ExceptionVerification.ExpectedWarns(1);
} }
@ -269,28 +270,28 @@ namespace NzbDrone.Common.Test.Http
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}"); var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
request.LogHttpError = false; request.LogHttpError = false;
Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request)); Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
ExceptionVerification.ExpectedWarns(0); ExceptionVerification.ExpectedWarns(0);
} }
[Test] [Test]
public void should_not_follow_redirects_when_not_in_production() public async Task should_not_follow_redirects_when_not_in_production()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1"); var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
Subject.Get(request); await Subject.GetAsync(request);
ExceptionVerification.ExpectedErrors(1); ExceptionVerification.ExpectedErrors(1);
} }
[Test] [Test]
public void should_follow_redirects() public async Task should_follow_redirects()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1"); var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
request.AllowAutoRedirect = true; request.AllowAutoRedirect = true;
var response = Subject.Get(request); var response = await Subject.GetAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK); response.StatusCode.Should().Be(HttpStatusCode.OK);
@ -298,12 +299,12 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_not_follow_redirects() public async Task should_not_follow_redirects()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1"); var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
request.AllowAutoRedirect = false; request.AllowAutoRedirect = false;
var response = Subject.Get(request); var response = await Subject.GetAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.Found); response.StatusCode.Should().Be(HttpStatusCode.Found);
@ -311,14 +312,14 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_follow_redirects_to_https() public async Task should_follow_redirects_to_https()
{ {
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to") var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
.AddQueryParam("url", $"https://lidarr.audio/") .AddQueryParam("url", $"https://lidarr.audio/")
.Build(); .Build();
request.AllowAutoRedirect = true; request.AllowAutoRedirect = true;
var response = Subject.Get(request); var response = await Subject.GetAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK); response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Should().Contain("Lidarr"); response.Content.Should().Contain("Lidarr");
@ -332,17 +333,17 @@ namespace NzbDrone.Common.Test.Http
var request = new HttpRequest($"https://{_httpBinHost}/redirect/6"); var request = new HttpRequest($"https://{_httpBinHost}/redirect/6");
request.AllowAutoRedirect = true; request.AllowAutoRedirect = true;
Assert.Throws<WebException>(() => Subject.Get(request)); Assert.ThrowsAsync<WebException>(async () => await Subject.GetAsync(request));
ExceptionVerification.ExpectedErrors(0); ExceptionVerification.ExpectedErrors(0);
} }
[Test] [Test]
public void should_send_user_agent() public async Task should_send_user_agent()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().ContainKey("User-Agent"); response.Resource.Headers.Should().ContainKey("User-Agent");
@ -352,24 +353,24 @@ namespace NzbDrone.Common.Test.Http
} }
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")] [TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
public void should_send_headers(string header, string value) public async Task should_send_headers(string header, string value)
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
request.Headers.Add(header, value); request.Headers.Add(header, value);
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers[header].ToString().Should().Be(value); response.Resource.Headers[header].ToString().Should().Be(value);
} }
[Test] [Test]
public void should_download_file() public async Task should_download_file()
{ {
var file = GetTempFilePath(); var file = GetTempFilePath();
var url = "https://lidarr.audio/img/slider/artistdetails.png"; var url = "https://lidarr.audio/img/slider/artistdetails.png";
Subject.DownloadFile(url, file); await Subject.DownloadFileAsync(url, file);
File.Exists(file).Should().BeTrue(); File.Exists(file).Should().BeTrue();
File.Exists(file + ".part").Should().BeFalse(); File.Exists(file + ".part").Should().BeFalse();
@ -380,7 +381,7 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_download_file_with_redirect() public async Task should_download_file_with_redirect()
{ {
var file = GetTempFilePath(); var file = GetTempFilePath();
@ -388,7 +389,7 @@ namespace NzbDrone.Common.Test.Http
.AddQueryParam("url", $"https://lidarr.audio/img/slider/artistdetails.png") .AddQueryParam("url", $"https://lidarr.audio/img/slider/artistdetails.png")
.Build(); .Build();
Subject.DownloadFile(request.Url.FullUri, file); await Subject.DownloadFileAsync(request.Url.FullUri, file);
ExceptionVerification.ExpectedErrors(0); ExceptionVerification.ExpectedErrors(0);
@ -402,7 +403,7 @@ namespace NzbDrone.Common.Test.Http
{ {
var file = GetTempFilePath(); var file = GetTempFilePath();
Assert.Throws<HttpException>(() => Subject.DownloadFile("https://download.sonarr.tv/wrongpath", file)); Assert.ThrowsAsync<HttpException>(async () => await Subject.DownloadFileAsync("https://download.sonarr.tv/wrongpath", file));
File.Exists(file).Should().BeFalse(); File.Exists(file).Should().BeFalse();
File.Exists(file + ".part").Should().BeFalse(); File.Exists(file + ".part").Should().BeFalse();
@ -411,7 +412,7 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_not_write_redirect_content_to_stream() public async Task should_not_write_redirect_content_to_stream()
{ {
var file = GetTempFilePath(); var file = GetTempFilePath();
@ -421,7 +422,7 @@ namespace NzbDrone.Common.Test.Http
request.AllowAutoRedirect = false; request.AllowAutoRedirect = false;
request.ResponseStream = fileStream; request.ResponseStream = fileStream;
var response = Subject.Get(request); var response = await Subject.GetAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.StatusCode.Should().Be(HttpStatusCode.Redirect);
} }
@ -436,12 +437,12 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_send_cookie() public async Task should_send_cookie()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
request.Cookies["my"] = "cookie"; request.Cookies["my"] = "cookie";
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().ContainKey("Cookie"); response.Resource.Headers.Should().ContainKey("Cookie");
@ -470,13 +471,13 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_preserve_cookie_during_session() public async Task should_preserve_cookie_during_session()
{ {
GivenOldCookie(); GivenOldCookie();
var request = new HttpRequest($"https://{_httpBinHost2}/get"); var request = new HttpRequest($"https://{_httpBinHost2}/get");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().ContainKey("Cookie"); response.Resource.Headers.Should().ContainKey("Cookie");
@ -486,30 +487,30 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_not_send_cookie_to_other_host() public async Task should_not_send_cookie_to_other_host()
{ {
GivenOldCookie(); GivenOldCookie();
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().NotContainKey("Cookie"); response.Resource.Headers.Should().NotContainKey("Cookie");
} }
[Test] [Test]
public void should_not_store_request_cookie() public async Task should_not_store_request_cookie()
{ {
var requestGet = new HttpRequest($"https://{_httpBinHost}/get"); var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
requestGet.Cookies.Add("my", "cookie"); requestGet.Cookies.Add("my", "cookie");
requestGet.AllowAutoRedirect = false; requestGet.AllowAutoRedirect = false;
requestGet.StoreRequestCookie = false; requestGet.StoreRequestCookie = false;
requestGet.StoreResponseCookie = false; requestGet.StoreResponseCookie = false;
var responseGet = Subject.Get<HttpBinResource>(requestGet); var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.AllowAutoRedirect = false; requestCookies.AllowAutoRedirect = false;
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().BeEmpty(); responseCookies.Resource.Cookies.Should().BeEmpty();
@ -517,18 +518,18 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_store_request_cookie() public async Task should_store_request_cookie()
{ {
var requestGet = new HttpRequest($"https://{_httpBinHost}/get"); var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
requestGet.Cookies.Add("my", "cookie"); requestGet.Cookies.Add("my", "cookie");
requestGet.AllowAutoRedirect = false; requestGet.AllowAutoRedirect = false;
requestGet.StoreRequestCookie.Should().BeTrue(); requestGet.StoreRequestCookie.Should().BeTrue();
requestGet.StoreResponseCookie = false; requestGet.StoreResponseCookie = false;
var responseGet = Subject.Get<HttpBinResource>(requestGet); var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.AllowAutoRedirect = false; requestCookies.AllowAutoRedirect = false;
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -536,7 +537,7 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_delete_request_cookie() public async Task should_delete_request_cookie()
{ {
var requestDelete = new HttpRequest($"https://{_httpBinHost}/cookies/delete?my"); var requestDelete = new HttpRequest($"https://{_httpBinHost}/cookies/delete?my");
requestDelete.Cookies.Add("my", "cookie"); requestDelete.Cookies.Add("my", "cookie");
@ -545,13 +546,13 @@ namespace NzbDrone.Common.Test.Http
requestDelete.StoreResponseCookie = false; requestDelete.StoreResponseCookie = false;
// Delete and redirect since that's the only way to check the internal temporary cookie container // Delete and redirect since that's the only way to check the internal temporary cookie container
var responseCookies = Subject.Get<HttpCookieResource>(requestDelete); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestDelete);
responseCookies.Resource.Cookies.Should().BeEmpty(); responseCookies.Resource.Cookies.Should().BeEmpty();
} }
[Test] [Test]
public void should_clear_request_cookie() public async Task should_clear_request_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies");
requestSet.Cookies.Add("my", "cookie"); requestSet.Cookies.Add("my", "cookie");
@ -559,7 +560,7 @@ namespace NzbDrone.Common.Test.Http
requestSet.StoreRequestCookie = true; requestSet.StoreRequestCookie = true;
requestSet.StoreResponseCookie = false; requestSet.StoreResponseCookie = false;
var responseSet = Subject.Get<HttpCookieResource>(requestSet); var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
var requestClear = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestClear = new HttpRequest($"https://{_httpBinHost}/cookies");
requestClear.Cookies.Add("my", null); requestClear.Cookies.Add("my", null);
@ -567,24 +568,24 @@ namespace NzbDrone.Common.Test.Http
requestClear.StoreRequestCookie = true; requestClear.StoreRequestCookie = true;
requestClear.StoreResponseCookie = false; requestClear.StoreResponseCookie = false;
var responseClear = Subject.Get<HttpCookieResource>(requestClear); var responseClear = await Subject.GetAsync<HttpCookieResource>(requestClear);
responseClear.Resource.Cookies.Should().BeEmpty(); responseClear.Resource.Cookies.Should().BeEmpty();
} }
[Test] [Test]
public void should_not_store_response_cookie() public async Task should_not_store_response_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie.Should().BeFalse(); requestSet.StoreResponseCookie.Should().BeFalse();
var responseSet = Subject.Get(requestSet); var responseSet = await Subject.GetAsync(requestSet);
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().BeEmpty(); responseCookies.Resource.Cookies.Should().BeEmpty();
@ -592,18 +593,18 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_store_response_cookie() public async Task should_store_response_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet); var responseSet = await Subject.GetAsync(requestSet);
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -611,13 +612,13 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_temp_store_response_cookie() public async Task should_temp_store_response_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
requestSet.AllowAutoRedirect = true; requestSet.AllowAutoRedirect = true;
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie.Should().BeFalse(); requestSet.StoreResponseCookie.Should().BeFalse();
var responseSet = Subject.Get<HttpCookieResource>(requestSet); var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
// Set and redirect since that's the only way to check the internal temporary cookie container // Set and redirect since that's the only way to check the internal temporary cookie container
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -626,7 +627,7 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_overwrite_response_cookie() public async Task should_overwrite_response_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
requestSet.Cookies.Add("my", "oldcookie"); requestSet.Cookies.Add("my", "oldcookie");
@ -634,11 +635,11 @@ namespace NzbDrone.Common.Test.Http
requestSet.StoreRequestCookie = false; requestSet.StoreRequestCookie = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet); var responseSet = await Subject.GetAsync(requestSet);
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -646,7 +647,7 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_overwrite_temp_response_cookie() public async Task should_overwrite_temp_response_cookie()
{ {
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie"); var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
requestSet.Cookies.Add("my", "oldcookie"); requestSet.Cookies.Add("my", "oldcookie");
@ -654,13 +655,13 @@ namespace NzbDrone.Common.Test.Http
requestSet.StoreRequestCookie = true; requestSet.StoreRequestCookie = true;
requestSet.StoreResponseCookie = false; requestSet.StoreResponseCookie = false;
var responseSet = Subject.Get<HttpCookieResource>(requestSet); var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "oldcookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "oldcookie");
@ -668,14 +669,14 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_not_delete_response_cookie() public async Task should_not_delete_response_cookie()
{ {
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.Cookies.Add("my", "cookie"); requestCookies.Cookies.Add("my", "cookie");
requestCookies.AllowAutoRedirect = false; requestCookies.AllowAutoRedirect = false;
requestCookies.StoreRequestCookie = true; requestCookies.StoreRequestCookie = true;
requestCookies.StoreResponseCookie = false; requestCookies.StoreResponseCookie = false;
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -684,13 +685,13 @@ namespace NzbDrone.Common.Test.Http
requestDelete.StoreRequestCookie = false; requestDelete.StoreRequestCookie = false;
requestDelete.StoreResponseCookie = false; requestDelete.StoreResponseCookie = false;
var responseDelete = Subject.Get(requestDelete); var responseDelete = await Subject.GetAsync(requestDelete);
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.StoreRequestCookie = false; requestCookies.StoreRequestCookie = false;
requestCookies.StoreResponseCookie = false; requestCookies.StoreResponseCookie = false;
responseCookies = Subject.Get<HttpCookieResource>(requestCookies); responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -698,14 +699,14 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_delete_response_cookie() public async Task should_delete_response_cookie()
{ {
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.Cookies.Add("my", "cookie"); requestCookies.Cookies.Add("my", "cookie");
requestCookies.AllowAutoRedirect = false; requestCookies.AllowAutoRedirect = false;
requestCookies.StoreRequestCookie = true; requestCookies.StoreRequestCookie = true;
requestCookies.StoreResponseCookie = false; requestCookies.StoreResponseCookie = false;
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -714,13 +715,13 @@ namespace NzbDrone.Common.Test.Http
requestDelete.StoreRequestCookie = false; requestDelete.StoreRequestCookie = false;
requestDelete.StoreResponseCookie = true; requestDelete.StoreResponseCookie = true;
var responseDelete = Subject.Get(requestDelete); var responseDelete = await Subject.GetAsync(requestDelete);
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.StoreRequestCookie = false; requestCookies.StoreRequestCookie = false;
requestCookies.StoreResponseCookie = false; requestCookies.StoreResponseCookie = false;
responseCookies = Subject.Get<HttpCookieResource>(requestCookies); responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().BeEmpty(); responseCookies.Resource.Cookies.Should().BeEmpty();
@ -728,14 +729,14 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_delete_temp_response_cookie() public async Task should_delete_temp_response_cookie()
{ {
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies"); var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
requestCookies.Cookies.Add("my", "cookie"); requestCookies.Cookies.Add("my", "cookie");
requestCookies.AllowAutoRedirect = false; requestCookies.AllowAutoRedirect = false;
requestCookies.StoreRequestCookie = true; requestCookies.StoreRequestCookie = true;
requestCookies.StoreResponseCookie = false; requestCookies.StoreResponseCookie = false;
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies); var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie"); responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
@ -743,7 +744,7 @@ namespace NzbDrone.Common.Test.Http
requestDelete.AllowAutoRedirect = true; requestDelete.AllowAutoRedirect = true;
requestDelete.StoreRequestCookie = false; requestDelete.StoreRequestCookie = false;
requestDelete.StoreResponseCookie = false; requestDelete.StoreResponseCookie = false;
var responseDelete = Subject.Get<HttpCookieResource>(requestDelete); var responseDelete = await Subject.GetAsync<HttpCookieResource>(requestDelete);
responseDelete.Resource.Cookies.Should().BeEmpty(); responseDelete.Resource.Cookies.Should().BeEmpty();
@ -761,13 +762,13 @@ namespace NzbDrone.Common.Test.Http
{ {
var request = new HttpRequest($"https://{_httpBinHost}/status/429"); var request = new HttpRequest($"https://{_httpBinHost}/status/429");
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request)); Assert.ThrowsAsync<TooManyRequestsException>(async () => await Subject.GetAsync(request));
ExceptionVerification.IgnoreWarns(); ExceptionVerification.IgnoreWarns();
} }
[Test] [Test]
public void should_call_interceptor() public async Task should_call_interceptor()
{ {
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new[] { Mocker.GetMock<IHttpRequestInterceptor>().Object }); Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new[] { Mocker.GetMock<IHttpRequestInterceptor>().Object });
@ -781,7 +782,7 @@ namespace NzbDrone.Common.Test.Http
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
Subject.Get(request); await Subject.GetAsync(request);
Mocker.GetMock<IHttpRequestInterceptor>() Mocker.GetMock<IHttpRequestInterceptor>()
.Verify(v => v.PreRequest(It.IsAny<HttpRequest>()), Times.Once()); .Verify(v => v.PreRequest(It.IsAny<HttpRequest>()), Times.Once());
@ -792,7 +793,7 @@ namespace NzbDrone.Common.Test.Http
[TestCase("en-US")] [TestCase("en-US")]
[TestCase("es-ES")] [TestCase("es-ES")]
public void should_parse_malformed_cloudflare_cookie(string culture) public async Task should_parse_malformed_cloudflare_cookie(string culture)
{ {
var origCulture = Thread.CurrentThread.CurrentCulture; var origCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
@ -808,11 +809,11 @@ namespace NzbDrone.Common.Test.Http
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet); var responseSet = await Subject.GetAsync(requestSet);
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().ContainKey("Cookie"); response.Resource.Headers.Should().ContainKey("Cookie");
@ -830,7 +831,7 @@ namespace NzbDrone.Common.Test.Http
} }
[TestCase("lang_code=en; expires=Wed, 23-Dec-2026 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")] [TestCase("lang_code=en; expires=Wed, 23-Dec-2026 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")]
public void should_reject_malformed_domain_cookie(string malformedCookie) public async Task should_reject_malformed_domain_cookie(string malformedCookie)
{ {
try try
{ {
@ -840,11 +841,11 @@ namespace NzbDrone.Common.Test.Http
requestSet.AllowAutoRedirect = false; requestSet.AllowAutoRedirect = false;
requestSet.StoreResponseCookie = true; requestSet.StoreResponseCookie = true;
var responseSet = Subject.Get(requestSet); var responseSet = await Subject.GetAsync(requestSet);
var request = new HttpRequest($"https://{_httpBinHost}/get"); var request = new HttpRequest($"https://{_httpBinHost}/get");
var response = Subject.Get<HttpBinResource>(request); var response = await Subject.GetAsync<HttpBinResource>(request);
response.Resource.Headers.Should().NotContainKey("Cookie"); response.Resource.Headers.Should().NotContainKey("Cookie");
@ -856,12 +857,12 @@ namespace NzbDrone.Common.Test.Http
} }
[Test] [Test]
public void should_correctly_use_basic_auth() public async Task should_correctly_use_basic_auth()
{ {
var request = new HttpRequest($"https://{_httpBinHost}/basic-auth/username/password"); var request = new HttpRequest($"https://{_httpBinHost}/basic-auth/username/password");
request.Credentials = new BasicNetworkCredential("username", "password"); request.Credentials = new BasicNetworkCredential("username", "password");
var response = Subject.Execute(request); var response = await Subject.ExecuteAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK); response.StatusCode.Should().Be(HttpStatusCode.OK);
} }

@ -1,9 +1,10 @@
using System.Net; using System.Net;
using System.Threading.Tasks;
namespace NzbDrone.Common.Http.Dispatchers namespace NzbDrone.Common.Http.Dispatchers
{ {
public interface IHttpDispatcher public interface IHttpDispatcher
{ {
HttpResponse GetResponse(HttpRequest request, CookieContainer cookies); Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies);
} }
} }

@ -43,7 +43,7 @@ namespace NzbDrone.Common.Http.Dispatchers
_credentialCache = cacheManager.GetCache<CredentialCache>(typeof(ManagedHttpDispatcher), "credentialcache"); _credentialCache = cacheManager.GetCache<CredentialCache>(typeof(ManagedHttpDispatcher), "credentialcache");
} }
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) public async Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies)
{ {
var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url) var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url)
{ {
@ -102,7 +102,7 @@ namespace NzbDrone.Common.Http.Dispatchers
var httpClient = GetClient(request.Url); var httpClient = GetClient(request.Url);
using var responseMessage = httpClient.Send(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token); using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
{ {
byte[] data = null; byte[] data = null;
@ -110,7 +110,7 @@ namespace NzbDrone.Common.Http.Dispatchers
{ {
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK) if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
{ {
responseMessage.Content.CopyTo(request.ResponseStream, null, cts.Token); await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token);
} }
else else
{ {

@ -5,6 +5,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -25,6 +26,16 @@ namespace NzbDrone.Common.Http
HttpResponse Post(HttpRequest request); HttpResponse Post(HttpRequest request);
HttpResponse<T> Post<T>(HttpRequest request) HttpResponse<T> Post<T>(HttpRequest request)
where T : new(); where T : new();
Task<HttpResponse> ExecuteAsync(HttpRequest request);
Task DownloadFileAsync(string url, string fileName);
Task<HttpResponse> GetAsync(HttpRequest request);
Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
where T : new();
Task<HttpResponse> HeadAsync(HttpRequest request);
Task<HttpResponse> PostAsync(HttpRequest request);
Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
where T : new();
} }
public class HttpClient : IHttpClient public class HttpClient : IHttpClient
@ -52,11 +63,11 @@ namespace NzbDrone.Common.Http
_cookieContainerCache = cacheManager.GetCache<CookieContainer>(typeof(HttpClient)); _cookieContainerCache = cacheManager.GetCache<CookieContainer>(typeof(HttpClient));
} }
public HttpResponse Execute(HttpRequest request) public virtual async Task<HttpResponse> ExecuteAsync(HttpRequest request)
{ {
var cookieContainer = InitializeRequestCookies(request); var cookieContainer = InitializeRequestCookies(request);
var response = ExecuteRequest(request, cookieContainer); var response = await ExecuteRequestAsync(request, cookieContainer);
if (request.AllowAutoRedirect && response.HasHttpRedirect) if (request.AllowAutoRedirect && response.HasHttpRedirect)
{ {
@ -82,7 +93,7 @@ namespace NzbDrone.Common.Http
request.ContentSummary = null; request.ContentSummary = null;
} }
response = ExecuteRequest(request, cookieContainer); response = await ExecuteRequestAsync(request, cookieContainer);
} }
while (response.HasHttpRedirect); while (response.HasHttpRedirect);
} }
@ -112,6 +123,11 @@ namespace NzbDrone.Common.Http
return response; return response;
} }
public HttpResponse Execute(HttpRequest request)
{
return ExecuteAsync(request).GetAwaiter().GetResult();
}
private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod) private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
{ {
return statusCode switch return statusCode switch
@ -122,7 +138,7 @@ namespace NzbDrone.Common.Http
}; };
} }
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer) private async Task<HttpResponse> ExecuteRequestAsync(HttpRequest request, CookieContainer cookieContainer)
{ {
foreach (var interceptor in _requestInterceptors) foreach (var interceptor in _requestInterceptors)
{ {
@ -131,14 +147,14 @@ namespace NzbDrone.Common.Http
if (request.RateLimit != TimeSpan.Zero) if (request.RateLimit != TimeSpan.Zero)
{ {
_rateLimitService.WaitAndPulse(request.Url.Host, request.RateLimitKey, request.RateLimit); await _rateLimitService.WaitAndPulseAsync(request.Url.Host, request.RateLimitKey, request.RateLimit);
} }
_logger.Trace(request); _logger.Trace(request);
var stopWatch = Stopwatch.StartNew(); var stopWatch = Stopwatch.StartNew();
var response = _httpDispatcher.GetResponse(request, cookieContainer); var response = await _httpDispatcher.GetResponseAsync(request, cookieContainer);
HandleResponseCookies(response, cookieContainer); HandleResponseCookies(response, cookieContainer);
@ -246,7 +262,7 @@ namespace NzbDrone.Common.Http
} }
} }
public void DownloadFile(string url, string fileName) public async Task DownloadFileAsync(string url, string fileName)
{ {
var fileNamePart = fileName + ".part"; var fileNamePart = fileName + ".part";
@ -261,12 +277,12 @@ namespace NzbDrone.Common.Http
_logger.Debug("Downloading [{0}] to [{1}]", url, fileName); _logger.Debug("Downloading [{0}] to [{1}]", url, fileName);
var stopWatch = Stopwatch.StartNew(); var stopWatch = Stopwatch.StartNew();
using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite)) await using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite))
{ {
var request = new HttpRequest(url); var request = new HttpRequest(url);
request.AllowAutoRedirect = true; request.AllowAutoRedirect = true;
request.ResponseStream = fileStream; request.ResponseStream = fileStream;
var response = Get(request); var response = await GetAsync(request);
if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html")) if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html"))
{ {
@ -275,6 +291,7 @@ namespace NzbDrone.Common.Http
} }
stopWatch.Stop(); stopWatch.Stop();
if (File.Exists(fileName)) if (File.Exists(fileName))
{ {
File.Delete(fileName); File.Delete(fileName);
@ -292,40 +309,73 @@ namespace NzbDrone.Common.Http
} }
} }
public HttpResponse Get(HttpRequest request) public void DownloadFile(string url, string fileName)
{
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development#the-thread-pool-hack
Task.Run(() => DownloadFileAsync(url, fileName)).GetAwaiter().GetResult();
}
public Task<HttpResponse> GetAsync(HttpRequest request)
{ {
request.Method = HttpMethod.Get; request.Method = HttpMethod.Get;
return Execute(request); return ExecuteAsync(request);
} }
public HttpResponse<T> Get<T>(HttpRequest request) public HttpResponse Get(HttpRequest request)
{
return Task.Run(() => GetAsync(request)).GetAwaiter().GetResult();
}
public async Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
where T : new() where T : new()
{ {
var response = Get(request); var response = await GetAsync(request);
CheckResponseContentType(response); CheckResponseContentType(response);
return new HttpResponse<T>(response); return new HttpResponse<T>(response);
} }
public HttpResponse Head(HttpRequest request) public HttpResponse<T> Get<T>(HttpRequest request)
where T : new()
{
return Task.Run(() => GetAsync<T>(request)).GetAwaiter().GetResult();
}
public Task<HttpResponse> HeadAsync(HttpRequest request)
{ {
request.Method = HttpMethod.Head; request.Method = HttpMethod.Head;
return Execute(request); return ExecuteAsync(request);
} }
public HttpResponse Post(HttpRequest request) public HttpResponse Head(HttpRequest request)
{
return Task.Run(() => HeadAsync(request)).GetAwaiter().GetResult();
}
public Task<HttpResponse> PostAsync(HttpRequest request)
{ {
request.Method = HttpMethod.Post; request.Method = HttpMethod.Post;
return Execute(request); return ExecuteAsync(request);
} }
public HttpResponse<T> Post<T>(HttpRequest request) public HttpResponse Post(HttpRequest request)
{
return Task.Run(() => PostAsync(request)).GetAwaiter().GetResult();
}
public async Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
where T : new() where T : new()
{ {
var response = Post(request); var response = await PostAsync(request);
CheckResponseContentType(response); CheckResponseContentType(response);
return new HttpResponse<T>(response); return new HttpResponse<T>(response);
} }
public HttpResponse<T> Post<T>(HttpRequest request)
where T : new()
{
return Task.Run(() => PostAsync<T>(request)).GetAwaiter().GetResult();
}
private void CheckResponseContentType(HttpResponse response) private void CheckResponseContentType(HttpResponse response)
{ {
if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html")) if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html"))

@ -16,6 +16,7 @@ namespace NzbDrone.Common.Http
Method = HttpMethod.Get; Method = HttpMethod.Get;
Url = new HttpUri(url); Url = new HttpUri(url);
Headers = new HttpHeader(); Headers = new HttpHeader();
ConnectionKeepAlive = true;
AllowAutoRedirect = true; AllowAutoRedirect = true;
StoreRequestCookie = true; StoreRequestCookie = true;
LogHttpError = true; LogHttpError = true;

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -10,6 +11,8 @@ namespace NzbDrone.Common.TPL
{ {
void WaitAndPulse(string key, TimeSpan interval); void WaitAndPulse(string key, TimeSpan interval);
void WaitAndPulse(string key, string subKey, TimeSpan interval); void WaitAndPulse(string key, string subKey, TimeSpan interval);
Task WaitAndPulseAsync(string key, TimeSpan interval);
Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval);
} }
public class RateLimitService : IRateLimitService public class RateLimitService : IRateLimitService
@ -28,7 +31,34 @@ namespace NzbDrone.Common.TPL
WaitAndPulse(key, null, interval); WaitAndPulse(key, null, interval);
} }
public async Task WaitAndPulseAsync(string key, TimeSpan interval)
{
await WaitAndPulseAsync(key, null, interval);
}
public void WaitAndPulse(string key, string subKey, TimeSpan interval) public void WaitAndPulse(string key, string subKey, TimeSpan interval)
{
var delay = GetDelay(key, subKey, interval);
if (delay.TotalSeconds > 0.0)
{
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
System.Threading.Thread.Sleep(delay);
}
}
public async Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval)
{
var delay = GetDelay(key, subKey, interval);
if (delay.TotalSeconds > 0.0)
{
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
await Task.Delay(delay);
}
}
private TimeSpan GetDelay(string key, string subKey, TimeSpan interval)
{ {
var waitUntil = DateTime.UtcNow.Add(interval); var waitUntil = DateTime.UtcNow.Add(interval);
@ -59,13 +89,7 @@ namespace NzbDrone.Common.TPL
waitUntil -= interval; waitUntil -= interval;
var delay = waitUntil - DateTime.UtcNow; return waitUntil - DateTime.UtcNow;
if (delay.TotalSeconds > 0.0)
{
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
System.Threading.Thread.Sleep(delay);
}
} }
} }
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
} }
[Test] [Test]
public void should_download_report_if_album_was_not_already_downloaded() public async Task should_download_report_if_album_was_not_already_downloaded()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -66,12 +67,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once());
} }
[Test] [Test]
public void should_only_download_album_once() public async Task should_only_download_album_once()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -80,12 +81,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once());
} }
[Test] [Test]
public void should_not_download_if_any_album_was_already_downloaded() public async Task should_not_download_if_any_album_was_already_downloaded()
{ {
var remoteAlbum1 = GetRemoteAlbum( var remoteAlbum1 = GetRemoteAlbum(
new List<Album> { GetAlbum(1) }, new List<Album> { GetAlbum(1) },
@ -99,12 +100,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum1)); decisions.Add(new DownloadDecision(remoteAlbum1));
decisions.Add(new DownloadDecision(remoteAlbum2)); decisions.Add(new DownloadDecision(remoteAlbum2));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once());
} }
[Test] [Test]
public void should_return_downloaded_reports() public async Task should_return_downloaded_reports()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -112,11 +113,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(1); var result = await Subject.ProcessDecisions(decisions);
result.Grabbed.Should().HaveCount(1);
} }
[Test] [Test]
public void should_return_all_downloaded_reports() public async Task should_return_all_downloaded_reports()
{ {
var remoteAlbum1 = GetRemoteAlbum( var remoteAlbum1 = GetRemoteAlbum(
new List<Album> { GetAlbum(1) }, new List<Album> { GetAlbum(1) },
@ -130,11 +133,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum1)); decisions.Add(new DownloadDecision(remoteAlbum1));
decisions.Add(new DownloadDecision(remoteAlbum2)); decisions.Add(new DownloadDecision(remoteAlbum2));
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2); var result = await Subject.ProcessDecisions(decisions);
result.Grabbed.Should().HaveCount(2);
} }
[Test] [Test]
public void should_only_return_downloaded_reports() public async Task should_only_return_downloaded_reports()
{ {
var remoteAlbum1 = GetRemoteAlbum( var remoteAlbum1 = GetRemoteAlbum(
new List<Album> { GetAlbum(1) }, new List<Album> { GetAlbum(1) },
@ -153,11 +158,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum2)); decisions.Add(new DownloadDecision(remoteAlbum2));
decisions.Add(new DownloadDecision(remoteAlbum3)); decisions.Add(new DownloadDecision(remoteAlbum3));
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2); var result = await Subject.ProcessDecisions(decisions);
result.Grabbed.Should().HaveCount(2);
} }
[Test] [Test]
public void should_not_add_to_downloaded_list_when_download_fails() public async Task should_not_add_to_downloaded_list_when_download_fails()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -166,7 +173,11 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>())).Throws(new Exception()); Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>())).Throws(new Exception());
Subject.ProcessDecisions(decisions).Grabbed.Should().BeEmpty();
var result = await Subject.ProcessDecisions(decisions);
result.Grabbed.Should().BeEmpty();
ExceptionVerification.ExpectedWarns(1); ExceptionVerification.ExpectedWarns(1);
} }
@ -181,7 +192,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
} }
[Test] [Test]
public void should_not_grab_if_pending() public async Task should_not_grab_if_pending()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -189,12 +200,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary))); decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Never()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Never());
} }
[Test] [Test]
public void should_not_add_to_pending_if_album_was_grabbed() public async Task should_not_add_to_pending_if_album_was_grabbed()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -203,12 +214,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum)); decisions.Add(new DownloadDecision(remoteAlbum));
decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary))); decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never()); Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never());
} }
[Test] [Test]
public void should_add_to_pending_even_if_already_added_to_pending() public async Task should_add_to_pending_even_if_already_added_to_pending()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_192));
@ -217,12 +228,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary))); decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary)));
decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary))); decisions.Add(new DownloadDecision(remoteAlbum, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once()); Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once());
} }
[Test] [Test]
public void should_add_to_failed_if_already_failed_for_that_protocol() public async Task should_add_to_failed_if_already_failed_for_that_protocol()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320));
@ -234,12 +245,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>())) Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>()))
.Throws(new DownloadClientUnavailableException("Download client failed")); .Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteAlbum>()), Times.Once());
} }
[Test] [Test]
public void should_not_add_to_failed_if_failed_for_a_different_protocol() public async Task should_not_add_to_failed_if_failed_for_a_different_protocol()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320), DownloadProtocol.Usenet); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320), DownloadProtocol.Usenet);
@ -252,13 +263,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet))) Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)))
.Throws(new DownloadClientUnavailableException("Download client failed")); .Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions); await Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once());
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once()); Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteAlbum>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once());
} }
[Test] [Test]
public void should_add_to_rejected_if_release_unavailable_on_indexer() public async Task should_add_to_rejected_if_release_unavailable_on_indexer()
{ {
var albums = new List<Album> { GetAlbum(1) }; var albums = new List<Album> { GetAlbum(1) };
var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320)); var remoteAlbum = GetRemoteAlbum(albums, new QualityModel(Quality.MP3_320));
@ -270,7 +281,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
.Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>())) .Setup(s => s.DownloadReport(It.IsAny<RemoteAlbum>()))
.Throws(new ReleaseUnavailableException(remoteAlbum.Release, "That 404 Error is not just a Quirk")); .Throws(new ReleaseUnavailableException(remoteAlbum.Release, "That 404 Error is not just a Quirk"));
var result = Subject.ProcessDecisions(decisions); var result = await Subject.ProcessDecisions(decisions);
result.Grabbed.Should().BeEmpty(); result.Grabbed.Should().BeEmpty();
result.Rejected.Should().NotBeEmpty(); result.Rejected.Should().NotBeEmpty();

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Abstractions; using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -70,7 +71,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
protected void GivenFailedDownload() protected void GivenFailedDownload()
{ {
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>())) .Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
.Throws(new WebException()); .Throws(new WebException());
} }
@ -148,19 +149,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
} }
[Test] [Test]
public void Download_should_download_file_if_it_doesnt_exist() public async Task Download_should_download_file_if_it_doesnt_exist()
{ {
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
public void Download_should_save_magnet_if_enabled() public async Task Download_should_save_magnet_if_enabled()
{ {
GivenMagnetFilePath(); GivenMagnetFilePath();
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true; Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
@ -168,16 +169,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = null; remoteAlbum.Release.DownloadUrl = null;
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
public void Download_should_save_magnet_using_specified_extension() public async Task Download_should_save_magnet_using_specified_extension()
{ {
var magnetFileExtension = ".url"; var magnetFileExtension = ".url";
GivenMagnetFilePath(magnetFileExtension); GivenMagnetFilePath(magnetFileExtension);
@ -188,12 +189,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = null; remoteAlbum.Release.DownloadUrl = null;
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
@ -203,31 +204,31 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = null; remoteAlbum.Release.DownloadUrl = null;
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
public void Download_should_prefer_torrent_over_magnet() public async Task Download_should_prefer_torrent_over_magnet()
{ {
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true; Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
public void Download_should_replace_illegal_characters_in_title() public async Task Download_should_replace_illegal_characters_in_title()
{ {
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]"; var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath)); var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
@ -235,11 +236,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.Title = illegalTitle; remoteAlbum.Release.Title = illegalTitle;
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
@ -248,7 +249,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = null; remoteAlbum.Release.DownloadUrl = null;
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
} }
[Test] [Test]
@ -318,11 +319,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
} }
[Test] [Test]
public void should_return_null_hash() public async Task should_return_null_hash()
{ {
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Subject.Download(remoteAlbum, CreateIndexer()).Should().BeNull(); var result = await Subject.Download(remoteAlbum, CreateIndexer());
result.Should().BeNull();
} }
} }
} }

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Abstractions; using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -119,19 +120,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
} }
[Test] [Test]
public void Download_should_download_file_if_it_doesnt_exist() public async Task Download_should_download_file_if_it_doesnt_exist()
{ {
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]
public void Download_should_replace_illegal_characters_in_title() public async Task Download_should_replace_illegal_characters_in_title()
{ {
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]"; var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath)); var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
@ -139,11 +140,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.Title = illegalTitle; remoteAlbum.Release.Title = illegalTitle;
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once()); Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
} }
[Test] [Test]

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -200,26 +201,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = magnetUrl; remoteAlbum.Release.DownloadUrl = magnetUrl;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NLog; using NLog;
@ -36,8 +37,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
.Returns(() => CreateRemoteAlbum()); .Returns(() => CreateRemoteAlbum());
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>())) .Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>())); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), Array.Empty<byte>())));
Mocker.GetMock<IRemotePathMappingService>() Mocker.GetMock<IRemotePathMappingService>()
.Setup(v => v.RemapRemoteToLocal(It.IsAny<string>(), It.IsAny<OsPath>())) .Setup(v => v.RemapRemoteToLocal(It.IsAny<string>(), It.IsAny<OsPath>()))

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -385,7 +386,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_with_MusicDirectory_should_force_directory() public async Task Download_with_MusicDirectory_should_force_directory()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenMusicDirectory(); GivenMusicDirectory();
@ -393,7 +394,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -402,7 +403,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_with_category_should_force_directory() public async Task Download_with_category_should_force_directory()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenMusicCategory(); GivenMusicCategory();
@ -410,7 +411,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -419,14 +420,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_without_MusicDirectory_and_Category_should_use_default() public async Task Download_without_MusicDirectory_and_Category_should_use_default()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -505,7 +506,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
.Setup(s => s.GetSerialNumber(_settings)) .Setup(s => s.GetSerialNumber(_settings))
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException")); .Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteAlbum, CreateIndexer()));
Mocker.GetMock<IDownloadStationTaskProxy>() Mocker.GetMock<IDownloadStationTaskProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never()); .Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -267,7 +268,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_with_MusicDirectory_should_force_directory() public async Task Download_with_MusicDirectory_should_force_directory()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenMusicDirectory(); GivenMusicDirectory();
@ -275,7 +276,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -284,7 +285,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_with_category_should_force_directory() public async Task Download_with_category_should_force_directory()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenMusicCategory(); GivenMusicCategory();
@ -292,7 +293,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -301,14 +302,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
} }
[Test] [Test]
public void Download_without_MusicDirectory_and_Category_should_use_default() public async Task Download_without_MusicDirectory_and_Category_should_use_default()
{ {
GivenSerialNumber(); GivenSerialNumber();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -387,7 +388,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
.Setup(s => s.GetSerialNumber(_settings)) .Setup(s => s.GetSerialNumber(_settings))
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException")); .Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteAlbum, CreateIndexer()));
Mocker.GetMock<IDownloadStationTaskProxy>() Mocker.GetMock<IDownloadStationTaskProxy>()
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never()); .Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -103,8 +104,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
protected void GivenSuccessfulDownload() protected void GivenSuccessfulDownload()
{ {
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>())) .Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000])); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new byte[1000])));
Mocker.GetMock<IHadoukenProxy>() Mocker.GetMock<IHadoukenProxy>()
.Setup(s => s.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>())) .Setup(s => s.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()))
@ -196,13 +197,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
@ -277,7 +278,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
} }
[Test] [Test]
public void Download_from_magnet_link_should_return_hash_uppercase() public async Task Download_from_magnet_link_should_return_hash_uppercase()
{ {
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
@ -286,13 +287,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
Mocker.GetMock<IHadoukenProxy>() Mocker.GetMock<IHadoukenProxy>()
.Setup(v => v.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>())); .Setup(v => v.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()));
var result = Subject.Download(remoteAlbum, CreateIndexer()); var result = await Subject.Download(remoteAlbum, CreateIndexer());
Assert.IsFalse(result.Any(c => char.IsLower(c))); Assert.IsFalse(result.Any(c => char.IsLower(c)));
} }
[Test] [Test]
public void Download_from_torrent_file_should_return_hash_uppercase() public async Task Download_from_torrent_file_should_return_hash_uppercase()
{ {
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
@ -300,7 +301,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
.Setup(v => v.AddTorrentFile(It.IsAny<HadoukenSettings>(), It.IsAny<byte[]>())) .Setup(v => v.AddTorrentFile(It.IsAny<HadoukenSettings>(), It.IsAny<byte[]>()))
.Returns("hash"); .Returns("hash");
var result = Subject.Download(remoteAlbum, CreateIndexer()); var result = await Subject.Download(remoteAlbum, CreateIndexer());
Assert.IsFalse(result.Any(c => char.IsLower(c))); Assert.IsFalse(result.Any(c => char.IsLower(c)));
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -200,13 +201,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
@ -218,7 +219,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Assert.Throws<DownloadClientException>(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
} }
[Test] [Test]

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -339,13 +340,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
@ -357,7 +358,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
Assert.Throws<DownloadClientRejectedReleaseException>(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync<DownloadClientRejectedReleaseException>(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
} }
[Test] [Test]

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
using NLog; using NLog;
@ -65,15 +66,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
private void WithFailedDownload() private void WithFailedDownload()
{ {
Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException()); Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
} }
[Test] [Test]
public void should_download_file_if_it_doesnt_exist() public async Task should_download_file_if_it_doesnt_exist()
{ {
Subject.Download(_remoteAlbum, _indexer); await Subject.Download(_remoteAlbum, _indexer);
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(_nzbUrl, _nzbPath), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(_nzbUrl, _nzbPath), Times.Once());
} }
[Test] [Test]
@ -81,7 +82,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
{ {
WithFailedDownload(); WithFailedDownload();
Assert.Throws<WebException>(() => Subject.Download(_remoteAlbum, _indexer)); Assert.ThrowsAsync<WebException>(async () => await Subject.Download(_remoteAlbum, _indexer));
} }
[Test] [Test]
@ -90,7 +91,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
_remoteAlbum.Release.Title = "Alien Ant Farm - Discography"; _remoteAlbum.Release.Title = "Alien Ant Farm - Discography";
_remoteAlbum.ParsedAlbumInfo.Discography = true; _remoteAlbum.ParsedAlbumInfo.Discography = true;
Assert.Throws<NotSupportedException>(() => Subject.Download(_remoteAlbum, _indexer)); Assert.ThrowsAsync<NotSupportedException>(async () => await Subject.Download(_remoteAlbum, _indexer));
} }
[Test] [Test]
@ -100,15 +101,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
} }
[Test] [Test]
public void should_replace_illegal_characters_in_title() public async Task should_replace_illegal_characters_in_title()
{ {
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteAlbum.Release.Title = illegalTitle; _remoteAlbum.Release.Title = illegalTitle;
Subject.Download(_remoteAlbum, _indexer); await Subject.Download(_remoteAlbum, _indexer);
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename), Times.Once()); Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), expectedFilename), Times.Once());
} }
} }
} }

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -448,26 +449,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = magnetUrl; remoteAlbum.Release.DownloadUrl = magnetUrl;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }
@ -482,7 +483,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR"; remoteAlbum.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR";
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
} }
[Test] [Test]
@ -495,28 +496,28 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc"; remoteAlbum.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc";
Assert.DoesNotThrow(() => Subject.Download(remoteAlbum, CreateIndexer())); Assert.DoesNotThrowAsync(async () => await Subject.Download(remoteAlbum, CreateIndexer()));
Mocker.GetMock<IQBittorrentProxy>() Mocker.GetMock<IQBittorrentProxy>()
.Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()), Times.Once()); .Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()), Times.Once());
} }
[Test] [Test]
public void Download_should_set_top_priority() public async Task Download_should_set_top_priority()
{ {
GivenHighPriority(); GivenHighPriority();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<IQBittorrentProxy>() Mocker.GetMock<IQBittorrentProxy>()
.Verify(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once()); .Verify(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
} }
[Test] [Test]
public void Download_should_not_fail_if_top_priority_not_available() public async Task Download_should_not_fail_if_top_priority_not_available()
{ {
GivenHighPriority(); GivenHighPriority();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
@ -527,7 +528,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -554,27 +555,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
} }
[Test] [Test]
public void Download_should_handle_http_redirect_to_magnet() public async Task Download_should_handle_http_redirect_to_magnet()
{ {
GivenRedirectToMagnet(); GivenRedirectToMagnet();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[Test] [Test]
public void Download_should_handle_http_redirect_to_torrent() public async Task Download_should_handle_http_redirect_to_torrent()
{ {
GivenRedirectToTorrent(); GivenRedirectToTorrent();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -111,13 +112,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -300,27 +301,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
} }
[TestCase("[ TOWN ]-[ http://www.town.ag ]-[ ANIME ]-[Usenet Provider >> http://www.ssl- <<] - [Commie] Aldnoah Zero 18 [234C8FC7]", "[ TOWN ]-[ http-++www.town.ag ]-[ ANIME ]-[Usenet Provider http-++www.ssl- ] - [Commie] Aldnoah Zero 18 [234C8FC7].nzb")] [TestCase("[ TOWN ]-[ http://www.town.ag ]-[ ANIME ]-[Usenet Provider >> http://www.ssl- <<] - [Commie] Aldnoah Zero 18 [234C8FC7]", "[ TOWN ]-[ http-++www.town.ag ]-[ ANIME ]-[Usenet Provider http-++www.ssl- ] - [Commie] Aldnoah Zero 18 [234C8FC7].nzb")]
public void Download_should_use_clean_title(string title, string filename) public async Task Download_should_use_clean_title(string title, string filename)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.Title = title; remoteAlbum.Release.Title = title;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<ISabnzbdProxy>() Mocker.GetMock<ISabnzbdProxy>()
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), filename, It.IsAny<string>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()), Times.Once()); .Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), filename, It.IsAny<string>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()), Times.Once());
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
@ -353,7 +354,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
} }
[Test] [Test]
public void Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true() public async Task Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
{ {
Mocker.GetMock<ISabnzbdProxy>() Mocker.GetMock<ISabnzbdProxy>()
.Setup(s => s.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>())) .Setup(s => s.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()))
@ -366,7 +367,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
.Build() .Build()
.ToList(); .ToList();
Subject.Download(remoteAlbum, CreateIndexer()); await Subject.Download(remoteAlbum, CreateIndexer());
Mocker.GetMock<ISabnzbdProxy>() Mocker.GetMock<ISabnzbdProxy>()
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()), Times.Once()); .Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()), Times.Once());

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -55,26 +56,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[Test] [Test]
public void Download_with_MusicDirectory_should_force_directory() public async Task Download_with_MusicDirectory_should_force_directory()
{ {
GivenMusicDirectory(); GivenMusicDirectory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -83,14 +84,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
} }
[Test] [Test]
public void Download_with_category_should_force_directory() public async Task Download_with_category_should_force_directory()
{ {
GivenMusicCategory(); GivenMusicCategory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -99,7 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
} }
[Test] [Test]
public void Download_with_category_should_not_have_double_slashes() public async Task Download_with_category_should_not_have_double_slashes()
{ {
GivenMusicCategory(); GivenMusicCategory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
@ -108,7 +109,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -117,13 +118,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
} }
[Test] [Test]
public void Download_without_MusicDirectory_and_Category_should_use_default() public async Task Download_without_MusicDirectory_and_Category_should_use_default()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -132,14 +133,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
} }
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = magnetUrl; remoteAlbum.Release.DownloadUrl = magnetUrl;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -228,13 +229,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
@ -252,14 +253,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
// Proxy.GetTorrents does not return original url. So item has to be found via magnet url. // Proxy.GetTorrents does not return original url. So item has to be found via magnet url.
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = magnetUrl; remoteAlbum.Release.DownloadUrl = magnetUrl;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }
@ -350,27 +351,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
} }
[Test] [Test]
public void Download_should_handle_http_redirect_to_magnet() public async Task Download_should_handle_http_redirect_to_magnet()
{ {
GivenRedirectToMagnet(); GivenRedirectToMagnet();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[Test] [Test]
public void Download_should_handle_http_redirect_to_torrent() public async Task Download_should_handle_http_redirect_to_torrent()
{ {
GivenRedirectToTorrent(); GivenRedirectToTorrent();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -63,26 +64,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
} }
[Test] [Test]
public void Download_should_return_unique_id() public async Task Download_should_return_unique_id()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
} }
[Test] [Test]
public void Download_with_MusicDirectory_should_force_directory() public async Task Download_with_MusicDirectory_should_force_directory()
{ {
GivenMusicDirectory(); GivenMusicDirectory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -91,14 +92,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
} }
[Test] [Test]
public void Download_with_category_should_force_directory() public async Task Download_with_category_should_force_directory()
{ {
GivenMusicCategory(); GivenMusicCategory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -107,7 +108,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
} }
[Test] [Test]
public void Download_with_category_should_not_have_double_slashes() public async Task Download_with_category_should_not_have_double_slashes()
{ {
GivenMusicCategory(); GivenMusicCategory();
GivenSuccessfulDownload(); GivenSuccessfulDownload();
@ -116,7 +117,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -125,13 +126,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
} }
[Test] [Test]
public void Download_without_MusicDirectory_and_Category_should_use_default() public async Task Download_without_MusicDirectory_and_Category_should_use_default()
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().NotBeNullOrEmpty(); id.Should().NotBeNullOrEmpty();
@ -140,14 +141,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
} }
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")] [TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash) public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
{ {
GivenSuccessfulDownload(); GivenSuccessfulDownload();
var remoteAlbum = CreateRemoteAlbum(); var remoteAlbum = CreateRemoteAlbum();
remoteAlbum.Release.DownloadUrl = magnetUrl; remoteAlbum.Release.DownloadUrl = magnetUrl;
var id = Subject.Download(remoteAlbum, CreateIndexer()); var id = await Subject.Download(remoteAlbum, CreateIndexer());
id.Should().Be(expectedHash); id.Should().Be(expectedHash);
} }

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -77,23 +78,23 @@ namespace NzbDrone.Core.Test.Download
} }
[Test] [Test]
public void Download_report_should_publish_on_grab_event() public async Task Download_report_should_publish_on_grab_event()
{ {
var mock = WithUsenetClient(); var mock = WithUsenetClient();
mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>())); mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()));
Subject.DownloadReport(_parseResult); await Subject.DownloadReport(_parseResult);
VerifyEventPublished<AlbumGrabbedEvent>(); VerifyEventPublished<AlbumGrabbedEvent>();
} }
[Test] [Test]
public void Download_report_should_grab_using_client() public async Task Download_report_should_grab_using_client()
{ {
var mock = WithUsenetClient(); var mock = WithUsenetClient();
mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>())); mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()));
Subject.DownloadReport(_parseResult); await Subject.DownloadReport(_parseResult);
mock.Verify(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once()); mock.Verify(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once());
} }
@ -105,7 +106,7 @@ namespace NzbDrone.Core.Test.Download
mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>())) mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()))
.Throws(new WebException()); .Throws(new WebException());
Assert.Throws<WebException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<WebException>(async () => await Subject.DownloadReport(_parseResult));
VerifyEventNotPublished<AlbumGrabbedEvent>(); VerifyEventNotPublished<AlbumGrabbedEvent>();
} }
@ -120,7 +121,7 @@ namespace NzbDrone.Core.Test.Download
throw new ReleaseDownloadException(v.Release, "Error", new WebException()); throw new ReleaseDownloadException(v.Release, "Error", new WebException());
}); });
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once()); .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once());
@ -140,7 +141,7 @@ namespace NzbDrone.Core.Test.Download
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response)); throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
}); });
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once()); .Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
@ -160,7 +161,7 @@ namespace NzbDrone.Core.Test.Download
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response)); throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
}); });
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), .Verify(v => v.RecordFailure(It.IsAny<int>(),
@ -174,7 +175,7 @@ namespace NzbDrone.Core.Test.Download
mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>())) mock.Setup(s => s.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()))
.Throws(new DownloadClientException("Some Error")); .Throws(new DownloadClientException("Some Error"));
Assert.Throws<DownloadClientException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never()); .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
@ -190,7 +191,7 @@ namespace NzbDrone.Core.Test.Download
throw new ReleaseUnavailableException(v.Release, "Error", new WebException()); throw new ReleaseUnavailableException(v.Release, "Error", new WebException());
}); });
Assert.Throws<ReleaseUnavailableException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<ReleaseUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never()); .Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
@ -199,14 +200,14 @@ namespace NzbDrone.Core.Test.Download
[Test] [Test]
public void should_not_attempt_download_if_client_isnt_configured() public void should_not_attempt_download_if_client_isnt_configured()
{ {
Assert.Throws<DownloadClientUnavailableException>(() => Subject.DownloadReport(_parseResult)); Assert.ThrowsAsync<DownloadClientUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never()); Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never());
VerifyEventNotPublished<AlbumGrabbedEvent>(); VerifyEventNotPublished<AlbumGrabbedEvent>();
} }
[Test] [Test]
public void should_attempt_download_even_if_client_is_disabled() public async Task should_attempt_download_even_if_client_is_disabled()
{ {
var mockUsenet = WithUsenetClient(); var mockUsenet = WithUsenetClient();
@ -221,7 +222,7 @@ namespace NzbDrone.Core.Test.Download
} }
}); });
Subject.DownloadReport(_parseResult); await Subject.DownloadReport(_parseResult);
Mocker.GetMock<IDownloadClientStatusService>().Verify(c => c.GetBlockedProviders(), Times.Never()); Mocker.GetMock<IDownloadClientStatusService>().Verify(c => c.GetBlockedProviders(), Times.Never());
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once()); mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once());
@ -229,26 +230,26 @@ namespace NzbDrone.Core.Test.Download
} }
[Test] [Test]
public void should_send_download_to_correct_usenet_client() public async Task should_send_download_to_correct_usenet_client()
{ {
var mockTorrent = WithTorrentClient(); var mockTorrent = WithTorrentClient();
var mockUsenet = WithUsenetClient(); var mockUsenet = WithUsenetClient();
Subject.DownloadReport(_parseResult); await Subject.DownloadReport(_parseResult);
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never()); mockTorrent.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never());
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once()); mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once());
} }
[Test] [Test]
public void should_send_download_to_correct_torrent_client() public async Task should_send_download_to_correct_torrent_client()
{ {
var mockTorrent = WithTorrentClient(); var mockTorrent = WithTorrentClient();
var mockUsenet = WithUsenetClient(); var mockUsenet = WithUsenetClient();
_parseResult.Release.DownloadProtocol = DownloadProtocol.Torrent; _parseResult.Release.DownloadProtocol = DownloadProtocol.Torrent;
Subject.DownloadReport(_parseResult); await Subject.DownloadReport(_parseResult);
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once()); mockTorrent.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Once());
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never()); mockUsenet.Verify(c => c.Download(It.IsAny<RemoteAlbum>(), It.IsAny<IIndexer>()), Times.Never());

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
@ -27,11 +28,11 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
Mocker.GetMock<ISearchForReleases>() Mocker.GetMock<ISearchForReleases>()
.Setup(s => s.ArtistSearch(_artist.Id, false, true, false)) .Setup(s => s.ArtistSearch(_artist.Id, false, true, false))
.Returns(new List<DownloadDecision>()); .Returns(Task.FromResult(new List<DownloadDecision>()));
Mocker.GetMock<IProcessDownloadDecisions>() Mocker.GetMock<IProcessDownloadDecisions>()
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>())) .Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>())); .Returns(Task.FromResult(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>())));
} }
[Test] [Test]

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_FileList() public async Task should_parse_recent_feed_from_FileList()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/FileList/RecentFeed.json"); var recentFeed = ReadAllText(@"Files/Indexers/FileList/RecentFeed.json");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(2); releases.Should().HaveCount(2);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -30,24 +31,24 @@ namespace NzbDrone.Core.Test.IndexerTests.GazelleTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_gazelle() public async Task should_parse_recent_feed_from_gazelle()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json"); var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json");
var indexFeed = ReadAllText(@"Files/Indexers/Gazelle/GazelleIndex.json"); var indexFeed = ReadAllText(@"Files/Indexers/Gazelle/GazelleIndex.json");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse")))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse"))))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)));
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index")))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index"))))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php")))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php"))))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(4); releases.Should().HaveCount(4);

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NLog; using NLog;
@ -10,7 +11,7 @@ using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.NewznabTests namespace NzbDrone.Core.Test.IndexerTests.GazelleTests
{ {
public class GazelleRequestGeneratorFixture : CoreTest<GazelleRequestGenerator> public class GazelleRequestGeneratorFixture : CoreTest<GazelleRequestGenerator>
{ {
@ -40,8 +41,8 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
}; };
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(v => v.Execute(It.IsAny<HttpRequest>())) .Setup(v => v.ExecuteAsync(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), "{ \"status\": \"success\", \"response\": { \"authkey\": \"key\", \"passkey\": \"key\" } }")); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), "{ \"status\": \"success\", \"response\": { \"authkey\": \"key\", \"passkey\": \"key\" } }")));
Mocker.GetMock<ICached<Dictionary<string, string>>>() Mocker.GetMock<ICached<Dictionary<string, string>>>()
.Setup(v => v.Find(It.IsAny<string>())) .Setup(v => v.Find(It.IsAny<string>()))

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -38,15 +39,15 @@ namespace NzbDrone.Core.Test.IndexerTests.HeadphonesTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_headphones() public async Task should_parse_recent_feed_from_headphones()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Headphones/Headphones.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Headphones/Headphones.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(16); releases.Should().HaveCount(16);

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -84,15 +85,15 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_IPTorrents() public async Task should_parse_recent_feed_from_IPTorrents()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/IPTorrents/IPTorrents.xml"); var recentFeed = ReadAllText(@"Files/Indexers/IPTorrents/IPTorrents.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -39,15 +40,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_newznab_nzb_su() public async Task should_parse_recent_feed_from_newznab_nzb_su()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(100); releases.Should().HaveCount(100);
@ -83,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
} }
[Test] [Test]
public void should_record_indexer_failure_if_caps_throw() public async Task should_record_indexer_failure_if_caps_throw()
{ {
var request = new HttpRequest("http://my.indexer.com"); var request = new HttpRequest("http://my.indexer.com");
var response = new HttpResponse(request, new HttpHeader(), Array.Empty<byte>(), (HttpStatusCode)429); var response = new HttpResponse(request, new HttpHeader(), Array.Empty<byte>(), (HttpStatusCode)429);
@ -96,7 +97,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
_caps.MaxPageSize = 30; _caps.MaxPageSize = 30;
_caps.DefaultPageSize = 25; _caps.DefaultPageSize = 25;
Subject.FetchRecent().Should().BeEmpty(); var releases = await Subject.FetchRecent();
releases.Should().BeEmpty();
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once()); .Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NyaaTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_Nyaa() public async Task should_parse_recent_feed_from_Nyaa()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Nyaa/Nyaa.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Nyaa/Nyaa.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(4); releases.Should().HaveCount(4);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -28,17 +29,17 @@ namespace NzbDrone.Core.Test.IndexerTests.RedactedTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_redacted() public async Task should_parse_recent_feed_from_redacted()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json"); var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get &&
v.Url.FullUri.Contains("ajax.php?action=browse") && v.Url.FullUri.Contains("ajax.php?action=browse") &&
v.Headers.Get("Authorization") == ((RedactedSettings)Subject.Definition.Settings).ApiKey))) v.Headers.Get("Authorization") == ((RedactedSettings)Subject.Definition.Settings).ApiKey)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(4); releases.Should().HaveCount(4);

@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
{ {
var result = new List<ValidationFailure>(); var result = new List<ValidationFailure>();
SetupNLog(); // Enable this to enable trace logging with nlog for debugging purposes SetupNLog(); // Enable this to enable trace logging with nlog for debugging purposes
Test(result); Test(result).GetAwaiter().GetResult();
return result; return result;
} }

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -34,17 +35,21 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
{ {
var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile); var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile);
Mocker.GetMock<IHttpClient>()
.Setup(o => o.ExecuteAsync(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.IsAny<HttpRequest>())) .Setup(o => o.Execute(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
} }
[Test] [Test]
public void should_parse_recent_feed_from_ImmortalSeed() public async Task should_parse_recent_feed_from_ImmortalSeed()
{ {
GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml"); GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(50); releases.Should().HaveCount(50);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -66,11 +71,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_Ezrss() public async Task should_parse_recent_feed_from_Ezrss()
{ {
GivenRecentFeedResponse("TorrentRss/Ezrss.xml"); GivenRecentFeedResponse("TorrentRss/Ezrss.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(3); releases.Should().HaveCount(3);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -92,13 +97,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_ShowRSS_info() public async Task should_parse_recent_feed_from_ShowRSS_info()
{ {
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true; Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml"); GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -120,13 +125,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_Doki() public async Task should_parse_recent_feed_from_Doki()
{ {
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true; Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
GivenRecentFeedResponse("TorrentRss/Doki.xml"); GivenRecentFeedResponse("TorrentRss/Doki.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -148,11 +153,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_ExtraTorrents() public async Task should_parse_recent_feed_from_ExtraTorrents()
{ {
GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml"); GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -174,11 +179,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_LimeTorrents() public async Task should_parse_recent_feed_from_LimeTorrents()
{ {
GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml"); GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -200,11 +205,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_AnimeTosho_without_size() public async Task should_parse_recent_feed_from_AnimeTosho_without_size()
{ {
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml"); GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(2); releases.Should().HaveCount(2);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -226,11 +231,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_multi_enclosure_from_AnimeTosho() public async Task should_parse_multi_enclosure_from_AnimeTosho()
{ {
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml"); GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(2); releases.Should().HaveCount(2);
releases.Last().Should().BeOfType<TorrentInfo>(); releases.Last().Should().BeOfType<TorrentInfo>();
@ -243,11 +248,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_AlphaRatio() public async Task should_parse_recent_feed_from_AlphaRatio()
{ {
GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml"); GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(2); releases.Should().HaveCount(2);
releases.Last().Should().BeOfType<TorrentInfo>(); releases.Last().Should().BeOfType<TorrentInfo>();
@ -260,12 +265,12 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_EveolutionWorld_without_size() public async Task should_parse_recent_feed_from_EveolutionWorld_without_size()
{ {
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true; Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
GivenRecentFeedResponse("TorrentRss/EvolutionWorld.xml"); GivenRecentFeedResponse("TorrentRss/EvolutionWorld.xml");
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(2); releases.Should().HaveCount(2);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();
@ -287,11 +292,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
} }
[Test] [Test]
public void should_record_indexer_failure_if_unsupported_feed() public async Task should_record_indexer_failure_if_unsupported_feed()
{ {
GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml"); GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml");
Subject.FetchRecent().Should().BeEmpty(); var releases = await Subject.FetchRecent();
releases.Should().BeEmpty();
Mocker.GetMock<IIndexerStatusService>() Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.Zero), Times.Once()); .Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.Zero), Times.Once());

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentleechTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_Torrentleech() public async Task should_parse_recent_feed_from_Torrentleech()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Torrentleech/Torrentleech.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Torrentleech/Torrentleech.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
releases.First().Should().BeOfType<TorrentInfo>(); releases.First().Should().BeOfType<TorrentInfo>();

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -44,15 +45,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_torznab_hdaccess_net() public async Task should_parse_recent_feed_from_torznab_hdaccess_net()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
@ -73,15 +74,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
} }
[Test] [Test]
public void should_parse_recent_feed_from_torznab_tpb() public async Task should_parse_recent_feed_from_torznab_tpb()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var releases = Subject.FetchRecent(); var releases = await Subject.FetchRecent();
releases.Should().HaveCount(5); releases.Should().HaveCount(5);
@ -140,8 +141,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
(Subject.Definition.Settings as TorznabSettings).BaseUrl = baseUrl; (Subject.Definition.Settings as TorznabSettings).BaseUrl = baseUrl;
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
var result = new NzbDroneValidationResult(Subject.Test()); var result = new NzbDroneValidationResult(Subject.Test());
result.IsValid.Should().BeTrue(); result.IsValid.Should().BeTrue();
@ -155,8 +156,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml"); var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get))) .Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed)); .Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
(Subject.Definition.Settings as TorznabSettings).ApiPath = apiPath; (Subject.Definition.Settings as TorznabSettings).ApiPath = apiPath;

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@ -32,7 +33,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
public override DownloadProtocol Protocol => DownloadProtocol.Usenet; public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override string Download(RemoteAlbum remoteAlbum, IIndexer indexer) public override async Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer)
{ {
var url = remoteAlbum.Release.DownloadUrl; var url = remoteAlbum.Release.DownloadUrl;
var title = remoteAlbum.Release.Title; var title = remoteAlbum.Release.Title;
@ -48,7 +49,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb"); var nzbFile = Path.Combine(Settings.NzbFolder, title + ".nzb");
_logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile); _logger.Debug("Downloading NZB from: {0} to: {1}", url, nzbFile);
_httpClient.DownloadFile(url, nzbFile); await _httpClient.DownloadFileAsync(url, nzbFile);
_logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile); _logger.Debug("NZB Download succeeded, saved to: {0}", nzbFile);

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@ -58,7 +59,7 @@ namespace NzbDrone.Core.Download
get; get;
} }
public abstract string Download(RemoteAlbum remoteAlbum, IIndexer indexer); public abstract Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer);
public abstract IEnumerable<DownloadClientItem> GetItems(); public abstract IEnumerable<DownloadClientItem> GetItems();
public virtual DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt) public virtual DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt)

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -16,7 +17,7 @@ namespace NzbDrone.Core.Download
{ {
public interface IDownloadService public interface IDownloadService
{ {
void DownloadReport(RemoteAlbum remoteAlbum); Task DownloadReport(RemoteAlbum remoteAlbum);
} }
public class DownloadService : IDownloadService public class DownloadService : IDownloadService
@ -49,7 +50,7 @@ namespace NzbDrone.Core.Download
_logger = logger; _logger = logger;
} }
public void DownloadReport(RemoteAlbum remoteAlbum) public async Task DownloadReport(RemoteAlbum remoteAlbum)
{ {
var filterBlockedClients = remoteAlbum.Release.PendingReleaseReason == PendingReleaseReason.DownloadClientUnavailable; var filterBlockedClients = remoteAlbum.Release.PendingReleaseReason == PendingReleaseReason.DownloadClientUnavailable;
@ -57,10 +58,10 @@ namespace NzbDrone.Core.Download
var downloadClient = _downloadClientProvider.GetDownloadClient(remoteAlbum.Release.DownloadProtocol, remoteAlbum.Release.IndexerId, filterBlockedClients, tags); var downloadClient = _downloadClientProvider.GetDownloadClient(remoteAlbum.Release.DownloadProtocol, remoteAlbum.Release.IndexerId, filterBlockedClients, tags);
DownloadReport(remoteAlbum, downloadClient); await DownloadReport(remoteAlbum, downloadClient);
} }
private void DownloadReport(RemoteAlbum remoteAlbum, IDownloadClient downloadClient) private async Task DownloadReport(RemoteAlbum remoteAlbum, IDownloadClient downloadClient)
{ {
Ensure.That(remoteAlbum.Artist, () => remoteAlbum.Artist).IsNotNull(); Ensure.That(remoteAlbum.Artist, () => remoteAlbum.Artist).IsNotNull();
Ensure.That(remoteAlbum.Albums, () => remoteAlbum.Albums).HasItems(); Ensure.That(remoteAlbum.Albums, () => remoteAlbum.Albums).HasItems();
@ -79,7 +80,7 @@ namespace NzbDrone.Core.Download
if (remoteAlbum.Release.DownloadUrl.IsNotNullOrWhiteSpace() && !remoteAlbum.Release.DownloadUrl.StartsWith("magnet:")) if (remoteAlbum.Release.DownloadUrl.IsNotNullOrWhiteSpace() && !remoteAlbum.Release.DownloadUrl.StartsWith("magnet:"))
{ {
var url = new HttpUri(remoteAlbum.Release.DownloadUrl); var url = new HttpUri(remoteAlbum.Release.DownloadUrl);
_rateLimitService.WaitAndPulse(url.Host, TimeSpan.FromSeconds(2)); await _rateLimitService.WaitAndPulseAsync(url.Host, TimeSpan.FromSeconds(2));
} }
IIndexer indexer = null; IIndexer indexer = null;
@ -92,7 +93,7 @@ namespace NzbDrone.Core.Download
string downloadClientId; string downloadClientId;
try try
{ {
downloadClientId = downloadClient.Download(remoteAlbum, indexer); downloadClientId = await downloadClient.Download(remoteAlbum, indexer);
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id); _downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
_indexerStatusService.RecordSuccess(remoteAlbum.Release.IndexerId); _indexerStatusService.RecordSuccess(remoteAlbum.Release.IndexerId);
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -8,8 +9,7 @@ namespace NzbDrone.Core.Download
public interface IDownloadClient : IProvider public interface IDownloadClient : IProvider
{ {
DownloadProtocol Protocol { get; } DownloadProtocol Protocol { get; }
Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer);
string Download(RemoteAlbum remoteAlbum, IIndexer indexer);
IEnumerable<DownloadClientItem> GetItems(); IEnumerable<DownloadClientItem> GetItems();
DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt); DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt);
void RemoveItem(DownloadClientItem item, bool deleteData); void RemoveItem(DownloadClientItem item, bool deleteData);

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download.Clients; using NzbDrone.Core.Download.Clients;
@ -12,7 +13,7 @@ namespace NzbDrone.Core.Download
{ {
public interface IProcessDownloadDecisions public interface IProcessDownloadDecisions
{ {
ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions); Task<ProcessedDecisions> ProcessDecisions(List<DownloadDecision> decisions);
} }
public class ProcessDownloadDecisions : IProcessDownloadDecisions public class ProcessDownloadDecisions : IProcessDownloadDecisions
@ -33,7 +34,7 @@ namespace NzbDrone.Core.Download
_logger = logger; _logger = logger;
} }
public ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions) public async Task<ProcessedDecisions> ProcessDecisions(List<DownloadDecision> decisions)
{ {
var qualifiedReports = GetQualifiedReports(decisions); var qualifiedReports = GetQualifiedReports(decisions);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports); var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports);
@ -75,7 +76,7 @@ namespace NzbDrone.Core.Download
try try
{ {
_logger.Trace("Grabbing from Indexer {0} at priority {1}.", remoteAlbum.Release.Indexer, remoteAlbum.Release.IndexerPriority); _logger.Trace("Grabbing from Indexer {0} at priority {1}.", remoteAlbum.Release.Indexer, remoteAlbum.Release.IndexerPriority);
_downloadService.DownloadReport(remoteAlbum); await _downloadService.DownloadReport(remoteAlbum);
grabbed.Add(report); grabbed.Add(report);
} }
catch (ReleaseUnavailableException) catch (ReleaseUnavailableException)

@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using System.Threading.Tasks;
using MonoTorrent; using MonoTorrent;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@ -41,7 +42,7 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromMagnetLink(RemoteAlbum remoteAlbum, string hash, string magnetLink); protected abstract string AddFromMagnetLink(RemoteAlbum remoteAlbum, string hash, string magnetLink);
protected abstract string AddFromTorrentFile(RemoteAlbum remoteAlbum, string hash, string filename, byte[] fileContent); protected abstract string AddFromTorrentFile(RemoteAlbum remoteAlbum, string hash, string filename, byte[] fileContent);
public override string Download(RemoteAlbum remoteAlbum, IIndexer indexer) public override async Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer)
{ {
var torrentInfo = remoteAlbum.Release as TorrentInfo; var torrentInfo = remoteAlbum.Release as TorrentInfo;
@ -68,7 +69,7 @@ namespace NzbDrone.Core.Download
{ {
try try
{ {
return DownloadFromWebUrl(remoteAlbum, indexer, torrentUrl); return await DownloadFromWebUrl(remoteAlbum, indexer, torrentUrl);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -114,14 +115,14 @@ namespace NzbDrone.Core.Download
if (torrentUrl.IsNotNullOrWhiteSpace()) if (torrentUrl.IsNotNullOrWhiteSpace())
{ {
return DownloadFromWebUrl(remoteAlbum, indexer, torrentUrl); return await DownloadFromWebUrl(remoteAlbum, indexer, torrentUrl);
} }
} }
return null; return null;
} }
private string DownloadFromWebUrl(RemoteAlbum remoteAlbum, IIndexer indexer, string torrentUrl) private async Task<string> DownloadFromWebUrl(RemoteAlbum remoteAlbum, IIndexer indexer, string torrentUrl)
{ {
byte[] torrentFile = null; byte[] torrentFile = null;
@ -132,7 +133,7 @@ namespace NzbDrone.Core.Download
request.Headers.Accept = "application/x-bittorrent"; request.Headers.Accept = "application/x-bittorrent";
request.AllowAutoRedirect = false; request.AllowAutoRedirect = false;
var response = _httpClient.Get(request); var response = await _httpClient.GetAsync(request);
if (response.StatusCode == HttpStatusCode.MovedPermanently || if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.Found ||
@ -151,7 +152,7 @@ namespace NzbDrone.Core.Download
request.Url += new HttpUri(locationHeader); request.Url += new HttpUri(locationHeader);
return DownloadFromWebUrl(remoteAlbum, indexer, request.Url.ToString()); return await DownloadFromWebUrl(remoteAlbum, indexer, request.Url.ToString());
} }
throw new WebException("Remote website tried to redirect without providing a location."); throw new WebException("Remote website tried to redirect without providing a location.");

@ -1,4 +1,5 @@
using System.Net; using System.Net;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -34,7 +35,7 @@ namespace NzbDrone.Core.Download
protected abstract string AddFromNzbFile(RemoteAlbum remoteAlbum, string filename, byte[] fileContent); protected abstract string AddFromNzbFile(RemoteAlbum remoteAlbum, string filename, byte[] fileContent);
public override string Download(RemoteAlbum remoteAlbum, IIndexer indexer) public override async Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer)
{ {
var url = remoteAlbum.Release.DownloadUrl; var url = remoteAlbum.Release.DownloadUrl;
var filename = FileNameBuilder.CleanFileName(remoteAlbum.Release.Title) + ".nzb"; var filename = FileNameBuilder.CleanFileName(remoteAlbum.Release.Title) + ".nzb";
@ -46,7 +47,9 @@ namespace NzbDrone.Core.Download
var request = indexer?.GetDownloadRequest(url) ?? new HttpRequest(url); var request = indexer?.GetDownloadRequest(url) ?? new HttpRequest(url);
request.RateLimitKey = remoteAlbum?.Release?.IndexerId.ToString(); request.RateLimitKey = remoteAlbum?.Release?.IndexerId.ToString();
nzbData = _httpClient.Get(request).ResponseData; var response = await _httpClient.GetAsync(request);
nzbData = response.ResponseData;
_logger.Debug("Downloaded nzb for release '{0}' finished ({1} bytes from {2})", remoteAlbum.Release.Title, nzbData.Length, url); _logger.Debug("Downloaded nzb for release '{0}' finished ({1} bytes from {2})", remoteAlbum.Release.Title, nzbData.Length, url);
} }

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
@ -39,7 +40,7 @@ namespace NzbDrone.Core.IndexerSearch
_logger = logger; _logger = logger;
} }
private void SearchForMissingAlbums(List<Album> albums, bool userInvokedSearch) private async Task SearchForMissingAlbums(List<Album> albums, bool userInvokedSearch)
{ {
_logger.ProgressInfo("Performing missing search for {0} albums", albums.Count); _logger.ProgressInfo("Performing missing search for {0} albums", albums.Count);
var downloadedCount = 0; var downloadedCount = 0;
@ -47,8 +48,8 @@ namespace NzbDrone.Core.IndexerSearch
foreach (var album in albums) foreach (var album in albums)
{ {
List<DownloadDecision> decisions; List<DownloadDecision> decisions;
decisions = _releaseSearchService.AlbumSearch(album.Id, false, userInvokedSearch, false); decisions = await _releaseSearchService.AlbumSearch(album.Id, false, userInvokedSearch, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions); var processed = await _processDownloadDecisions.ProcessDecisions(decisions);
downloadedCount += processed.Grabbed.Count; downloadedCount += processed.Grabbed.Count;
} }
@ -60,9 +61,8 @@ namespace NzbDrone.Core.IndexerSearch
{ {
foreach (var albumId in message.AlbumIds) foreach (var albumId in message.AlbumIds)
{ {
var decisions = var decisions = _releaseSearchService.AlbumSearch(albumId, false, message.Trigger == CommandTrigger.Manual, false).GetAwaiter().GetResult();
_releaseSearchService.AlbumSearch(albumId, false, message.Trigger == CommandTrigger.Manual, false); var processed = _processDownloadDecisions.ProcessDecisions(decisions).GetAwaiter().GetResult();
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Album search completed. {0} reports downloaded.", processed.Grabbed.Count); _logger.ProgressInfo("Album search completed. {0} reports downloaded.", processed.Grabbed.Count);
} }
@ -106,7 +106,7 @@ namespace NzbDrone.Core.IndexerSearch
var queue = _queueService.GetQueue().Where(q => q.Album != null).Select(q => q.Album.Id); var queue = _queueService.GetQueue().Where(q => q.Album != null).Select(q => q.Album.Id);
var missing = albums.Where(e => !queue.Contains(e.Id)).ToList(); var missing = albums.Where(e => !queue.Contains(e.Id)).ToList();
SearchForMissingAlbums(missing, message.Trigger == CommandTrigger.Manual); SearchForMissingAlbums(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult();
} }
public void Execute(CutoffUnmetAlbumSearchCommand message) public void Execute(CutoffUnmetAlbumSearchCommand message)
@ -132,7 +132,7 @@ namespace NzbDrone.Core.IndexerSearch
var queue = _queueService.GetQueue().Where(q => q.Album != null).Select(q => q.Album.Id); var queue = _queueService.GetQueue().Where(q => q.Album != null).Select(q => q.Album.Id);
var missing = albums.Where(e => !queue.Contains(e.Id)).ToList(); var missing = albums.Where(e => !queue.Contains(e.Id)).ToList();
SearchForMissingAlbums(missing, message.Trigger == CommandTrigger.Manual); SearchForMissingAlbums(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult();
} }
} }
} }

@ -22,8 +22,8 @@ namespace NzbDrone.Core.IndexerSearch
public void Execute(ArtistSearchCommand message) public void Execute(ArtistSearchCommand message)
{ {
var decisions = _releaseSearchService.ArtistSearch(message.ArtistId, false, message.Trigger == CommandTrigger.Manual, false); var decisions = _releaseSearchService.ArtistSearch(message.ArtistId, false, message.Trigger == CommandTrigger.Manual, false).GetAwaiter().GetResult();
var processed = _processDownloadDecisions.ProcessDecisions(decisions); var processed = _processDownloadDecisions.ProcessDecisions(decisions).GetAwaiter().GetResult();
_logger.ProgressInfo("Artist search completed. {0} reports downloaded.", processed.Grabbed.Count); _logger.ProgressInfo("Artist search completed. {0} reports downloaded.", processed.Grabbed.Count);
} }

@ -5,7 +5,6 @@ using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.TPL;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@ -16,8 +15,8 @@ namespace NzbDrone.Core.IndexerSearch
{ {
public interface ISearchForReleases public interface ISearchForReleases
{ {
List<DownloadDecision> AlbumSearch(int albumId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch); Task<List<DownloadDecision>> AlbumSearch(int albumId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
List<DownloadDecision> ArtistSearch(int artistId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch); Task<List<DownloadDecision>> ArtistSearch(int artistId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
} }
public class ReleaseSearchService : ISearchForReleases public class ReleaseSearchService : ISearchForReleases
@ -41,19 +40,21 @@ namespace NzbDrone.Core.IndexerSearch
_logger = logger; _logger = logger;
} }
public List<DownloadDecision> AlbumSearch(int albumId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch) public async Task<List<DownloadDecision>> AlbumSearch(int albumId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{ {
var album = _albumService.GetAlbum(albumId); var album = _albumService.GetAlbum(albumId);
return AlbumSearch(album, missingOnly, userInvokedSearch, interactiveSearch);
return await AlbumSearch(album, missingOnly, userInvokedSearch, interactiveSearch);
} }
public List<DownloadDecision> ArtistSearch(int artistId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch) public async Task<List<DownloadDecision>> ArtistSearch(int artistId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{ {
var artist = _artistService.GetArtist(artistId); var artist = _artistService.GetArtist(artistId);
return ArtistSearch(artist, missingOnly, userInvokedSearch, interactiveSearch);
return await ArtistSearch(artist, missingOnly, userInvokedSearch, interactiveSearch);
} }
public List<DownloadDecision> ArtistSearch(Artist artist, bool missingOnly, bool userInvokedSearch, bool interactiveSearch) public async Task<List<DownloadDecision>> ArtistSearch(Artist artist, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{ {
var downloadDecisions = new List<DownloadDecision>(); var downloadDecisions = new List<DownloadDecision>();
@ -64,13 +65,13 @@ namespace NzbDrone.Core.IndexerSearch
searchSpec.Albums = albums; searchSpec.Albums = albums;
var decisions = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec); var decisions = await Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
downloadDecisions.AddRange(decisions); downloadDecisions.AddRange(decisions);
return DeDupeDecisions(downloadDecisions); return DeDupeDecisions(downloadDecisions);
} }
public List<DownloadDecision> AlbumSearch(Album album, bool missingOnly, bool userInvokedSearch, bool interactiveSearch) public async Task<List<DownloadDecision>> AlbumSearch(Album album, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{ {
var downloadDecisions = new List<DownloadDecision>(); var downloadDecisions = new List<DownloadDecision>();
@ -89,7 +90,7 @@ namespace NzbDrone.Core.IndexerSearch
searchSpec.Disambiguation = album.Disambiguation; searchSpec.Disambiguation = album.Disambiguation;
} }
var decisions = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec); var decisions = await Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
downloadDecisions.AddRange(decisions); downloadDecisions.AddRange(decisions);
return DeDupeDecisions(downloadDecisions); return DeDupeDecisions(downloadDecisions);
@ -119,7 +120,7 @@ namespace NzbDrone.Core.IndexerSearch
return spec; return spec;
} }
private List<DownloadDecision> Dispatch(Func<IIndexer, IEnumerable<ReleaseInfo>> searchAction, SearchCriteriaBase criteriaBase) private async Task<List<DownloadDecision>> Dispatch(Func<IIndexer, Task<IList<ReleaseInfo>>> searchAction, SearchCriteriaBase criteriaBase)
{ {
var indexers = criteriaBase.InteractiveSearch ? var indexers = criteriaBase.InteractiveSearch ?
_indexerFactory.InteractiveSearchEnabled() : _indexerFactory.InteractiveSearchEnabled() :
@ -128,42 +129,33 @@ namespace NzbDrone.Core.IndexerSearch
// Filter indexers to untagged indexers and indexers with intersecting tags // Filter indexers to untagged indexers and indexers with intersecting tags
indexers = indexers.Where(i => i.Definition.Tags.Empty() || i.Definition.Tags.Intersect(criteriaBase.Artist.Tags).Any()).ToList(); indexers = indexers.Where(i => i.Definition.Tags.Empty() || i.Definition.Tags.Intersect(criteriaBase.Artist.Tags).Any()).ToList();
var reports = new List<ReleaseInfo>();
_logger.ProgressInfo("Searching indexers for {0}. {1} active indexers", criteriaBase, indexers.Count); _logger.ProgressInfo("Searching indexers for {0}. {1} active indexers", criteriaBase, indexers.Count);
var taskList = new List<Task>(); var tasks = indexers.Select(indexer => DispatchIndexer(searchAction, indexer, criteriaBase));
var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
foreach (var indexer in indexers) var batch = await Task.WhenAll(tasks);
{
var indexerLocal = indexer;
taskList.Add(taskFactory.StartNew(() =>
{
try
{
var indexerReports = searchAction(indexerLocal);
lock (reports)
{
reports.AddRange(indexerReports);
}
}
catch (Exception e)
{
_logger.Error(e, "Error while searching for {0}", criteriaBase);
}
}).LogExceptions());
}
Task.WaitAll(taskList.ToArray()); var reports = batch.SelectMany(x => x).ToList();
_logger.Debug("Total of {0} reports were found for {1} from {2} indexers", reports.Count, criteriaBase, indexers.Count); _logger.Debug("Total of {0} reports were found for {1} from {2} indexers", reports.Count, criteriaBase, indexers.Count);
return _makeDownloadDecision.GetSearchDecision(reports, criteriaBase).ToList(); return _makeDownloadDecision.GetSearchDecision(reports, criteriaBase).ToList();
} }
private async Task<IList<ReleaseInfo>> DispatchIndexer(Func<IIndexer, Task<IList<ReleaseInfo>>> searchAction, IIndexer indexer, SearchCriteriaBase criteriaBase)
{
try
{
return await searchAction(indexer);
}
catch (Exception ex)
{
_logger.Error(ex, "Error while searching for {0}", criteriaBase);
}
return Array.Empty<ReleaseInfo>();
}
private List<DownloadDecision> DeDupeDecisions(List<DownloadDecision> decisions) private List<DownloadDecision> DeDupeDecisions(List<DownloadDecision> decisions)
{ {
// De-dupe reports by guid so duplicate results aren't returned. Pick the one with the least rejections and higher indexer priority. // De-dupe reports by guid so duplicate results aren't returned. Pick the one with the least rejections and higher indexer priority.

@ -3,13 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.TPL;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers namespace NzbDrone.Core.Indexers
{ {
public interface IFetchAndParseRss public interface IFetchAndParseRss
{ {
List<ReleaseInfo> Fetch(); Task<List<ReleaseInfo>> Fetch();
} }
public class FetchAndParseRssService : IFetchAndParseRss public class FetchAndParseRssService : IFetchAndParseRss
@ -23,54 +22,42 @@ namespace NzbDrone.Core.Indexers
_logger = logger; _logger = logger;
} }
public List<ReleaseInfo> Fetch() public async Task<List<ReleaseInfo>> Fetch()
{ {
var result = new List<ReleaseInfo>();
var indexers = _indexerFactory.RssEnabled(); var indexers = _indexerFactory.RssEnabled();
if (!indexers.Any()) if (!indexers.Any())
{ {
_logger.Warn("No available indexers. check your configuration."); _logger.Warn("No available indexers. check your configuration.");
return result;
return new List<ReleaseInfo>();
} }
_logger.Debug("Available indexers {0}", indexers.Count); _logger.Debug("Available indexers {0}", indexers.Count);
var taskList = new List<Task>(); var tasks = indexers.Select(FetchIndexer);
var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
foreach (var indexer in indexers) var batch = await Task.WhenAll(tasks);
{
var indexerLocal = indexer;
var task = taskFactory.StartNew(() => var result = batch.SelectMany(x => x).ToList();
{
try
{
var indexerReports = indexerLocal.FetchRecent();
lock (result) _logger.Debug("Found {0} reports", result.Count);
{
_logger.Debug("Found {0} from {1}", indexerReports.Count, indexer.Name);
result.AddRange(indexerReports); return result;
} }
}
catch (Exception e)
{
_logger.Error(e, "Error during RSS Sync");
}
}).LogExceptions();
taskList.Add(task); private async Task<IList<ReleaseInfo>> FetchIndexer(IIndexer indexer)
{
try
{
return await indexer.FetchRecent();
}
catch (Exception ex)
{
_logger.Error(ex, "Error during RSS Sync");
} }
Task.WaitAll(taskList.ToArray()); return Array.Empty<ReleaseInfo>();
_logger.Debug("Found {0} reports", result.Count);
return result;
} }
} }
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
@ -78,11 +79,12 @@ namespace NzbDrone.Core.Indexers.Gazelle
return settings; return settings;
} }
protected override void Test(List<ValidationFailure> failures) protected override async Task Test(List<ValidationFailure> failures)
{ {
// Remove previous cookies when testing incase user or pwd change // Remove previous cookies when testing incase user or pwd change
_authCookieCache.Remove(Settings.BaseUrl.Trim().TrimEnd('/')); _authCookieCache.Remove(Settings.BaseUrl.Trim().TrimEnd('/'));
base.Test(failures);
await base.Test(failures);
} }
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -52,17 +53,14 @@ namespace NzbDrone.Core.Indexers.Gazelle
private IEnumerable<IndexerRequest> GetRequest(string searchParameters) private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{ {
Authenticate(); Authenticate().GetAwaiter().GetResult();
var filter = ""; var filter = "";
if (searchParameters == null) if (searchParameters == null)
{ {
} }
var request = var request = new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/ajax.php?action=browse&searchstr={searchParameters}{filter}", HttpAccept.Json);
new IndexerRequest(
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/ajax.php?action=browse&searchstr={searchParameters}{filter}",
HttpAccept.Json);
var cookies = AuthCookieCache.Find(Settings.BaseUrl.Trim().TrimEnd('/')); var cookies = AuthCookieCache.Find(Settings.BaseUrl.Trim().TrimEnd('/'));
foreach (var cookie in cookies) foreach (var cookie in cookies)
@ -73,36 +71,36 @@ namespace NzbDrone.Core.Indexers.Gazelle
yield return request; yield return request;
} }
private GazelleAuthResponse GetIndex(Dictionary<string, string> cookies) private async Task<GazelleAuthResponse> GetIndex(Dictionary<string, string> cookies)
{ {
var indexRequestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}") var indexRequestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
{ {
LogResponseContent = true LogResponseContent = true,
Method = HttpMethod.Post
}; };
indexRequestBuilder.SetCookies(cookies); indexRequestBuilder.SetCookies(cookies);
indexRequestBuilder.Method = HttpMethod.Post;
indexRequestBuilder.Resource("ajax.php?action=index"); indexRequestBuilder.Resource("ajax.php?action=index");
var authIndexRequest = indexRequestBuilder var authIndexRequest = indexRequestBuilder
.Accept(HttpAccept.Json) .Accept(HttpAccept.Json)
.Build(); .Build();
var indexResponse = HttpClient.Execute(authIndexRequest); var indexResponse = await HttpClient.ExecuteAsync(authIndexRequest);
var result = Json.Deserialize<GazelleAuthResponse>(indexResponse.Content); var result = Json.Deserialize<GazelleAuthResponse>(indexResponse.Content);
return result; return result;
} }
private void Authenticate() private async Task Authenticate()
{ {
var requestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}") var requestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
{ {
LogResponseContent = true LogResponseContent = true,
Method = HttpMethod.Post
}; };
requestBuilder.Method = HttpMethod.Post;
requestBuilder.Resource("login.php"); requestBuilder.Resource("login.php");
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
@ -120,19 +118,20 @@ namespace NzbDrone.Core.Indexers.Gazelle
.Accept(HttpAccept.Json) .Accept(HttpAccept.Json)
.Build(); .Build();
var response = HttpClient.Execute(authLoginRequest); var response = await HttpClient.ExecuteAsync(authLoginRequest);
cookies = response.GetCookies(); cookies = response.GetCookies();
AuthCookieCache.Set(authKey, cookies); AuthCookieCache.Set(authKey, cookies);
} }
var index = GetIndex(cookies); var index = await GetIndex(cookies);
if (index == null || index.Status.IsNullOrWhiteSpace() || index.Status != "success") if (index == null || index.Status.IsNullOrWhiteSpace() || index.Status != "success")
{ {
Logger.Debug("Gazelle authentication failed."); Logger.Debug("Gazelle authentication failed.");
AuthCookieCache.Remove(authKey); AuthCookieCache.Remove(authKey);
throw new Exception("Failed to authenticate with Gazelle."); throw new Exception("Failed to authenticate with Gazelle.");
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -51,9 +52,9 @@ namespace NzbDrone.Core.Indexers.Headphones
return request; return request;
} }
protected override void Test(List<ValidationFailure> failures) protected override async Task Test(List<ValidationFailure> failures)
{ {
base.Test(failures); await base.Test(failures);
if (failures.Any()) if (failures.Any())
{ {

@ -40,31 +40,31 @@ namespace NzbDrone.Core.Indexers
_httpClient = httpClient; _httpClient = httpClient;
} }
public override IList<ReleaseInfo> FetchRecent() public override Task<IList<ReleaseInfo>> FetchRecent()
{ {
if (!SupportsRss) if (!SupportsRss)
{ {
return Array.Empty<ReleaseInfo>(); return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
} }
return FetchReleases(g => g.GetRecentRequests(), true); return FetchReleases(g => g.GetRecentRequests(), true);
} }
public override IList<ReleaseInfo> Fetch(AlbumSearchCriteria searchCriteria) public override Task<IList<ReleaseInfo>> Fetch(AlbumSearchCriteria searchCriteria)
{ {
if (!SupportsSearch) if (!SupportsSearch)
{ {
return Array.Empty<ReleaseInfo>(); return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
} }
return FetchReleases(g => g.GetSearchRequests(searchCriteria)); return FetchReleases(g => g.GetSearchRequests(searchCriteria));
} }
public override IList<ReleaseInfo> Fetch(ArtistSearchCriteria searchCriteria) public override Task<IList<ReleaseInfo>> Fetch(ArtistSearchCriteria searchCriteria)
{ {
if (!SupportsSearch) if (!SupportsSearch)
{ {
return Array.Empty<ReleaseInfo>(); return Task.FromResult<IList<ReleaseInfo>>(Array.Empty<ReleaseInfo>());
} }
return FetchReleases(g => g.GetSearchRequests(searchCriteria)); return FetchReleases(g => g.GetSearchRequests(searchCriteria));
@ -75,7 +75,7 @@ namespace NzbDrone.Core.Indexers
return new HttpRequest(link); return new HttpRequest(link);
} }
protected virtual IList<ReleaseInfo> FetchReleases(Func<IIndexerRequestGenerator, IndexerPageableRequestChain> pageableRequestChainSelector, bool isRecent = false) protected virtual async Task<IList<ReleaseInfo>> FetchReleases(Func<IIndexerRequestGenerator, IndexerPageableRequestChain> pageableRequestChainSelector, bool isRecent = false)
{ {
var releases = new List<ReleaseInfo>(); var releases = new List<ReleaseInfo>();
var url = string.Empty; var url = string.Empty;
@ -107,7 +107,7 @@ namespace NzbDrone.Core.Indexers
{ {
url = request.Url.FullUri; url = request.Url.FullUri;
var page = FetchPage(request, parser); var page = await FetchPage(request, parser);
pagedReleases.AddRange(page); pagedReleases.AddRange(page);
@ -271,9 +271,9 @@ namespace NzbDrone.Core.Indexers
return PageSize != 0 && page.Count >= PageSize; return PageSize != 0 && page.Count >= PageSize;
} }
protected virtual IList<ReleaseInfo> FetchPage(IndexerRequest request, IParseIndexerResponse parser) protected virtual async Task<IList<ReleaseInfo>> FetchPage(IndexerRequest request, IParseIndexerResponse parser)
{ {
var response = FetchIndexerResponse(request); var response = await FetchIndexerResponse(request);
try try
{ {
@ -287,7 +287,7 @@ namespace NzbDrone.Core.Indexers
} }
} }
protected virtual IndexerResponse FetchIndexerResponse(IndexerRequest request) protected virtual async Task<IndexerResponse> FetchIndexerResponse(IndexerRequest request)
{ {
_logger.Debug("Downloading Feed " + request.HttpRequest.ToString(false)); _logger.Debug("Downloading Feed " + request.HttpRequest.ToString(false));
@ -298,15 +298,17 @@ namespace NzbDrone.Core.Indexers
request.HttpRequest.RateLimitKey = Definition.Id.ToString(); request.HttpRequest.RateLimitKey = Definition.Id.ToString();
return new IndexerResponse(request, _httpClient.Execute(request.HttpRequest)); var response = await _httpClient.ExecuteAsync(request.HttpRequest);
return new IndexerResponse(request, response);
} }
protected override void Test(List<ValidationFailure> failures) protected override async Task Test(List<ValidationFailure> failures)
{ {
failures.AddIfNotNull(TestConnection()); failures.AddIfNotNull(await TestConnection());
} }
protected virtual ValidationFailure TestConnection() protected virtual async Task<ValidationFailure> TestConnection()
{ {
try try
{ {
@ -319,7 +321,7 @@ namespace NzbDrone.Core.Indexers
return new ValidationFailure(string.Empty, "No rss feed query available. This may be an issue with the indexer or your indexer category settings."); return new ValidationFailure(string.Empty, "No rss feed query available. This may be an issue with the indexer or your indexer category settings.");
} }
var releases = FetchPage(firstRequest, parser); var releases = await FetchPage(firstRequest, parser);
if (releases.Empty()) if (releases.Empty())
{ {

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -12,9 +13,9 @@ namespace NzbDrone.Core.Indexers
bool SupportsSearch { get; } bool SupportsSearch { get; }
DownloadProtocol Protocol { get; } DownloadProtocol Protocol { get; }
IList<ReleaseInfo> FetchRecent(); Task<IList<ReleaseInfo>> FetchRecent();
IList<ReleaseInfo> Fetch(AlbumSearchCriteria searchCriteria); Task<IList<ReleaseInfo>> Fetch(AlbumSearchCriteria searchCriteria);
IList<ReleaseInfo> Fetch(ArtistSearchCriteria searchCriteria); Task<IList<ReleaseInfo>> Fetch(ArtistSearchCriteria searchCriteria);
HttpRequest GetDownloadRequest(string link); HttpRequest GetDownloadRequest(string link);
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -66,16 +67,14 @@ namespace NzbDrone.Core.Indexers
protected TSettings Settings => (TSettings)Definition.Settings; protected TSettings Settings => (TSettings)Definition.Settings;
public abstract IList<ReleaseInfo> FetchRecent(); public abstract Task<IList<ReleaseInfo>> FetchRecent();
public abstract Task<IList<ReleaseInfo>> Fetch(AlbumSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(AlbumSearchCriteria searchCriteria); public abstract Task<IList<ReleaseInfo>> Fetch(ArtistSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(ArtistSearchCriteria searchCriteria);
public abstract HttpRequest GetDownloadRequest(string link); public abstract HttpRequest GetDownloadRequest(string link);
protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases) protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases)
{ {
var result = releases.DistinctBy(v => v.Guid).ToList(); var result = releases.DistinctBy(v => v.Guid).ToList();
var settings = Definition.Settings as IIndexerSettings;
result.ForEach(c => result.ForEach(c =>
{ {
@ -95,7 +94,7 @@ namespace NzbDrone.Core.Indexers
try try
{ {
Test(failures); Test(failures).GetAwaiter().GetResult();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -106,7 +105,7 @@ namespace NzbDrone.Core.Indexers
return new ValidationResult(failures); return new ValidationResult(failures);
} }
protected abstract void Test(List<ValidationFailure> failures); protected abstract Task Test(List<ValidationFailure> failures);
public override string ToString() public override string ToString()
{ {

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -92,9 +93,10 @@ namespace NzbDrone.Core.Indexers.Newznab
return settings; return settings;
} }
protected override void Test(List<ValidationFailure> failures) protected override async Task Test(List<ValidationFailure> failures)
{ {
base.Test(failures); await base.Test(failures);
if (failures.HasErrors()) if (failures.HasErrors())
{ {
return; return;

@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
@ -39,16 +40,16 @@ namespace NzbDrone.Core.Indexers
_logger = logger; _logger = logger;
} }
private ProcessedDecisions Sync() private async Task<ProcessedDecisions> Sync()
{ {
_logger.ProgressInfo("Starting RSS Sync"); _logger.ProgressInfo("Starting RSS Sync");
var rssReleases = _rssFetcherAndParser.Fetch(); var rssReleases = await _rssFetcherAndParser.Fetch();
var pendingReleases = _pendingReleaseService.GetPending(); var pendingReleases = _pendingReleaseService.GetPending();
var reports = rssReleases.Concat(pendingReleases).ToList(); var reports = rssReleases.Concat(pendingReleases).ToList();
var decisions = _downloadDecisionMaker.GetRssDecision(reports); var decisions = _downloadDecisionMaker.GetRssDecision(reports);
var processed = _processDownloadDecisions.ProcessDecisions(decisions); var processed = await _processDownloadDecisions.ProcessDecisions(decisions);
var message = string.Format("RSS Sync Completed. Reports found: {0}, Reports grabbed: {1}", reports.Count, processed.Grabbed.Count); var message = string.Format("RSS Sync Completed. Reports found: {0}, Reports grabbed: {1}", reports.Count, processed.Grabbed.Count);
@ -64,7 +65,7 @@ namespace NzbDrone.Core.Indexers
public void Execute(RssSyncCommand message) public void Execute(RssSyncCommand message)
{ {
var processed = Sync(); var processed = Sync().GetAwaiter().GetResult();
var grabbedOrPending = processed.Grabbed.Concat(processed.Pending).ToList(); var grabbedOrPending = processed.Grabbed.Concat(processed.Pending).ToList();
_eventAggregator.PublishEvent(new RssSyncCompleteEvent(processed)); _eventAggregator.PublishEvent(new RssSyncCompleteEvent(processed));

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using FluentValidation.Results; using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -69,9 +70,10 @@ namespace NzbDrone.Core.Indexers.Torznab
return settings; return settings;
} }
protected override void Test(List<ValidationFailure> failures) protected override async Task Test(List<ValidationFailure> failures)
{ {
base.Test(failures); await base.Test(failures);
if (failures.HasErrors()) if (failures.HasErrors())
{ {
return; return;

@ -171,7 +171,7 @@ namespace NzbDrone.Host
}); });
builder.ConfigureKestrel(serverOptions => builder.ConfigureKestrel(serverOptions =>
{ {
serverOptions.AllowSynchronousIO = true; serverOptions.AllowSynchronousIO = false;
serverOptions.Limits.MaxRequestBodySize = null; serverOptions.Limits.MaxRequestBodySize = null;
}); });
builder.UseStartup<Startup>(); builder.UseStartup<Startup>();

Loading…
Cancel
Save