Skip to content

Commit b8262ce

Browse files
committed
TEMP
1 parent cb78633 commit b8262ce

37 files changed

Lines changed: 200 additions & 4177 deletions

src/TurboHTTP.AcceptanceTests/H11/RequestCompressionSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static byte[] GzipCompress(byte[] data)
111111
HttpRequestMessage request,
112112
Func<int, byte[], byte[]?> factory)
113113
{
114-
var fake = CreateScriptedConnection(factory);
114+
var fake = CreateAccumulatingScriptedConnection(factory);
115115
var flow = engine.Join(fake.AsFlow());
116116

117117
var tcs = new TaskCompletionSource<HttpResponseMessage>();

src/TurboHTTP.AcceptanceTests/TLS/RequestCompressionSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static byte[] GzipCompress(byte[] data)
111111
HttpRequestMessage request,
112112
Func<int, byte[], byte[]?> factory)
113113
{
114-
var fake = CreateScriptedConnection(factory);
114+
var fake = CreateAccumulatingScriptedConnection(factory);
115115
var flow = engine.Join(fake.AsFlow());
116116

117117
var tcs = new TaskCompletionSource<HttpResponseMessage>();

src/TurboHTTP.Tests.Shared/AcceptanceTestBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ internal async Task<HttpResponseMessage> SendScriptedAsync(
5858
HttpRequestMessage request,
5959
Func<int, byte[], byte[]?> responseFactory)
6060
{
61-
var stage = CreateScriptedConnection(responseFactory);
61+
var stage = CreateAccumulatingScriptedConnection(responseFactory);
6262
var flow = engine.CreateFlow().Join(stage.AsFlow());
6363

6464
var tcs = new TaskCompletionSource<HttpResponseMessage>();
@@ -69,7 +69,7 @@ internal async Task<HttpResponseMessage> SendScriptedAsync(
6969
var response = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
7070

7171
var rawBuilder = new StringBuilder();
72-
foreach (var outbound in stage.ReceivedOutbound)
72+
while (stage.TryGetOutbound(out var outbound))
7373
{
7474
if (outbound is TransportData { Buffer: var buf })
7575
{

src/TurboHTTP.Tests.Shared/EngineTestBase.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,68 @@ internal static TestConnectionStage CreateScriptedConnection(Func<int, byte[], b
6363
return stage;
6464
}
6565

66+
internal static TestConnectionStage CreateAccumulatingScriptedConnection(Func<int, byte[], byte[]?> responseFactory)
67+
{
68+
var index = 0;
69+
var accumulated = new List<byte>();
70+
71+
static (int HeaderEnd, int ContentLength) TryParseRequest(List<byte> bytes)
72+
{
73+
var arr = bytes.ToArray();
74+
var headerEnd = arr.AsSpan().IndexOf("\r\n\r\n"u8);
75+
if (headerEnd < 0)
76+
{
77+
return (-1, 0);
78+
}
79+
80+
var headerStr = Encoding.Latin1.GetString(arr, 0, headerEnd);
81+
var contentLength = 0;
82+
foreach (var line in headerStr.Split("\r\n"))
83+
{
84+
if (line.StartsWith("Content-Length:", StringComparison.OrdinalIgnoreCase))
85+
{
86+
int.TryParse(line["Content-Length:".Length..].Trim(), out contentLength);
87+
break;
88+
}
89+
}
90+
91+
return (headerEnd, contentLength);
92+
}
93+
94+
var stage = new TestConnectionStageBuilder()
95+
.AutoConnect()
96+
.OnOutbound<TransportData>((data, ctx) =>
97+
{
98+
accumulated.AddRange(data.Buffer.Span.ToArray());
99+
100+
var (headerEnd, contentLength) = TryParseRequest(accumulated);
101+
if (headerEnd < 0)
102+
{
103+
return;
104+
}
105+
106+
var totalExpected = headerEnd + 4 + contentLength;
107+
if (accumulated.Count < totalExpected)
108+
{
109+
return;
110+
}
111+
112+
var completeRequest = accumulated.GetRange(0, totalExpected).ToArray();
113+
accumulated.RemoveRange(0, totalExpected);
114+
115+
var response = responseFactory(index++, completeRequest);
116+
if (response is null)
117+
{
118+
ctx.Complete();
119+
return;
120+
}
121+
122+
ctx.Push(new TransportData(response));
123+
})
124+
.Build();
125+
return stage;
126+
}
127+
66128
internal static TestConnectionStage CreateScriptedConnectionWithClose(Func<int, byte[], byte[]?> responseFactory)
67129
{
68130
var index = 0;

src/TurboHTTP.Tests/Http11/Http11ClientEncoderSpec.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void Encode_should_add_host_header()
3535
}
3636

3737
[Fact(Timeout = 5000)]
38-
public void Encode_should_handle_post_with_body()
38+
public void Encode_should_write_headers_with_content_length()
3939
{
4040
var request = new HttpRequestMessage(HttpMethod.Post, "http://example.com/")
4141
{
@@ -49,7 +49,6 @@ public void Encode_should_handle_post_with_body()
4949
var result = Encoding.ASCII.GetString(buffer, 0, written);
5050
Assert.Contains("POST / HTTP/1.1", result);
5151
Assert.Contains("Content-Length: 9", result);
52-
Assert.DoesNotContain("test body", result);
5352
}
5453

5554
[Fact(Timeout = 5000)]

src/TurboHTTP.Tests/Protocol/LineBased/BodyEncoderFactorySpec.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,15 @@ public override void Flush() { }
2222
[Fact(Timeout = 5000)]
2323
public void Create_should_return_null_for_null_content()
2424
{
25-
var encoder = BodyEncoderFactory.Create(null, HttpVersion.Version11, 65_536);
25+
var encoder = BodyEncoderFactory.Create(null, HttpVersion.Version11);
2626
Assert.Null(encoder);
2727
}
2828

2929
[Fact(Timeout = 5000)]
30-
public void Create_should_return_buffered_for_http11_small_body()
30+
public void Create_should_return_streamed_for_http11_known_length()
3131
{
3232
var content = new ByteArrayContent(new byte[100]);
33-
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version11, 65_536);
34-
Assert.IsType<ContentLengthBufferedBodyEncoder>(encoder);
35-
encoder.Dispose();
36-
}
37-
38-
[Fact(Timeout = 5000)]
39-
public void Create_should_return_streamed_for_http11_large_body()
40-
{
41-
var content = new ByteArrayContent(new byte[200_000]);
42-
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version11, 65_536);
33+
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version11);
4334
Assert.IsType<ContentLengthStreamedBodyEncoder>(encoder);
4435
encoder.Dispose();
4536
}
@@ -51,7 +42,7 @@ public void Create_should_return_chunked_and_set_header_for_http11_unknown_lengt
5142
var content = new StreamContent(new NonSeekableStream());
5243
request.Content = content;
5344

54-
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version11, 65_536, request.Headers);
45+
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version11, request.Headers);
5546

5647
Assert.IsType<ChunkedBodyEncoder>(encoder);
5748
Assert.True(request.Headers.TransferEncodingChunked);
@@ -62,7 +53,7 @@ public void Create_should_return_chunked_and_set_header_for_http11_unknown_lengt
6253
public void Create_should_return_buffered_for_http10_known_length()
6354
{
6455
var content = new ByteArrayContent(new byte[200_000]);
65-
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version10, 65_536);
56+
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version10);
6657
Assert.IsType<ContentLengthBufferedBodyEncoder>(encoder);
6758
encoder.Dispose();
6859
}
@@ -71,7 +62,7 @@ public void Create_should_return_buffered_for_http10_known_length()
7162
public void Create_should_return_buffered_for_http10_unknown_length()
7263
{
7364
var content = new StreamContent(new MemoryStream(new byte[100]));
74-
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version10, 65_536);
65+
var encoder = BodyEncoderFactory.Create(content, HttpVersion.Version10);
7566
Assert.IsType<ContentLengthBufferedBodyEncoder>(encoder);
7667
encoder.Dispose();
7768
}

