diff --git a/Benchmarks/Infrastructure/BenchmarkContext.cs b/Benchmarks/Infrastructure/BenchmarkContext.cs index 1651292c..3dc8c105 100644 --- a/Benchmarks/Infrastructure/BenchmarkContext.cs +++ b/Benchmarks/Infrastructure/BenchmarkContext.cs @@ -29,7 +29,7 @@ public BenchmarkContext(string request, IHandler handler) public async ValueTask Execute() { - if (!UltraHardenedParser.TryExtractFullHeaderROM(ref _request, _context.Request.Source, Limits, out _)) + if (!UltraHardenedParser.TryParseRequestHeader(ref _request, _context.Request.Source, Limits, out _)) { throw new ArgumentException("Given request could not be parsed."); } diff --git a/Engine/Internal/GenHTTP.Engine.Internal.csproj b/Engine/Internal/GenHTTP.Engine.Internal.csproj index 25609076..ab2376d4 100644 --- a/Engine/Internal/GenHTTP.Engine.Internal.csproj +++ b/Engine/Internal/GenHTTP.Engine.Internal.csproj @@ -18,7 +18,7 @@ - + diff --git a/Engine/Internal/Protocol/ClientHandler.cs b/Engine/Internal/Protocol/ClientHandler.cs index 65e02b64..baa5b7f9 100644 --- a/Engine/Internal/Protocol/ClientHandler.cs +++ b/Engine/Internal/Protocol/ClientHandler.cs @@ -185,12 +185,12 @@ private async ValueTask HandlePipeAsync(PipeReader reader) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryParseRequest(ref ReadOnlySequence buffer, BinaryRequest into) { - if (!UltraHardenedParser.TryExtractFullHeaderValidated(ref buffer, into, Limits, out var bytesRead)) + if (!UltraHardenedParser.TryParseRequestHeader(ref buffer, into, Limits, out var consumed)) { return false; } - buffer = buffer.Slice(bytesRead + 1); + buffer = buffer.Slice(consumed); return true; } diff --git a/Engine/Ioxide/GenHTTP.Engine.Ioxide.csproj b/Engine/Ioxide/GenHTTP.Engine.Ioxide.csproj index 1f848c8e..df36c082 100644 --- a/Engine/Ioxide/GenHTTP.Engine.Ioxide.csproj +++ b/Engine/Ioxide/GenHTTP.Engine.Ioxide.csproj @@ -22,8 +22,7 @@ - - + diff --git a/Engine/Ioxide/Protocol/Drivers/Tcp/Http1Driver.cs b/Engine/Ioxide/Protocol/Drivers/Tcp/Http1Driver.cs index 87b5597f..065cc6fc 100644 --- a/Engine/Ioxide/Protocol/Drivers/Tcp/Http1Driver.cs +++ b/Engine/Ioxide/Protocol/Drivers/Tcp/Http1Driver.cs @@ -10,7 +10,6 @@ using Glyph11; using Glyph11.Parser; using Glyph11.Parser.UltraHardened; -using Glyph11.Pico; using Glyph11.Protocol; using Microsoft.Extensions.Logging; @@ -28,11 +27,6 @@ internal static class Http1Driver { private static readonly ParserLimits Limits = ParserLimits.Default; - // GENHTTP_IOXIDE_PARSER=pico swaps the hardened managed parser for picohttpparser, which - // does no path/token/smuggling hardening. For benchmarking, not for untrusted traffic. - private static readonly bool UsePico = - string.Equals(Environment.GetEnvironmentVariable("GENHTTP_IOXIDE_PARSER"), "pico", StringComparison.OrdinalIgnoreCase); - private static readonly ByteString KeepAliveValue = new("Keep-Alive"); [ThreadStatic] @@ -147,31 +141,15 @@ private static async ValueTask SendErrorAsync(IServer server, PipeWriter writer, } } - // Parses one request head, through whichever parser this process was started with. + // Parses one request head: full RFC validation plus smuggling hardening. private static bool TryParseRequest(ref ReadOnlySequence buffer, BinaryRequest into) - => UsePico ? TryParseRequestPico(ref buffer, into) : TryParseRequestGlyph11(ref buffer, into); - - // The default path: full RFC validation plus smuggling hardening. - private static bool TryParseRequestGlyph11(ref ReadOnlySequence buffer, BinaryRequest into) - { - if (!UltraHardenedParser.TryExtractFullHeaderValidated(ref buffer, into, Limits, out var bytesRead)) - { - return false; - } - - buffer = buffer.Slice(bytesRead + 1); - return true; - } - - // The benchmark path: picohttpparser, which validates only as far as picohttpparser does. - private static bool TryParseRequestPico(ref ReadOnlySequence buffer, BinaryRequest into) { - if (!PicoParser.TryParse(buffer, into, out var consumed)) + if (!UltraHardenedParser.TryParseRequestHeader(ref buffer, into, Limits, out var consumed)) { return false; } - buffer = buffer.Slice(consumed + 1); + buffer = buffer.Slice(consumed); return true; } diff --git a/Engine/Shared/GenHTTP.Engine.Shared.csproj b/Engine/Shared/GenHTTP.Engine.Shared.csproj index ccdbba89..e4afd6cc 100644 --- a/Engine/Shared/GenHTTP.Engine.Shared.csproj +++ b/Engine/Shared/GenHTTP.Engine.Shared.csproj @@ -14,7 +14,7 @@ - + diff --git a/Testing/Acceptance/Engine/Body/ChunkedFramingTests.cs b/Testing/Acceptance/Engine/Body/ChunkedFramingTests.cs index b1542ada..78584a33 100644 --- a/Testing/Acceptance/Engine/Body/ChunkedFramingTests.cs +++ b/Testing/Acceptance/Engine/Body/ChunkedFramingTests.cs @@ -194,7 +194,12 @@ public async Task TestTruncatedBodyFails() { var (_, framing) = await CreateAsync("5\r\nHel"); - var e = await Assert.ThrowsExactlyAsync(async () => await framing.ReadAsync(new byte[16], CancellationToken.None)); + var buffer = new byte[16]; + + Assert.AreEqual(3, await framing.ReadAsync(buffer, CancellationToken.None)); + Assert.AreEqual("Hel", Encoding.ASCII.GetString(buffer, 0, 3)); + + var e = await Assert.ThrowsExactlyAsync(async () => await framing.ReadAsync(buffer, CancellationToken.None)); Assert.AreEqual(ResponseStatus.BadRequest, e.Status); } diff --git a/Testing/Acceptance/Engine/Body/RequestBodyLifecycleTests.cs b/Testing/Acceptance/Engine/Body/RequestBodyLifecycleTests.cs index cfc38f8b..26d4efcd 100644 --- a/Testing/Acceptance/Engine/Body/RequestBodyLifecycleTests.cs +++ b/Testing/Acceptance/Engine/Body/RequestBodyLifecycleTests.cs @@ -9,8 +9,8 @@ using GenHTTP.Engine.Shared.Types; using Glyph11.Parser; +using Glyph11.Parser.FlexibleParser; using Glyph11.Parser.UltraHardened; -using Glyph11.Pico; using NSubstitute; @@ -453,9 +453,9 @@ public async Task TestContinueNotCarriedOver() var buffer = (await pipe.Reader.ReadAsync()).Buffer; - Assert.IsTrue(UltraHardenedParser.TryExtractFullHeaderValidated(ref buffer, request.Source, ParserLimits.Default, out var bytesRead)); + Assert.IsTrue(UltraHardenedParser.TryParseRequestHeader(ref buffer, request.Source, ParserLimits.Default, out var consumed)); - request.Apply(Substitute.For(), Substitute.For(), pipe.Reader, buffer.Slice(bytesRead + 1).Start, null, null, output.Writer); + request.Apply(Substitute.For(), Substitute.For(), pipe.Reader, buffer.Slice(consumed).Start, null, null, output.Writer); await request.GetBody()!.AsMemoryAsync(); @@ -520,14 +520,14 @@ public async Task TestWithoutConnection() if (validate) { - Assert.IsTrue(UltraHardenedParser.TryExtractFullHeaderValidated(ref buffer, request.Source, ParserLimits.Default, out var bytesRead)); - buffer = buffer.Slice(bytesRead + 1); + Assert.IsTrue(UltraHardenedParser.TryParseRequestHeader(ref buffer, request.Source, ParserLimits.Default, out var consumed)); + buffer = buffer.Slice(consumed); } else { - // picohttpparser does not validate the framing headers, so they reach the request as sent - Assert.IsTrue(PicoParser.TryParse(buffer, request.Source, out var consumed)); - buffer = buffer.Slice(consumed + 1); + // FlexibleParser does not validate the framing headers, so they reach the request as sent + Assert.IsTrue(FlexibleParser.TryParseRequestHeader(ref buffer, request.Source, out var consumed)); + buffer = buffer.Slice(consumed); } var endPoint = Substitute.For(); diff --git a/Testing/Acceptance/Engine/Compliance/ComplianceTests.cs b/Testing/Acceptance/Engine/Compliance/ComplianceTests.cs index d811e26d..a62610a2 100644 --- a/Testing/Acceptance/Engine/Compliance/ComplianceTests.cs +++ b/Testing/Acceptance/Engine/Compliance/ComplianceTests.cs @@ -12,7 +12,7 @@ public class ComplianceTests : WireTest [MultiEngineTest] public async Task TestNoMinorHttpVersion(ServerEngine engine) { - await TestAsync(["GET / HTTP/1", "Host: localhost"], "505", Layout.Create(), engine); + await TestAsync(["GET / HTTP/1", "Host: localhost"], "400", Layout.Create(), engine); } [TestMethod] diff --git a/Testing/Acceptance/Engine/WireTests.cs b/Testing/Acceptance/Engine/WireTests.cs index ae8d29b4..042dd0d2 100644 --- a/Testing/Acceptance/Engine/WireTests.cs +++ b/Testing/Acceptance/Engine/WireTests.cs @@ -18,7 +18,7 @@ public async Task TestNoHost() [TestMethod] public async Task TestUnsupportedProtocolVersion() { - await TestAsync("GET / HTTP/2.0\r\n", "Invalid HTTP version"); + await TestAsync("GET / HTTP/2.0\r\n", "Unsupported HTTP version"); } [TestMethod]