diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index 8df5d23b9..cce6d125f 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -28,6 +28,8 @@ HttpResponse Post(HttpRequest request) public class HttpClient : IHttpClient { + private const int MaxRedirects = 5; + private readonly Logger _logger; private readonly IRateLimitService _rateLimitService; private readonly ICached _cookieContainerCache; @@ -70,7 +72,7 @@ public HttpResponse Execute(HttpRequest request) _logger.Trace("Redirected to {0}", request.Url); - if (autoRedirectChain.Count > 3) + if (autoRedirectChain.Count > MaxRedirects) { throw new WebException($"Too many automatic redirections were attempted for {autoRedirectChain.Join(" -> ")}", WebExceptionStatus.ProtocolError); } diff --git a/src/NzbDrone.Common/Http/Proxy/IHttpProxySettingsProvider.cs b/src/NzbDrone.Common/Http/Proxy/IHttpProxySettingsProvider.cs index a288a46c4..bb5909395 100644 --- a/src/NzbDrone.Common/Http/Proxy/IHttpProxySettingsProvider.cs +++ b/src/NzbDrone.Common/Http/Proxy/IHttpProxySettingsProvider.cs @@ -3,5 +3,6 @@ namespace NzbDrone.Common.Http.Proxy public interface IHttpProxySettingsProvider { HttpProxySettings GetProxySettings(HttpRequest request); + HttpProxySettings GetProxySettings(); } } diff --git a/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs b/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs index 26c7d9341..dbea6dcd0 100644 --- a/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs +++ b/src/NzbDrone.Core/Http/HttpProxySettingsProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net; using NzbDrone.Common.Http; using NzbDrone.Common.Http.Proxy; @@ -17,19 +17,12 @@ public HttpProxySettingsProvider(IConfigService configService) public HttpProxySettings GetProxySettings(HttpRequest request) { - if (!_configService.ProxyEnabled) + var proxySettings = GetProxySettings(); + if (proxySettings == null) { return null; } - var proxySettings = new HttpProxySettings(_configService.ProxyType, - _configService.ProxyHostname, - _configService.ProxyPort, - _configService.ProxyBypassFilter, - _configService.ProxyBypassLocalAddresses, - _configService.ProxyUsername, - _configService.ProxyPassword); - if (ShouldProxyBeBypassed(proxySettings, request.Url)) { return null; @@ -38,6 +31,22 @@ public HttpProxySettings GetProxySettings(HttpRequest request) return proxySettings; } + public HttpProxySettings GetProxySettings() + { + if (!_configService.ProxyEnabled) + { + return null; + } + + return new HttpProxySettings(_configService.ProxyType, + _configService.ProxyHostname, + _configService.ProxyPort, + _configService.ProxyBypassFilter, + _configService.ProxyBypassLocalAddresses, + _configService.ProxyUsername, + _configService.ProxyPassword); + } + 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 diff --git a/src/NzbDrone.Core/Notifications/Boxcar/BoxcarProxy.cs b/src/NzbDrone.Core/Notifications/Boxcar/BoxcarProxy.cs index 3bf8560d4..6f08caf57 100644 --- a/src/NzbDrone.Core/Notifications/Boxcar/BoxcarProxy.cs +++ b/src/NzbDrone.Core/Notifications/Boxcar/BoxcarProxy.cs @@ -17,10 +17,12 @@ public interface IBoxcarProxy public class BoxcarProxy : IBoxcarProxy { private const string URL = "https://new.boxcar.io/api/notifications"; + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public BoxcarProxy(Logger logger) + public BoxcarProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } @@ -71,7 +73,7 @@ private void SendNotification(string title, string message, RestRequest request, { try { - var client = RestClientFactory.BuildClient(URL); + var client = _restClientFactory.BuildClient(URL); request.AddParameter("user_credentials", settings.Token); request.AddParameter("notification[title]", title); diff --git a/src/NzbDrone.Core/Notifications/Gotify/GotifyProxy.cs b/src/NzbDrone.Core/Notifications/Gotify/GotifyProxy.cs index a6e711516..15a1c40d4 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/GotifyProxy.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/GotifyProxy.cs @@ -10,9 +10,16 @@ public interface IGotifyProxy public class GotifyProxy : IGotifyProxy { + private readonly IRestClientFactory _restClientFactory; + + public GotifyProxy(IRestClientFactory restClientFactory) + { + _restClientFactory = restClientFactory; + } + public void SendNotification(string title, string message, GotifySettings settings) { - var client = RestClientFactory.BuildClient(settings.Server); + var client = _restClientFactory.BuildClient(settings.Server); var request = new RestRequest("message", Method.POST); request.AddQueryParameter("token", settings.AppToken); diff --git a/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs b/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs index d0d968ecb..81382cc4c 100644 --- a/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs +++ b/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs @@ -17,10 +17,12 @@ public interface IJoinProxy public class JoinProxy : IJoinProxy { private const string URL = "https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush?"; + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public JoinProxy(Logger logger) + public JoinProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } @@ -73,7 +75,7 @@ public ValidationFailure Test(JoinSettings settings) private void SendNotification(string title, string message, RestRequest request, JoinSettings settings) { - var client = RestClientFactory.BuildClient(URL); + var client = _restClientFactory.BuildClient(URL); if (settings.DeviceNames.IsNotNullOrWhiteSpace()) { diff --git a/src/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs b/src/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs index 451f3b828..5cc9867b8 100644 --- a/src/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs +++ b/src/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs @@ -23,10 +23,12 @@ public class PushBulletProxy : IPushBulletProxy { private const string PUSH_URL = "https://api.pushbullet.com/v2/pushes"; private const string DEVICE_URL = "https://api.pushbullet.com/v2/devices"; + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public PushBulletProxy(Logger logger) + public PushBulletProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } @@ -96,7 +98,7 @@ public List GetDevices(PushBulletSettings settings) { try { - var client = RestClientFactory.BuildClient(DEVICE_URL); + var client = _restClientFactory.BuildClient(DEVICE_URL); var request = new RestRequest(Method.GET); client.Authenticator = new HttpBasicAuthenticator(settings.ApiKey, string.Empty); @@ -174,7 +176,7 @@ private void SendNotification(string title, string message, RestRequest request, { try { - var client = RestClientFactory.BuildClient(PUSH_URL); + var client = _restClientFactory.BuildClient(PUSH_URL); request.AddParameter("type", "note"); request.AddParameter("title", title); diff --git a/src/NzbDrone.Core/Notifications/Pushover/PushoverService.cs b/src/NzbDrone.Core/Notifications/Pushover/PushoverProxy.cs similarity index 82% rename from src/NzbDrone.Core/Notifications/Pushover/PushoverService.cs rename to src/NzbDrone.Core/Notifications/Pushover/PushoverProxy.cs index fddae9d91..ea32f0efc 100644 --- a/src/NzbDrone.Core/Notifications/Pushover/PushoverService.cs +++ b/src/NzbDrone.Core/Notifications/Pushover/PushoverProxy.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentValidation.Results; using NLog; using NzbDrone.Common.Extensions; @@ -16,19 +16,22 @@ public interface IPushoverProxy public class PushoverProxy : IPushoverProxy { private const string URL = "https://api.pushover.net/1/messages.json"; + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public PushoverProxy(Logger logger) + public PushoverProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } public void SendNotification(string title, string message, PushoverSettings settings) { - var client = RestClientFactory.BuildClient(URL); + var client = _restClientFactory.BuildClient(URL); var request = new RestRequest(Method.POST); request.AddParameter("token", settings.ApiKey); request.AddParameter("user", settings.UserKey); + request.AddParameter("device", string.Join(",", settings.Devices)); request.AddParameter("title", title); request.AddParameter("message", message); request.AddParameter("priority", settings.Priority); @@ -52,13 +55,13 @@ public ValidationFailure Test(PushoverSettings settings) try { const string title = "Test Notification"; - const string body = "This is a test message from Radarr"; + const string body = "This is a test message from Sonarr"; SendNotification(title, body, settings); } catch (Exception ex) { - _logger.Error(ex, "Unable to send test message: " + ex.Message); + _logger.Error(ex, "Unable to send test message"); return new ValidationFailure("ApiKey", "Unable to send test message"); } diff --git a/src/NzbDrone.Core/Notifications/Telegram/TelegramService.cs b/src/NzbDrone.Core/Notifications/Telegram/TelegramService.cs index cefc637ab..3cac68688 100644 --- a/src/NzbDrone.Core/Notifications/Telegram/TelegramService.cs +++ b/src/NzbDrone.Core/Notifications/Telegram/TelegramService.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Net; using System.Web; using FluentValidation.Results; using NLog; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Serializer; using NzbDrone.Core.Rest; using RestSharp; @@ -19,10 +20,12 @@ public interface ITelegramProxy public class TelegramProxy : ITelegramProxy { private const string URL = "https://api.telegram.org"; + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public TelegramProxy(Logger logger) + public TelegramProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } @@ -30,7 +33,8 @@ public void SendNotification(string title, string message, TelegramSettings sett { //Format text to add the title before and bold using markdown var text = $"{HttpUtility.HtmlEncode(title)}\n{HttpUtility.HtmlEncode(message)}"; - var client = RestClientFactory.BuildClient(URL); + var client = _restClientFactory.BuildClient(URL); + var request = new RestRequest("bot{token}/sendmessage", Method.POST); request.AddUrlSegment("token", settings.BotToken); @@ -54,9 +58,11 @@ public ValidationFailure Test(TelegramSettings settings) { _logger.Error(ex, "Unable to send test message"); - var restException = ex as RestException; - - if (restException != null && restException.Response.StatusCode == HttpStatusCode.BadRequest) + if (ex is WebException webException) + { + return new ValidationFailure("Connection", $"{webException.Status.ToString()}: {webException.Message}"); + } + else if (ex is RestException restException && restException.Response.StatusCode == HttpStatusCode.BadRequest) { var error = Json.Deserialize(restException.Response.Content); var property = error.Description.ContainsIgnoreCase("chat not found") ? "ChatId" : "BotToken"; diff --git a/src/NzbDrone.Core/Notifications/Xbmc/XbmcJsonApiProxy.cs b/src/NzbDrone.Core/Notifications/Xbmc/XbmcJsonApiProxy.cs index a436dc62f..ae6923f9b 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/XbmcJsonApiProxy.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/XbmcJsonApiProxy.cs @@ -21,10 +21,12 @@ public interface IXbmcJsonApiProxy public class XbmcJsonApiProxy : IXbmcJsonApiProxy { + private readonly IRestClientFactory _restClientFactory; private readonly Logger _logger; - public XbmcJsonApiProxy(Logger logger) + public XbmcJsonApiProxy(IRestClientFactory restClientFactory, Logger logger) { + _restClientFactory = restClientFactory; _logger = logger; } @@ -110,7 +112,7 @@ private string ProcessRequest(IRestRequest request, XbmcSettings settings, strin private IRestClient BuildClient(XbmcSettings settings) { var url = string.Format(@"http://{0}/jsonrpc", settings.Address); - var client = RestClientFactory.BuildClient(url); + var client = _restClientFactory.BuildClient(url); if (!settings.Username.IsNullOrWhiteSpace()) { diff --git a/src/NzbDrone.Core/Rest/IRestClientFactory.cs b/src/NzbDrone.Core/Rest/IRestClientFactory.cs new file mode 100644 index 000000000..39754016d --- /dev/null +++ b/src/NzbDrone.Core/Rest/IRestClientFactory.cs @@ -0,0 +1,9 @@ +using RestSharp; + +namespace NzbDrone.Core.Rest +{ + public interface IRestClientFactory + { + RestClient BuildClient(string baseUrl); + } +} diff --git a/src/NzbDrone.Core/Rest/RestClientFactory.cs b/src/NzbDrone.Core/Rest/RestClientFactory.cs index bcce3b825..2614840c8 100644 --- a/src/NzbDrone.Core/Rest/RestClientFactory.cs +++ b/src/NzbDrone.Core/Rest/RestClientFactory.cs @@ -1,17 +1,33 @@ using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Http.Proxy; using RestSharp; namespace NzbDrone.Core.Rest { - public static class RestClientFactory + public class RestClientFactory : IRestClientFactory { - public static RestClient BuildClient(string baseUrl) + private readonly IHttpProxySettingsProvider _httpProxySettingsProvider; + private readonly ICreateManagedWebProxy _createManagedWebProxy; + + public RestClientFactory(IHttpProxySettingsProvider httpProxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy) + { + _httpProxySettingsProvider = httpProxySettingsProvider; + _createManagedWebProxy = createManagedWebProxy; + } + + public RestClient BuildClient(string baseUrl) { var restClient = new RestClient(baseUrl) { UserAgent = $"{BuildInfo.AppName}/{BuildInfo.Version} ({OsInfo.Os})" }; + var proxySettings = _httpProxySettingsProvider.GetProxySettings(); + if (proxySettings != null) + { + restClient.Proxy = _createManagedWebProxy.GetWebProxy(proxySettings); + } + return restClient; } }