Moved Proxy types around and refactored/renamed a few things.
This commit is contained in:
parent
9e7927acec
commit
b479064abd
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
|
@ -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" />
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue