diff --git a/DevBase.Net/Core/BaseRequest.cs b/DevBase.Net/Core/BaseRequest.cs index 3f0c66b..ed154fc 100644 --- a/DevBase.Net/Core/BaseRequest.cs +++ b/DevBase.Net/Core/BaseRequest.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Text; using DevBase.Net.Configuration; using DevBase.Net.Data.Body; @@ -20,6 +21,8 @@ public abstract class BaseRequest : IDisposable, IAsyncDisposable protected bool _validateCertificates = true; protected bool _followRedirects = true; protected int _maxRedirects = 50; + protected Version _httpVersion = new Version(3, 0); + protected HttpVersionPolicy _httpVersionPolicy = HttpVersionPolicy.RequestVersionOrLower; protected bool _isBuilt; protected bool _disposed; @@ -66,6 +69,16 @@ public abstract class BaseRequest : IDisposable, IAsyncDisposable /// public int MaxRedirects => this._maxRedirects; + /// + /// Gets the HTTP version for this request. + /// + public Version HttpVersion => this._httpVersion; + + /// + /// Gets the HTTP version policy for this request. + /// + public HttpVersionPolicy HttpVersionPolicy => this._httpVersionPolicy; + /// /// Gets whether the request has been built. /// diff --git a/DevBase.Net/Core/RequestConfiguration.cs b/DevBase.Net/Core/RequestConfiguration.cs index 70af202..71728ca 100644 --- a/DevBase.Net/Core/RequestConfiguration.cs +++ b/DevBase.Net/Core/RequestConfiguration.cs @@ -274,4 +274,36 @@ public Request WithResponseInterceptor(IResponseInterceptor interceptor) this._responseInterceptors.Add(interceptor); return this; } + + /// + /// Sets the HTTP version for the request. + /// + /// The HTTP version to use. + /// The version policy. Default is RequestVersionOrLower. + /// The request instance for method chaining. + public Request WithHttpVersion(Version version, HttpVersionPolicy policy = HttpVersionPolicy.RequestVersionOrLower) + { + ArgumentNullException.ThrowIfNull(version); + this._httpVersion = version; + this._httpVersionPolicy = policy; + return this; + } + + /// + /// Configures the request to use HTTP/1.1. + /// + /// The request instance for method chaining. + public Request AsHttp11() => this.WithHttpVersion(System.Net.HttpVersion.Version11); + + /// + /// Configures the request to use HTTP/2. + /// + /// The request instance for method chaining. + public Request AsHttp2() => this.WithHttpVersion(System.Net.HttpVersion.Version20); + + /// + /// Configures the request to use HTTP/3. + /// + /// The request instance for method chaining. + public Request AsHttp3() => this.WithHttpVersion(System.Net.HttpVersion.Version30); } diff --git a/DevBase.Net/Core/RequestHttp.cs b/DevBase.Net/Core/RequestHttp.cs index b36f37e..7b76621 100644 --- a/DevBase.Net/Core/RequestHttp.cs +++ b/DevBase.Net/Core/RequestHttp.cs @@ -132,8 +132,8 @@ public override async Task SendAsync(CancellationToken cancellationTok metricsBuilder.SetProxy(this._proxy != null, this._proxy?.Key); using HttpRequestMessage httpRequest = this.ToHttpRequestMessage(); - httpRequest.Version = new Version(3, 0); - httpRequest.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower; + httpRequest.Version = this._httpVersion; + httpRequest.VersionPolicy = this._httpVersionPolicy; metricsBuilder.MarkConnectStart(); HttpResponseMessage httpResponse = await client.SendAsync(httpRequest, @@ -284,6 +284,8 @@ private string BuildClientKey() sb.Append(this._validateCertificates); sb.Append("|redirect:"); sb.Append(this._followRedirects); + sb.Append("|httpver:"); + sb.Append(this._httpVersion); return sb.ToStringAndRelease(); } diff --git a/DevBase.Net/DevBase.Net.csproj b/DevBase.Net/DevBase.Net.csproj index d3da0fe..5d8a7e1 100644 --- a/DevBase.Net/DevBase.Net.csproj +++ b/DevBase.Net/DevBase.Net.csproj @@ -15,7 +15,7 @@ https://github.com/AlexanderDotH/DevBase.git https://github.com/AlexanderDotH/DevBase.git git - 1.3.2 + 1.4.0 MIT false http;client;requests;proxy;socks5;jwt;authentication;fluent-api;async;retry;rate-limiting;json;html-parsing diff --git a/DevBase.Test/DevBaseRequests/RequestTest.cs b/DevBase.Test/DevBaseRequests/RequestTest.cs index 1822ea0..8936e33 100644 --- a/DevBase.Test/DevBaseRequests/RequestTest.cs +++ b/DevBase.Test/DevBaseRequests/RequestTest.cs @@ -577,6 +577,89 @@ public async Task DisposeAsync_ClearsInterceptors() #endregion + #region HTTP Version Tests + + [Test] + public void WithHttpVersion_SetsHttpVersion() + { + var request = new Request("https://example.com") + .WithHttpVersion(HttpVersion.Version20); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version20)); + } + + [Test] + public void WithHttpVersion_WithPolicy_SetsBoth() + { + var request = new Request("https://example.com") + .WithHttpVersion(HttpVersion.Version20, HttpVersionPolicy.RequestVersionExact); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version20)); + Assert.That(request.HttpVersionPolicy, Is.EqualTo(HttpVersionPolicy.RequestVersionExact)); + } + + [Test] + public void AsHttp11_SetsHttpVersion11() + { + var request = new Request("https://example.com") + .AsHttp11(); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version11)); + } + + [Test] + public void AsHttp2_SetsHttpVersion20() + { + var request = new Request("https://example.com") + .AsHttp2(); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version20)); + } + + [Test] + public void AsHttp3_SetsHttpVersion30() + { + var request = new Request("https://example.com") + .AsHttp3(); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version30)); + } + + [Test] + public void DefaultHttpVersion_IsHttp3() + { + var request = new Request("https://example.com"); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version30)); + Assert.That(request.HttpVersionPolicy, Is.EqualTo(HttpVersionPolicy.RequestVersionOrLower)); + } + + [Test] + public void HttpVersion_CanBeSwitchedMultipleTimes() + { + var request = new Request("https://example.com") + .AsHttp3() + .AsHttp2() + .AsHttp11(); + + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version11)); + } + + [Test] + public void FluentApi_WithHttpVersion_ChainsCorrectly() + { + var request = new Request("https://example.com") + .AsPost() + .AsHttp2() + .WithHeader("X-Test", "Value") + .Build(); + + Assert.That(request.Method, Is.EqualTo(HttpMethod.Post)); + Assert.That(request.HttpVersion, Is.EqualTo(HttpVersion.Version20)); + } + + #endregion + #region Fluent API Tests [Test] diff --git a/DevBaseLive/Program.cs b/DevBaseLive/Program.cs index 0d30afe..cea1978 100644 --- a/DevBaseLive/Program.cs +++ b/DevBaseLive/Program.cs @@ -15,13 +15,6 @@ namespace DevBaseLive; -/// -/// Represents a person record. -/// -/// The name of the person. -/// The age of the person. -record Person(string name, int age); - /// /// Entry point class for the DevBaseLive application. /// @@ -34,41 +27,38 @@ class Program /// Command line arguments. public static async Task Main(string[] args) { - Person p = new Person("alex", 1); - var l = new LoggerConfiguration() .WriteTo.Console() .MinimumLevel.Information() .CreateLogger(); - for (int i = 0; i < 20; i++) - { - Request request = new Request() - .AsGet() - .WithHostCheck(new HostCheckConfig()) - .UseBasicAuthentication("joe", "mama") - .WithRetryPolicy(new RetryPolicy() - { - MaxRetries = 2 - }) - .WithLogging(new LoggingConfig() - { - Logger = l - }) - .WithMultipleFiles( - ("file1", AFile.ReadFileToObject("C:\\Users\\alex\\Desktop\\zoom1.txt")), - ("file2", AFile.ReadFileToObject("C:\\Users\\alex\\Desktop\\zoom2.txt")) - ) + + + Request request = new Request() + .AsGet() + .WithHostCheck(new HostCheckConfig()) + .UseBasicAuthentication("joe", "mama") + .WithRetryPolicy(new RetryPolicy() + { + MaxRetries = 2 + }) + .WithLogging(new LoggingConfig() + { + Logger = l + }) + .WithMultipleFiles( + ("file1", AFile.ReadFileToObject("C:\\Users\\alex\\Desktop\\zoom1.txt")), + ("file2", AFile.ReadFileToObject("C:\\Users\\alex\\Desktop\\zoom2.txt")) + ) - .WithScrapingBypass(new ScrapingBypassConfig() - { - BrowserProfile = EnumBrowserProfile.Firefox - }).WithHeader("sec-fetch-mode", "yoemamam") - .WithUrl("https://webhook.site/bd100268-d633-43f5-b298-28ee17c97ccf"); - Response response = await request.SendAsync(); + .WithScrapingBypass(new ScrapingBypassConfig() + { + BrowserProfile = EnumBrowserProfile.Firefox + }).WithHeader("sec-fetch-mode", "yoemamam") + .WithUrl("https://webhook.site/bd100268-d633-43f5-b298-28ee17c97ccf"); + Response response = await request.SendAsync(); - string data = await response.GetStringAsync(); - data.DumpConsole(); - } + string data = await response.GetStringAsync(); + data.DumpConsole(); } } \ No newline at end of file