diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 680de6066..015aa518f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-sonarscanner": { - "version": "11.0.0", + "version": "11.2.1", "commands": [ "dotnet-sonarscanner" ] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 111af2d01..58395fdea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -36,7 +36,7 @@ jobs: sudo apt-get install -y libmsquic - name: Download .NET SDK - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0 @@ -110,24 +110,33 @@ jobs: - os: name: 🍎 - runs-on: macos-15-intel + runs-on: macos-26-intel arch: x64 runtime: osx-x64 platform: macos/amd64 - os: name: 🍎 - runs-on: macos-15 + runs-on: macos-26 arch: arm64 runtime: osx-arm64 platform: macos/arm64 steps: - name: Checkout source - uses: actions/checkout@v4 + uses: actions/checkout@v6 + + - name: Harden macOS x64 environment + + if: matrix.os.runs-on == 'macos-26-intel' + run: | + sudo rm -rf /opt/homebrew || true + export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + export DYLD_LIBRARY_PATH="" + export DYLD_FALLBACK_LIBRARY_PATH="" - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 if: matrix.os.runs-on != 'linux-arm32' with: dotnet-version: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c07984002..199c3c423 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,17 +20,17 @@ jobs: steps: - name: Checkout source - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0 - name: Setup NuGet CLI - uses: NuGet/setup-nuget@v2 + uses: NuGet/setup-nuget@v4 - name: Restore dependencies run: dotnet restore diff --git a/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj b/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj index d6f0cac36..13482f227 100644 --- a/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj +++ b/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj @@ -35,7 +35,7 @@ - + diff --git a/Directory.Build.props b/Directory.Build.props index 9c27b106d..f53dfb2a4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ - + diff --git a/Engine/Internal/GenHTTP.Engine.Internal.csproj b/Engine/Internal/GenHTTP.Engine.Internal.csproj index bdb1a4719..98bb93d50 100644 --- a/Engine/Internal/GenHTTP.Engine.Internal.csproj +++ b/Engine/Internal/GenHTTP.Engine.Internal.csproj @@ -19,9 +19,9 @@ - + - + diff --git a/Modules/Archives/GenHTTP.Modules.Archives.csproj b/Modules/Archives/GenHTTP.Modules.Archives.csproj index 08b9ed305..95232adb1 100644 --- a/Modules/Archives/GenHTTP.Modules.Archives.csproj +++ b/Modules/Archives/GenHTTP.Modules.Archives.csproj @@ -14,7 +14,7 @@ - + diff --git a/Modules/Caching/GenHTTP.Modules.Caching.csproj b/Modules/Caching/GenHTTP.Modules.Caching.csproj index 1e2a159f2..5ca1f3436 100644 --- a/Modules/Caching/GenHTTP.Modules.Caching.csproj +++ b/Modules/Caching/GenHTTP.Modules.Caching.csproj @@ -14,7 +14,7 @@ - + diff --git a/Modules/Compression/Providers/CompressionConcern.cs b/Modules/Compression/Providers/CompressionConcern.cs index 963f2e919..63ec00850 100644 --- a/Modules/Compression/Providers/CompressionConcern.cs +++ b/Modules/Compression/Providers/CompressionConcern.cs @@ -1,4 +1,5 @@ -using System.IO.Compression; +using System.Buffers; +using System.IO.Compression; using GenHTTP.Api.Content; using GenHTTP.Api.Content.IO; @@ -131,31 +132,39 @@ private bool ShouldCompressBySize(IResponse response) return MinimumSize is null || contentLength is null || contentLength >= MinimumSize; } + + private static readonly SearchValues Delimiters = SearchValues.Create([',', ';']); private static HashSet ParseSupported(ReadOnlySpan acceptHeader) { var result = new HashSet(StringComparer.OrdinalIgnoreCase); - var start = 0; - - while (start <= acceptHeader.Length) + while (!acceptHeader.IsEmpty) { - var comma = acceptHeader[start..].IndexOf(','); - var end = comma >= 0 ? start + comma : acceptHeader.Length; - - var part = acceptHeader.Slice(start, end - start).Trim(); + var delimIdx = acceptHeader.IndexOfAny(Delimiters); - if (!part.IsEmpty) + ReadOnlySpan token; + if (delimIdx < 0) { - result.Add(part.ToString()); + token = acceptHeader.Trim(); + acceptHeader = default; } - - if (comma < 0) + else if (acceptHeader[delimIdx] == ',') + { + token = acceptHeader[..delimIdx].Trim(); + acceptHeader = acceptHeader[(delimIdx + 1)..]; + } + else { - break; + token = acceptHeader[..delimIdx].TrimEnd(); + var commaIdx = acceptHeader[delimIdx..].IndexOf(','); + acceptHeader = commaIdx >= 0 + ? acceptHeader[(delimIdx + commaIdx + 1)..] + : default; } - start = end + 1; + if (!token.IsEmpty) + result.Add(token.ToString()); } return result; diff --git a/Modules/Conversion/GenHTTP.Modules.Conversion.csproj b/Modules/Conversion/GenHTTP.Modules.Conversion.csproj index d85fee0eb..e4e02ad89 100644 --- a/Modules/Conversion/GenHTTP.Modules.Conversion.csproj +++ b/Modules/Conversion/GenHTTP.Modules.Conversion.csproj @@ -14,7 +14,7 @@ - + diff --git a/Modules/DependencyInjection/GenHTTP.Modules.DependencyInjection.csproj b/Modules/DependencyInjection/GenHTTP.Modules.DependencyInjection.csproj index f36480a5e..695a2ea30 100644 --- a/Modules/DependencyInjection/GenHTTP.Modules.DependencyInjection.csproj +++ b/Modules/DependencyInjection/GenHTTP.Modules.DependencyInjection.csproj @@ -29,7 +29,7 @@ - + diff --git a/Modules/OpenApi/GenHTTP.Modules.OpenApi.csproj b/Modules/OpenApi/GenHTTP.Modules.OpenApi.csproj index 03e73e535..f482e33eb 100644 --- a/Modules/OpenApi/GenHTTP.Modules.OpenApi.csproj +++ b/Modules/OpenApi/GenHTTP.Modules.OpenApi.csproj @@ -16,11 +16,11 @@ - + - + - + diff --git a/Modules/ReverseProxy/GenHTTP.Modules.ReverseProxy.csproj b/Modules/ReverseProxy/GenHTTP.Modules.ReverseProxy.csproj index acfdab81b..96995d620 100644 --- a/Modules/ReverseProxy/GenHTTP.Modules.ReverseProxy.csproj +++ b/Modules/ReverseProxy/GenHTTP.Modules.ReverseProxy.csproj @@ -18,7 +18,7 @@ - + diff --git a/Modules/Websockets/GenHTTP.Modules.Websockets.csproj b/Modules/Websockets/GenHTTP.Modules.Websockets.csproj index 84d40cf24..da8868ef2 100644 --- a/Modules/Websockets/GenHTTP.Modules.Websockets.csproj +++ b/Modules/Websockets/GenHTTP.Modules.Websockets.csproj @@ -15,7 +15,7 @@ - + diff --git a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj index 2fdcdb2f6..23dba0324 100644 --- a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj +++ b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj @@ -43,16 +43,16 @@ - + - - + + - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Testing/Acceptance/Modules/Compression/CompressionTests.cs b/Testing/Acceptance/Modules/Compression/CompressionTests.cs index 12e40c5a9..097e06ff1 100644 --- a/Testing/Acceptance/Modules/Compression/CompressionTests.cs +++ b/Testing/Acceptance/Modules/Compression/CompressionTests.cs @@ -35,7 +35,7 @@ public async Task TestCompression(TestEngine engine) Assert.AreEqual("zstd", response.Content.Headers.ContentEncoding.First()); } - + /// /// As a browser, I expect only supported compression algorithms to be used /// to generate my response. @@ -378,6 +378,20 @@ public async Task TestCompressionThreshold_Disabled_AllContentCompressed(TestEng await runner.DisposeAsync(); } + + [TestMethod] + [MultiEngineTest] + public async Task TestWeights(TestEngine engine) + { + await using var runner = await TestHost.RunAsync(CreateLargeContentHandler().Build(), engine: engine); + + var request = runner.GetRequest(); + request.Headers.Add("Accept-Encoding", "br;q=1, gzip;q=0.8"); + + using var response = await runner.GetResponseAsync(request); + + Assert.AreEqual("br", response.Content.Headers.ContentEncoding.First()); + } /// /// Helper class to create content with unknown length (null)