Moved Proxy types around and refactored/renamed a few things.

This commit is contained in:
Taloth Saldono 2016-04-25 21:53:26 +02:00
parent 9e7927acec
commit b479064abd
14 changed files with 141 additions and 90 deletions

View File

@ -2,8 +2,7 @@
using NzbDrone.Api.REST;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Update;
using NzbDrone.Core.Http;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Proxy;
namespace NzbDrone.Api.Config
{
@ -33,7 +32,7 @@ namespace NzbDrone.Api.Config
public int ProxyPort { get; set; }
public string ProxyUsername { get; set; }
public string ProxyPassword { get; set; }
public string ProxySubnetFilter { get; set; }
public string ProxyBypassFilter { get; set; }
public bool ProxyBypassLocalAddresses { get; set; }
}
}

View File

@ -11,6 +11,7 @@ using NUnit.Framework;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Common.TPL;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
@ -26,9 +27,14 @@ namespace NzbDrone.Common.Test.Http
public void SetUp()
{
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
Mocker.SetConstant<ICreateManagedWebProxy>(Mocker.Resolve<ManagedWebProxyFactory>());
Mocker.SetConstant<IRateLimitService>(Mocker.Resolve<RateLimitService>());
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new IHttpRequestInterceptor[0]);
Mocker.SetConstant<IHttpDispatcher>(Mocker.Resolve<TDispatcher>());
//Mocker.GetMock<IHttpProxySettingsProvider>()
// .Setup(v => v.GetProxySettings(It.IsAny<HttpRequest>()))
// .Returns(new HttpProxySettings(ProxyType.Socks5, "127.0.0.1", 5476, "", false));
}
[Test]

View File

@ -4,6 +4,7 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
@ -11,8 +12,7 @@ using CurlSharp;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using System.Reflection;
using NzbDrone.Common.Http.Proxy;
namespace NzbDrone.Common.Http.Dispatchers
{

View File

@ -4,20 +4,20 @@ using NzbDrone.Common.Extensions;
using com.LandonKey.SocksWebProxy.Proxy;
using com.LandonKey.SocksWebProxy;
using System.Net.Sockets;
using System.Linq;
using NzbDrone.Common.Http.Proxy;
namespace NzbDrone.Common.Http.Dispatchers
{
public class ManagedHttpDispatcher : IHttpDispatcher
{
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
private readonly ICreateManagedWebProxy _createManagedWebProxy;
private readonly ICached<IWebProxy> _webProxyCache;
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICacheManager cacheManager)
public ManagedHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy)
{
_proxySettingsProvider = proxySettingsProvider;
_webProxyCache = cacheManager.GetCache<IWebProxy>(GetType(), "webProxy");
_createManagedWebProxy = createManagedWebProxy;
}
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
@ -90,45 +90,10 @@ namespace NzbDrone.Common.Http.Dispatchers
var proxySettings = _proxySettingsProvider.GetProxySettings(request);
if (proxySettings != null)
{
webRequest.Proxy = _webProxyCache.Get(proxySettings.Key, () => CreateWebProxy(proxySettings), TimeSpan.FromMinutes(5));
webRequest.Proxy = _createManagedWebProxy.GetWebProxy(proxySettings);
}
_webProxyCache.ClearExpired();
}
private IWebProxy CreateWebProxy(HttpRequestProxySettings proxySettings)
{
var addresses = Dns.GetHostAddresses(proxySettings.Host);
if(addresses.Length > 1)
{
var ipv4Only = addresses.Where(a => a.AddressFamily == AddressFamily.InterNetwork);
if (ipv4Only.Any())
{
addresses = ipv4Only.ToArray();
}
}
switch (proxySettings.Type)
{
case ProxyType.Http:
if (proxySettings.Username.IsNotNullOrWhiteSpace() && proxySettings.Password.IsNotNullOrWhiteSpace())
{
return new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.SubnetFilterAsArray, new NetworkCredential(proxySettings.Username, proxySettings.Password));
}
else
{
return new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.SubnetFilterAsArray);
}
case ProxyType.Socks4:
return new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), addresses[0], proxySettings.Port, ProxyConfig.SocksVersion.Four, proxySettings.Username, proxySettings.Password), false);
case ProxyType.Socks5:
return new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), addresses[0], proxySettings.Port, ProxyConfig.SocksVersion.Five, proxySettings.Username, proxySettings.Password), false);
}
return null;
}
protected virtual void AddRequestHeaders(HttpWebRequest webRequest, HttpHeader headers)
{
foreach (var header in headers)
@ -177,15 +142,5 @@ namespace NzbDrone.Common.Http.Dispatchers
}
}
}
private static int GetNextFreePort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
}
}

View File