src/TurboHTTP.Tests/Protocol/Syntax/Http10/HeaderRouterSpec.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void Apply_should_route_content_headers_to_content()
2222
}
2323

2424
[Fact(Timeout = 5000)]
25-
public void Apply_should_strip_HopByHop_headers()
25+
public void Apply_should_include_hop_by_hop_headers()
2626
{
2727
var parsed = new HeaderCollection();
2828
parsed.Add("Connection", "close");
@@ -32,8 +32,8 @@ public void Apply_should_strip_HopByHop_headers()
3232
var msg = new HttpResponseMessage { Content = new ByteArrayContent([]) };
3333
HeaderRouter.ApplyToResponse(msg, parsed);
3434

35-
Assert.False(msg.Headers.Contains("Connection"));
36-
Assert.False(msg.Headers.Contains("Keep-Alive"));
35+
Assert.True(msg.Headers.Contains("Connection"));
36+
Assert.True(msg.Headers.Contains("Keep-Alive"));
3737
Assert.True(msg.Headers.Contains("X-Custom"));
3838
}
3939

src/TurboHTTP.Tests/Security/Http11FuzzBodySpec.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,52 @@
11
using System.Text;
2-
using Decoder = TurboHTTP.Protocol.Http11.Decoder;
2+
using TurboHTTP.Protocol.Syntax;
3+
using TurboHTTP.Protocol.Syntax.Http11.Client;
4+
using TurboHTTP.Protocol.Syntax.Http11.Options;
35

