diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index df63ee85a..ee07d7e57 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -10,6 +10,7 @@ using NzbDrone.Test.Common; using NzbDrone.Test.Common.Categories; using NLog; using NzbDrone.Common.TPL; +using Moq; namespace NzbDrone.Common.Test.Http { @@ -22,6 +23,7 @@ namespace NzbDrone.Common.Test.Http { Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); + Mocker.SetConstant>(new IHttpRequestInterceptor[0]); } [Test] @@ -161,7 +163,7 @@ namespace NzbDrone.Common.Test.Http var oldRequest = new HttpRequest("http://eu.httpbin.org/get"); oldRequest.AddCookie("my", "cookie"); - var oldClient = new HttpClient(Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve()); + var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve()); oldClient.Should().NotBeSameAs(Subject); @@ -269,6 +271,30 @@ namespace NzbDrone.Common.Test.Http ExceptionVerification.IgnoreWarns(); } + + [Test] + public void should_call_interceptor() + { + Mocker.SetConstant>(new [] { Mocker.GetMock().Object }); + + Mocker.GetMock() + .Setup(v => v.PreRequest(It.IsAny())) + .Returns(r => r); + + Mocker.GetMock() + .Setup(v => v.PostResponse(It.IsAny())) + .Returns(r => r); + + var request = new HttpRequest("http://eu.httpbin.org/get"); + + Subject.Get(request); + + Mocker.GetMock() + .Verify(v => v.PreRequest(It.IsAny()), Times.Once()); + + Mocker.GetMock() + .Verify(v => v.PostResponse(It.IsAny()), Times.Once()); + } } public class HttpBinResource diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index d12263f8a..c05805af5 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; @@ -27,11 +28,13 @@ namespace NzbDrone.Common.Http private readonly IRateLimitService _rateLimitService; private readonly ICached _cookieContainerCache; private readonly ICached _curlTLSFallbackCache; + private readonly IEnumerable _requestInterceptors; - public HttpClient(ICacheManager cacheManager, IRateLimitService rateLimitService, Logger logger) + public HttpClient(IEnumerable requestInterceptors, ICacheManager cacheManager, IRateLimitService rateLimitService, Logger logger) { _logger = logger; _rateLimitService = rateLimitService; + _requestInterceptors = requestInterceptors; ServicePointManager.DefaultConnectionLimit = 12; _cookieContainerCache = cacheManager.GetCache(typeof(HttpClient)); @@ -40,6 +43,11 @@ namespace NzbDrone.Common.Http public HttpResponse Execute(HttpRequest request) { + foreach (var interceptor in _requestInterceptors) + { + request = interceptor.PreRequest(request); + } + if (request.RateLimit != TimeSpan.Zero) { _rateLimitService.WaitAndPulse(request.Url.Host, request.RateLimit); @@ -100,6 +108,11 @@ namespace NzbDrone.Common.Http } } + foreach (var interceptor in _requestInterceptors) + { + response = interceptor.PostResponse(response); + } + return response; } diff --git a/src/NzbDrone.Common/Http/IHttpRequestInterceptor.cs b/src/NzbDrone.Common/Http/IHttpRequestInterceptor.cs new file mode 100644 index 000000000..018c0928f --- /dev/null +++ b/src/NzbDrone.Common/Http/IHttpRequestInterceptor.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Common.Http +{ + public interface IHttpRequestInterceptor + { + HttpRequest PreRequest(HttpRequest request); + HttpResponse PostResponse(HttpResponse response); + } +} diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj index 265234252..12ab380c6 100644 --- a/src/NzbDrone.Common/NzbDrone.Common.csproj +++ b/src/NzbDrone.Common/NzbDrone.Common.csproj @@ -153,6 +153,7 @@ + diff --git a/src/NzbDrone.Core.Test/Framework/CoreTest.cs b/src/NzbDrone.Core.Test/Framework/CoreTest.cs index 8c588531a..aa6d1aaa4 100644 --- a/src/NzbDrone.Core.Test/Framework/CoreTest.cs +++ b/src/NzbDrone.Core.Test/Framework/CoreTest.cs @@ -18,7 +18,7 @@ namespace NzbDrone.Core.Test.Framework protected void UseRealHttp() { Mocker.SetConstant(new HttpProvider(TestLogger)); - Mocker.SetConstant(new HttpClient(Mocker.Resolve(), Mocker.Resolve(), TestLogger)); + Mocker.SetConstant(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve(), Mocker.Resolve(), TestLogger)); Mocker.SetConstant(new DroneServicesHttpRequestBuilder()); } } diff --git a/src/NzbDrone.Core.Test/Http/TorCacheHttpRequestInterceptorFixture.cs b/src/NzbDrone.Core.Test/Http/TorCacheHttpRequestInterceptorFixture.cs new file mode 100644 index 000000000..165510b55 --- /dev/null +++ b/src/NzbDrone.Core.Test/Http/TorCacheHttpRequestInterceptorFixture.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using NzbDrone.Common.Http; +using NzbDrone.Core.Http; +using NzbDrone.Test.Common; +using FluentAssertions; + +namespace NzbDrone.Core.Test.Http +{ + [TestFixture] + public class TorCacheHttpRequestInterceptorFixture : TestBase + { + [Test] + public void should_remove_query_params_from_torcache_request() + { + var request = new HttpRequest("http://torcache.net/download/123.torrent?title=something"); + + var newRequest = Subject.PreRequest(request); + + newRequest.Url.AbsoluteUri.Should().Be("http://torcache.net/download/123.torrent"); + } + + [TestCase("http://site.com/download?url=torcache.net&blaat=1")] + [TestCase("http://torcache.net.com/download?url=123")] + public void should_not_remove_query_params_from_other_requests(string url) + { + var request = new HttpRequest(url); + + var newRequest = Subject.PreRequest(request); + + newRequest.Url.AbsoluteUri.Should().Be(url); + } + } +} diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 16d62a3be..59ab1b43c 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -210,6 +210,7 @@ + diff --git a/src/NzbDrone.Core/Http/TorcacheHttpInterceptor.cs b/src/NzbDrone.Core/Http/TorcacheHttpInterceptor.cs new file mode 100644 index 000000000..31cfbba0a --- /dev/null +++ b/src/NzbDrone.Core/Http/TorcacheHttpInterceptor.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Common.Http; +using NzbDrone.Common.Extensions; + +namespace NzbDrone.Core.Http +{ + public class TorCacheHttpRequestInterceptor : IHttpRequestInterceptor + { + public HttpRequest PreRequest(HttpRequest request) + { + if (request.Url.Host == "torcache.net" && request.UriBuilder.Query.IsNotNullOrWhiteSpace()) + { + request.UriBuilder.Query = string.Empty; + } + + return request; + } + + public HttpResponse PostResponse(HttpResponse response) + { + return response; + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index d0b5703d0..e1299e004 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -471,6 +471,7 @@ +