diff --git a/cadente/Sisk.Cadente.CoreEngine/Sisk.Cadente.CoreEngine.csproj b/cadente/Sisk.Cadente.CoreEngine/Sisk.Cadente.CoreEngine.csproj
index 8ac9a0d..10500cf 100644
--- a/cadente/Sisk.Cadente.CoreEngine/Sisk.Cadente.CoreEngine.csproj
+++ b/cadente/Sisk.Cadente.CoreEngine/Sisk.Cadente.CoreEngine.csproj
@@ -2,7 +2,7 @@
- net9.0
+ net8.0;net9.0
Sisk.Cadente.CoreEngine
Debug;Release
diff --git a/cadente/Sisk.Cadente/Sisk.Cadente.csproj b/cadente/Sisk.Cadente/Sisk.Cadente.csproj
index 82459a8..f8ae95b 100644
--- a/cadente/Sisk.Cadente/Sisk.Cadente.csproj
+++ b/cadente/Sisk.Cadente/Sisk.Cadente.csproj
@@ -2,8 +2,9 @@
- net9.0
+ net8.0;net9.0
Sisk.Cadente
+ preview
Debug;Release
diff --git a/tests/Sisk.Core/Tests/LargePayloadTests.cs b/tests/Sisk.Core/Tests/LargePayloadTests.cs
new file mode 100644
index 0000000..d04f60f
--- /dev/null
+++ b/tests/Sisk.Core/Tests/LargePayloadTests.cs
@@ -0,0 +1,120 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using System.Net.Http;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Sisk.Cadente;
+using Sisk.Cadente.CoreEngine;
+using Sisk.Core.Http;
+using Sisk.Core.Http.Hosting;
+using Sisk.Core.Routing;
+
+namespace tests;
+
+[TestClass]
+public class LargePayloadTests
+{
+ private HttpServerHostContext? _server;
+ private int _port;
+
+ private static int GetRandomPort()
+ {
+ using var listener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
+ listener.Start();
+ return ((System.Net.IPEndPoint)listener.LocalEndpoint).Port;
+ }
+
+ private async Task CalculateHashAsync(Stream stream)
+ {
+ byte[] hashBytes = await SHA1.HashDataAsync(stream);
+ return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
+ }
+
+ [TestInitialize]
+ public void Setup()
+ {
+ _port = GetRandomPort();
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ _server?.Dispose();
+ }
+
+ private async Task RunPayloadTest(long size)
+ {
+ var router = new Router();
+ router.SetRoute(RouteMethod.Post, "/payload", async (HttpRequest req) =>
+ {
+ using var stream = req.GetRequestStream();
+ string hash = await CalculateHashAsync(stream);
+ return new HttpResponse(200) { Content = new StringContent(hash) };
+ });
+
+ var engine = new CadenteHttpServerEngine(host => {
+ // Configure large timeouts for the test
+ host.TimeoutManager.BodyDrainTimeout = TimeSpan.FromMinutes(10);
+ host.TimeoutManager.ClientReadTimeout = TimeSpan.FromMinutes(10);
+ host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromMinutes(10);
+ });
+
+ _server = HttpServer.CreateBuilder()
+ .UseListeningPort((ushort)_port)
+ .UseRouter(router)
+ .UseEngine(engine)
+ .UseConfiguration(config =>
+ {
+ config.AccessLogsStream = null;
+ config.ErrorsLogsStream = LogStream.ConsoleOutput;
+ })
+ .Build();
+
+ _server.Start(verbose: false, preventHault: false);
+
+ using var client = new HttpClient();
+ client.Timeout = TimeSpan.FromMinutes(10);
+
+ int seed = 12345;
+ using var payloadStream = new RandomStream(size, seed);
+ using var content = new StreamContent(payloadStream);
+ content.Headers.ContentLength = size;
+
+ // Calculate expected hash
+ using var calculationStream = new RandomStream(size, seed);
+ string expectedHash = await CalculateHashAsync(calculationStream);
+
+ var response = await client.PostAsync($"http://localhost:{_port}/payload", content);
+ response.EnsureSuccessStatusCode();
+
+ string serverHash = await response.Content.ReadAsStringAsync();
+ Assert.AreEqual(expectedHash, serverHash, $"Hash mismatch for payload size {size}");
+ }
+
+ [TestMethod]
+ public async Task TestPayload_10MB()
+ {
+ await RunPayloadTest(10 * 1024 * 1024);
+ }
+
+ [TestMethod]
+ public async Task TestPayload_100MB()
+ {
+ await RunPayloadTest(100 * 1024 * 1024);
+ }
+
+ [TestMethod]
+ public async Task TestPayload_500MB()
+ {
+ await RunPayloadTest(500 * 1024 * 1024);
+ }
+
+ [TestMethod]
+ [Timeout(600000)] // 10 minutes
+ public async Task TestPayload_1_5GB()
+ {
+ long size = (long)(1.5 * 1024 * 1024 * 1024);
+ await RunPayloadTest(size);
+ }
+}
diff --git a/tests/Sisk.Core/Tests/RandomStream.cs b/tests/Sisk.Core/Tests/RandomStream.cs
new file mode 100644
index 0000000..4b8ac64
--- /dev/null
+++ b/tests/Sisk.Core/Tests/RandomStream.cs
@@ -0,0 +1,49 @@
+using System;
+using System.IO;
+
+namespace tests;
+
+public class RandomStream : Stream
+{
+ private readonly long _length;
+ private long _position;
+ private readonly Random _random;
+
+ public RandomStream(long length, int seed)
+ {
+ _length = length;
+ _random = new Random(seed);
+ }
+
+ public override bool CanRead => true;
+ public override bool CanSeek => false;
+ public override bool CanWrite => false;
+ public override long Length => _length;
+ public override long Position
+ {
+ get => _position;
+ set => throw new NotSupportedException();
+ }
+
+ public override void Flush() { }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return Read(buffer.AsSpan(offset, count));
+ }
+
+ public override int Read(Span buffer)
+ {
+ long remaining = _length - _position;
+ if (remaining <= 0) return 0;
+
+ int toRead = (int)Math.Min(buffer.Length, remaining);
+ _random.NextBytes(buffer.Slice(0, toRead));
+ _position += toRead;
+ return toRead;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
+ public override void SetLength(long value) => throw new NotSupportedException();
+ public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
+}
diff --git a/tests/Sisk.Core/tests.csproj b/tests/Sisk.Core/tests.csproj
index 081a603..283d5f3 100644
--- a/tests/Sisk.Core/tests.csproj
+++ b/tests/Sisk.Core/tests.csproj
@@ -1,7 +1,7 @@
- net9.0
+ net8.0
latest
enable
enable
@@ -15,6 +15,7 @@
+