46
namespace TurboHTTP.Tests.Security;
57

68
public sealed class Http11FuzzBodySpec
79
{
810
private const int IterationsPerSeed = 100;
911
private const long MaxBytesPerIteration = 1_048_576;
12+
private static readonly Http11ClientDecoderOptions DecoderOptions = Http11ClientDecoderOptions.Default;
1013

11-
private static void AssertDecodeNeverCrashes(Decoder decoder, ReadOnlyMemory<byte> data)
14+
private static void AssertDecodeNeverCrashes(Http11ClientDecoder decoder, ReadOnlyMemory<byte> data)
1215
{
1316
try
1417
{
15-
decoder.TryDecodeHeaders(data, out var response, out _, out _);
16-
response?.Dispose();
18+
var outcome = decoder.Feed(data.Span, requestMethodWasHead: false, out _);
19+
if (outcome == DecodeOutcome.Complete)
20+
{
21+
var response = decoder.GetResponse();
22+
response.Dispose();
23+
decoder.Reset();
24+
}
1725
}
1826
catch (HttpProtocolException)
1927
{
20-
// Expected — malformed input correctly classified by our decoder.
2128
}
2229
catch (FormatException)
2330
{
24-
// Expected — .NET's HttpResponseMessage rejects invalid reason phrases
25-
// (newlines, NUL) that random bytes produce. Not a decoder bug.
2631
}
2732
}
2833

29-
private static void AssertDecodeEofNeverCrashes(Decoder decoder)
34+
private static void AssertDecodeEofNeverCrashes(Http11ClientDecoder decoder)
3035
{
3136
try
3237
{
33-
decoder.TryDecodeEof(out var response);
34-
response?.Dispose();
38+
if (decoder.SignalEof() || decoder.IsBodyComplete)
39+
{
40+
var response = decoder.GetResponse();
41+
response?.Dispose();
42+
decoder.Reset();
43+
}
3544
}
3645
catch (HttpProtocolException)
3746
{
38-
// Expected — malformed input correctly classified by our decoder.
3947
}
4048
catch (FormatException)
4149
{
42-
// Expected — .NET's HttpResponseMessage rejects invalid reason phrases.
4350
}
4451
}
4552

@@ -102,7 +109,7 @@ public void Http11Decoder_should_handle_mixed_transfer_encoding_and_content_leng
102109
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
103110
var allocBefore = GC.GetAllocatedBytesForCurrentThread();
104111

105-
using var decoder = new Decoder();
112+
var decoder = new Http11ClientDecoder(DecoderOptions);
106113

107114
var sb = new StringBuilder();
108115
sb.Append("HTTP/1.1 200 OK\r\n");
@@ -151,7 +158,7 @@ public void Http11Decoder_should_handle_extremely_large_content_length_without_o
151158
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
152159
var allocBefore = GC.GetAllocatedBytesForCurrentThread();
153160

154-
using var decoder = new Decoder();
161+
var decoder = new Http11ClientDecoder(DecoderOptions);
155162

156163
var claimedLengths = new[]
157164
{
@@ -207,7 +214,7 @@ public void Http11Decoder_should_handle_connection_close_with_trailing_data(int
207214
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
208215
var allocBefore = GC.GetAllocatedBytesForCurrentThread();
209216

210-
using var decoder = new Decoder();
217+
var decoder = new Http11ClientDecoder(DecoderOptions);
211218

212219
var body = "Hello, World!";
213220
var validResponse = BuildValidResponse(200, "OK", body,
@@ -250,7 +257,7 @@ public void Http11Decoder_should_maintain_consistent_state_with_fragmented_deliv
250257
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
251258
var allocBefore = GC.GetAllocatedBytesForCurrentThread();
252259

253-
using var decoder = new Decoder();
260+
var decoder = new Http11ClientDecoder(DecoderOptions);
254261

255262
byte[] fullResponse;
256263
if (rng.Next(2) == 0)

0 commit comments

Comments
 (0)