From d716c5fe201320a1a3d0e972f0c071b168b39f4c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 13 May 2026 06:23:23 +0000
Subject: [PATCH 1/3] Replicate proxy and protocol options across HTTP task
projects
Agent-Logs-Url: https://github.com/FrendsPlatform/Frends.HTTP/sessions/2c0a6a2a-230b-4d67-af17-87d3e474f51f
Co-authored-by: MichalFrends1 <167774394+MichalFrends1@users.noreply.github.com>
---
Frends.HTTP.DownloadFile/CHANGELOG.md | 4 ++
.../UnitTests.cs | 48 ++++++++++++++++++
.../Definitions/Enums.cs | 40 +++++++++++++++
.../Definitions/Options.cs | 46 +++++++++++++++++
.../Frends.HTTP.DownloadFile/DownloadFile.cs | 4 +-
.../Frends.HTTP.DownloadFile/Extensions.cs | 40 +++++++++++++++
.../Frends.HTTP.DownloadFile.csproj | 2 +-
Frends.HTTP.RequestBytes/CHANGELOG.md | 4 ++
.../UnitTests.cs | 43 ++++++++++++++++
.../Definitions/HttpVersion.cs | 18 +++++++
.../Definitions/Options.cs | 46 +++++++++++++++++
.../Definitions/SslVersion.cs | 25 ++++++++++
.../Frends.HTTP.RequestBytes/Extensions.cs | 40 +++++++++++++++
.../Frends.HTTP.RequestBytes.csproj | 2 +-
.../Frends.HTTP.RequestBytes/RequestBytes.cs | 4 +-
Frends.HTTP.SendAndReceiveBytes/CHANGELOG.md | 4 ++
.../UnitTests.cs | 50 +++++++++++++++++++
.../Definitions/HttpVersion.cs | 18 +++++++
.../Definitions/Options.cs | 46 +++++++++++++++++
.../Definitions/SslVersion.cs | 25 ++++++++++
.../Extensions.cs | 40 +++++++++++++++
.../Frends.HTTP.SendAndReceiveBytes.csproj | 2 +-
.../SendAndReceiveBytes.cs | 4 +-
Frends.HTTP.SendBytes/CHANGELOG.md | 4 ++
.../Frends.HTTP.SendBytes.Tests/UnitTests.cs | 50 +++++++++++++++++++
.../Definitions/HttpVersion.cs | 18 +++++++
.../Definitions/Options.cs | 46 +++++++++++++++++
.../Definitions/SslVersion.cs | 25 ++++++++++
.../Frends.HTTP.SendBytes/Extensions.cs | 40 +++++++++++++++
.../Frends.HTTP.SendBytes.csproj | 2 +-
.../Frends.HTTP.SendBytes/SendBytes.cs | 4 +-
31 files changed, 736 insertions(+), 8 deletions(-)
create mode 100644 Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/HttpVersion.cs
create mode 100644 Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/SslVersion.cs
create mode 100644 Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/HttpVersion.cs
create mode 100644 Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/SslVersion.cs
create mode 100644 Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/HttpVersion.cs
create mode 100644 Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/SslVersion.cs
diff --git a/Frends.HTTP.DownloadFile/CHANGELOG.md b/Frends.HTTP.DownloadFile/CHANGELOG.md
index efa744c..a912e7b 100644
--- a/Frends.HTTP.DownloadFile/CHANGELOG.md
+++ b/Frends.HTTP.DownloadFile/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.5.0] - 2026-05-13
+### Added
+- Added UseProxy, ProxyUrl, ProxyUsername, ProxyPassword, HttpProtocolVersion, and SslProtocolVersion options with proxy handler and protocol configuration support.
+
## [1.4.0] - 2026-03-02
### Added
- Added CertificateStoreLocation option to allow selection between CurrentUser and LocalMachine certificate stores when using certificate authentication.
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
index ff7e677..f237c6c 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
+using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
@@ -546,4 +547,51 @@ public void CorrectStoreSearched(CertificateStoreLocation storeLocation, string
StringAssert.Contains(ex.Message,
$"Certificate with thumbprint: 'INVALIDTHUMBPRINT' not found in {storeLocationText} cert store.");
}
+
+ [TestMethod]
+ public void HandlerShouldUseConfiguredProxy()
+ {
+ var handler = new HttpClientHandler();
+ var options = new Options
+ {
+ UseProxy = true,
+ ProxyUrl = "http://proxy.example.com:8080",
+ ProxyUsername = "proxy-user",
+ ProxyPassword = "proxy-password"
+ };
+
+ handler.SetHandlerSettingsBasedOnOptions(options);
+
+ var proxy = handler.Proxy as WebProxy;
+ Assert.IsNotNull(proxy);
+ Assert.IsTrue(handler.UseProxy);
+ Assert.AreEqual(new Uri("http://proxy.example.com:8080/"), proxy.Address);
+ var credentials = proxy.Credentials.GetCredential(proxy.Address, string.Empty);
+ Assert.AreEqual("proxy-user", credentials.UserName);
+ Assert.AreEqual("proxy-password", credentials.Password);
+ }
+
+ [TestMethod]
+ public async Task RequestShouldSetTls12And13WhenConfigured()
+ {
+ var input = new Input
+ {
+ Url = _targetFileAddress,
+ FilePath = _filePath,
+ Headers = null
+ };
+
+ var options = new Options
+ {
+ ConnectionTimeoutSeconds = 60,
+ SslProtocolVersion = SslVersion.Tls12And13,
+ HttpProtocolVersion = HttpVersion.Http20,
+ Overwrite = true
+ };
+
+ var result = await HTTP.DownloadFile(input, options, default);
+
+ Assert.IsTrue(result.Success);
+ }
+
}
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Enums.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Enums.cs
index 5fa6856..738d336 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Enums.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Enums.cs
@@ -49,3 +49,43 @@ public enum Authentication
///
ClientCertificate
}
+
+
+///
+/// HTTP protocol version used for requests.
+///
+public enum HttpVersion
+{
+ ///
+ /// HTTP/1.1 - default, widely supported.
+ ///
+ Http11,
+ ///
+ /// HTTP/2 - multiplexed, requires server support.
+ /// If the server does not support HTTP/2 via ALPN, the request will fail with an exception instead of falling back to HTTP/1.1.
+ ///
+ Http20
+}
+
+///
+/// SSL/TLS protocol version used for secure connections.
+///
+public enum SslVersion
+{
+ ///
+ /// OS decides the protocol version.
+ ///
+ Default,
+ ///
+ /// TLS 1.2 only.
+ ///
+ Tls12,
+ ///
+ /// TLS 1.3 only.
+ ///
+ Tls13,
+ ///
+ /// TLS 1.2 and TLS 1.3 - use when server compatibility is uncertain.
+ ///
+ Tls12And13
+}
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Options.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Options.cs
index 0b09d00..7b40609 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Options.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Definitions/Options.cs
@@ -38,6 +38,37 @@ public class Options
[UIHint(nameof(Authentication), "", Authentication.OAuth)]
public string Token { get; set; }
+ ///
+ /// Whether to route requests through a proxy server. When set to true, the ProxyUrl must be provided.
+ ///
+ /// false
+ [DefaultValue(false)]
+ public bool UseProxy { get; set; } = false;
+
+ ///
+ /// Proxy URL used for the request.
+ ///
+ /// http://proxy.example.com:8080
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUrl { get; set; }
+
+ ///
+ /// Username for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Username
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUsername { get; set; }
+
+ ///
+ /// Password for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Password123
+ [PasswordPropertyText]
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyPassword { get; set; }
+
///
/// Specifies where the Client Certificate should be loaded from.
///
@@ -137,6 +168,21 @@ public class Options
[DefaultValue(true)]
public bool AutomaticCookieHandling { get; set; } = true;
+ ///
+ /// HTTP protocol version to use for requests. HTTP/1.1 is the default and most compatible option.
+ ///
+ /// Http11
+ [DefaultValue(HttpVersion.Http11)]
+ public HttpVersion HttpProtocolVersion { get; set; } = HttpVersion.Http11;
+
+ ///
+ /// SSL/TLS protocol version to use for secure connections. Default lets the OS decide, which matches previous behavior.
+ /// Use Tls12 or Tls12And13 if the server requires a specific version.
+ ///
+ /// Default
+ [DefaultValue(SslVersion.Default)]
+ public SslVersion SslProtocolVersion { get; set; } = SslVersion.Default;
+
///
/// If set to true, an existing file at the target path will be overwritten during download.
///
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/DownloadFile.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/DownloadFile.cs
index ed509ac..5e26c56 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/DownloadFile.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/DownloadFile.cs
@@ -135,7 +135,9 @@ private static string GetHttpClientCacheKey(Options options)
+ $":{options.ClientCertificateFilePath}:{options.ClientCertificateInBase64}:{options.ClientCertificateKeyPhrase}"
+ $":{options.CertificateThumbprint}:{options.LoadEntireChainForCertificate}:{options.ConnectionTimeoutSeconds}"
+ $":{options.FollowRedirects}:{options.AllowInvalidCertificate}:{options.AllowInvalidResponseContentTypeCharSet}"
- + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}";
+ + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}"
+ + $":{options.SslProtocolVersion}:{options.HttpProtocolVersion}"
+ + $":{options.UseProxy}:{options.ProxyUrl}:{options.ProxyUsername}:{options.ProxyPassword}";
}
private static void OnPluginUnloadingRequested(AssemblyLoadContext obj)
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Extensions.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Extensions.cs
index 8575040..82ab3bf 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Extensions.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Extensions.cs
@@ -5,6 +5,7 @@
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
using System.Text.RegularExpressions;
namespace Frends.HTTP.DownloadFile;
@@ -39,8 +40,35 @@ internal static void SetHandlerSettingsBasedOnOptions(this HttpClientHandler han
handler.AllowAutoRedirect = options.FollowRedirects;
handler.UseCookies = options.AutomaticCookieHandling;
+ if (options.UseProxy && !string.IsNullOrWhiteSpace(options.ProxyUrl))
+ {
+ handler.Proxy = new WebProxy(options.ProxyUrl);
+ handler.UseProxy = true;
+
+ var hasUsername = !string.IsNullOrWhiteSpace(options.ProxyUsername);
+ var hasPassword = !string.IsNullOrWhiteSpace(options.ProxyPassword);
+
+ if (hasUsername != hasPassword)
+ {
+ throw new ArgumentException("Both ProxyUsername and ProxyPassword must be provided together or left empty.");
+ }
+
+ if (hasUsername)
+ {
+ handler.Proxy.Credentials = new NetworkCredential(options.ProxyUsername, options.ProxyPassword);
+ }
+ }
+
if (options.AllowInvalidCertificate)
handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;
+
+ handler.SslProtocols = options.SslProtocolVersion switch
+ {
+ SslVersion.Tls12 => SslProtocols.Tls12,
+ SslVersion.Tls13 => SslProtocols.Tls13,
+ SslVersion.Tls12And13 => SslProtocols.Tls12 | SslProtocols.Tls13,
+ _ => SslProtocols.None
+ };
}
internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient httpClient, Options options)
@@ -49,6 +77,18 @@ internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient http
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json");
httpClient.Timeout = TimeSpan.FromSeconds(Convert.ToDouble(options.ConnectionTimeoutSeconds));
+
+ httpClient.DefaultRequestVersion = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => System.Net.HttpVersion.Version20,
+ _ => System.Net.HttpVersion.Version11
+ };
+
+ httpClient.DefaultVersionPolicy = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => HttpVersionPolicy.RequestVersionExact,
+ _ => HttpVersionPolicy.RequestVersionOrLower
+ };
}
private static X509Certificate[] GetCertificates(Options options)
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.csproj b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.csproj
index f1e21d7..e7f88fb 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.csproj
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.csproj
@@ -2,7 +2,7 @@
net6.0
- 1.4.0
+ 1.5.0
Frends
Frends
Frends
diff --git a/Frends.HTTP.RequestBytes/CHANGELOG.md b/Frends.HTTP.RequestBytes/CHANGELOG.md
index 1da35ca..15c1379 100644
--- a/Frends.HTTP.RequestBytes/CHANGELOG.md
+++ b/Frends.HTTP.RequestBytes/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.5.0] - 2026-05-13
+### Added
+- Added UseProxy, ProxyUrl, ProxyUsername, ProxyPassword, HttpProtocolVersion, and SslProtocolVersion options with proxy handler and protocol configuration support.
+
## [1.4.0] - 2026-03-02
### Added
- Added CertificateStoreLocation option to allow selection between CurrentUser and LocalMachine certificate stores when using certificate authentication.
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.Tests/UnitTests.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.Tests/UnitTests.cs
index 83da029..22269fb 100644
--- a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.Tests/UnitTests.cs
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.Tests/UnitTests.cs
@@ -7,6 +7,7 @@
using RichardSzalay.MockHttp;
using Assert = NUnit.Framework.Assert;
using Frends.HTTP.RequestBytes.Definitions;
+using Definitions = Frends.HTTP.RequestBytes.Definitions;
using System.Collections.Generic;
using System.Text;
using Method = Frends.HTTP.RequestBytes.Definitions.Method;
@@ -399,6 +400,48 @@ public async Task RequestShouldSetEncodingWithContentTypeCharsetIgnoringCase()
_mockHttpMessageHandler.VerifyNoOutstandingExpectation();
ClassicAssert.AreEqual("foo åäö", Encoding.UTF8.GetString(result.Body));
}
+
+ [TestMethod]
+ public void HandlerShouldUseConfiguredProxy()
+ {
+ var handler = new HttpClientHandler();
+ var options = new Options
+ {
+ UseProxy = true,
+ ProxyUrl = "http://proxy.example.com:8080",
+ ProxyUsername = "proxy-user",
+ ProxyPassword = "proxy-password"
+ };
+
+ handler.SetHandlerSettingsBasedOnOptions(options);
+
+ var proxy = handler.Proxy as WebProxy;
+ Assert.That(proxy, Is.Not.Null);
+ ClassicAssert.IsTrue(handler.UseProxy);
+ ClassicAssert.AreEqual(new Uri("http://proxy.example.com:8080/"), proxy.Address);
+ var credentials = proxy.Credentials.GetCredential(proxy.Address, string.Empty);
+ ClassicAssert.AreEqual("proxy-user", credentials.UserName);
+ ClassicAssert.AreEqual("proxy-password", credentials.Password);
+ }
+
+ [TestMethod]
+ public async Task RequestShouldSetTls12And13WhenConfigured()
+ {
+ HTTP.ClientFactory = new HttpClientFactory();
+
+ var input = GetInputParams(url: "https://httpbin.org/anything");
+ var options = new Options
+ {
+ ConnectionTimeoutSeconds = 60,
+ SslProtocolVersion = SslVersion.Tls12And13,
+ HttpProtocolVersion = Definitions.HttpVersion.Http20
+ };
+
+ var result = await HTTP.RequestBytes(input, options, CancellationToken.None);
+
+ ClassicAssert.AreEqual(200, result.StatusCode);
+ }
+
}
public class MockHttpClientFactory : IHttpClientFactory
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/HttpVersion.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/HttpVersion.cs
new file mode 100644
index 0000000..5de4a58
--- /dev/null
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/HttpVersion.cs
@@ -0,0 +1,18 @@
+namespace Frends.HTTP.RequestBytes.Definitions
+{
+ ///
+ /// HTTP protocol version used for requests.
+ ///
+ public enum HttpVersion
+ {
+ ///
+ /// HTTP/1.1 - default, widely supported.
+ ///
+ Http11,
+ ///
+ /// HTTP/2 - multiplexed, requires server support.
+ /// If the server does not support HTTP/2 via ALPN, the request will fail with an exception instead of falling back to HTTP/1.1.
+ ///
+ Http20
+ }
+}
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/Options.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/Options.cs
index d652b2a..3ead407 100644
--- a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/Options.cs
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/Options.cs
@@ -37,6 +37,37 @@ public class Options
[UIHint(nameof(Authentication), "", Authentication.OAuth)]
public string Token { get; set; }
+ ///
+ /// Whether to route requests through a proxy server. When set to true, the ProxyUrl must be provided.
+ ///
+ /// false
+ [DefaultValue(false)]
+ public bool UseProxy { get; set; } = false;
+
+ ///
+ /// Proxy URL used for the request.
+ ///
+ /// http://proxy.example.com:8080
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUrl { get; set; }
+
+ ///
+ /// Username for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Username
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUsername { get; set; }
+
+ ///
+ /// Password for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Password123
+ [PasswordPropertyText]
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyPassword { get; set; }
+
///
/// Specifies where the Client Certificate should be loaded from.
///
@@ -135,4 +166,19 @@ public class Options
/// true
[DefaultValue(true)]
public bool AutomaticCookieHandling { get; set; } = true;
+
+ ///
+ /// HTTP protocol version to use for requests. HTTP/1.1 is the default and most compatible option.
+ ///
+ /// Http11
+ [DefaultValue(HttpVersion.Http11)]
+ public HttpVersion HttpProtocolVersion { get; set; } = HttpVersion.Http11;
+
+ ///
+ /// SSL/TLS protocol version to use for secure connections. Default lets the OS decide, which matches previous behavior.
+ /// Use Tls12 or Tls12And13 if the server requires a specific version.
+ ///
+ /// Default
+ [DefaultValue(SslVersion.Default)]
+ public SslVersion SslProtocolVersion { get; set; } = SslVersion.Default;
}
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/SslVersion.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/SslVersion.cs
new file mode 100644
index 0000000..687a87e
--- /dev/null
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Definitions/SslVersion.cs
@@ -0,0 +1,25 @@
+namespace Frends.HTTP.RequestBytes.Definitions
+{
+ ///
+ /// SSL/TLS protocol version used for secure connections.
+ ///
+ public enum SslVersion
+ {
+ ///
+ /// OS decides the protocol version.
+ ///
+ Default,
+ ///
+ /// TLS 1.2 only.
+ ///
+ Tls12,
+ ///
+ /// TLS 1.3 only.
+ ///
+ Tls13,
+ ///
+ /// TLS 1.2 and TLS 1.3 - use when server compatibility is uncertain.
+ ///
+ Tls12And13
+ }
+}
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Extensions.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Extensions.cs
index 41b5f94..2a79c1d 100644
--- a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Extensions.cs
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Extensions.cs
@@ -5,6 +5,7 @@
using System.Net.Http;
using System.Net;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
using System.Text.RegularExpressions;
using Frends.HTTP.RequestBytes.Definitions;
@@ -43,10 +44,37 @@ internal static void SetHandlerSettingsBasedOnOptions(this HttpClientHandler han
handler.AllowAutoRedirect = options.FollowRedirects;
handler.UseCookies = options.AutomaticCookieHandling;
+ if (options.UseProxy && !string.IsNullOrWhiteSpace(options.ProxyUrl))
+ {
+ handler.Proxy = new WebProxy(options.ProxyUrl);
+ handler.UseProxy = true;
+
+ var hasUsername = !string.IsNullOrWhiteSpace(options.ProxyUsername);
+ var hasPassword = !string.IsNullOrWhiteSpace(options.ProxyPassword);
+
+ if (hasUsername != hasPassword)
+ {
+ throw new ArgumentException("Both ProxyUsername and ProxyPassword must be provided together or left empty.");
+ }
+
+ if (hasUsername)
+ {
+ handler.Proxy.Credentials = new NetworkCredential(options.ProxyUsername, options.ProxyPassword);
+ }
+ }
+
if (options.AllowInvalidCertificate)
{
handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;
}
+
+ handler.SslProtocols = options.SslProtocolVersion switch
+ {
+ SslVersion.Tls12 => SslProtocols.Tls12,
+ SslVersion.Tls13 => SslProtocols.Tls13,
+ SslVersion.Tls12And13 => SslProtocols.Tls12 | SslProtocols.Tls13,
+ _ => SslProtocols.None
+ };
}
internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient httpClient, Options options)
@@ -55,6 +83,18 @@ internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient http
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json");
httpClient.Timeout = TimeSpan.FromSeconds(Convert.ToDouble(options.ConnectionTimeoutSeconds));
+
+ httpClient.DefaultRequestVersion = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => System.Net.HttpVersion.Version20,
+ _ => System.Net.HttpVersion.Version11
+ };
+
+ httpClient.DefaultVersionPolicy = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => HttpVersionPolicy.RequestVersionExact,
+ _ => HttpVersionPolicy.RequestVersionOrLower
+ };
}
private static X509Certificate[] GetCertificates(Options options)
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.csproj b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.csproj
index 94040f2..be1defb 100644
--- a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.csproj
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes.csproj
@@ -2,7 +2,7 @@
net6.0
- 1.4.0
+ 1.5.0
Frends
Frends
Frends
diff --git a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/RequestBytes.cs b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/RequestBytes.cs
index 0fbc4fa..77f76bf 100644
--- a/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/RequestBytes.cs
+++ b/Frends.HTTP.RequestBytes/Frends.HTTP.RequestBytes/RequestBytes.cs
@@ -141,7 +141,9 @@ private static string GetHttpClientCacheKey(Options options)
+ $":{options.ClientCertificateFilePath}:{options.ClientCertificateInBase64}:{options.ClientCertificateKeyPhrase}"
+ $":{options.CertificateThumbprint}:{options.LoadEntireChainForCertificate}:{options.ConnectionTimeoutSeconds}"
+ $":{options.FollowRedirects}:{options.AllowInvalidCertificate}:{options.AllowInvalidResponseContentTypeCharSet}"
- + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}";
+ + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}"
+ + $":{options.SslProtocolVersion}:{options.HttpProtocolVersion}"
+ + $":{options.UseProxy}:{options.ProxyUrl}:{options.ProxyUsername}:{options.ProxyPassword}";
}
private static async Task GetHttpRequestResponseAsync(
diff --git a/Frends.HTTP.SendAndReceiveBytes/CHANGELOG.md b/Frends.HTTP.SendAndReceiveBytes/CHANGELOG.md
index 13c581f..d3286d0 100644
--- a/Frends.HTTP.SendAndReceiveBytes/CHANGELOG.md
+++ b/Frends.HTTP.SendAndReceiveBytes/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.5.0] - 2026-05-13
+### Added
+- Added UseProxy, ProxyUrl, ProxyUsername, ProxyPassword, HttpProtocolVersion, and SslProtocolVersion options with proxy handler and protocol configuration support.
+
## [1.4.0] - 2026-03-02
### Added
- Added CertificateStoreLocation option to allow selection between CurrentUser and LocalMachine certificate stores when using certificate authentication.
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.Tests/UnitTests.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.Tests/UnitTests.cs
index 082f9e2..2da0630 100644
--- a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.Tests/UnitTests.cs
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.Tests/UnitTests.cs
@@ -7,6 +7,7 @@
using RichardSzalay.MockHttp;
using Assert = NUnit.Framework.Assert;
using Frends.HTTP.SendAndReceiveBytes.Definitions;
+using Definitions = Frends.HTTP.SendAndReceiveBytes.Definitions;
using System.Collections.Generic;
using System.Text;
using Method = Frends.HTTP.SendAndReceiveBytes.Definitions.Method;
@@ -335,6 +336,55 @@ public void CorrectStoreSearched(CertificateStoreLocation storeLocation, string
Assert.That(ex.Message.Contains(
$"Certificate with thumbprint: 'INVALIDTHUMBPRINT' not found in {storeLocationText} cert store."));
}
+
+ [TestMethod]
+ public void HandlerShouldUseConfiguredProxy()
+ {
+ var handler = new HttpClientHandler();
+ var options = new Options
+ {
+ UseProxy = true,
+ ProxyUrl = "http://proxy.example.com:8080",
+ ProxyUsername = "proxy-user",
+ ProxyPassword = "proxy-password"
+ };
+
+ handler.SetHandlerSettingsBasedOnOptions(options);
+
+ var proxy = handler.Proxy as WebProxy;
+ Assert.That(proxy, Is.Not.Null);
+ ClassicAssert.IsTrue(handler.UseProxy);
+ ClassicAssert.AreEqual(new Uri("http://proxy.example.com:8080/"), proxy.Address);
+ var credentials = proxy.Credentials.GetCredential(proxy.Address, string.Empty);
+ ClassicAssert.AreEqual("proxy-user", credentials.UserName);
+ ClassicAssert.AreEqual("proxy-password", credentials.Password);
+ }
+
+ [TestMethod]
+ public async Task RequestShouldSetTls12And13WhenConfigured()
+ {
+ HTTP.ClientFactory = new HttpClientFactory();
+
+ var input = new Input
+ {
+ Method = Method.POST,
+ Url = "https://httpbin.org/anything",
+ Headers = Array.Empty(),
+ ContentBytes = Encoding.UTF8.GetBytes("test")
+ };
+
+ var options = new Options
+ {
+ ConnectionTimeoutSeconds = 60,
+ SslProtocolVersion = SslVersion.Tls12And13,
+ HttpProtocolVersion = Definitions.HttpVersion.Http20
+ };
+
+ var result = await HTTP.SendAndReceiveBytes(input, options, CancellationToken.None);
+
+ ClassicAssert.AreEqual(200, result.StatusCode);
+ }
+
}
public class MockHttpClientFactory : IHttpClientFactory
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/HttpVersion.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/HttpVersion.cs
new file mode 100644
index 0000000..135171d
--- /dev/null
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/HttpVersion.cs
@@ -0,0 +1,18 @@
+namespace Frends.HTTP.SendAndReceiveBytes.Definitions
+{
+ ///
+ /// HTTP protocol version used for requests.
+ ///
+ public enum HttpVersion
+ {
+ ///
+ /// HTTP/1.1 - default, widely supported.
+ ///
+ Http11,
+ ///
+ /// HTTP/2 - multiplexed, requires server support.
+ /// If the server does not support HTTP/2 via ALPN, the request will fail with an exception instead of falling back to HTTP/1.1.
+ ///
+ Http20
+ }
+}
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/Options.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/Options.cs
index f5cd482..d223863 100644
--- a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/Options.cs
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/Options.cs
@@ -39,6 +39,37 @@ public class Options
[UIHint(nameof(Authentication), "", Authentication.OAuth)]
public string Token { get; set; }
+ ///
+ /// Whether to route requests through a proxy server. When set to true, the ProxyUrl must be provided.
+ ///
+ /// false
+ [DefaultValue(false)]
+ public bool UseProxy { get; set; } = false;
+
+ ///
+ /// Proxy URL used for the request.
+ ///
+ /// http://proxy.example.com:8080
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUrl { get; set; }
+
+ ///
+ /// Username for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Username
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUsername { get; set; }
+
+ ///
+ /// Password for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Password123
+ [PasswordPropertyText]
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyPassword { get; set; }
+
///
/// Specifies where the Client Certificate should be loaded from.
///
@@ -138,4 +169,19 @@ public class Options
/// true
[DefaultValue(true)]
public bool AutomaticCookieHandling { get; set; } = true;
+
+ ///
+ /// HTTP protocol version to use for requests. HTTP/1.1 is the default and most compatible option.
+ ///
+ /// Http11
+ [DefaultValue(HttpVersion.Http11)]
+ public HttpVersion HttpProtocolVersion { get; set; } = HttpVersion.Http11;
+
+ ///
+ /// SSL/TLS protocol version to use for secure connections. Default lets the OS decide, which matches previous behavior.
+ /// Use Tls12 or Tls12And13 if the server requires a specific version.
+ ///
+ /// Default
+ [DefaultValue(SslVersion.Default)]
+ public SslVersion SslProtocolVersion { get; set; } = SslVersion.Default;
}
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/SslVersion.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/SslVersion.cs
new file mode 100644
index 0000000..2c9b944
--- /dev/null
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Definitions/SslVersion.cs
@@ -0,0 +1,25 @@
+namespace Frends.HTTP.SendAndReceiveBytes.Definitions
+{
+ ///
+ /// SSL/TLS protocol version used for secure connections.
+ ///
+ public enum SslVersion
+ {
+ ///
+ /// OS decides the protocol version.
+ ///
+ Default,
+ ///
+ /// TLS 1.2 only.
+ ///
+ Tls12,
+ ///
+ /// TLS 1.3 only.
+ ///
+ Tls13,
+ ///
+ /// TLS 1.2 and TLS 1.3 - use when server compatibility is uncertain.
+ ///
+ Tls12And13
+ }
+}
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Extensions.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Extensions.cs
index fdd3fe5..c8dfda6 100644
--- a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Extensions.cs
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Extensions.cs
@@ -4,6 +4,7 @@
using System.Net.Http;
using System.Net;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
using System.Text.RegularExpressions;
using System.Diagnostics.CodeAnalysis;
using Frends.HTTP.SendAndReceiveBytes.Definitions;
@@ -41,10 +42,37 @@ internal static void SetHandlerSettingsBasedOnOptions(this HttpClientHandler han
handler.AllowAutoRedirect = options.FollowRedirects;
handler.UseCookies = options.AutomaticCookieHandling;
+ if (options.UseProxy && !string.IsNullOrWhiteSpace(options.ProxyUrl))
+ {
+ handler.Proxy = new WebProxy(options.ProxyUrl);
+ handler.UseProxy = true;
+
+ var hasUsername = !string.IsNullOrWhiteSpace(options.ProxyUsername);
+ var hasPassword = !string.IsNullOrWhiteSpace(options.ProxyPassword);
+
+ if (hasUsername != hasPassword)
+ {
+ throw new ArgumentException("Both ProxyUsername and ProxyPassword must be provided together or left empty.");
+ }
+
+ if (hasUsername)
+ {
+ handler.Proxy.Credentials = new NetworkCredential(options.ProxyUsername, options.ProxyPassword);
+ }
+ }
+
if (options.AllowInvalidCertificate)
{
handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;
}
+
+ handler.SslProtocols = options.SslProtocolVersion switch
+ {
+ SslVersion.Tls12 => SslProtocols.Tls12,
+ SslVersion.Tls13 => SslProtocols.Tls13,
+ SslVersion.Tls12And13 => SslProtocols.Tls12 | SslProtocols.Tls13,
+ _ => SslProtocols.None
+ };
}
internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient httpClient, Options options)
@@ -53,6 +81,18 @@ internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient http
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json");
httpClient.Timeout = TimeSpan.FromSeconds(Convert.ToDouble(options.ConnectionTimeoutSeconds));
+
+ httpClient.DefaultRequestVersion = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => System.Net.HttpVersion.Version20,
+ _ => System.Net.HttpVersion.Version11
+ };
+
+ httpClient.DefaultVersionPolicy = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => HttpVersionPolicy.RequestVersionExact,
+ _ => HttpVersionPolicy.RequestVersionOrLower
+ };
}
private static X509Certificate[] GetCertificates(Options options)
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.csproj b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.csproj
index faf664b..7d53500 100644
--- a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.csproj
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes.csproj
@@ -2,7 +2,7 @@
net6.0
- 1.4.0
+ 1.5.0
Frends
Frends
Frends
diff --git a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/SendAndReceiveBytes.cs b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/SendAndReceiveBytes.cs
index 8017887..e6a4f8e 100644
--- a/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/SendAndReceiveBytes.cs
+++ b/Frends.HTTP.SendAndReceiveBytes/Frends.HTTP.SendAndReceiveBytes/SendAndReceiveBytes.cs
@@ -121,7 +121,9 @@ private static string GetHttpClientCacheKey(Options options)
+ $":{options.ClientCertificateFilePath}:{options.ClientCertificateInBase64}:{options.ClientCertificateKeyPhrase}"
+ $":{options.CertificateThumbprint}:{options.LoadEntireChainForCertificate}:{options.ConnectionTimeoutSeconds}"
+ $":{options.FollowRedirects}:{options.AllowInvalidCertificate}:{options.AllowInvalidResponseContentTypeCharSet}"
- + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}";
+ + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}"
+ + $":{options.SslProtocolVersion}:{options.HttpProtocolVersion}"
+ + $":{options.UseProxy}:{options.ProxyUrl}:{options.ProxyUsername}:{options.ProxyPassword}";
}
private static async Task GetHttpRequestResponseAsync(
diff --git a/Frends.HTTP.SendBytes/CHANGELOG.md b/Frends.HTTP.SendBytes/CHANGELOG.md
index 15b2e3f..900b40d 100644
--- a/Frends.HTTP.SendBytes/CHANGELOG.md
+++ b/Frends.HTTP.SendBytes/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## [1.6.0] - 2026-05-13
+### Added
+- Added UseProxy, ProxyUrl, ProxyUsername, ProxyPassword, HttpProtocolVersion, and SslProtocolVersion options with proxy handler and protocol configuration support.
+
## [1.5.0] - 2026-03-02
### Added
- Added CertificateStoreLocation option to allow selection between CurrentUser and LocalMachine certificate stores when using certificate authentication.
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.Tests/UnitTests.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.Tests/UnitTests.cs
index 2955928..ea71a8a 100644
--- a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.Tests/UnitTests.cs
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.Tests/UnitTests.cs
@@ -7,6 +7,7 @@
using RichardSzalay.MockHttp;
using Assert = NUnit.Framework.Assert;
using Frends.HTTP.SendBytes.Definitions;
+using Definitions = Frends.HTTP.SendBytes.Definitions;
using System.Collections.Generic;
using System.Text;
using Method = Frends.HTTP.SendBytes.Definitions.Method;
@@ -335,6 +336,55 @@ public async Task PatchShouldComeThrough()
_mockHttpMessageHandler.VerifyNoOutstandingExpectation();
ClassicAssert.AreEqual("foo åäö", result.Body);
}
+
+ [TestMethod]
+ public void HandlerShouldUseConfiguredProxy()
+ {
+ var handler = new HttpClientHandler();
+ var options = new Options
+ {
+ UseProxy = true,
+ ProxyUrl = "http://proxy.example.com:8080",
+ ProxyUsername = "proxy-user",
+ ProxyPassword = "proxy-password"
+ };
+
+ handler.SetHandlerSettingsBasedOnOptions(options);
+
+ var proxy = handler.Proxy as WebProxy;
+ Assert.That(proxy, Is.Not.Null);
+ ClassicAssert.IsTrue(handler.UseProxy);
+ ClassicAssert.AreEqual(new Uri("http://proxy.example.com:8080/"), proxy.Address);
+ var credentials = proxy.Credentials.GetCredential(proxy.Address, string.Empty);
+ ClassicAssert.AreEqual("proxy-user", credentials.UserName);
+ ClassicAssert.AreEqual("proxy-password", credentials.Password);
+ }
+
+ [TestMethod]
+ public async Task RequestShouldSetTls12And13WhenConfigured()
+ {
+ HTTP.ClientFactory = new HttpClientFactory();
+
+ var input = new Input
+ {
+ Method = Method.POST,
+ Url = "https://httpbin.org/anything",
+ Headers = Array.Empty(),
+ ContentBytes = Encoding.UTF8.GetBytes("test")
+ };
+
+ var options = new Options
+ {
+ ConnectionTimeoutSeconds = 60,
+ SslProtocolVersion = SslVersion.Tls12And13,
+ HttpProtocolVersion = Definitions.HttpVersion.Http20
+ };
+
+ var result = await HTTP.SendBytes(input, options, CancellationToken.None);
+
+ ClassicAssert.AreEqual(200, result.StatusCode);
+ }
+
}
public class MockHttpClientFactory : IHttpClientFactory
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/HttpVersion.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/HttpVersion.cs
new file mode 100644
index 0000000..989daf2
--- /dev/null
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/HttpVersion.cs
@@ -0,0 +1,18 @@
+namespace Frends.HTTP.SendBytes.Definitions
+{
+ ///
+ /// HTTP protocol version used for requests.
+ ///
+ public enum HttpVersion
+ {
+ ///
+ /// HTTP/1.1 - default, widely supported.
+ ///
+ Http11,
+ ///
+ /// HTTP/2 - multiplexed, requires server support.
+ /// If the server does not support HTTP/2 via ALPN, the request will fail with an exception instead of falling back to HTTP/1.1.
+ ///
+ Http20
+ }
+}
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/Options.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/Options.cs
index 9479810..921e26a 100644
--- a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/Options.cs
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/Options.cs
@@ -37,6 +37,37 @@ public class Options
[UIHint(nameof(Authentication), "", Authentication.OAuth)]
public string Token { get; set; }
+ ///
+ /// Whether to route requests through a proxy server. When set to true, the ProxyUrl must be provided.
+ ///
+ /// false
+ [DefaultValue(false)]
+ public bool UseProxy { get; set; } = false;
+
+ ///
+ /// Proxy URL used for the request.
+ ///
+ /// http://proxy.example.com:8080
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUrl { get; set; }
+
+ ///
+ /// Username for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Username
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyUsername { get; set; }
+
+ ///
+ /// Password for proxy authentication. Leave empty if proxy does not require authentication.
+ /// Both username and password must be provided together or left empty.
+ ///
+ /// Password123
+ [PasswordPropertyText]
+ [UIHint(nameof(UseProxy), "", true)]
+ public string ProxyPassword { get; set; }
+
///
/// Specifies where the Client Certificate should be loaded from.
///
@@ -135,4 +166,19 @@ public class Options
/// true
[DefaultValue(true)]
public bool AutomaticCookieHandling { get; set; } = true;
+
+ ///
+ /// HTTP protocol version to use for requests. HTTP/1.1 is the default and most compatible option.
+ ///
+ /// Http11
+ [DefaultValue(HttpVersion.Http11)]
+ public HttpVersion HttpProtocolVersion { get; set; } = HttpVersion.Http11;
+
+ ///
+ /// SSL/TLS protocol version to use for secure connections. Default lets the OS decide, which matches previous behavior.
+ /// Use Tls12 or Tls12And13 if the server requires a specific version.
+ ///
+ /// Default
+ [DefaultValue(SslVersion.Default)]
+ public SslVersion SslProtocolVersion { get; set; } = SslVersion.Default;
}
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/SslVersion.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/SslVersion.cs
new file mode 100644
index 0000000..3bbdca3
--- /dev/null
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Definitions/SslVersion.cs
@@ -0,0 +1,25 @@
+namespace Frends.HTTP.SendBytes.Definitions
+{
+ ///
+ /// SSL/TLS protocol version used for secure connections.
+ ///
+ public enum SslVersion
+ {
+ ///
+ /// OS decides the protocol version.
+ ///
+ Default,
+ ///
+ /// TLS 1.2 only.
+ ///
+ Tls12,
+ ///
+ /// TLS 1.3 only.
+ ///
+ Tls13,
+ ///
+ /// TLS 1.2 and TLS 1.3 - use when server compatibility is uncertain.
+ ///
+ Tls12And13
+ }
+}
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Extensions.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Extensions.cs
index 93e6276..cac81c7 100644
--- a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Extensions.cs
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Extensions.cs
@@ -4,6 +4,7 @@
using System.Net.Http;
using System.Net;
using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
using System.Text.RegularExpressions;
using System.Diagnostics.CodeAnalysis;
using Frends.HTTP.SendBytes.Definitions;
@@ -41,10 +42,37 @@ internal static void SetHandlerSettingsBasedOnOptions(this HttpClientHandler han
handler.AllowAutoRedirect = options.FollowRedirects;
handler.UseCookies = options.AutomaticCookieHandling;
+ if (options.UseProxy && !string.IsNullOrWhiteSpace(options.ProxyUrl))
+ {
+ handler.Proxy = new WebProxy(options.ProxyUrl);
+ handler.UseProxy = true;
+
+ var hasUsername = !string.IsNullOrWhiteSpace(options.ProxyUsername);
+ var hasPassword = !string.IsNullOrWhiteSpace(options.ProxyPassword);
+
+ if (hasUsername != hasPassword)
+ {
+ throw new ArgumentException("Both ProxyUsername and ProxyPassword must be provided together or left empty.");
+ }
+
+ if (hasUsername)
+ {
+ handler.Proxy.Credentials = new NetworkCredential(options.ProxyUsername, options.ProxyPassword);
+ }
+ }
+
if (options.AllowInvalidCertificate)
{
handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;
}
+
+ handler.SslProtocols = options.SslProtocolVersion switch
+ {
+ SslVersion.Tls12 => SslProtocols.Tls12,
+ SslVersion.Tls13 => SslProtocols.Tls13,
+ SslVersion.Tls12And13 => SslProtocols.Tls12 | SslProtocols.Tls13,
+ _ => SslProtocols.None
+ };
}
internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient httpClient, Options options)
@@ -53,6 +81,18 @@ internal static void SetDefaultRequestHeadersBasedOnOptions(this HttpClient http
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-type", "application/json");
httpClient.Timeout = TimeSpan.FromSeconds(Convert.ToDouble(options.ConnectionTimeoutSeconds));
+
+ httpClient.DefaultRequestVersion = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => System.Net.HttpVersion.Version20,
+ _ => System.Net.HttpVersion.Version11
+ };
+
+ httpClient.DefaultVersionPolicy = options.HttpProtocolVersion switch
+ {
+ Definitions.HttpVersion.Http20 => HttpVersionPolicy.RequestVersionExact,
+ _ => HttpVersionPolicy.RequestVersionOrLower
+ };
}
private static X509Certificate[] GetCertificates(Options options)
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.csproj b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.csproj
index 52a6588..58ebd6b 100644
--- a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.csproj
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes.csproj
@@ -2,7 +2,7 @@
net6.0
- 1.5.0
+ 1.6.0
Frends
Frends
Frends
diff --git a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/SendBytes.cs b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/SendBytes.cs
index 317808c..ff5269d 100644
--- a/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/SendBytes.cs
+++ b/Frends.HTTP.SendBytes/Frends.HTTP.SendBytes/SendBytes.cs
@@ -120,7 +120,9 @@ private static string GetHttpClientCacheKey(Options options)
+ $":{options.ClientCertificateFilePath}:{options.ClientCertificateInBase64}:{options.ClientCertificateKeyPhrase}"
+ $":{options.CertificateThumbprint}:{options.LoadEntireChainForCertificate}:{options.ConnectionTimeoutSeconds}"
+ $":{options.FollowRedirects}:{options.AllowInvalidCertificate}:{options.AllowInvalidResponseContentTypeCharSet}"
- + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}";
+ + $":{options.ThrowExceptionOnErrorResponse}:{options.AutomaticCookieHandling}"
+ + $":{options.SslProtocolVersion}:{options.HttpProtocolVersion}"
+ + $":{options.UseProxy}:{options.ProxyUrl}:{options.ProxyUsername}:{options.ProxyPassword}";
}
private static async Task GetHttpRequestResponseAsync(
From 86933c89d650ca513d9b4a37991633dc46698017 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 13 May 2026 06:24:25 +0000
Subject: [PATCH 2/3] Fix DownloadFile test enum reference and finalize
implementation
Agent-Logs-Url: https://github.com/FrendsPlatform/Frends.HTTP/sessions/2c0a6a2a-230b-4d67-af17-87d3e474f51f
Co-authored-by: MichalFrends1 <167774394+MichalFrends1@users.noreply.github.com>
---
.../Frends.HTTP.DownloadFile.Tests/UnitTests.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
index f237c6c..82411ee 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
@@ -1,4 +1,5 @@
-using Frends.HTTP.DownloadFile.Definitions;
+using Frends.HTTP.DownloadFile.Definitions;
+using Definitions = Frends.HTTP.DownloadFile.Definitions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
@@ -585,7 +586,7 @@ public async Task RequestShouldSetTls12And13WhenConfigured()
{
ConnectionTimeoutSeconds = 60,
SslProtocolVersion = SslVersion.Tls12And13,
- HttpProtocolVersion = HttpVersion.Http20,
+ HttpProtocolVersion = Definitions.HttpVersion.Http20,
Overwrite = true
};
From 4f4160593572a316141b7c009babe3416a85d10d Mon Sep 17 00:00:00 2001
From: MichalFrends1
Date: Wed, 13 May 2026 09:01:34 +0200
Subject: [PATCH 3/3] fix test
---
.../Frends.HTTP.DownloadFile.Tests/UnitTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
index 82411ee..3f0b16c 100644
--- a/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
+++ b/Frends.HTTP.DownloadFile/Frends.HTTP.DownloadFile.Tests/UnitTests.cs
@@ -586,7 +586,7 @@ public async Task RequestShouldSetTls12And13WhenConfigured()
{
ConnectionTimeoutSeconds = 60,
SslProtocolVersion = SslVersion.Tls12And13,
- HttpProtocolVersion = Definitions.HttpVersion.Http20,
+ HttpProtocolVersion = Definitions.HttpVersion.Http11,
Overwrite = true
};