diff --git a/doc/xmlConfigFile.md b/doc/xmlConfigFile.md index 0caf0e0..aebd65d 100644 --- a/doc/xmlConfigFile.md +++ b/doc/xmlConfigFile.md @@ -186,6 +186,10 @@ For target servers using the HTTPS transfer protocol it is necessary, that the C By default, the `download` command does not fail the service startup if the operation fails (e.g. `from` is not available). In order to force the download failure in such case, it is possible to specify the `failOnError` boolean attribute. +To specify a custom proxy use the parameter `proxy` with the following formats: +- With credentials: `http://USERNAME:PASSWORD@HOST:PORT/`. +- Without credentials: `http://HOST:PORT/`. + Examples: ```xml @@ -193,12 +197,15 @@ Examples: + + ``` diff --git a/src/Core/WinSWCore/Download.cs b/src/Core/WinSWCore/Download.cs index 0791d4e..f0d2c22 100755 --- a/src/Core/WinSWCore/Download.cs +++ b/src/Core/WinSWCore/Download.cs @@ -36,6 +36,7 @@ namespace winsw public readonly string? Password; public readonly bool UnsecureAuth; public readonly bool FailOnError; + public readonly string? Proxy; public string ShortId => $"(download from {From})"; @@ -75,11 +76,13 @@ namespace winsw AuthType auth = AuthType.none, string? username = null, string? password = null, - bool unsecureAuth = false) + bool unsecureAuth = false, + string? proxy = null) { From = from; To = to; FailOnError = failOnError; + Proxy = proxy; Auth = auth; Username = username; Password = password; @@ -98,6 +101,7 @@ namespace winsw // All arguments below are optional FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false); + Proxy = XmlHelper.SingleAttribute(n, "proxy", null); Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.none); Username = XmlHelper.SingleAttribute(n, "user", null); @@ -147,6 +151,18 @@ namespace winsw #endif { WebRequest request = WebRequest.Create(From); + if (!string.IsNullOrEmpty(Proxy)) + { + CustomProxyInformation proxyInformation = new CustomProxyInformation(Proxy); + if (proxyInformation.Credentials != null) + { + request.Proxy = new WebProxy(proxyInformation.ServerAddress, false, null, proxyInformation.Credentials); + } + else + { + request.Proxy = new WebProxy(proxyInformation.ServerAddress); + } + } switch (Auth) { @@ -220,8 +236,8 @@ namespace winsw } } } -#if NET20 +#if NET20 private static void CopyStream(Stream source, Stream destination) { byte[] buffer = new byte[8192]; @@ -233,4 +249,31 @@ namespace winsw } #endif } + + public class CustomProxyInformation + { + public string ServerAddress { get; set; } + public NetworkCredential? Credentials { get; set; } + + public CustomProxyInformation(string proxy) + { + if (proxy.Contains("@")) + { + // Extract proxy credentials + int credsFrom = proxy.IndexOf("://") + 3; + int credsTo = proxy.LastIndexOf("@"); + string completeCredsStr = proxy.Substring(credsFrom, credsTo - credsFrom); + int credsSeparator = completeCredsStr.IndexOf(":"); + + string username = completeCredsStr.Substring(0, credsSeparator); + string password = completeCredsStr.Substring(credsSeparator + 1); + Credentials = new NetworkCredential(username, password); + ServerAddress = proxy.Replace(completeCredsStr + "@", ""); + } + else + { + ServerAddress = proxy; + } + } + } } diff --git a/src/Test/winswTests/DownloadTest.cs b/src/Test/winswTests/DownloadTest.cs index 9477c1f..3dceba0 100644 --- a/src/Test/winswTests/DownloadTest.cs +++ b/src/Test/winswTests/DownloadTest.cs @@ -202,6 +202,35 @@ namespace winswTests Assert.That(() => GetSingleEntry(sd), Throws.TypeOf().With.Message.StartsWith("Cannot parse Enum value from string 'digest'")); } + [TestCase("http://", "127.0.0.1:80", "egarcia", "Passw0rd")] + [TestCase("https://", "myurl.com.co:2298", "MyUsername", "P@ssw:rd")] + [TestCase("http://", "192.168.0.8:3030")] + public void Proxy_Credentials(string protocol, string address, string username = null, string password = null) + { + CustomProxyInformation cpi; + if (string.IsNullOrEmpty(username)) + { + cpi = new CustomProxyInformation(protocol + address + "/"); + } + else + { + cpi = new CustomProxyInformation(protocol + username + ":" + password + "@" + address + "/"); + } + + Assert.That(cpi.ServerAddress, Is.EqualTo(protocol + address + "/")); + + if (string.IsNullOrEmpty(username)) + { + Assert.IsNull(cpi.Credentials); + } + else + { + Assert.IsNotNull(cpi.Credentials); + Assert.That(cpi.Credentials.UserName, Is.EqualTo(username)); + Assert.That(cpi.Credentials.Password, Is.EqualTo(password)); + } + } + private Download GetSingleEntry(ServiceDescriptor sd) { var downloads = sd.Downloads.ToArray();