@ -2,11 +2,11 @@
using System.Net;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Http
namespace NzbDrone.Common.Http.Proxy
{
public class HttpRequestProxySettings
public class HttpProxySettings
{
public HttpRequestProxySettings(ProxyType type, string host, int port, string bypassFilter, bool bypassLocalAddress, string username = null, string password = null)
public HttpProxySettings(ProxyType type, string host, int port, string bypassFilter, bool bypassLocalAddress, string username = null, string password = null)
{
Type = type;
Host = host.IsNullOrWhiteSpace() ? "127.0.0.1" : host;

View File

@ -2,10 +2,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace NzbDrone.Common.Http
namespace NzbDrone.Common.Http.Proxy
{
public interface IHttpProxySettingsProvider
{
HttpRequestProxySettings GetProxySettings(HttpRequest request);
HttpProxySettings GetProxySettings(HttpRequest request);
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using com.LandonKey.SocksWebProxy;
using com.LandonKey.SocksWebProxy.Proxy;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Http.Proxy
{
public interface ICreateManagedWebProxy
{
IWebProxy GetWebProxy(HttpProxySettings proxySettings);
}
public class ManagedWebProxyFactory : ICreateManagedWebProxy
{
private readonly ICached<IWebProxy> _webProxyCache;
public ManagedWebProxyFactory(ICacheManager cacheManager)
{
_webProxyCache = cacheManager.GetCache<IWebProxy>(GetType(), "webProxy");
}
public IWebProxy GetWebProxy(HttpProxySettings proxySettings)
{
var proxy = _webProxyCache.Get(proxySettings.Key, () => CreateWebProxy(proxySettings), TimeSpan.FromMinutes(5));
_webProxyCache.ClearExpired();
return proxy;
}
private IWebProxy CreateWebProxy(HttpProxySettings proxySettings)
{
switch (proxySettings.Type)
{
case ProxyType.Http:
if (proxySettings.Username.IsNotNullOrWhiteSpace() && proxySettings.Password.IsNotNullOrWhiteSpace())
{
return new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.SubnetFilterAsArray, new NetworkCredential(proxySettings.Username, proxySettings.Password));
}
else
{
return new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.SubnetFilterAsArray);
}
case ProxyType.Socks4:
return new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(proxySettings.Host), proxySettings.Port, ProxyConfig.SocksVersion.Four, proxySettings.Username, proxySettings.Password), false);
case ProxyType.Socks5:
return new SocksWebProxy(new ProxyConfig(IPAddress.Loopback, GetNextFreePort(), GetProxyIpAddress(proxySettings.Host), proxySettings.Port, ProxyConfig.SocksVersion.Five, proxySettings.Username, proxySettings.Password), false);
}
return null;
}
private static IPAddress GetProxyIpAddress(string host)
{
IPAddress ipAddress;
if (!IPAddress.TryParse(host, out ipAddress))
{
try
{
ipAddress = Dns.GetHostEntry(host).AddressList.OrderByDescending(a => a.AddressFamily == AddressFamily.InterNetwork).First();
}
catch (Exception e)
{
throw new InvalidOperationException(string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", host), e);
}
}
return ipAddress;
}
private static int GetNextFreePort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Common.Http
namespace NzbDrone.Common.Http.Proxy
{
public enum ProxyType
{

View File

@ -157,6 +157,7 @@
<Compile Include="Http\Dispatchers\FallbackHttpDispatcher.cs" />
<Compile Include="Http\Dispatchers\IHttpDispatcher.cs" />
<Compile Include="Http\Dispatchers\ManagedHttpDispatcher.cs" />
<Compile Include="Http\Proxy\ManagedWebProxyFactory.cs" />
<Compile Include="Http\GZipWebClient.cs">
<SubType>Component</SubType>
</Compile>
@ -168,10 +169,10 @@
<Compile Include="Http\HttpMethod.cs" />
<Compile Include="Http\HttpProvider.cs" />
<Compile Include="Http\HttpRequest.cs" />
<Compile Include="Http\HttpRequestProxySettings.cs" />
<Compile Include="Http\Proxy\HttpProxySettings.cs" />
<Compile Include="Http\HttpResponse.cs" />
<Compile Include="Http\HttpUri.cs" />
<Compile Include="Http\IHttpProxySettingsProvider.cs" />
<Compile Include="Http\Proxy\IHttpProxySettingsProvider.cs" />
<Compile Include="Http\IHttpRequestInterceptor.cs" />
<Compile Include="Http\JsonRpcRequestBuilder.cs" />
<Compile Include="Http\JsonRpcResponse.cs" />
@ -180,7 +181,7 @@
</Compile>
<Compile Include="Http\HttpRequestBuilder.cs" />
<Compile Include="Http\HttpRequestBuilderFactory.cs" />
<Compile Include="Http\ProxyType.cs" />
<Compile Include="Http\Proxy\ProxyType.cs" />
<Compile Include="Http\TooManyRequestsException.cs" />
<Compile Include="Extensions\IEnumerableExtensions.cs" />
<Compile Include="Http\UserAgentBuilder.cs" />

View File

@ -7,8 +7,7 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Http;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Proxy;
namespace NzbDrone.Core.Configuration
{
@ -344,7 +343,7 @@ namespace NzbDrone.Core.Configuration
get { return GetValue("ProxyPassword", string.Empty); }
}
public string ProxySubnetFilter
public string ProxyBypassFilter
{
get { return GetValue("ProxySubnetFilter", string.Empty); }
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Http;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Proxy;
namespace NzbDrone.Core.Configuration
{
@ -74,7 +75,7 @@ namespace NzbDrone.Core.Configuration
int ProxyPort { get; }
string ProxyUsername { get; }
string ProxyPassword { get; }
string ProxySubnetFilter { get; }
string ProxyBypassFilter { get; }
bool ProxyBypassLocalAddresses { get; }
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using NzbDrone.Common.Cloud;
namespace NzbDrone.Core.HealthCheck.Checks
{
@ -15,11 +16,15 @@ namespace NzbDrone.Core.HealthCheck.Checks
private readonly IConfigService _configService;
private readonly IHttpClient _client;
public ProxyCheck(IConfigService configService, IHttpClient client, Logger logger)
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
public ProxyCheck(ISonarrCloudRequestBuilder cloudRequestBuilder, IConfigService configService, IHttpClient client, Logger logger)
{
_configService = configService;
_client = client;
_logger = logger;
_cloudRequestBuilder = cloudRequestBuilder.Services;
}
public override HealthCheck Check()
@ -29,10 +34,12 @@ namespace NzbDrone.Core.HealthCheck.Checks
var addresses = Dns.GetHostAddresses(_configService.ProxyHostname);
if(!addresses.Any())
{
return new HealthCheck(GetType(), HealthCheckResult.Error, "Failed to resolve the IP Address for the Configured Proxy Host: " + _configService.ProxyHostname);
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to resolve the IP Address for the Configured Proxy Host {0}", _configService.ProxyHostname));
}
var request = new HttpRequestBuilder("https://services.sonarr.tv/ping").Build();
var request = _cloudRequestBuilder.Create()
.Resource("/ping")
.Build();
try
{
@ -41,14 +48,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
// We only care about 400 responses, other error codes can be ignored
if (response.StatusCode == HttpStatusCode.BadRequest)
{
_logger.Error("Proxy Health Check failed: {0}. Response Data: {1} ", response.StatusCode.ToString(), response.ResponseData);
return new HealthCheck(GetType(), HealthCheckResult.Error, "Failed to load https://sonarr.tv/, got HTTP " + response.StatusCode.ToString());
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to test proxy: StatusCode {1}", request.Url, response.StatusCode));
}
}
catch (Exception ex)
{
_logger.ErrorException("Proxy Health Check failed.", ex);
return new HealthCheck(GetType(), HealthCheckResult.Error, "An exception occured while trying to load https://sonarr.tv/: " + ex.Message);
_logger.Error(ex, "Proxy Health Check failed: {0}", ex.Message);
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to test proxy: {1}", request.Url, ex.Message));
}
}

View File

@ -1,11 +1,8 @@
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using System.Net;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Http
{
@ -18,17 +15,17 @@ namespace NzbDrone.Core.Http
_configService = configService;
}
public HttpRequestProxySettings GetProxySettings(HttpRequest request)
public HttpProxySettings GetProxySettings(HttpRequest request)
{
if (!_configService.ProxyEnabled)
{
return null;
}
var proxySettings = new HttpRequestProxySettings(_configService.ProxyType,
var proxySettings = new HttpProxySettings(_configService.ProxyType,
_configService.ProxyHostname,
_configService.ProxyPort,
_configService.ProxySubnetFilter,
_configService.ProxyBypassFilter,
_configService.ProxyBypassLocalAddresses,
_configService.ProxyUsername,
_configService.ProxyPassword);
@ -41,7 +38,7 @@ namespace NzbDrone.Core.Http
return proxySettings;
}
public bool ShouldProxyBeBypassed(HttpRequestProxySettings proxySettings, HttpUri url)
public bool ShouldProxyBeBypassed(HttpProxySettings proxySettings, HttpUri url)
{
//We are utilising the WebProxy implementation here to save us having to reimplement it. This way we use Microsofts implementation
var proxy = new WebProxy(proxySettings.Host + ":" + proxySettings.Port, proxySettings.BypassLocalAddress, proxySettings.SubnetFilterAsArray);

View File

@ -246,12 +246,12 @@
</div>
<div class="col-sm-4 col-sm-pull-1">
<input type="text" name="proxySubnetFilter" class="form-control"/>
<input type="text" name="proxyBypassFilter" class="form-control"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Bypass Proxy for Local Addresses?</label>
<label class="col-sm-3 control-label">Bypass Proxy for Local Addresses</label>
<div class="col-sm-8">
<div class="input-group">