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();