Open
Conversation
- Added `RegisterExpectContinueRoutes` to Routes.cs with 3 routes: - POST /expect/echo — echoes body after 100-continue - POST /expect/reject — returns 417 Expectation Failed - POST /expect/large — accepts large body with 100-continue flow - ServerFixture.RegisterRoutes calls `RegisterExpectContinueRoutes` - Build passes with 0 warnings
- Created `src/TurboHttp.IntegrationTests/H2/ConnectionIntegrationTests.cs` with 5 tests
- Tests cover: sequential reuse, concurrent multiplexing (Task.WhenAll), binary POST echo, multi-endpoint, and POST+GET on same connection
- All tests use `[Collection("H2")]` and `new Version(2, 0)`
- DisplayNames follow `Conn-H2-001` through `Conn-H2-005` pattern
- Build: 0 errors, 0 warnings
- All 5 tests pass
## Summary Add 12 integration tests covering `ExpectContinueBidiStage` end-to-end across all four HTTP version collections. ## Files Added - `src/TurboHttp.IntegrationTests/H11/ExpectContinueIntegrationTests.cs` — Expect-001 to Expect-003 - `src/TurboHttp.IntegrationTests/H10/ExpectContinueHIntegrationTests.cs` — Expect-H10-001 to Expect-H10-003 - `src/TurboHttp.IntegrationTests/H2/ExpectContinueIntegrationTests.cs` — Expect-H2-001 to Expect-H2-003 - `src/TurboHttp.IntegrationTests/TLS/ExpectContinueIntegrationTests.cs` — Expect-TLS-001 to Expect-TLS-003 ## Files Modified - `.maggus/features/feature_021.md` — TASK-021-008 acceptance criteria marked complete ## Test Scenarios (per version) 1. Small body (<1KB) — sent without Expect header, returns 200 echo 2. Large body (>1KB) — triggers 100-continue flow (HTTP/1.1 and TLS), returns 200 echo 3. Server rejection — 417 Expectation Failed returned **Notes:** - HTTP/1.0 and HTTP/2 do not support 100 Continue responses; the pipeline forwards the final response directly - `Expect100Policy` configured via `builder.Services.Configure<TurboClientDescriptor>` (no builder extension exists) - All 12 tests pass; build: 0 errors, 0 warnings
Add edge case integration tests for HTTP/1.1, HTTP/1.0, and HTTP/2 covering chunked trailers, large bodies, range requests, and version-specific quirks. - H11/EdgeCaseIntegrationTests.cs: 8 tests (Edge-001 through Edge-008) chunked trailers, exact boundaries, Content-MD5 trailer, chunked POST, 256KB large body, multi-value headers, form URL-encoded POST, range request - H10/EdgeCaseIntegrationTests.cs: 7 test cases (Edge-H10-001 through Edge-H10-005) large body via connection-close, POST echo, status codes (200/404/500), custom header echo, empty body - H2/EdgeCaseIntegrationTests.cs: 5 tests (Edge-H2-001 through Edge-H2-005) 60KB binary POST echo, 20 custom headers, large HPACK headers, stream priority route, echo-path with query string All 20 tests pass. Build is clean with zero warnings.
Add 11 concurrency integration tests for HTTP/1.1, HTTP/1.0, and HTTP/2 covering parallel requests and connection pool behavior under load. ## Changes - `src/TurboHttp.IntegrationTests/H11/ConcurrencyIntegrationTests.cs` (NEW) — 4 tests: parallel GETs, parallel POSTs, sequential burst 20, mixed methods concurrent - `src/TurboHttp.IntegrationTests/H10/ConcurrencyIntegrationTests.cs` (NEW) — 3 tests: parallel GETs, sequential burst 10, mixed GET+POST concurrent - `src/TurboHttp.IntegrationTests/H2/ConcurrencyIntegrationTests.cs` (NEW) — 4 tests: 10 parallel GETs multiplexed, 20 parallel stress, mixed GET+POST multiplexed, 8 concurrent different endpoints ## Notes HTTP/1.1 and HTTP/1.0 parallel tests use separate `ClientHelper` instances per concurrent request. This is semantically correct for HTTP/1.x (each connection handles one request at a time) and avoids the pipelining issue in `Http11Engine` where `Broadcast` sends requests to both encoder and correlation stage simultaneously.
9ad62e1 to
9e51032
Compare
9e51032 to
b72a509
Compare
- Add `WithRequestCompression()` and `WithExpectContinue()` extension methods to `TurboHttpClientBuilderExtensions` - Add `Routes.RegisterResilienceRoutes()` with 8 routes (content-length mismatch, corrupt gzip/br, truncated body, slow headers/body, invalid header, empty response) - Add `Routes.RegisterRequestCompressionRoutes()` with 4 routes (echo, verify-gzip, verify-deflate, verify-br) - Register both new route groups in `ServerFixture`
## Summary
Created `FeatureInteractionIntegrationTests.cs` with 7 tests verifying that
multiple BidiStages work correctly together in pipeline composition:
- Interaction-001: Redirect preserves cookies across hops (Cookie + Redirect)
- Interaction-002: Compressed response served from cache (Cache + Compression)
- Interaction-003: Retry after redirect target returns 503 (Redirect + Retry)
- Interaction-004: Cookie survives retry cycle (Cookie + Retry)
- Interaction-005: Vary header separates cache entries with cookies active
- Interaction-006: Redirect chain accumulates cookies across hops (3 hops)
- Interaction-007: Cache hit bypasses retry logic (Cache + Retry)
Added `RegisterInteractionRoutes()` to `Routes.cs` with 3 new server routes:
- `GET /interaction/cache-gzip` — gzip body + Cache-Control: max-age=3600
- `GET /interaction/cookie-hop/{hop}` — 3-hop redirect chain, each hop sets a cookie
- `GET /interaction/redirect-succeed-after/{n}/{key}` — 302 → /retry/succeed-after
All 104 H11 integration tests pass (7 new, 97 existing). Zero build warnings.
## Files Changed
- `src/TurboHttp.IntegrationTests/H11/FeatureInteractionIntegrationTests.cs` (NEW)
- `src/TurboHttp.IntegrationTests/Shared/Routes.cs` — added RegisterInteractionRoutes()
- `src/TurboHttp.IntegrationTests/Shared/ServerFixture.cs` — registered new route group
- `.maggus/features/feature_023.md` — TASK-023-002 acceptance criteria checked off
## Commit Message
```
TASK-023-002: Feature Interaction Tests – HTTP/1.1
7 integration tests verifying BidiStage pipeline composition:
Cookie+Redirect, Cache+Compression, Redirect+Retry, Cookie+Retry,
Vary+Cache, multi-hop Cookie+Redirect, Cache+Retry.
Added 3 new interaction server routes. All 104 H11 tests green.
```
Add feature interaction integration tests for HTTP/1.0, HTTP/2, and TLS (HTTPS), porting the 7 BidiStage interaction scenarios from the H11 reference implementation to all remaining protocol variants. Files added: - src/TurboHttp.IntegrationTests/H10/FeatureInteractionH10IntegrationTests.cs (7 tests) - src/TurboHttp.IntegrationTests/H2/FeatureInteractionH2IntegrationTests.cs (7 tests) - src/TurboHttp.IntegrationTests/TLS/FeatureInteractionTlsIntegrationTests.cs (7 tests) All 21 tests pass. Build zero warnings.
Add 24 resilience integration tests covering HTTP/1.0, HTTP/2, and TLS (8 per version). Tests cover: Content-Length mismatch, corrupt gzip/brotli passthrough, truncated body, slow headers within/over timeout, slow body, and empty response. HTTP/2 variants use `Assert.ThrowsAnyAsync<Exception>` for RST_STREAM-sensitive scenarios (001, 004, 007, 008) since H2 may surface errors as `HttpRequestException` rather than `OperationCanceledException`.
…mentation)
Add end-to-end integration tests for request body compression (ContentEncodingBidiStage
with CompressionPolicy) verifying the client correctly sends compressed bodies.
## Changes
- `src/TurboHttp.IntegrationTests/H11/RequestCompressionIntegrationTests.cs` (NEW)
- 6 tests covering gzip/deflate/br compression, below-threshold passthrough,
Content-Encoding header verification, and a full roundtrip (compressed request +
compressed response decompression)
- Uses `[Collection("H11")]`, `WithRequestCompression()` via `configure` callback
- DisplayNames follow `ReqCompress-001` pattern
- `src/TurboHttp.IntegrationTests/Shared/Routes.cs`
- Fix `/compress/verify-deflate` to use `ZLibStream` instead of `DeflateStream`
(client compresses deflate as zlib-wrapped per RFC 1950; server must match)
Add request compression integration tests for HTTP/1.0, HTTP/2, and TLS, mirroring the H11 reference implementation from TASK-023-006. Each variant has 6 tests covering gzip, deflate, brotli, threshold behaviour, header verification, and roundtrip. Files added: - src/TurboHttp.IntegrationTests/H10/RequestCompressionIntegrationTests.cs - src/TurboHttp.IntegrationTests/H2/RequestCompressionIntegrationTests.cs - src/TurboHttp.IntegrationTests/TLS/RequestCompressionIntegrationTests.cs
Implements integration tests for the custom handler pipeline (`TurboHandler`, `UseRequest()`, `UseResponse()`, `AddHandler<T>()`).
## Changes
- `src/TurboHttp.IntegrationTests/H11/HandlerPipelineIntegrationTests.cs` (NEW)
- 8 tests: Handler-001 through Handler-008
- Covers UseRequest injection, UseResponse mutation, AddHandler<T> typed handler,
multi-handler ordering, original-request capture, redirect pipeline, compression pipeline,
and cookie+handler interaction
- `src/TurboHttp.IntegrationTests/H2/HandlerPipelineIntegrationTests.cs` (NEW)
- 4 tests: Handler-H2-001, Handler-H2-003, Handler-H2-004, Handler-H2-006
- Protocol-agnostic handler verification over HTTP/2 cleartext
- `src/TurboHttp.IntegrationTests/Shared/Routes.cs`
- Added `/interaction/echo-all-headers` route: echoes X-* request headers as response headers
and Cookie header as `X-Received-Cookie` (required for Handler-008 cookie+handler test)
- `.maggus/features/feature_023.md`
- Checked all TASK-023-008 acceptance criteria
## Test Results
- H11: 8/8 pass
- H2: 4/4 pass
- Build: 0 errors, 0 warnings
08c5bf6 to
54f02df
Compare
…Flight Rewrites Http1XCorrelationStage to enforce strict HTTP/1.x back-pressure per RFC 9112 §9. Only one request is in-flight at a time; the next request is not pulled until its response has been delivered on OutResponse. Changes: - Http1XCorrelationShape: InReset inlet removed; shape is now 2 inlets + 2 outlets. - Http1XCorrelationStage.Logic: removed _pending queue, _waiting queue, _pipelineUnlocked flag, and PreStart(). Replaced with a single _inFlightRequest field. - Back-pressure contract: InRequest is pulled only when _inFlightRequest == null. onPush(InRequest) stores the request, emits StreamAcquireItem, then pulls InResponse. onPush(InResponse) matches the in-flight request, sets _inFlightRequest = null, and pushes the response — only then does the downstream demand trigger the next InRequest pull. - Completion: stage completes when both upstreams finish and _inFlightRequest == null, OR gracefully when InResponse closes while a request is in-flight (no deadlock). - Http10Engine.cs / Http11Engine.cs: removed the two dead InReset wiring lines (Source.Empty<NotUsed>() + b.From(resetSrc).To(correlation.InReset)). - Test suite updated: removed InReset wiring from all test helpers and inline graphs, removed tests that verified removed behavior (pipelining, InReset reconnect), updated descriptions to reflect strict serial model. 831/831 stream tests pass.
Feature 038 TASK-038-002 (Http1XCorrelationStage rewrite) previously removed: - The InReset inlet from Http1XCorrelationShape (2 inlets, 2 outlets now) - All Source.Empty<NotUsed>() wiring from Http10Engine and Http11Engine - All _inReset field references This task verifies the cleanup is complete: ✓ Http10Engine.cs: no Source.Empty or InReset wiring ✓ Http11Engine.cs: no Source.Empty or InReset wiring ✓ Grep for InReset/_inReset returns zero code results (comments preserved for context) ✓ dotnet build Release succeeds with zero errors/warnings Acceptance criteria met. Feature 038 architecture is now clean.
Add deterministic stream tests for the one-request-in-flight back-pressure contract of Http1XCorrelationStage (RFC 9112 §9.3). - bp-001: three serial requests flow in FIFO order with one StreamAcquireItem each - bp-002: two simultaneous requests — second is gated until first response arrives - bp-003: upstream completion mid-flight — stage waits for response before completing - bp-004: response arrives with no in-flight request — stage does not pull until new request All 4 tests use Akka.Streams TestKit manual probes; no Thread.Sleep.
All acceptance criteria verified: Build & Tests: • dotnet build: 0 errors, 0 warnings (1.34s) • Stream Tests: 835 passed, 0 failed (7s 551ms) • Unit Tests: 3652 passed, 0 failed (8s 907ms) • Integration Tests: 472 passed, 0 failed, 1 skipped (826s) Code Verification: • Dead code (_pipelineUnlocked, _inReset, InReset, _pending) fully removed • Source.Empty<NotUsed>() InReset wiring removed from Http10Engine and Http11Engine • BidiFlowFeedbackRaceTests: BidiLoop-001 through BidiLoop-004 all passing • Http1XCorrelationBackPressureTests: bp-001 through bp-004 all passing Feature 038 successfully delivers RFC 9112-compliant one-request-in-flight back-pressure for HTTP/1.x and fixes BidiFlow feedback-loop race conditions.
843c118 to
dd7dcde
Compare
Add BenchmarkBaseClass with ConcurrencyLevel/PayloadType/HttpVersion parameter sets, CreateKestrelUri/GeneratePayload/WarmupRequest helpers, and EngineBenchmarkConfig (p50/p95/p100 + req/sec columns). Unit tests verify deterministic payload generation and URI construction. TurboHttp.Tests now references TurboHttp.Benchmarks so benchmark helpers are testable from the existing test project.
…<byte> Refactor QpackStringCodec.Encode to write directly into caller-provided Span, encoding Huffman inline without temporary buffers. Both Encode overloads now accept ref Span<byte> and return int bytes written. Callers (QpackEncoder, QpackEncoderInstructionWriter) use temporary bridge methods until their own migration in TASK-033-005/006.
Convert HpackEncoder to the zero-copy ref Span<byte> buffer strategy, eliminating all IBufferWriter, ArrayPool, stackalloc, and new byte[] allocations. WriteInteger and WriteString write directly into caller- provided spans. Huffman encoding uses the reserve-max-space strategy (write into tail of output span, compare lengths, keep shorter). Convenience overload uses MemoryPool<byte>.Shared for its internal buffer.
…ol → ref Span<byte> Migrate all 6 public methods to accept `ref Span<byte>` and return `int` bytes written. Remove IBufferWriter<byte>, ArrayPool, and stackalloc from the file. String overloads now use MemoryPool for UTF-8 encoding. Update callers (QpackEncoder, QpackEncoderStreamStage) with bridge patterns and update all test files to the new API.
…Sync — IBufferWriter + ArrayPool → ref Span<byte> Convert 3 QPACK files from IBufferWriter<byte>/ArrayPool output to ref Span<byte> return-int pattern. QpackEncoder convenience overload uses MemoryPool<byte>.Shared. Update all callers across unit tests, stream tests, and security tests. Files changed: - QpackDecoderInstructionWriter.cs — 3 static methods → ref Span<byte> - QpackEncoder.cs — ArrayBufferWriter → IMemoryOwner + ref Span<byte> - QpackTableSync.cs — WriteInsertCountIncrement → ref Span<byte> - QpackDecoder.cs — caller update (ArrayBufferWriter → IMemoryOwner) - 7 test files updated to match new APIs
Remove `Encode(Http3Frame, IBufferWriter<byte>)` and `EncodeAll(IEnumerable<Http3Frame>, IBufferWriter<byte>)` overloads from Http3FrameEncoder, leaving only the Span-based API. Updated test callers to use Span overloads.
…emoryPool + ref Span<byte> Replace ArrayBufferWriter and ArrayPool<byte> with MemoryPool<byte>.Shared in Http2RequestEncoder for consistent buffer strategy across all encoders. - Header encoding: MemoryPool rental → ref Span<byte> → HPACK encode → copy to owned array - Body chunks: MemoryPool<byte>.Shared.Rent() replaces ArrayPool<byte>.Shared.Rent() - DataFrame: new constructor accepting IMemoryOwner<byte> + length for pooled body data - _rentedBodyBuffers (List<byte[]>) → _rentedBodyOwners (List<IMemoryOwner<byte>>) - ReturnRentedBuffers() now disposes IMemoryOwner instances instead of returning to ArrayPool
…pan<byte> Replace RecyclableStreams/MemoryStream + ToArray() body reading with MemoryPool<byte> rentals. QPACK header encoding now writes directly into a MemoryPool-rented span instead of using the convenience overload. Rented owners are tracked and disposed on the next Encode() call, matching the Http2RequestEncoder lifecycle pattern.
Http10Decoder already uses IMemoryOwner<byte> from MemoryPool<byte>.Shared for its remainder buffer. No ArrayPool references exist in the file. Verified: all 234 Http10 unit tests pass. Marked acceptance criteria complete.
Http11Decoder already uses IMemoryOwner<byte> from MemoryPool<byte>.Shared for both remainder and body buffers. No ArrayPool usage remains. All 112 Http11 decoder unit tests pass.
…moryPool migration complete Both files already use IMemoryOwner<byte> from MemoryPool<byte>.Shared for remainder buffers, combined working buffers, and frame payloads. No ArrayPool or .ToArray() remainder storage remains. Dispose patterns in Reset/IDisposable are correct. All 41 Http3 unit tests pass.
ContentEncodingEncoder.Compress and ContentEncodingDecoder.Decompress now return Stream instead of ArrayPool-rented (byte[], int) tuples. ContentEncodingBidiStage reads streams into MemoryPool<byte> buffers and PooledArrayContent is replaced by IMemoryOwner<byte>-backed PooledMemoryContent. All ArrayPool usage removed from the three content-encoding files.
…lete Migrate all Akka.Streams encoding stages from byte[]/new byte[] allocations to MemoryPool<byte>.Shared.Rent() with proper IMemoryOwner<byte> lifetime management. Stages affected: Http20EncoderStage, Http30ControlStreamPrefaceStage, Http30QpackEncoderPrefaceStage, QpackEncoderStreamStage.
…c in Http11Encoder Replace stackalloc temp buffers in WriteInt/WriteHex with in-place reverse pattern, writing directly into the caller-provided Span<byte>. Verified zero IBufferWriter<byte>, ArrayPool<byte>, ArrayBufferWriter, and stackalloc across Protocol and Streams layers. Build and all tests (3267 unit + 765 stream) pass.
Create formal ITransportFactory interface to enable transport-agnostic pipeline design, encapsulating TCP and QUIC connection creation for HTTP/1.x, 2.0, and 3.0 protocols. - ITransportFactory: Single Create() method returning Flow<IOutputItem, IInputItem, NotUsed> - TcpTransportFactory: Wraps IActorRef connectionManager + TurboClientOptions - QuicTransportFactory: Self-contained, parameterless constructor
- TransportRegistry now stores Dictionary<Version, ITransportFactory> instead of Func<Flow> - Register() accepts ITransportFactory; Get() replaces TryGet and throws on missing version - TryGet removed — single Get() method is the only API - ProtocolCoreBuilder updated to use Get() instead of TryGet pattern - DelegateTransportFactory adapter created in StreamTests for test convenience - All 8 test files updated to wrap lambdas in DelegateTransportFactory - Build verified: zero errors
Remove all Transport/ imports from ProtocolCoreBuilder by delegating transport creation entirely to TransportRegistry. The Build signature now takes only TurboClientOptions and TransportRegistry — no more transportStageFactory or testTransports parameters. CreateFlowForEndpoint uses a single code path for both production and test, eliminating the test-vs-production branch. Engine.CreateFlow production overload now builds a TransportRegistry with TcpTransportFactory and QuicTransportFactory before calling ProtocolCoreBuilder.
Replace Engine's two CreateFlow overloads (production + test) with a single transport-agnostic signature that takes TransportRegistry as the first parameter. Transport registration moves to ClientStreamOwner, removing all TCP/QUIC imports from Engine.cs.
ClientStreamOwner.MaterializeStream() now builds a TransportRegistry with TcpTransportFactory and QuicTransportFactory after creating ConnectionManagerActor, then passes it to Engine.CreateFlow(). This completes the transport-agnostic wiring so that Engine and ProtocolCoreBuilder have zero Transport/ imports.
… Design Documentation updated to match the actual implementation after Feature 034: - Protocol Engine Core diagram: replaced old `Partition(version)` topology with actual `GroupByRequestEndpoint → EndpointDispatchStage` flow - Builders table: `ProtocolCoreBuilder` now noted as transport-layer-agnostic (zero Transport imports, uses TransportRegistry for transport selection) - Transport Layer: new `ITransportFactory` plugin section documenting the formal contract for transport creation (TcpTransportFactory, QuicTransportFactory, custom transports via Registry) - Extension Points: added rows for `ITransportFactory` custom implementation and transport registry override (production + test injection unified) - Implementation Status: HTTP/3 row updated to reflect fully wired QUIC transport (now 75/100, up from 60/100) No other sections modified. All acceptance criteria verified complete.
… TurboHttp to TurboHTTP Two-step git mv (via temp name) to work around Windows case-insensitive filesystem. Renames all 5 project folders, the solution file, and all 5 csproj files.
…P rename Update all project references from TurboHttp to TurboHTTP across the solution file, all csproj ProjectReference paths, InternalsVisibleTo entries, NuGet metadata (Title, PackageProjectUrl, RepositoryUrl), and CI workflow PROJECT_PATH.
…oHttp to TurboHTTP - Replace `namespace TurboHttp.*` → `namespace TurboHTTP.*` across all 530 .cs source files - Replace `using TurboHttp.*` → `using TurboHTTP.*` across all .cs source files - Fix using-alias declarations (`using Alias = TurboHttp.X` → `using Alias = TurboHTTP.X`) - Fix `typeof(TurboHttp.X)` assembly fixture attributes in integration test collections - Update ActivitySource string literal in TurboHttpInstrumentation: `"TurboHttp"` → `"TurboHTTP"` - `dotnet build --configuration Release ./src/TurboHTTP.sln` succeeds with zero errors - Public type names (TurboHttpClient, TurboHttpException, TurboHttpMetrics, etc.) unchanged
Rename all "TurboHttp" project/brand references to "TurboHTTP" across documentation files while preserving C# type names (TurboHttpClient, ITurboHttpClientFactory, etc.). Updated files: - Root: README.md, CONTRIBUTING.md, ARCHITECTURE.md, CLAUDE.md - VitePress config (docs/.vitepress/config.ts) - SVG logos (aria-label attributes) - All docs/guide/*.md, docs/architecture/*.md, docs/api/index.md - docs/benchmarks/baseline_030.md (display text only, class names preserved) - docs/why/index.md, docs/index.md, docs/CLAUDE.md - LikeC4 model files, custom CSS, Vue components
Replace all TurboHttp project/namespace/folder references with TurboHTTP across all 5 agent definitions (.claude/agents/). Other feature files listed in the task plan no longer exist on disk.
…anding Replace all TurboHttp project/namespace references with TurboHTTP across 109 Obsidian vault notes and the .gitignore file. Type names (TurboHttpClient, TurboHttpMetrics, etc.) are intentionally preserved. The likec4 config was already correct from a prior task.
…erify full build Fixed string literal references (diagnostics source names, metrics meter name, request option keys, comments) that were missed during the namespace rename. All 3267 unit tests and 765 stream tests pass. Build compiles with zero errors.
dba8b4e to
85971c9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.